Re: [PATCH 04/12] btf_encoder: Encode variant parts as union members in BTF
Yonghong Song <[email protected]>
| Newsgroups | org.kernel.vger.dwarves,org.kernel.vger.bpf |
|---|---|
| Message-ID | <[email protected]> |
On 7/31/26 12:30 PM, Arnaldo Carvalho de Melo wrote: > From: Arnaldo Carvalho de Melo <[email protected]> > > With the DWARF loader now populating DW_TAG_variant children (previous > commit), wire them into BTF encoding so Rust discriminated unions > (Option<T>, Result<T,E>, etc.) are no longer emitted as empty structs. > > Two changes: > > 1. Struct-to-union promotion: when a DW_TAG_structure_type has > variant_parts but no regular data members, encode it as BTF_KIND_UNION > instead of BTF_KIND_STRUCT, since the variants overlap at offset 0. > > 2. Variant member encoding: after encoding regular data members, iterate > the variant_parts and emit each variant as a BTF union field with the > variant's name and resolved type reference. > > Testing with the sashiko-cli Rust binary (a real-world async HTTP client > using tokio, hyper, serde, etc.): > > Before: > > $ bpftool btf dump file sashiko-cli | grep -c UNION > 2073 > $ bpftool btf dump file sashiko-cli | grep 'STRUCT.*vlen=0' | grep -vc 'size=0' > 24335 > > After: > > $ bpftool btf dump file sashiko-cli | grep -c UNION > 25750 > $ bpftool btf dump file sashiko-cli | grep 'STRUCT.*vlen=0' | grep -vc 'size=0' > 2236 > > 22,099 types that were previously encoded as empty structs are now > properly represented as unions with their variant members: > > Before: > > $ bpftool btf dump file code_with_type.o | grep -A1 'Option<u32>' > [11] STRUCT 'Option<u32>' size=8 vlen=0 > > After: > > $ bpftool btf dump file code_with_type.o | grep -A3 'Option<u32>' > [11] UNION 'Option<u32>' size=8 vlen=2 > 'None' type_id=9 bits_offset=0 > 'Some' type_id=10 bits_offset=0 FYI, the below is for llvm bpf backend to handle DW_TAG_variant_part to generate BTF. link: https://github.com/llvm/llvm-project/pull/155783 For a rust code like: ; Source: ; #![no_std] ; #![no_main] ; ; pub enum MyEnum { ; First { a: u32, b: i32 }, ; Second(u32), ; } ; ; #[unsafe(no_mangle)] ; pub static X: MyEnum = MyEnum::First { a: 54, b: -23 }; ; ; #[cfg(not(test))] ; #[panic_handler] ; fn panic(_info: &core::panic::PanicInfo) -> ! { ; loop {} ; } The BTF encoding: ; CHECK-BTF: [1] STRUCT 'MyEnum' size=12 vlen=1 ; CHECK-BTF-NEXT: '(anon)' type_id=3 bits_offset=0 ; CHECK-BTF-NEXT: [2] INT 'u32' size=4 bits_offset=0 nr_bits=32 encoding=(none) ; CHECK-BTF-NEXT: [3] UNION '(anon)' size=12 vlen=3 ; CHECK-BTF-NEXT: '(anon)' type_id=2 bits_offset=0 ; CHECK-BTF-NEXT: 'First' type_id=4 bits_offset=0 ; CHECK-BTF-NEXT: 'Second' type_id=6 bits_offset=0 ; CHECK-BTF-NEXT: [4] STRUCT 'First' size=12 vlen=2 ; CHECK-BTF-NEXT: 'a' type_id=2 bits_offset=32 ; CHECK-BTF-NEXT: 'b' type_id=5 bits_offset=64 ; CHECK-BTF-NEXT: [5] INT 'i32' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED ; CHECK-BTF-NEXT: [6] STRUCT 'Second' size=12 vlen=1 ; CHECK-BTF-NEXT: '__0' type_id=2 bits_offset=32 ; CHECK-BTF-NEXT: [7] VAR 'X' type_id=1, linkage=global ; CHECK-BTF-NEXT: [8] DATASEC '.rodata' size=0 vlen=1 ; CHECK-BTF-NEXT: type_id=7 offset=0 size=12 > > Assisted-by: Claude:claude-sonnet-4-5 > Signed-off-by: Arnaldo Carvalho de Melo <[email protected]> > --- > btf_encoder.c | 37 +++++++++++++++++++++++++++++++++++-- > 1 file changed, 35 insertions(+), 2 deletions(-) > > diff --git a/btf_encoder.c b/btf_encoder.c > index 83ce21186ea7da36..acf0a5ade5e29014 100644 > --- a/btf_encoder.c > +++ b/btf_encoder.c > @@ -1855,6 +1855,12 @@ static void dump_invalid_symbol(const char *msg, const char *sym, > fprintf(stderr, "PAHOLE: Error: Use '--btf_encode_force' to ignore such symbols and force emit the btf.\n"); > } > > +static bool type__has_variant_parts(const struct type *type) > +{ > + return !list_empty(&type->variant_parts); > +} > + > + > static int32_t btf_encoder__add_struct_type(struct btf_encoder *encoder, struct tag *tag) > { > struct type *type = tag__type(tag); > @@ -1863,8 +1869,18 @@ static int32_t btf_encoder__add_struct_type(struct btf_encoder *encoder, struct > int32_t type_id; > uint8_t kind; > > - kind = (tag->tag == DW_TAG_union_type) ? > - BTF_KIND_UNION : BTF_KIND_STRUCT; > + /* > + * Rust discriminated unions (enums) are represented in DWARF as > + * DW_TAG_structure_type with DW_TAG_variant_part children. > + * If the struct has only variant parts and no regular data members, > + * encode it as a BTF union since the variants overlap at offset 0. > + */ > + if (tag->tag == DW_TAG_union_type) > + kind = BTF_KIND_UNION; > + else if (type__has_variant_parts(type) && type->nr_members == 0) > + kind = BTF_KIND_UNION; > + else > + kind = BTF_KIND_STRUCT; > > type_id = btf_encoder__add_struct(encoder, kind, name, type->size); > if (type_id < 0) > @@ -1882,6 +1898,23 @@ static int32_t btf_encoder__add_struct_type(struct btf_encoder *encoder, struct > return -1; > } > > + if (type__has_variant_parts(type) && kind == BTF_KIND_UNION) { > + struct variant_part *vpart; > + > + type__for_each_variant_part(type, vpart) { > + struct variant *variant; > + > + variant_part__for_each_variant(vpart, variant) { > + if (variant->tag.type == 0) > + continue; > + > + uint32_t ref_type_id = btf_encoder__tag_type(encoder, variant->tag.type); > + > + if (btf_encoder__add_field(encoder, variant->name, ref_type_id, 0, 0)) > + return -1; > + } > + } > + } > > > return type_id;