selftests/bpf: Add distilled BTF test about marking BTF_IS_EMBEDDED
authorPu Lehui <pulehui@huawei.com>
Wed, 15 Jan 2025 10:02:41 +0000 (10:02 +0000)
committerAndrii Nakryiko <andrii@kernel.org>
Thu, 16 Jan 2025 23:34:18 +0000 (15:34 -0800)
When redirecting the split BTF to the vmlinux base BTF, we need to mark
the distilled base struct/union members of split BTF structs/unions in
id_map with BTF_IS_EMBEDDED. This indicates that these types must match
both name and size later. So if a needed composite type, which is the
member of composite type in the split BTF, has a different size in the
base BTF we wish to relocate with, btf__relocate() should error out.

Signed-off-by: Pu Lehui <pulehui@huawei.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20250115100241.4171581-4-pulehui@huaweicloud.com
tools/testing/selftests/bpf/prog_tests/btf_distill.c

index b72b966df77b90b2b6a5175aaa36d4cb5fab3c16..fb67ae195a7385c82c828fc2b718dac8485326b8 100644 (file)
@@ -601,6 +601,76 @@ cleanup:
        btf__free(base);
 }
 
+/* If a needed composite type, which is the member of composite type
+ * in the split BTF, has a different size in the base BTF we wish to
+ * relocate with, btf__relocate() should error out.
+ */
+static void test_distilled_base_embedded_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_struct(btf1, "s1", 4);                 /* [2] struct s1 { */
+       btf__add_field(btf1, "f1", 1, 0, 0);            /*      int f1; */
+                                                       /* } */
+       VALIDATE_RAW_BTF(
+               btf1,
+               "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+               "[2] STRUCT 's1' size=4 vlen=1\n"
+               "\t'f1' type_id=1 bits_offset=0");
+
+       btf2 = btf__new_empty_split(btf1);
+       if (!ASSERT_OK_PTR(btf2, "empty_split_btf"))
+               goto cleanup;
+
+       btf__add_struct(btf2, "with_embedded", 8);      /* [3] struct with_embedded { */
+       btf__add_field(btf2, "e1", 2, 0, 0);            /*      struct s1 e1; */
+                                                       /* } */
+
+       VALIDATE_RAW_BTF(
+               btf2,
+               "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+               "[2] STRUCT 's1' size=4 vlen=1\n"
+               "\t'f1' type_id=1 bits_offset=0",
+               "[3] STRUCT 'with_embedded' size=8 vlen=1\n"
+               "\t'e1' type_id=2 bits_offset=0");
+
+       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(
+               btf4,
+               "[1] STRUCT 's1' size=4 vlen=0",
+               "[2] STRUCT 'with_embedded' size=8 vlen=1\n"
+               "\t'e1' type_id=1 bits_offset=0");
+
+       btf5 = btf__new_empty();
+       if (!ASSERT_OK_PTR(btf5, "empty_reloc_btf"))
+               goto cleanup;
+
+       btf__add_int(btf5, "int", 4, BTF_INT_SIGNED);   /* [1] int */
+       /* struct with the same name but different size */
+       btf__add_struct(btf5, "s1", 8);                 /* [2] struct s1 { */
+       btf__add_field(btf5, "f1", 1, 0, 0);            /*      int f1; */
+                                                       /* } */
+
+       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);
+}
+
 void test_btf_distill(void)
 {
        if (test__start_subtest("distilled_base"))
@@ -613,6 +683,8 @@ void test_btf_distill(void)
                test_distilled_base_multi_err();
        if (test__start_subtest("distilled_base_multi_err2"))
                test_distilled_base_multi_err2();
+       if (test__start_subtest("distilled_base_embedded_err"))
+               test_distilled_base_embedded_err();
        if (test__start_subtest("distilled_base_vmlinux"))
                test_distilled_base_vmlinux();
        if (test__start_subtest("distilled_endianness"))