• Yonghong Song's avatar
    bpf: btf: fix struct/union/fwd types with kind_flag · 9d5f9f70
    Yonghong Song authored
    This patch fixed two issues with BTF. One is related to
    struct/union bitfield encoding and the other is related to
    forward type.
    
    Issue #1 and solution:
    
    ======================
    
    Current btf encoding of bitfield follows what pahole generates.
    For each bitfield, pahole will duplicate the type chain and
    put the bitfield size at the final int or enum type.
    Since the BTF enum type cannot encode bit size,
    pahole workarounds the issue by generating
    an int type whenever the enum bit size is not 32.
    
    For example,
      -bash-4.4$ cat t.c
      typedef int ___int;
      enum A { A1, A2, A3 };
      struct t {
        int a[5];
        ___int b:4;
        volatile enum A c:4;
      } g;
      -bash-4.4$ gcc -c -O2 -g t.c
    The current kernel supports the following BTF encoding:
      $ pahole -JV t.o
      [1] TYPEDEF ___int type_id=2
      [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
      [3] ENUM A size=4 vlen=3
            A1 val=0
            A2 val=1
            A3 val=2
      [4] STRUCT t size=24 vlen=3
            a type_id=5 bits_offset=0
            b type_id=9 bits_offset=160
            c type_id=11 bits_offset=164
      [5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
      [6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
      [7] VOLATILE (anon) type_id=3
      [8] INT int size=1 bit_offset=0 nr_bits=4 encoding=(none)
      [9] TYPEDEF ___int type_id=8
      [10] INT (anon) size=1 bit_offset=0 nr_bits=4 encoding=SIGNED
      [11] VOLATILE (anon) type_id=10
    
    Two issues are in the above:
      . by changing enum type to int, we lost the original
        type information and this will not be ideal later
        when we try to convert BTF to a header file.
      . the type duplication for bitfields will cause
        BTF bloat. Duplicated types cannot be deduplicated
        later if the bitfield size is different.
    
    To fix this issue, this patch implemented a compatible
    change for BTF struct type encoding:
      . the bit 31 of struct_type->info, previously reserved,
        now is used to indicate whether bitfield_size is
        encoded in btf_member or not.
      . if bit 31 of struct_type->info is set,
        btf_member->offset will encode like:
          bit 0 - 23: bit offset
          bit 24 - 31: bitfield size
        if bit 31 is not set, the old behavior is preserved:
          bit 0 - 31: bit offset
    
    So if the struct contains a bit field, the maximum bit offset
    will be reduced to (2^24 - 1) instead of MAX_UINT. The maximum
    bitfield size will be 256 which is enough for today as maximum
    bitfield in compiler can be 128 where int128 type is supported.
    
    This kernel patch intends to support the new BTF encoding:
      $ pahole -JV t.o
      [1] TYPEDEF ___int type_id=2
      [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
      [3] ENUM A size=4 vlen=3
            A1 val=0
            A2 val=1
            A3 val=2
      [4] STRUCT t kind_flag=1 size=24 vlen=3
            a type_id=5 bitfield_size=0 bits_offset=0
            b type_id=1 bitfield_size=4 bits_offset=160
            c type_id=7 bitfield_size=4 bits_offset=164
      [5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
      [6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
      [7] VOLATILE (anon) type_id=3
    
    Issue #2 and solution:
    ======================
    
    Current forward type in BTF does not specify whether the original
    type is struct or union. This will not work for type pretty print
    and BTF-to-header-file conversion as struct/union must be specified.
      $ cat tt.c
      struct t;
      union u;
      int foo(struct t *t, union u *u) { return 0; }
      $ gcc -c -g -O2 tt.c
      $ pahole -JV tt.o
      [1] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
      [2] FWD t type_id=0
      [3] PTR (anon) type_id=2
      [4] FWD u type_id=0
      [5] PTR (anon) type_id=4
    
    To fix this issue, similar to issue #1, type->info bit 31
    is used. If the bit is set, it is union type. Otherwise, it is
    a struct type.
    
      $ pahole -JV tt.o
      [1] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
      [2] FWD t kind_flag=0 type_id=0
      [3] PTR (anon) kind_flag=0 type_id=2
      [4] FWD u kind_flag=1 type_id=0
      [5] PTR (anon) kind_flag=0 type_id=4
    
    Pahole/LLVM change:
    ===================
    
    The new kind_flag functionality has been implemented in pahole
    and llvm:
      https://github.com/yonghong-song/pahole/tree/bitfield
      https://github.com/yonghong-song/llvm/tree/bitfield
    
    Note that pahole hasn't implemented func/func_proto kind
    and .BTF.ext. So to print function signature with bpftool,
    the llvm compiler should be used.
    
    Fixes: 69b693f0 ("bpf: btf: Introduce BPF Type Format (BTF)")
    Acked-by: default avatarMartin KaFai Lau <kafai@fb.com>
    Signed-off-by: default avatarMartin KaFai Lau <kafai@fb.com>
    Signed-off-by: default avatarYonghong Song <yhs@fb.com>
    Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
    9d5f9f70
btf.c 74 KB