]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
selftests/bpf: Extend distilled BTF tests to cover BTF relocation
authorAlan Maguire <alan.maguire@oracle.com>
Thu, 13 Jun 2024 09:50:09 +0000 (10:50 +0100)
committerAndrii Nakryiko <andrii@kernel.org>
Mon, 17 Jun 2024 21:38:31 +0000 (14:38 -0700)
Ensure relocated BTF looks as expected; in this case identical to
original split BTF, with a few duplicate anonymous types added to
split BTF by the relocation process.  Also add relocation tests
for edge cases like missing type in base BTF and multiple types
of the same name.

Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/bpf/20240613095014.357981-5-alan.maguire@oracle.com
tools/testing/selftests/bpf/prog_tests/btf_distill.c

index 5c3a3874796200a727fe0a0dee1bf0fda3c6d402..bfbe795823a29e6dfaf713c2e60a10da821d77e2 100644 (file)
@@ -217,7 +217,277 @@ static void test_distilled_base(void)
                "\t'p1' type_id=1",
                "[25] ARRAY '(anon)' type_id=1 index_type_id=1 nr_elems=3");
 
+       if (!ASSERT_EQ(btf__relocate(btf4, btf1), 0, "relocate_split"))
+               goto cleanup;
+
+       VALIDATE_RAW_BTF(
+               btf4,
+               "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+               "[2] PTR '(anon)' type_id=1",
+               "[3] STRUCT 's1' size=8 vlen=1\n"
+               "\t'f1' type_id=2 bits_offset=0",
+               "[4] STRUCT '(anon)' size=12 vlen=2\n"
+               "\t'f1' type_id=1 bits_offset=0\n"
+               "\t'f2' type_id=3 bits_offset=32",
+               "[5] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none)",
+               "[6] UNION 'u1' size=12 vlen=2\n"
+               "\t'f1' type_id=1 bits_offset=0\n"
+               "\t'f2' type_id=2 bits_offset=0",
+               "[7] UNION '(anon)' size=4 vlen=1\n"
+               "\t'f1' type_id=1 bits_offset=0",
+               "[8] ENUM 'e1' encoding=UNSIGNED size=4 vlen=1\n"
+               "\t'v1' val=1",
+               "[9] ENUM '(anon)' encoding=UNSIGNED size=4 vlen=1\n"
+               "\t'av1' val=2",
+               "[10] ENUM64 'e641' encoding=SIGNED size=8 vlen=1\n"
+               "\t'v1' val=1024",
+               "[11] ENUM64 '(anon)' encoding=SIGNED size=8 vlen=1\n"
+               "\t'v1' val=1025",
+               "[12] STRUCT 'unneeded' size=4 vlen=1\n"
+               "\t'f1' type_id=1 bits_offset=0",
+               "[13] STRUCT 'embedded' size=4 vlen=1\n"
+               "\t'f1' type_id=1 bits_offset=0",
+               "[14] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
+               "\t'p1' type_id=1",
+               "[15] ARRAY '(anon)' type_id=1 index_type_id=1 nr_elems=3",
+               "[16] STRUCT 'from_proto' size=4 vlen=1\n"
+               "\t'f1' type_id=1 bits_offset=0",
+               "[17] UNION 'u1' size=4 vlen=1\n"
+               "\t'f1' type_id=1 bits_offset=0",
+               "[18] PTR '(anon)' type_id=3",
+               "[19] PTR '(anon)' type_id=30",
+               "[20] CONST '(anon)' type_id=6",
+               "[21] RESTRICT '(anon)' type_id=31",
+               "[22] VOLATILE '(anon)' type_id=8",
+               "[23] TYPEDEF 'et' type_id=32",
+               "[24] CONST '(anon)' type_id=10",
+               "[25] PTR '(anon)' type_id=33",
+               "[26] STRUCT 'with_embedded' size=4 vlen=1\n"
+               "\t'f1' type_id=13 bits_offset=0",
+               "[27] FUNC 'fn' type_id=34 linkage=static",
+               "[28] TYPEDEF 'arraytype' type_id=35",
+               "[29] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
+               "\t'p1' type_id=16",
+               /* below here are (duplicate) anon base types added by distill
+                * process to split BTF.
+                */
+               "[30] STRUCT '(anon)' size=12 vlen=2\n"
+               "\t'f1' type_id=1 bits_offset=0\n"
+               "\t'f2' type_id=3 bits_offset=32",
+               "[31] UNION '(anon)' size=4 vlen=1\n"
+               "\t'f1' type_id=1 bits_offset=0",
+               "[32] ENUM '(anon)' encoding=UNSIGNED size=4 vlen=1\n"
+               "\t'av1' val=2",
+               "[33] ENUM64 '(anon)' encoding=SIGNED size=8 vlen=1\n"
+               "\t'v1' val=1025",
+               "[34] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
+               "\t'p1' type_id=1",
+               "[35] ARRAY '(anon)' type_id=1 index_type_id=1 nr_elems=3");
+
+cleanup:
+       btf__free(btf4);
+       btf__free(btf3);
+       btf__free(btf2);
+       btf__free(btf1);
+}
+
+/* ensure we can cope with multiple types with the same name in
+ * distilled base BTF.  In this case because sizes are different,
+ * we can still disambiguate them.
+ */
+static void test_distilled_base_multi(void)
+{
+       struct btf *btf1 = NULL, *btf2 = NULL, *btf3 = NULL, *btf4 = NULL;
+
+       btf1 = btf__new_empty();
+       if (!ASSERT_OK_PTR(btf1, "empty_main_btf"))
+               return;
+       btf__add_int(btf1, "int", 4, BTF_INT_SIGNED);   /* [1] int */
+       btf__add_int(btf1, "int", 8, BTF_INT_SIGNED);   /* [2] int */
+       VALIDATE_RAW_BTF(
+               btf1,
+               "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+               "[2] INT 'int' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED");
+       btf2 = btf__new_empty_split(btf1);
+       if (!ASSERT_OK_PTR(btf2, "empty_split_btf"))
+               goto cleanup;
+       btf__add_ptr(btf2, 1);
+       btf__add_const(btf2, 2);
+       VALIDATE_RAW_BTF(
+               btf2,
+               "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+               "[2] INT 'int' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED",
+               "[3] PTR '(anon)' type_id=1",
+               "[4] CONST '(anon)' type_id=2");
+       if (!ASSERT_EQ(0, btf__distill_base(btf2, &btf3, &btf4),
+                      "distilled_base") ||
+           !ASSERT_OK_PTR(btf3, "distilled_base") ||
+           !ASSERT_OK_PTR(btf4, "distilled_split") ||
+           !ASSERT_EQ(3, btf__type_cnt(btf3), "distilled_base_type_cnt"))
+               goto cleanup;
+       VALIDATE_RAW_BTF(
+               btf3,
+               "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+               "[2] INT 'int' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED");
+       if (!ASSERT_EQ(btf__relocate(btf4, btf1), 0, "relocate_split"))
+               goto cleanup;
+
+       VALIDATE_RAW_BTF(
+               btf4,
+               "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+               "[2] INT 'int' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED",
+               "[3] PTR '(anon)' type_id=1",
+               "[4] CONST '(anon)' type_id=2");
+
+cleanup:
+       btf__free(btf4);
+       btf__free(btf3);
+       btf__free(btf2);
+       btf__free(btf1);
+}
+
+/* If a needed type is not present in the base BTF we wish to relocate
+ * with, btf__relocate() should error our.
+ */
+static void test_distilled_base_missing_err(void)
+{
+       struct btf *btf1 = NULL, *btf2 = NULL, *btf3 = NULL, *btf4 = NULL, *btf5 = NULL;
+
+       btf1 = btf__new_empty();
+       if (!ASSERT_OK_PTR(btf1, "empty_main_btf"))
+               return;
+       btf__add_int(btf1, "int", 4, BTF_INT_SIGNED);   /* [1] int */
+       btf__add_int(btf1, "int", 8, BTF_INT_SIGNED);   /* [2] int */
+       VALIDATE_RAW_BTF(
+               btf1,
+               "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+               "[2] INT 'int' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED");
+       btf2 = btf__new_empty_split(btf1);
+       if (!ASSERT_OK_PTR(btf2, "empty_split_btf"))
+               goto cleanup;
+       btf__add_ptr(btf2, 1);
+       btf__add_const(btf2, 2);
+       VALIDATE_RAW_BTF(
+               btf2,
+               "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+               "[2] INT 'int' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED",
+               "[3] PTR '(anon)' type_id=1",
+               "[4] CONST '(anon)' type_id=2");
+       if (!ASSERT_EQ(0, btf__distill_base(btf2, &btf3, &btf4),
+                      "distilled_base") ||
+           !ASSERT_OK_PTR(btf3, "distilled_base") ||
+           !ASSERT_OK_PTR(btf4, "distilled_split") ||
+           !ASSERT_EQ(3, btf__type_cnt(btf3), "distilled_base_type_cnt"))
+               goto cleanup;
+       VALIDATE_RAW_BTF(
+               btf3,
+               "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+               "[2] INT 'int' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED");
+       btf5 = btf__new_empty();
+       if (!ASSERT_OK_PTR(btf5, "empty_reloc_btf"))
+               return;
+       btf__add_int(btf5, "int", 4, BTF_INT_SIGNED);   /* [1] int */
+       VALIDATE_RAW_BTF(
+               btf5,
+               "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED");
+       ASSERT_EQ(btf__relocate(btf4, btf5), -EINVAL, "relocate_split");
+
+cleanup:
+       btf__free(btf5);
+       btf__free(btf4);
+       btf__free(btf3);
+       btf__free(btf2);
+       btf__free(btf1);
+}
+
+/* With 2 types of same size in distilled base BTF, relocation should
+ * fail as we have no means to choose between them.
+ */
+static void test_distilled_base_multi_err(void)
+{
+       struct btf *btf1 = NULL, *btf2 = NULL, *btf3 = NULL, *btf4 = NULL;
+
+       btf1 = btf__new_empty();
+       if (!ASSERT_OK_PTR(btf1, "empty_main_btf"))
+               return;
+       btf__add_int(btf1, "int", 4, BTF_INT_SIGNED);   /* [1] int */
+       btf__add_int(btf1, "int", 4, BTF_INT_SIGNED);   /* [2] int */
+       VALIDATE_RAW_BTF(
+               btf1,
+               "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+               "[2] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED");
+       btf2 = btf__new_empty_split(btf1);
+       if (!ASSERT_OK_PTR(btf2, "empty_split_btf"))
+               goto cleanup;
+       btf__add_ptr(btf2, 1);
+       btf__add_const(btf2, 2);
+       VALIDATE_RAW_BTF(
+               btf2,
+               "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+               "[2] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+               "[3] PTR '(anon)' type_id=1",
+               "[4] CONST '(anon)' type_id=2");
+       if (!ASSERT_EQ(0, btf__distill_base(btf2, &btf3, &btf4),
+                      "distilled_base") ||
+           !ASSERT_OK_PTR(btf3, "distilled_base") ||
+           !ASSERT_OK_PTR(btf4, "distilled_split") ||
+           !ASSERT_EQ(3, btf__type_cnt(btf3), "distilled_base_type_cnt"))
+               goto cleanup;
+       VALIDATE_RAW_BTF(
+               btf3,
+               "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+               "[2] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED");
+       ASSERT_EQ(btf__relocate(btf4, btf1), -EINVAL, "relocate_split");
+cleanup:
+       btf__free(btf4);
+       btf__free(btf3);
+       btf__free(btf2);
+       btf__free(btf1);
+}
+
+/* With 2 types of same size in base BTF, relocation should
+ * fail as we have no means to choose between them.
+ */
+static void test_distilled_base_multi_err2(void)
+{
+       struct btf *btf1 = NULL, *btf2 = NULL, *btf3 = NULL, *btf4 = NULL, *btf5 = NULL;
+
+       btf1 = btf__new_empty();
+       if (!ASSERT_OK_PTR(btf1, "empty_main_btf"))
+               return;
+       btf__add_int(btf1, "int", 4, BTF_INT_SIGNED);   /* [1] int */
+       VALIDATE_RAW_BTF(
+               btf1,
+               "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED");
+       btf2 = btf__new_empty_split(btf1);
+       if (!ASSERT_OK_PTR(btf2, "empty_split_btf"))
+               goto cleanup;
+       btf__add_ptr(btf2, 1);
+       VALIDATE_RAW_BTF(
+               btf2,
+               "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+               "[2] PTR '(anon)' type_id=1");
+       if (!ASSERT_EQ(0, btf__distill_base(btf2, &btf3, &btf4),
+                      "distilled_base") ||
+           !ASSERT_OK_PTR(btf3, "distilled_base") ||
+           !ASSERT_OK_PTR(btf4, "distilled_split") ||
+           !ASSERT_EQ(2, btf__type_cnt(btf3), "distilled_base_type_cnt"))
+               goto cleanup;
+       VALIDATE_RAW_BTF(
+               btf3,
+               "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED");
+       btf5 = btf__new_empty();
+       if (!ASSERT_OK_PTR(btf5, "empty_reloc_btf"))
+               return;
+       btf__add_int(btf5, "int", 4, BTF_INT_SIGNED);   /* [1] int */
+       btf__add_int(btf5, "int", 4, BTF_INT_SIGNED);   /* [2] int */
+       VALIDATE_RAW_BTF(
+               btf5,
+               "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+               "[2] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED");
+       ASSERT_EQ(btf__relocate(btf4, btf5), -EINVAL, "relocate_split");
 cleanup:
+       btf__free(btf5);
        btf__free(btf4);
        btf__free(btf3);
        btf__free(btf2);
@@ -269,6 +539,14 @@ void test_btf_distill(void)
 {
        if (test__start_subtest("distilled_base"))
                test_distilled_base();
+       if (test__start_subtest("distilled_base_multi"))
+               test_distilled_base_multi();
+       if (test__start_subtest("distilled_base_missing_err"))
+               test_distilled_base_missing_err();
+       if (test__start_subtest("distilled_base_multi_err"))
+               test_distilled_base_multi_err();
+       if (test__start_subtest("distilled_base_multi_err2"))
+               test_distilled_base_multi_err2();
        if (test__start_subtest("distilled_base_vmlinux"))
                test_distilled_base_vmlinux();
 }