| 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* Copyright (c) 2020 Facebook */ |
| 3 | #include <test_progs.h> |
| 4 | #include <bpf/btf.h> |
| 5 | #include "btf_helpers.h" |
| 6 | |
| 7 | static void test_split_simple() { |
| 8 | const struct btf_type *t; |
| 9 | struct btf *btf1, *btf2; |
| 10 | int str_off, err; |
| 11 | |
| 12 | btf1 = btf__new_empty(); |
| 13 | if (!ASSERT_OK_PTR(btf1, "empty_main_btf" )) |
| 14 | return; |
| 15 | |
| 16 | btf__set_pointer_size(btf: btf1, ptr_sz: 8); /* enforce 64-bit arch */ |
| 17 | |
| 18 | btf__add_int(btf: btf1, name: "int" , byte_sz: 4, BTF_INT_SIGNED); /* [1] int */ |
| 19 | btf__add_ptr(btf: btf1, ref_type_id: 1); /* [2] ptr to int */ |
| 20 | btf__add_struct(btf: btf1, name: "s1" , sz: 4); /* [3] struct s1 { */ |
| 21 | btf__add_field(btf: btf1, name: "f1" , field_type_id: 1, bit_offset: 0, bit_size: 0); /* int f1; */ |
| 22 | /* } */ |
| 23 | |
| 24 | VALIDATE_RAW_BTF( |
| 25 | btf1, |
| 26 | "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED" , |
| 27 | "[2] PTR '(anon)' type_id=1" , |
| 28 | "[3] STRUCT 's1' size=4 vlen=1\n" |
| 29 | "\t'f1' type_id=1 bits_offset=0" ); |
| 30 | |
| 31 | ASSERT_STREQ(btf_type_c_dump(btf1), "\ |
| 32 | struct s1 {\n\ |
| 33 | int f1;\n\ |
| 34 | };\n\n" , "c_dump" ); |
| 35 | |
| 36 | btf2 = btf__new_empty_split(base_btf: btf1); |
| 37 | if (!ASSERT_OK_PTR(btf2, "empty_split_btf" )) |
| 38 | goto cleanup; |
| 39 | |
| 40 | /* pointer size should be "inherited" from main BTF */ |
| 41 | ASSERT_EQ(btf__pointer_size(btf: btf2), 8, "inherit_ptr_sz" ); |
| 42 | |
| 43 | str_off = btf__find_str(btf: btf2, s: "int" ); |
| 44 | ASSERT_NEQ(str_off, -ENOENT, "str_int_missing" ); |
| 45 | |
| 46 | t = btf__type_by_id(btf: btf2, id: 1); |
| 47 | if (!ASSERT_OK_PTR(t, "int_type" )) |
| 48 | goto cleanup; |
| 49 | ASSERT_EQ(btf_is_int(t), true, "int_kind" ); |
| 50 | ASSERT_STREQ(btf__str_by_offset(btf: btf2, offset: t->name_off), "int" , "int_name" ); |
| 51 | |
| 52 | btf__add_struct(btf: btf2, name: "s2" , sz: 16); /* [4] struct s2 { */ |
| 53 | btf__add_field(btf: btf2, name: "f1" , field_type_id: 6, bit_offset: 0, bit_size: 0); /* struct s1 f1; */ |
| 54 | btf__add_field(btf: btf2, name: "f2" , field_type_id: 5, bit_offset: 32, bit_size: 0); /* int f2; */ |
| 55 | btf__add_field(btf: btf2, name: "f3" , field_type_id: 2, bit_offset: 64, bit_size: 0); /* int *f3; */ |
| 56 | /* } */ |
| 57 | |
| 58 | /* duplicated int */ |
| 59 | btf__add_int(btf: btf2, name: "int" , byte_sz: 4, BTF_INT_SIGNED); /* [5] int */ |
| 60 | |
| 61 | /* duplicated struct s1 */ |
| 62 | btf__add_struct(btf: btf2, name: "s1" , sz: 4); /* [6] struct s1 { */ |
| 63 | btf__add_field(btf: btf2, name: "f1" , field_type_id: 5, bit_offset: 0, bit_size: 0); /* int f1; */ |
| 64 | /* } */ |
| 65 | |
| 66 | VALIDATE_RAW_BTF( |
| 67 | btf2, |
| 68 | "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED" , |
| 69 | "[2] PTR '(anon)' type_id=1" , |
| 70 | "[3] STRUCT 's1' size=4 vlen=1\n" |
| 71 | "\t'f1' type_id=1 bits_offset=0" , |
| 72 | "[4] STRUCT 's2' size=16 vlen=3\n" |
| 73 | "\t'f1' type_id=6 bits_offset=0\n" |
| 74 | "\t'f2' type_id=5 bits_offset=32\n" |
| 75 | "\t'f3' type_id=2 bits_offset=64" , |
| 76 | "[5] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED" , |
| 77 | "[6] STRUCT 's1' size=4 vlen=1\n" |
| 78 | "\t'f1' type_id=5 bits_offset=0" ); |
| 79 | |
| 80 | ASSERT_STREQ(btf_type_c_dump(btf2), "\ |
| 81 | struct s1 {\n\ |
| 82 | int f1;\n\ |
| 83 | };\n\ |
| 84 | \n\ |
| 85 | struct s1___2 {\n\ |
| 86 | int f1;\n\ |
| 87 | };\n\ |
| 88 | \n\ |
| 89 | struct s2 {\n\ |
| 90 | struct s1___2 f1;\n\ |
| 91 | int f2;\n\ |
| 92 | int *f3;\n\ |
| 93 | };\n\n" , "c_dump" ); |
| 94 | |
| 95 | err = btf__dedup(btf: btf2, NULL); |
| 96 | if (!ASSERT_OK(err, "btf_dedup" )) |
| 97 | goto cleanup; |
| 98 | |
| 99 | VALIDATE_RAW_BTF( |
| 100 | btf2, |
| 101 | "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED" , |
| 102 | "[2] PTR '(anon)' type_id=1" , |
| 103 | "[3] STRUCT 's1' size=4 vlen=1\n" |
| 104 | "\t'f1' type_id=1 bits_offset=0" , |
| 105 | "[4] STRUCT 's2' size=16 vlen=3\n" |
| 106 | "\t'f1' type_id=3 bits_offset=0\n" |
| 107 | "\t'f2' type_id=1 bits_offset=32\n" |
| 108 | "\t'f3' type_id=2 bits_offset=64" ); |
| 109 | |
| 110 | ASSERT_STREQ(btf_type_c_dump(btf2), "\ |
| 111 | struct s1 {\n\ |
| 112 | int f1;\n\ |
| 113 | };\n\ |
| 114 | \n\ |
| 115 | struct s2 {\n\ |
| 116 | struct s1 f1;\n\ |
| 117 | int f2;\n\ |
| 118 | int *f3;\n\ |
| 119 | };\n\n" , "c_dump" ); |
| 120 | |
| 121 | cleanup: |
| 122 | btf__free(btf: btf2); |
| 123 | btf__free(btf: btf1); |
| 124 | } |
| 125 | |
| 126 | static void test_split_fwd_resolve() { |
| 127 | struct btf *btf1, *btf2; |
| 128 | int err; |
| 129 | |
| 130 | btf1 = btf__new_empty(); |
| 131 | if (!ASSERT_OK_PTR(btf1, "empty_main_btf" )) |
| 132 | return; |
| 133 | |
| 134 | btf__set_pointer_size(btf: btf1, ptr_sz: 8); /* enforce 64-bit arch */ |
| 135 | |
| 136 | btf__add_int(btf: btf1, name: "int" , byte_sz: 4, BTF_INT_SIGNED); /* [1] int */ |
| 137 | btf__add_ptr(btf: btf1, ref_type_id: 4); /* [2] ptr to struct s1 */ |
| 138 | btf__add_ptr(btf: btf1, ref_type_id: 5); /* [3] ptr to struct s2 */ |
| 139 | btf__add_struct(btf: btf1, name: "s1" , sz: 16); /* [4] struct s1 { */ |
| 140 | btf__add_field(btf: btf1, name: "f1" , field_type_id: 2, bit_offset: 0, bit_size: 0); /* struct s1 *f1; */ |
| 141 | btf__add_field(btf: btf1, name: "f2" , field_type_id: 3, bit_offset: 64, bit_size: 0); /* struct s2 *f2; */ |
| 142 | /* } */ |
| 143 | btf__add_struct(btf: btf1, name: "s2" , sz: 4); /* [5] struct s2 { */ |
| 144 | btf__add_field(btf: btf1, name: "f1" , field_type_id: 1, bit_offset: 0, bit_size: 0); /* int f1; */ |
| 145 | /* } */ |
| 146 | /* keep this not a part of type the graph to test btf_dedup_resolve_fwds */ |
| 147 | btf__add_struct(btf: btf1, name: "s3" , sz: 4); /* [6] struct s3 { */ |
| 148 | btf__add_field(btf: btf1, name: "f1" , field_type_id: 1, bit_offset: 0, bit_size: 0); /* int f1; */ |
| 149 | /* } */ |
| 150 | |
| 151 | VALIDATE_RAW_BTF( |
| 152 | btf1, |
| 153 | "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED" , |
| 154 | "[2] PTR '(anon)' type_id=4" , |
| 155 | "[3] PTR '(anon)' type_id=5" , |
| 156 | "[4] STRUCT 's1' size=16 vlen=2\n" |
| 157 | "\t'f1' type_id=2 bits_offset=0\n" |
| 158 | "\t'f2' type_id=3 bits_offset=64" , |
| 159 | "[5] STRUCT 's2' size=4 vlen=1\n" |
| 160 | "\t'f1' type_id=1 bits_offset=0" , |
| 161 | "[6] STRUCT 's3' size=4 vlen=1\n" |
| 162 | "\t'f1' type_id=1 bits_offset=0" ); |
| 163 | |
| 164 | btf2 = btf__new_empty_split(base_btf: btf1); |
| 165 | if (!ASSERT_OK_PTR(btf2, "empty_split_btf" )) |
| 166 | goto cleanup; |
| 167 | |
| 168 | btf__add_int(btf: btf2, name: "int" , byte_sz: 4, BTF_INT_SIGNED); /* [7] int */ |
| 169 | btf__add_ptr(btf: btf2, ref_type_id: 11); /* [8] ptr to struct s1 */ |
| 170 | btf__add_fwd(btf: btf2, name: "s2" , fwd_kind: BTF_FWD_STRUCT); /* [9] fwd for struct s2 */ |
| 171 | btf__add_ptr(btf: btf2, ref_type_id: 9); /* [10] ptr to fwd struct s2 */ |
| 172 | btf__add_struct(btf: btf2, name: "s1" , sz: 16); /* [11] struct s1 { */ |
| 173 | btf__add_field(btf: btf2, name: "f1" , field_type_id: 8, bit_offset: 0, bit_size: 0); /* struct s1 *f1; */ |
| 174 | btf__add_field(btf: btf2, name: "f2" , field_type_id: 10, bit_offset: 64, bit_size: 0); /* struct s2 *f2; */ |
| 175 | /* } */ |
| 176 | btf__add_fwd(btf: btf2, name: "s3" , fwd_kind: BTF_FWD_STRUCT); /* [12] fwd for struct s3 */ |
| 177 | btf__add_ptr(btf: btf2, ref_type_id: 12); /* [13] ptr to struct s1 */ |
| 178 | |
| 179 | VALIDATE_RAW_BTF( |
| 180 | btf2, |
| 181 | "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED" , |
| 182 | "[2] PTR '(anon)' type_id=4" , |
| 183 | "[3] PTR '(anon)' type_id=5" , |
| 184 | "[4] STRUCT 's1' size=16 vlen=2\n" |
| 185 | "\t'f1' type_id=2 bits_offset=0\n" |
| 186 | "\t'f2' type_id=3 bits_offset=64" , |
| 187 | "[5] STRUCT 's2' size=4 vlen=1\n" |
| 188 | "\t'f1' type_id=1 bits_offset=0" , |
| 189 | "[6] STRUCT 's3' size=4 vlen=1\n" |
| 190 | "\t'f1' type_id=1 bits_offset=0" , |
| 191 | "[7] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED" , |
| 192 | "[8] PTR '(anon)' type_id=11" , |
| 193 | "[9] FWD 's2' fwd_kind=struct" , |
| 194 | "[10] PTR '(anon)' type_id=9" , |
| 195 | "[11] STRUCT 's1' size=16 vlen=2\n" |
| 196 | "\t'f1' type_id=8 bits_offset=0\n" |
| 197 | "\t'f2' type_id=10 bits_offset=64" , |
| 198 | "[12] FWD 's3' fwd_kind=struct" , |
| 199 | "[13] PTR '(anon)' type_id=12" ); |
| 200 | |
| 201 | err = btf__dedup(btf: btf2, NULL); |
| 202 | if (!ASSERT_OK(err, "btf_dedup" )) |
| 203 | goto cleanup; |
| 204 | |
| 205 | VALIDATE_RAW_BTF( |
| 206 | btf2, |
| 207 | "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED" , |
| 208 | "[2] PTR '(anon)' type_id=4" , |
| 209 | "[3] PTR '(anon)' type_id=5" , |
| 210 | "[4] STRUCT 's1' size=16 vlen=2\n" |
| 211 | "\t'f1' type_id=2 bits_offset=0\n" |
| 212 | "\t'f2' type_id=3 bits_offset=64" , |
| 213 | "[5] STRUCT 's2' size=4 vlen=1\n" |
| 214 | "\t'f1' type_id=1 bits_offset=0" , |
| 215 | "[6] STRUCT 's3' size=4 vlen=1\n" |
| 216 | "\t'f1' type_id=1 bits_offset=0" , |
| 217 | "[7] PTR '(anon)' type_id=6" ); |
| 218 | |
| 219 | cleanup: |
| 220 | btf__free(btf: btf2); |
| 221 | btf__free(btf: btf1); |
| 222 | } |
| 223 | |
| 224 | static void test_split_struct_duped() { |
| 225 | struct btf *btf1, *btf2; |
| 226 | int err; |
| 227 | |
| 228 | btf1 = btf__new_empty(); |
| 229 | if (!ASSERT_OK_PTR(btf1, "empty_main_btf" )) |
| 230 | return; |
| 231 | |
| 232 | btf__set_pointer_size(btf: btf1, ptr_sz: 8); /* enforce 64-bit arch */ |
| 233 | |
| 234 | btf__add_int(btf: btf1, name: "int" , byte_sz: 4, BTF_INT_SIGNED); /* [1] int */ |
| 235 | btf__add_ptr(btf: btf1, ref_type_id: 5); /* [2] ptr to struct s1 */ |
| 236 | btf__add_fwd(btf: btf1, name: "s2" , fwd_kind: BTF_FWD_STRUCT); /* [3] fwd for struct s2 */ |
| 237 | btf__add_ptr(btf: btf1, ref_type_id: 3); /* [4] ptr to fwd struct s2 */ |
| 238 | btf__add_struct(btf: btf1, name: "s1" , sz: 16); /* [5] struct s1 { */ |
| 239 | btf__add_field(btf: btf1, name: "f1" , field_type_id: 2, bit_offset: 0, bit_size: 0); /* struct s1 *f1; */ |
| 240 | btf__add_field(btf: btf1, name: "f2" , field_type_id: 4, bit_offset: 64, bit_size: 0); /* struct s2 *f2; */ |
| 241 | /* } */ |
| 242 | |
| 243 | VALIDATE_RAW_BTF( |
| 244 | btf1, |
| 245 | "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED" , |
| 246 | "[2] PTR '(anon)' type_id=5" , |
| 247 | "[3] FWD 's2' fwd_kind=struct" , |
| 248 | "[4] PTR '(anon)' type_id=3" , |
| 249 | "[5] STRUCT 's1' size=16 vlen=2\n" |
| 250 | "\t'f1' type_id=2 bits_offset=0\n" |
| 251 | "\t'f2' type_id=4 bits_offset=64" ); |
| 252 | |
| 253 | btf2 = btf__new_empty_split(base_btf: btf1); |
| 254 | if (!ASSERT_OK_PTR(btf2, "empty_split_btf" )) |
| 255 | goto cleanup; |
| 256 | |
| 257 | btf__add_int(btf: btf2, name: "int" , byte_sz: 4, BTF_INT_SIGNED); /* [6] int */ |
| 258 | btf__add_ptr(btf: btf2, ref_type_id: 10); /* [7] ptr to struct s1 */ |
| 259 | btf__add_fwd(btf: btf2, name: "s2" , fwd_kind: BTF_FWD_STRUCT); /* [8] fwd for struct s2 */ |
| 260 | btf__add_ptr(btf: btf2, ref_type_id: 11); /* [9] ptr to struct s2 */ |
| 261 | btf__add_struct(btf: btf2, name: "s1" , sz: 16); /* [10] struct s1 { */ |
| 262 | btf__add_field(btf: btf2, name: "f1" , field_type_id: 7, bit_offset: 0, bit_size: 0); /* struct s1 *f1; */ |
| 263 | btf__add_field(btf: btf2, name: "f2" , field_type_id: 9, bit_offset: 64, bit_size: 0); /* struct s2 *f2; */ |
| 264 | /* } */ |
| 265 | btf__add_struct(btf: btf2, name: "s2" , sz: 40); /* [11] struct s2 { */ |
| 266 | btf__add_field(btf: btf2, name: "f1" , field_type_id: 7, bit_offset: 0, bit_size: 0); /* struct s1 *f1; */ |
| 267 | btf__add_field(btf: btf2, name: "f2" , field_type_id: 9, bit_offset: 64, bit_size: 0); /* struct s2 *f2; */ |
| 268 | btf__add_field(btf: btf2, name: "f3" , field_type_id: 6, bit_offset: 128, bit_size: 0); /* int f3; */ |
| 269 | btf__add_field(btf: btf2, name: "f4" , field_type_id: 10, bit_offset: 192, bit_size: 0); /* struct s1 f4; */ |
| 270 | /* } */ |
| 271 | btf__add_ptr(btf: btf2, ref_type_id: 8); /* [12] ptr to fwd struct s2 */ |
| 272 | btf__add_struct(btf: btf2, name: "s3" , sz: 8); /* [13] struct s3 { */ |
| 273 | btf__add_field(btf: btf2, name: "f1" , field_type_id: 12, bit_offset: 0, bit_size: 0); /* struct s2 *f1; (fwd) */ |
| 274 | /* } */ |
| 275 | |
| 276 | VALIDATE_RAW_BTF( |
| 277 | btf2, |
| 278 | "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED" , |
| 279 | "[2] PTR '(anon)' type_id=5" , |
| 280 | "[3] FWD 's2' fwd_kind=struct" , |
| 281 | "[4] PTR '(anon)' type_id=3" , |
| 282 | "[5] STRUCT 's1' size=16 vlen=2\n" |
| 283 | "\t'f1' type_id=2 bits_offset=0\n" |
| 284 | "\t'f2' type_id=4 bits_offset=64" , |
| 285 | "[6] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED" , |
| 286 | "[7] PTR '(anon)' type_id=10" , |
| 287 | "[8] FWD 's2' fwd_kind=struct" , |
| 288 | "[9] PTR '(anon)' type_id=11" , |
| 289 | "[10] STRUCT 's1' size=16 vlen=2\n" |
| 290 | "\t'f1' type_id=7 bits_offset=0\n" |
| 291 | "\t'f2' type_id=9 bits_offset=64" , |
| 292 | "[11] STRUCT 's2' size=40 vlen=4\n" |
| 293 | "\t'f1' type_id=7 bits_offset=0\n" |
| 294 | "\t'f2' type_id=9 bits_offset=64\n" |
| 295 | "\t'f3' type_id=6 bits_offset=128\n" |
| 296 | "\t'f4' type_id=10 bits_offset=192" , |
| 297 | "[12] PTR '(anon)' type_id=8" , |
| 298 | "[13] STRUCT 's3' size=8 vlen=1\n" |
| 299 | "\t'f1' type_id=12 bits_offset=0" ); |
| 300 | |
| 301 | err = btf__dedup(btf: btf2, NULL); |
| 302 | if (!ASSERT_OK(err, "btf_dedup" )) |
| 303 | goto cleanup; |
| 304 | |
| 305 | VALIDATE_RAW_BTF( |
| 306 | btf2, |
| 307 | "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED" , |
| 308 | "[2] PTR '(anon)' type_id=5" , |
| 309 | "[3] FWD 's2' fwd_kind=struct" , |
| 310 | "[4] PTR '(anon)' type_id=3" , |
| 311 | "[5] STRUCT 's1' size=16 vlen=2\n" |
| 312 | "\t'f1' type_id=2 bits_offset=0\n" |
| 313 | "\t'f2' type_id=4 bits_offset=64" , |
| 314 | "[6] PTR '(anon)' type_id=8" , |
| 315 | "[7] PTR '(anon)' type_id=9" , |
| 316 | "[8] STRUCT 's1' size=16 vlen=2\n" |
| 317 | "\t'f1' type_id=6 bits_offset=0\n" |
| 318 | "\t'f2' type_id=7 bits_offset=64" , |
| 319 | "[9] STRUCT 's2' size=40 vlen=4\n" |
| 320 | "\t'f1' type_id=6 bits_offset=0\n" |
| 321 | "\t'f2' type_id=7 bits_offset=64\n" |
| 322 | "\t'f3' type_id=1 bits_offset=128\n" |
| 323 | "\t'f4' type_id=8 bits_offset=192" , |
| 324 | "[10] STRUCT 's3' size=8 vlen=1\n" |
| 325 | "\t'f1' type_id=7 bits_offset=0" ); |
| 326 | |
| 327 | cleanup: |
| 328 | btf__free(btf: btf2); |
| 329 | btf__free(btf: btf1); |
| 330 | } |
| 331 | |
| 332 | static void btf_add_dup_struct_in_cu(struct btf *btf, int start_id) |
| 333 | { |
| 334 | #define ID(n) (start_id + n) |
| 335 | btf__set_pointer_size(btf, ptr_sz: 8); /* enforce 64-bit arch */ |
| 336 | |
| 337 | btf__add_int(btf, name: "int" , byte_sz: 4, BTF_INT_SIGNED); /* [1] int */ |
| 338 | |
| 339 | btf__add_struct(btf, name: "s" , sz: 8); /* [2] struct s { */ |
| 340 | btf__add_field(btf, name: "a" , ID(3), bit_offset: 0, bit_size: 0); /* struct anon a; */ |
| 341 | btf__add_field(btf, name: "b" , ID(4), bit_offset: 0, bit_size: 0); /* struct anon b; */ |
| 342 | /* } */ |
| 343 | |
| 344 | btf__add_struct(btf, name: "(anon)" , sz: 8); /* [3] struct anon { */ |
| 345 | btf__add_field(btf, name: "f1" , ID(1), bit_offset: 0, bit_size: 0); /* int f1; */ |
| 346 | btf__add_field(btf, name: "f2" , ID(1), bit_offset: 32, bit_size: 0); /* int f2; */ |
| 347 | /* } */ |
| 348 | |
| 349 | btf__add_struct(btf, name: "(anon)" , sz: 8); /* [4] struct anon { */ |
| 350 | btf__add_field(btf, name: "f1" , ID(1), bit_offset: 0, bit_size: 0); /* int f1; */ |
| 351 | btf__add_field(btf, name: "f2" , ID(1), bit_offset: 32, bit_size: 0); /* int f2; */ |
| 352 | /* } */ |
| 353 | #undef ID |
| 354 | } |
| 355 | |
| 356 | static void test_split_dup_struct_in_cu() |
| 357 | { |
| 358 | struct btf *btf1, *btf2 = NULL; |
| 359 | int err; |
| 360 | |
| 361 | /* generate the base data.. */ |
| 362 | btf1 = btf__new_empty(); |
| 363 | if (!ASSERT_OK_PTR(btf1, "empty_main_btf" )) |
| 364 | return; |
| 365 | |
| 366 | btf_add_dup_struct_in_cu(btf: btf1, start_id: 0); |
| 367 | |
| 368 | VALIDATE_RAW_BTF( |
| 369 | btf1, |
| 370 | "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED" , |
| 371 | "[2] STRUCT 's' size=8 vlen=2\n" |
| 372 | "\t'a' type_id=3 bits_offset=0\n" |
| 373 | "\t'b' type_id=4 bits_offset=0" , |
| 374 | "[3] STRUCT '(anon)' size=8 vlen=2\n" |
| 375 | "\t'f1' type_id=1 bits_offset=0\n" |
| 376 | "\t'f2' type_id=1 bits_offset=32" , |
| 377 | "[4] STRUCT '(anon)' size=8 vlen=2\n" |
| 378 | "\t'f1' type_id=1 bits_offset=0\n" |
| 379 | "\t'f2' type_id=1 bits_offset=32" ); |
| 380 | |
| 381 | /* ..dedup them... */ |
| 382 | err = btf__dedup(btf: btf1, NULL); |
| 383 | if (!ASSERT_OK(err, "btf_dedup" )) |
| 384 | goto cleanup; |
| 385 | |
| 386 | VALIDATE_RAW_BTF( |
| 387 | btf1, |
| 388 | "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED" , |
| 389 | "[2] STRUCT 's' size=8 vlen=2\n" |
| 390 | "\t'a' type_id=3 bits_offset=0\n" |
| 391 | "\t'b' type_id=3 bits_offset=0" , |
| 392 | "[3] STRUCT '(anon)' size=8 vlen=2\n" |
| 393 | "\t'f1' type_id=1 bits_offset=0\n" |
| 394 | "\t'f2' type_id=1 bits_offset=32" ); |
| 395 | |
| 396 | /* and add the same data on top of it */ |
| 397 | btf2 = btf__new_empty_split(base_btf: btf1); |
| 398 | if (!ASSERT_OK_PTR(btf2, "empty_split_btf" )) |
| 399 | goto cleanup; |
| 400 | |
| 401 | btf_add_dup_struct_in_cu(btf: btf2, start_id: 3); |
| 402 | |
| 403 | VALIDATE_RAW_BTF( |
| 404 | btf2, |
| 405 | "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED" , |
| 406 | "[2] STRUCT 's' size=8 vlen=2\n" |
| 407 | "\t'a' type_id=3 bits_offset=0\n" |
| 408 | "\t'b' type_id=3 bits_offset=0" , |
| 409 | "[3] STRUCT '(anon)' size=8 vlen=2\n" |
| 410 | "\t'f1' type_id=1 bits_offset=0\n" |
| 411 | "\t'f2' type_id=1 bits_offset=32" , |
| 412 | "[4] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED" , |
| 413 | "[5] STRUCT 's' size=8 vlen=2\n" |
| 414 | "\t'a' type_id=6 bits_offset=0\n" |
| 415 | "\t'b' type_id=7 bits_offset=0" , |
| 416 | "[6] STRUCT '(anon)' size=8 vlen=2\n" |
| 417 | "\t'f1' type_id=4 bits_offset=0\n" |
| 418 | "\t'f2' type_id=4 bits_offset=32" , |
| 419 | "[7] STRUCT '(anon)' size=8 vlen=2\n" |
| 420 | "\t'f1' type_id=4 bits_offset=0\n" |
| 421 | "\t'f2' type_id=4 bits_offset=32" ); |
| 422 | |
| 423 | err = btf__dedup(btf: btf2, NULL); |
| 424 | if (!ASSERT_OK(err, "btf_dedup" )) |
| 425 | goto cleanup; |
| 426 | |
| 427 | /* after dedup it should match the original data */ |
| 428 | VALIDATE_RAW_BTF( |
| 429 | btf2, |
| 430 | "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED" , |
| 431 | "[2] STRUCT 's' size=8 vlen=2\n" |
| 432 | "\t'a' type_id=3 bits_offset=0\n" |
| 433 | "\t'b' type_id=3 bits_offset=0" , |
| 434 | "[3] STRUCT '(anon)' size=8 vlen=2\n" |
| 435 | "\t'f1' type_id=1 bits_offset=0\n" |
| 436 | "\t'f2' type_id=1 bits_offset=32" ); |
| 437 | |
| 438 | cleanup: |
| 439 | btf__free(btf: btf2); |
| 440 | btf__free(btf: btf1); |
| 441 | } |
| 442 | |
| 443 | /* Ensure module split BTF dedup worked correctly; when dedup fails badly |
| 444 | * core kernel types are in split BTF also, so ensure that references to |
| 445 | * such types point at base - not split - BTF. |
| 446 | * |
| 447 | * bpf_testmod_test_write() has multiple core kernel type parameters; |
| 448 | * |
| 449 | * ssize_t |
| 450 | * bpf_testmod_test_write(struct file *file, struct kobject *kobj, |
| 451 | * struct bin_attribute *bin_attr, |
| 452 | * char *buf, loff_t off, size_t len); |
| 453 | * |
| 454 | * Ensure each of the FUNC_PROTO params is a core kernel type. |
| 455 | * |
| 456 | * Do the same for |
| 457 | * |
| 458 | * __bpf_kfunc struct sock *bpf_kfunc_call_test3(struct sock *sk); |
| 459 | * |
| 460 | * ...and |
| 461 | * |
| 462 | * __bpf_kfunc void bpf_kfunc_call_test_pass_ctx(struct __sk_buff *skb); |
| 463 | * |
| 464 | */ |
| 465 | const char *mod_funcs[] = { |
| 466 | "bpf_testmod_test_write" , |
| 467 | "bpf_kfunc_call_test3" , |
| 468 | "bpf_kfunc_call_test_pass_ctx" |
| 469 | }; |
| 470 | |
| 471 | static void test_split_module(void) |
| 472 | { |
| 473 | struct btf *vmlinux_btf, *btf1 = NULL; |
| 474 | int i, nr_base_types; |
| 475 | |
| 476 | vmlinux_btf = btf__load_vmlinux_btf(); |
| 477 | if (!ASSERT_OK_PTR(vmlinux_btf, "vmlinux_btf" )) |
| 478 | return; |
| 479 | nr_base_types = btf__type_cnt(btf: vmlinux_btf); |
| 480 | if (!ASSERT_GT(nr_base_types, 0, "nr_base_types" )) |
| 481 | goto cleanup; |
| 482 | |
| 483 | btf1 = btf__parse_split(path: "/sys/kernel/btf/bpf_testmod" , base_btf: vmlinux_btf); |
| 484 | if (!ASSERT_OK_PTR(btf1, "split_btf" )) |
| 485 | return; |
| 486 | |
| 487 | for (i = 0; i < ARRAY_SIZE(mod_funcs); i++) { |
| 488 | const struct btf_param *p; |
| 489 | const struct btf_type *t; |
| 490 | __u16 vlen; |
| 491 | __u32 id; |
| 492 | int j; |
| 493 | |
| 494 | id = btf__find_by_name_kind(btf: btf1, type_name: mod_funcs[i], BTF_KIND_FUNC); |
| 495 | if (!ASSERT_GE(id, nr_base_types, "func_id" )) |
| 496 | goto cleanup; |
| 497 | t = btf__type_by_id(btf: btf1, id); |
| 498 | if (!ASSERT_OK_PTR(t, "func_id_type" )) |
| 499 | goto cleanup; |
| 500 | t = btf__type_by_id(btf: btf1, id: t->type); |
| 501 | if (!ASSERT_OK_PTR(t, "func_proto_id_type" )) |
| 502 | goto cleanup; |
| 503 | if (!ASSERT_EQ(btf_is_func_proto(t), true, "is_func_proto" )) |
| 504 | goto cleanup; |
| 505 | vlen = btf_vlen(t); |
| 506 | |
| 507 | for (j = 0, p = btf_params(t); j < vlen; j++, p++) { |
| 508 | /* bpf_testmod uses resilient split BTF, so any |
| 509 | * reference types will be added to split BTF and their |
| 510 | * associated targets will be base BTF types; for example |
| 511 | * for a "struct sock *" the PTR will be in split BTF |
| 512 | * while the "struct sock" will be in base. |
| 513 | * |
| 514 | * In some cases like loff_t we have to resolve |
| 515 | * multiple typedefs hence the while() loop below. |
| 516 | * |
| 517 | * Note that resilient split BTF generation depends |
| 518 | * on pahole version, so we do not assert that |
| 519 | * reference types are in split BTF, as if pahole |
| 520 | * does not support resilient split BTF they will |
| 521 | * also be base BTF types. |
| 522 | */ |
| 523 | id = p->type; |
| 524 | do { |
| 525 | t = btf__type_by_id(btf: btf1, id); |
| 526 | if (!ASSERT_OK_PTR(t, "param_ref_type" )) |
| 527 | goto cleanup; |
| 528 | if (!btf_is_mod(t) && !btf_is_ptr(t) && !btf_is_typedef(t)) |
| 529 | break; |
| 530 | id = t->type; |
| 531 | } while (true); |
| 532 | |
| 533 | if (!ASSERT_LT(id, nr_base_types, "verify_base_type" )) |
| 534 | goto cleanup; |
| 535 | } |
| 536 | } |
| 537 | cleanup: |
| 538 | btf__free(btf: btf1); |
| 539 | btf__free(btf: vmlinux_btf); |
| 540 | } |
| 541 | |
| 542 | void test_btf_dedup_split() |
| 543 | { |
| 544 | if (test__start_subtest("split_simple" )) |
| 545 | test_split_simple(); |
| 546 | if (test__start_subtest("split_struct_duped" )) |
| 547 | test_split_struct_duped(); |
| 548 | if (test__start_subtest("split_fwd_resolve" )) |
| 549 | test_split_fwd_resolve(); |
| 550 | if (test__start_subtest("split_dup_struct_in_cu" )) |
| 551 | test_split_dup_struct_in_cu(); |
| 552 | if (test__start_subtest("split_module" )) |
| 553 | test_split_module(); |
| 554 | } |
| 555 | |