2019-01-15 18:28:24 +01:00
|
|
|
/*
|
|
|
|
SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
|
2019-01-18 19:42:37 +01:00
|
|
|
Copyright (C) 2019 Facebook
|
|
|
|
|
|
|
|
Derived from ctf_encoder.c, which is:
|
|
|
|
|
|
|
|
Copyright (C) Arnaldo Carvalho de Melo <acme@redhat.com>
|
|
|
|
Copyright (C) Red Hat Inc
|
2019-01-15 18:28:24 +01:00
|
|
|
*/
|
|
|
|
|
2018-03-06 01:53:50 +01:00
|
|
|
#include "dwarves.h"
|
|
|
|
#include "libbtf.h"
|
2019-02-07 20:00:37 +01:00
|
|
|
#include "lib/bpf/include/uapi/linux/btf.h"
|
2018-03-06 01:53:50 +01:00
|
|
|
#include "hash.h"
|
|
|
|
#include "elf_symtab.h"
|
|
|
|
#include "btf_encoder.h"
|
|
|
|
|
|
|
|
#include <inttypes.h>
|
|
|
|
|
|
|
|
static int tag__check_id_drift(const struct tag *tag,
|
2018-12-18 23:09:39 +01:00
|
|
|
uint32_t core_id, uint32_t btf_type_id,
|
|
|
|
uint32_t type_id_off)
|
2018-03-06 01:53:50 +01:00
|
|
|
{
|
2018-12-18 23:09:39 +01:00
|
|
|
if (btf_type_id != (core_id + type_id_off)) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"%s: %s id drift, core_id: %u, btf_type_id: %u, type_id_off: %u\n",
|
2018-03-06 01:53:50 +01:00
|
|
|
__func__, dwarf_tag_name(tag->tag),
|
2018-12-18 23:09:39 +01:00
|
|
|
core_id, btf_type_id, type_id_off);
|
2018-03-06 01:53:50 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-02-14 14:47:32 +01:00
|
|
|
static int32_t structure_type__encode(struct btf_elf *btfe, struct tag *tag, uint32_t type_id_off)
|
2018-03-06 01:53:50 +01:00
|
|
|
{
|
|
|
|
struct type *type = tag__type(tag);
|
|
|
|
struct class_member *pos;
|
btf: fix struct/union/fwd types with kind_flag
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,
commit b18354f64cc2 ("btf: Generate correct struct bitfield
member types") workarounds the issue by generating
an int type whenever the enum bit size is not 32.
The above workaround is not ideal as we lost original type
in BTF. Another undesiable fact is the type duplication
as the pahole duplicates the type chain.
To fix this issue, this patch implemented a compatible
change for BTF struct type encoding:
. the bit 31 of 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 255 which is enough for today as maximum
bitfield in compiler can be 128 where int128 type is supported.
A new global, no_bitfield_type_recode, is introduced and which
will be set to true if BTF encoding is enabled. This global
will prevent pahole duplicating the bitfield types to avoid
type duplication in BTF.
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.
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.
Examples:
=========
-bash-4.4$ cat t.c
struct s;
union u;
typedef int ___int;
enum A { A1, A2, A3 };
struct t {
int a[5];
___int b:4;
volatile enum A c:4;
struct s *p1;
union u *p2;
} g;
-bash-4.4$ gcc -c -O2 -g t.c
Without this patch:
$ 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=40 vlen=5
a type_id=5 bits_offset=0
b type_id=13 bits_offset=160
c type_id=15 bits_offset=164
p1 type_id=9 bits_offset=192
p2 type_id=11 bits_offset=256
[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] FWD s type_id=0
[9] PTR (anon) type_id=8
[10] FWD u type_id=0
[11] PTR (anon) type_id=10
[12] INT int size=1 bit_offset=0 nr_bits=4 encoding=(none)
[13] TYPEDEF ___int type_id=12
[14] INT (anon) size=1 bit_offset=0 nr_bits=4 encoding=SIGNED
[15] VOLATILE (anon) type_id=14
With this patch:
$ pahole -JV t.o
File 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=40 vlen=5
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
p1 type_id=9 bitfield_size=0 bits_offset=192
p2 type_id=11 bitfield_size=0 bits_offset=256
[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] FWD s struct
[9] PTR (anon) type_id=8
[10] FWD u union
[11] PTR (anon) type_id=10
The fix removed the type duplication, preserved the enum type for the
bitfield, and have correct struct/union information for the forward
type.
Signed-off-by: Yonghong Song <yhs@fb.com>
Acked-by: Martin KaFai Lau <kafai@fb.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@fb.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-12-18 23:09:41 +01:00
|
|
|
bool kind_flag = false;
|
2018-03-06 01:53:50 +01:00
|
|
|
int32_t type_id;
|
|
|
|
uint8_t kind;
|
|
|
|
|
|
|
|
kind = (tag->tag == DW_TAG_union_type) ?
|
|
|
|
BTF_KIND_UNION : BTF_KIND_STRUCT;
|
btf: fix struct/union/fwd types with kind_flag
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,
commit b18354f64cc2 ("btf: Generate correct struct bitfield
member types") workarounds the issue by generating
an int type whenever the enum bit size is not 32.
The above workaround is not ideal as we lost original type
in BTF. Another undesiable fact is the type duplication
as the pahole duplicates the type chain.
To fix this issue, this patch implemented a compatible
change for BTF struct type encoding:
. the bit 31 of 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 255 which is enough for today as maximum
bitfield in compiler can be 128 where int128 type is supported.
A new global, no_bitfield_type_recode, is introduced and which
will be set to true if BTF encoding is enabled. This global
will prevent pahole duplicating the bitfield types to avoid
type duplication in BTF.
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.
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.
Examples:
=========
-bash-4.4$ cat t.c
struct s;
union u;
typedef int ___int;
enum A { A1, A2, A3 };
struct t {
int a[5];
___int b:4;
volatile enum A c:4;
struct s *p1;
union u *p2;
} g;
-bash-4.4$ gcc -c -O2 -g t.c
Without this patch:
$ 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=40 vlen=5
a type_id=5 bits_offset=0
b type_id=13 bits_offset=160
c type_id=15 bits_offset=164
p1 type_id=9 bits_offset=192
p2 type_id=11 bits_offset=256
[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] FWD s type_id=0
[9] PTR (anon) type_id=8
[10] FWD u type_id=0
[11] PTR (anon) type_id=10
[12] INT int size=1 bit_offset=0 nr_bits=4 encoding=(none)
[13] TYPEDEF ___int type_id=12
[14] INT (anon) size=1 bit_offset=0 nr_bits=4 encoding=SIGNED
[15] VOLATILE (anon) type_id=14
With this patch:
$ pahole -JV t.o
File 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=40 vlen=5
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
p1 type_id=9 bitfield_size=0 bits_offset=192
p2 type_id=11 bitfield_size=0 bits_offset=256
[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] FWD s struct
[9] PTR (anon) type_id=8
[10] FWD u union
[11] PTR (anon) type_id=10
The fix removed the type duplication, preserved the enum type for the
bitfield, and have correct struct/union information for the forward
type.
Signed-off-by: Yonghong Song <yhs@fb.com>
Acked-by: Martin KaFai Lau <kafai@fb.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@fb.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-12-18 23:09:41 +01:00
|
|
|
|
|
|
|
/* Although no_bitfield_type_recode has been set true
|
|
|
|
* in pahole.c if BTF encoding is requested, we still check
|
|
|
|
* the value here. So if no_bitfield_type_recode is set
|
|
|
|
* to false for whatever reason, we do not accidentally
|
|
|
|
* set kind_flag incorrectly.
|
|
|
|
*/
|
|
|
|
if (no_bitfield_type_recode) {
|
|
|
|
/* kind_flag only set where there is a bitfield
|
|
|
|
* in the struct.
|
|
|
|
*/
|
|
|
|
type__for_each_data_member(type, pos) {
|
|
|
|
if (pos->bitfield_size) {
|
|
|
|
kind_flag = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-14 14:47:32 +01:00
|
|
|
type_id = btf_elf__add_struct(btfe, kind, type->namespace.name, kind_flag, type->size, type->nr_members);
|
2018-03-06 01:53:50 +01:00
|
|
|
if (type_id < 0)
|
|
|
|
return type_id;
|
|
|
|
|
2018-09-13 18:19:40 +02:00
|
|
|
type__for_each_data_member(type, pos) {
|
dwarf_loader: Use DWARF recommended uniform bit offset scheme
Use uniform bit offset scheme as described in DWARF standard (though
apparently not really followed by major compilers), in which bit offset
is a natural extension of byte offset in both big- and little-endian
architectures.
BEFORE:
1. Bit offsets for little-endian are output as offsets from highest-order bit
of underlying int to highest-order bit of bitfield, so double-backwards for
little-endian arch and counter to how byte offsets are used, which point to
lowest-order bit of underlying type. This makes first bitfield to have bit
offset 27, instead of natural 0.
2. Bit offsets for big-endian are output as expected, by referencing
highest-order bit offset from highest-order bit of underlying int. This is
natural for big-endian platform, e.g., first bitfield has bit offset of 0.
3. Big-endian target also has problem with determining bit holes, because bit
positions have to be calculated differently for little- and big-endian
platforms and previous commit changed pahole logic to follow little-endian
semantics.
4. BTF encoder outputs uniform bit offset for both little- and big-endian
format (following DWARF's recommended bit offset scheme)
5. BTF loader, though, follows DWARF loader's format and outputs little-endian
bit offsets "double-backwards".
$ gcc -g dwarf_test.c -o dwarf_test
$ pahole -F dwarf dwarf_test
struct S {
int j:5; /* 0:27 4 */
int k:6; /* 0:21 4 */
int m:5; /* 0:16 4 */
int n:8; /* 0: 8 4 */
/* size: 4, cachelines: 1, members: 4 */
/* bit_padding: 8 bits */
/* last cacheline: 4 bytes */
};
$ pahole -JV dwarf_test
File dwarf_test:
[1] STRUCT S kind_flag=1 size=4 vlen=4
j type_id=2 bitfield_size=5 bits_offset=0
k type_id=2 bitfield_size=6 bits_offset=5
m type_id=2 bitfield_size=5 bits_offset=11
n type_id=2 bitfield_size=8 bits_offset=16
[2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
$ pahole -F btf dwarf_test
struct S {
int j:5; /* 0:27 4 */
int k:6; /* 0:21 4 */
int m:5; /* 0:16 4 */
int n:8; /* 0: 8 4 */
/* size: 4, cachelines: 1, members: 4 */
/* bit_padding: 8 bits */
/* last cacheline: 4 bytes */
};
$ aarch64-linux-gnu-gcc -mbig-endian -g -c dwarf_test.c -o dwarf_test.be
$ pahole -F dwarf dwarf_test.be
struct S {
/* XXX 27 bits hole, try to pack */
int j:5; /* 0: 0 4 */
/* XXX 245 bits hole, try to pack */
int k:6; /* 0: 5 4 */
/* XXX 245 bits hole, try to pack */
int m:5; /* 0:11 4 */
/* XXX 243 bits hole, try to pack */
int n:8; /* 0:16 4 */
/* size: 4, cachelines: 1, members: 4 */
/* bit holes: 4, sum bit holes: 760 bits */
/* bit_padding: 16 bits */
/* last cacheline: 4 bytes */
/* BRAIN FART ALERT! 4 bytes != 24 (member bits) + 0 (byte holes) + 760 (bit holes), diff = -768 bits */
};
$ pahole -JV dwarf_test.be
File dwarf_test.be:
[1] STRUCT S kind_flag=1 size=4 vlen=4
j type_id=2 bitfield_size=5 bits_offset=0
k type_id=2 bitfield_size=6 bits_offset=5
m type_id=2 bitfield_size=5 bits_offset=11
n type_id=2 bitfield_size=8 bits_offset=16
[2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
$ pahole -F btf dwarf_test.be
struct S {
/* XXX 27 bits hole, try to pack */
int j:5; /* 0: 0 4 */
/* XXX 245 bits hole, try to pack */
int k:6; /* 0: 5 4 */
/* XXX 245 bits hole, try to pack */
int m:5; /* 0:11 4 */
/* XXX 243 bits hole, try to pack */
int n:8; /* 0:16 4 */
/* size: 4, cachelines: 1, members: 4 */
/* bit holes: 4, sum bit holes: 760 bits */
/* bit_padding: 16 bits */
/* last cacheline: 4 bytes */
/* BRAIN FART ALERT! 4 bytes != 24 (member bits) + 0 (byte holes) + 760 (bit holes), diff = -768 bits */
};
AFTER:
1. Same output for little- and big-endian binaries, both for BTF and DWARF
loader.
2. For little-endian target, bit offsets are natural extensions of byte offset,
counting from lowest-order bit of underlying int to lowest-order bit of a
bitfield.
3. BTF encoder still emits correct and natural bit offsets (for both binaries).
4. No more BRAIN FART ALERTs for big-endian.
$ pahole -F dwarf dwarf_test
struct S {
int j:5; /* 0: 0 4 */
int k:6; /* 0: 5 4 */
int m:5; /* 0:11 4 */
int n:8; /* 0:16 4 */
/* size: 4, cachelines: 1, members: 4 */
/* bit_padding: 8 bits */
/* last cacheline: 4 bytes */
};
$ pahole -JV dwarf_test
File dwarf_test:
[1] STRUCT S kind_flag=1 size=4 vlen=4
j type_id=2 bitfield_size=5 bits_offset=0
k type_id=2 bitfield_size=6 bits_offset=5
m type_id=2 bitfield_size=5 bits_offset=11
n type_id=2 bitfield_size=8 bits_offset=16
[2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
$ pahole -F btf dwarf_test
struct S {
int j:5; /* 0: 0 4 */
int k:6; /* 0: 5 4 */
int m:5; /* 0:11 4 */
int n:8; /* 0:16 4 */
/* size: 4, cachelines: 1, members: 4 */
/* bit_padding: 8 bits */
/* last cacheline: 4 bytes */
};
$ pahole -F dwarf dwarf_test.be
struct S {
int j:5; /* 0: 0 4 */
int k:6; /* 0: 5 4 */
int m:5; /* 0:11 4 */
int n:8; /* 0:16 4 */
/* size: 4, cachelines: 1, members: 4 */
/* bit_padding: 8 bits */
/* last cacheline: 4 bytes */
};
$ pahole -JV dwarf_test.be
File dwarf_test.be:
[1] STRUCT S kind_flag=1 size=4 vlen=4
j type_id=2 bitfield_size=5 bits_offset=0
k type_id=2 bitfield_size=6 bits_offset=5
m type_id=2 bitfield_size=5 bits_offset=11
n type_id=2 bitfield_size=8 bits_offset=16
[2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
$ pahole -F btf dwarf_test.be
struct S {
int j:5; /* 0: 0 4 */
int k:6; /* 0: 5 4 */
int m:5; /* 0:11 4 */
int n:8; /* 0:16 4 */
/* size: 4, cachelines: 1, members: 4 */
/* bit_padding: 8 bits */
/* last cacheline: 4 bytes */
};
FOR REFERENCE. Relevant parts of DWARF output from GCC (clang outputs exactly
the same data) for both little- and big-endian binaries:
$ readelf -wi dwarf_test
Contents of the .debug_info section:
<snip>
<1><2d>: Abbrev Number: 2 (DW_TAG_structure_type)
<2e> DW_AT_name : S
<30> DW_AT_byte_size : 4
<31> DW_AT_decl_file : 1
<32> DW_AT_decl_line : 1
<33> DW_AT_decl_column : 8
<34> DW_AT_sibling : <0x71>
<2><38>: Abbrev Number: 3 (DW_TAG_member)
<39> DW_AT_name : j
<3b> DW_AT_decl_file : 1
<3c> DW_AT_decl_line : 2
<3d> DW_AT_decl_column : 6
<3e> DW_AT_type : <0x71>
<42> DW_AT_byte_size : 4
<43> DW_AT_bit_size : 5
<44> DW_AT_bit_offset : 27
<45> DW_AT_data_member_location: 0
<2><46>: Abbrev Number: 3 (DW_TAG_member)
<47> DW_AT_name : k
<49> DW_AT_decl_file : 1
<4a> DW_AT_decl_line : 3
<4b> DW_AT_decl_column : 6
<4c> DW_AT_type : <0x71>
<50> DW_AT_byte_size : 4
<51> DW_AT_bit_size : 6
<52> DW_AT_bit_offset : 21
<53> DW_AT_data_member_location: 0
<2><54>: Abbrev Number: 3 (DW_TAG_member)
<55> DW_AT_name : m
<57> DW_AT_decl_file : 1
<58> DW_AT_decl_line : 4
<59> DW_AT_decl_column : 6
<5a> DW_AT_type : <0x71>
<5e> DW_AT_byte_size : 4
<5f> DW_AT_bit_size : 5
<60> DW_AT_bit_offset : 16
<61> DW_AT_data_member_location: 0
<2><62>: Abbrev Number: 3 (DW_TAG_member)
<63> DW_AT_name : n
<65> DW_AT_decl_file : 1
<66> DW_AT_decl_line : 5
<67> DW_AT_decl_column : 6
<68> DW_AT_type : <0x71>
<6c> DW_AT_byte_size : 4
<6d> DW_AT_bit_size : 8
<6e> DW_AT_bit_offset : 8
<6f> DW_AT_data_member_location: 0
<2><70>: Abbrev Number: 0
<1><71>: Abbrev Number: 4 (DW_TAG_base_type)
<72> DW_AT_byte_size : 4
<73> DW_AT_encoding : 5 (signed)
<74> DW_AT_name : int
<snip>
$ readelf -wi dwarf_test.be
Contents of the .debug_info section:
<snip>
<1><2d>: Abbrev Number: 2 (DW_TAG_structure_type)
<2e> DW_AT_name : S
<30> DW_AT_byte_size : 4
<31> DW_AT_decl_file : 1
<32> DW_AT_decl_line : 1
<33> DW_AT_sibling : <0x6c>
<2><37>: Abbrev Number: 3 (DW_TAG_member)
<38> DW_AT_name : j
<3a> DW_AT_decl_file : 1
<3b> DW_AT_decl_line : 2
<3c> DW_AT_type : <0x6c>
<40> DW_AT_byte_size : 4
<41> DW_AT_bit_size : 5
<42> DW_AT_bit_offset : 0
<43> DW_AT_data_member_location: 0
<2><44>: Abbrev Number: 3 (DW_TAG_member)
<45> DW_AT_name : k
<47> DW_AT_decl_file : 1
<48> DW_AT_decl_line : 3
<49> DW_AT_type : <0x6c>
<4d> DW_AT_byte_size : 4
<4e> DW_AT_bit_size : 6
<4f> DW_AT_bit_offset : 5
<50> DW_AT_data_member_location: 0
<2><51>: Abbrev Number: 3 (DW_TAG_member)
<52> DW_AT_name : m
<54> DW_AT_decl_file : 1
<55> DW_AT_decl_line : 4
<56> DW_AT_type : <0x6c>
<5a> DW_AT_byte_size : 4
<5b> DW_AT_bit_size : 5
<5c> DW_AT_bit_offset : 11
<5d> DW_AT_data_member_location: 0
<2><5e>: Abbrev Number: 3 (DW_TAG_member)
<5f> DW_AT_name : n
<61> DW_AT_decl_file : 1
<62> DW_AT_decl_line : 5
<63> DW_AT_type : <0x6c>
<67> DW_AT_byte_size : 4
<68> DW_AT_bit_size : 8
<69> DW_AT_bit_offset : 16
<6a> DW_AT_data_member_location: 0
<snip>
Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Mark Wielaard <mark@klomp.org>
Cc: Martin KaFai Lau <kafai@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Cc: dwarves@vger.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-03-18 05:23:42 +01:00
|
|
|
/*
|
|
|
|
* dwarf_loader uses DWARF's recommended bit offset addressing
|
|
|
|
* scheme, which conforms to BTF requirement, so no conversion
|
|
|
|
* is required.
|
2018-09-13 18:19:40 +02:00
|
|
|
*/
|
dwarf_loader: Use DWARF recommended uniform bit offset scheme
Use uniform bit offset scheme as described in DWARF standard (though
apparently not really followed by major compilers), in which bit offset
is a natural extension of byte offset in both big- and little-endian
architectures.
BEFORE:
1. Bit offsets for little-endian are output as offsets from highest-order bit
of underlying int to highest-order bit of bitfield, so double-backwards for
little-endian arch and counter to how byte offsets are used, which point to
lowest-order bit of underlying type. This makes first bitfield to have bit
offset 27, instead of natural 0.
2. Bit offsets for big-endian are output as expected, by referencing
highest-order bit offset from highest-order bit of underlying int. This is
natural for big-endian platform, e.g., first bitfield has bit offset of 0.
3. Big-endian target also has problem with determining bit holes, because bit
positions have to be calculated differently for little- and big-endian
platforms and previous commit changed pahole logic to follow little-endian
semantics.
4. BTF encoder outputs uniform bit offset for both little- and big-endian
format (following DWARF's recommended bit offset scheme)
5. BTF loader, though, follows DWARF loader's format and outputs little-endian
bit offsets "double-backwards".
$ gcc -g dwarf_test.c -o dwarf_test
$ pahole -F dwarf dwarf_test
struct S {
int j:5; /* 0:27 4 */
int k:6; /* 0:21 4 */
int m:5; /* 0:16 4 */
int n:8; /* 0: 8 4 */
/* size: 4, cachelines: 1, members: 4 */
/* bit_padding: 8 bits */
/* last cacheline: 4 bytes */
};
$ pahole -JV dwarf_test
File dwarf_test:
[1] STRUCT S kind_flag=1 size=4 vlen=4
j type_id=2 bitfield_size=5 bits_offset=0
k type_id=2 bitfield_size=6 bits_offset=5
m type_id=2 bitfield_size=5 bits_offset=11
n type_id=2 bitfield_size=8 bits_offset=16
[2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
$ pahole -F btf dwarf_test
struct S {
int j:5; /* 0:27 4 */
int k:6; /* 0:21 4 */
int m:5; /* 0:16 4 */
int n:8; /* 0: 8 4 */
/* size: 4, cachelines: 1, members: 4 */
/* bit_padding: 8 bits */
/* last cacheline: 4 bytes */
};
$ aarch64-linux-gnu-gcc -mbig-endian -g -c dwarf_test.c -o dwarf_test.be
$ pahole -F dwarf dwarf_test.be
struct S {
/* XXX 27 bits hole, try to pack */
int j:5; /* 0: 0 4 */
/* XXX 245 bits hole, try to pack */
int k:6; /* 0: 5 4 */
/* XXX 245 bits hole, try to pack */
int m:5; /* 0:11 4 */
/* XXX 243 bits hole, try to pack */
int n:8; /* 0:16 4 */
/* size: 4, cachelines: 1, members: 4 */
/* bit holes: 4, sum bit holes: 760 bits */
/* bit_padding: 16 bits */
/* last cacheline: 4 bytes */
/* BRAIN FART ALERT! 4 bytes != 24 (member bits) + 0 (byte holes) + 760 (bit holes), diff = -768 bits */
};
$ pahole -JV dwarf_test.be
File dwarf_test.be:
[1] STRUCT S kind_flag=1 size=4 vlen=4
j type_id=2 bitfield_size=5 bits_offset=0
k type_id=2 bitfield_size=6 bits_offset=5
m type_id=2 bitfield_size=5 bits_offset=11
n type_id=2 bitfield_size=8 bits_offset=16
[2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
$ pahole -F btf dwarf_test.be
struct S {
/* XXX 27 bits hole, try to pack */
int j:5; /* 0: 0 4 */
/* XXX 245 bits hole, try to pack */
int k:6; /* 0: 5 4 */
/* XXX 245 bits hole, try to pack */
int m:5; /* 0:11 4 */
/* XXX 243 bits hole, try to pack */
int n:8; /* 0:16 4 */
/* size: 4, cachelines: 1, members: 4 */
/* bit holes: 4, sum bit holes: 760 bits */
/* bit_padding: 16 bits */
/* last cacheline: 4 bytes */
/* BRAIN FART ALERT! 4 bytes != 24 (member bits) + 0 (byte holes) + 760 (bit holes), diff = -768 bits */
};
AFTER:
1. Same output for little- and big-endian binaries, both for BTF and DWARF
loader.
2. For little-endian target, bit offsets are natural extensions of byte offset,
counting from lowest-order bit of underlying int to lowest-order bit of a
bitfield.
3. BTF encoder still emits correct and natural bit offsets (for both binaries).
4. No more BRAIN FART ALERTs for big-endian.
$ pahole -F dwarf dwarf_test
struct S {
int j:5; /* 0: 0 4 */
int k:6; /* 0: 5 4 */
int m:5; /* 0:11 4 */
int n:8; /* 0:16 4 */
/* size: 4, cachelines: 1, members: 4 */
/* bit_padding: 8 bits */
/* last cacheline: 4 bytes */
};
$ pahole -JV dwarf_test
File dwarf_test:
[1] STRUCT S kind_flag=1 size=4 vlen=4
j type_id=2 bitfield_size=5 bits_offset=0
k type_id=2 bitfield_size=6 bits_offset=5
m type_id=2 bitfield_size=5 bits_offset=11
n type_id=2 bitfield_size=8 bits_offset=16
[2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
$ pahole -F btf dwarf_test
struct S {
int j:5; /* 0: 0 4 */
int k:6; /* 0: 5 4 */
int m:5; /* 0:11 4 */
int n:8; /* 0:16 4 */
/* size: 4, cachelines: 1, members: 4 */
/* bit_padding: 8 bits */
/* last cacheline: 4 bytes */
};
$ pahole -F dwarf dwarf_test.be
struct S {
int j:5; /* 0: 0 4 */
int k:6; /* 0: 5 4 */
int m:5; /* 0:11 4 */
int n:8; /* 0:16 4 */
/* size: 4, cachelines: 1, members: 4 */
/* bit_padding: 8 bits */
/* last cacheline: 4 bytes */
};
$ pahole -JV dwarf_test.be
File dwarf_test.be:
[1] STRUCT S kind_flag=1 size=4 vlen=4
j type_id=2 bitfield_size=5 bits_offset=0
k type_id=2 bitfield_size=6 bits_offset=5
m type_id=2 bitfield_size=5 bits_offset=11
n type_id=2 bitfield_size=8 bits_offset=16
[2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
$ pahole -F btf dwarf_test.be
struct S {
int j:5; /* 0: 0 4 */
int k:6; /* 0: 5 4 */
int m:5; /* 0:11 4 */
int n:8; /* 0:16 4 */
/* size: 4, cachelines: 1, members: 4 */
/* bit_padding: 8 bits */
/* last cacheline: 4 bytes */
};
FOR REFERENCE. Relevant parts of DWARF output from GCC (clang outputs exactly
the same data) for both little- and big-endian binaries:
$ readelf -wi dwarf_test
Contents of the .debug_info section:
<snip>
<1><2d>: Abbrev Number: 2 (DW_TAG_structure_type)
<2e> DW_AT_name : S
<30> DW_AT_byte_size : 4
<31> DW_AT_decl_file : 1
<32> DW_AT_decl_line : 1
<33> DW_AT_decl_column : 8
<34> DW_AT_sibling : <0x71>
<2><38>: Abbrev Number: 3 (DW_TAG_member)
<39> DW_AT_name : j
<3b> DW_AT_decl_file : 1
<3c> DW_AT_decl_line : 2
<3d> DW_AT_decl_column : 6
<3e> DW_AT_type : <0x71>
<42> DW_AT_byte_size : 4
<43> DW_AT_bit_size : 5
<44> DW_AT_bit_offset : 27
<45> DW_AT_data_member_location: 0
<2><46>: Abbrev Number: 3 (DW_TAG_member)
<47> DW_AT_name : k
<49> DW_AT_decl_file : 1
<4a> DW_AT_decl_line : 3
<4b> DW_AT_decl_column : 6
<4c> DW_AT_type : <0x71>
<50> DW_AT_byte_size : 4
<51> DW_AT_bit_size : 6
<52> DW_AT_bit_offset : 21
<53> DW_AT_data_member_location: 0
<2><54>: Abbrev Number: 3 (DW_TAG_member)
<55> DW_AT_name : m
<57> DW_AT_decl_file : 1
<58> DW_AT_decl_line : 4
<59> DW_AT_decl_column : 6
<5a> DW_AT_type : <0x71>
<5e> DW_AT_byte_size : 4
<5f> DW_AT_bit_size : 5
<60> DW_AT_bit_offset : 16
<61> DW_AT_data_member_location: 0
<2><62>: Abbrev Number: 3 (DW_TAG_member)
<63> DW_AT_name : n
<65> DW_AT_decl_file : 1
<66> DW_AT_decl_line : 5
<67> DW_AT_decl_column : 6
<68> DW_AT_type : <0x71>
<6c> DW_AT_byte_size : 4
<6d> DW_AT_bit_size : 8
<6e> DW_AT_bit_offset : 8
<6f> DW_AT_data_member_location: 0
<2><70>: Abbrev Number: 0
<1><71>: Abbrev Number: 4 (DW_TAG_base_type)
<72> DW_AT_byte_size : 4
<73> DW_AT_encoding : 5 (signed)
<74> DW_AT_name : int
<snip>
$ readelf -wi dwarf_test.be
Contents of the .debug_info section:
<snip>
<1><2d>: Abbrev Number: 2 (DW_TAG_structure_type)
<2e> DW_AT_name : S
<30> DW_AT_byte_size : 4
<31> DW_AT_decl_file : 1
<32> DW_AT_decl_line : 1
<33> DW_AT_sibling : <0x6c>
<2><37>: Abbrev Number: 3 (DW_TAG_member)
<38> DW_AT_name : j
<3a> DW_AT_decl_file : 1
<3b> DW_AT_decl_line : 2
<3c> DW_AT_type : <0x6c>
<40> DW_AT_byte_size : 4
<41> DW_AT_bit_size : 5
<42> DW_AT_bit_offset : 0
<43> DW_AT_data_member_location: 0
<2><44>: Abbrev Number: 3 (DW_TAG_member)
<45> DW_AT_name : k
<47> DW_AT_decl_file : 1
<48> DW_AT_decl_line : 3
<49> DW_AT_type : <0x6c>
<4d> DW_AT_byte_size : 4
<4e> DW_AT_bit_size : 6
<4f> DW_AT_bit_offset : 5
<50> DW_AT_data_member_location: 0
<2><51>: Abbrev Number: 3 (DW_TAG_member)
<52> DW_AT_name : m
<54> DW_AT_decl_file : 1
<55> DW_AT_decl_line : 4
<56> DW_AT_type : <0x6c>
<5a> DW_AT_byte_size : 4
<5b> DW_AT_bit_size : 5
<5c> DW_AT_bit_offset : 11
<5d> DW_AT_data_member_location: 0
<2><5e>: Abbrev Number: 3 (DW_TAG_member)
<5f> DW_AT_name : n
<61> DW_AT_decl_file : 1
<62> DW_AT_decl_line : 5
<63> DW_AT_type : <0x6c>
<67> DW_AT_byte_size : 4
<68> DW_AT_bit_size : 8
<69> DW_AT_bit_offset : 16
<6a> DW_AT_data_member_location: 0
<snip>
Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Mark Wielaard <mark@klomp.org>
Cc: Martin KaFai Lau <kafai@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Cc: dwarves@vger.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-03-18 05:23:42 +01:00
|
|
|
if (btf_elf__add_member(btfe, pos->name, type_id_off + pos->tag.type, kind_flag, pos->bitfield_size, pos->bit_offset))
|
2018-03-06 01:53:50 +01:00
|
|
|
return -1;
|
2018-09-13 18:19:40 +02:00
|
|
|
}
|
2018-03-06 01:53:50 +01:00
|
|
|
|
|
|
|
return type_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t array_type__nelems(struct tag *tag)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
uint32_t nelem = 1;
|
|
|
|
struct array_type *array = tag__array_type(tag);
|
|
|
|
|
|
|
|
for (i = array->dimensions - 1; i >= 0; --i)
|
|
|
|
nelem *= array->nr_entries[i];
|
|
|
|
|
|
|
|
return nelem;
|
|
|
|
}
|
|
|
|
|
2019-02-14 14:47:32 +01:00
|
|
|
static int32_t enumeration_type__encode(struct btf_elf *btfe, struct tag *tag)
|
2018-03-06 01:53:50 +01:00
|
|
|
{
|
|
|
|
struct type *etype = tag__type(tag);
|
|
|
|
struct enumerator *pos;
|
|
|
|
int32_t type_id;
|
|
|
|
|
2019-02-14 14:47:32 +01:00
|
|
|
type_id = btf_elf__add_enum(btfe, etype->namespace.name, etype->size, etype->nr_members);
|
2018-03-06 01:53:50 +01:00
|
|
|
if (type_id < 0)
|
|
|
|
return type_id;
|
|
|
|
|
|
|
|
type__for_each_enumerator(etype, pos)
|
2019-02-14 14:47:32 +01:00
|
|
|
if (btf_elf__add_enum_val(btfe, pos->name, pos->value))
|
2018-03-06 01:53:50 +01:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
return type_id;
|
|
|
|
}
|
|
|
|
|
2019-02-14 14:47:32 +01:00
|
|
|
static int tag__encode_btf(struct tag *tag, uint32_t core_id, struct btf_elf *btfe,
|
2018-12-18 23:09:39 +01:00
|
|
|
uint32_t array_index_id, uint32_t type_id_off)
|
2018-03-06 01:53:50 +01:00
|
|
|
{
|
2018-12-18 23:09:39 +01:00
|
|
|
/* single out type 0 as it represents special type "void" */
|
|
|
|
uint32_t ref_type_id = tag->type == 0 ? 0 : type_id_off + tag->type;
|
|
|
|
|
2018-03-06 01:53:50 +01:00
|
|
|
switch (tag->tag) {
|
|
|
|
case DW_TAG_base_type:
|
2019-02-14 14:47:32 +01:00
|
|
|
return btf_elf__add_base_type(btfe, tag__base_type(tag));
|
2018-03-06 01:53:50 +01:00
|
|
|
case DW_TAG_const_type:
|
2019-02-14 14:47:32 +01:00
|
|
|
return btf_elf__add_ref_type(btfe, BTF_KIND_CONST, ref_type_id, 0, false);
|
2018-03-06 01:53:50 +01:00
|
|
|
case DW_TAG_pointer_type:
|
2019-02-14 14:47:32 +01:00
|
|
|
return btf_elf__add_ref_type(btfe, BTF_KIND_PTR, ref_type_id, 0, false);
|
2018-03-06 01:53:50 +01:00
|
|
|
case DW_TAG_restrict_type:
|
2019-02-14 14:47:32 +01:00
|
|
|
return btf_elf__add_ref_type(btfe, BTF_KIND_RESTRICT, ref_type_id, 0, false);
|
2018-03-06 01:53:50 +01:00
|
|
|
case DW_TAG_volatile_type:
|
2019-02-14 14:47:32 +01:00
|
|
|
return btf_elf__add_ref_type(btfe, BTF_KIND_VOLATILE, ref_type_id, 0, false);
|
2018-03-06 01:53:50 +01:00
|
|
|
case DW_TAG_typedef:
|
2019-02-14 14:47:32 +01:00
|
|
|
return btf_elf__add_ref_type(btfe, BTF_KIND_TYPEDEF, ref_type_id, tag__namespace(tag)->name, false);
|
2018-03-06 01:53:50 +01:00
|
|
|
case DW_TAG_structure_type:
|
|
|
|
case DW_TAG_union_type:
|
|
|
|
case DW_TAG_class_type:
|
|
|
|
if (tag__type(tag)->declaration)
|
2019-02-14 14:47:32 +01:00
|
|
|
return btf_elf__add_ref_type(btfe, BTF_KIND_FWD, 0, tag__namespace(tag)->name, tag->tag == DW_TAG_union_type);
|
2018-03-06 01:53:50 +01:00
|
|
|
else
|
2019-02-14 14:47:32 +01:00
|
|
|
return structure_type__encode(btfe, tag, type_id_off);
|
2018-03-06 01:53:50 +01:00
|
|
|
case DW_TAG_array_type:
|
2019-02-14 14:47:32 +01:00
|
|
|
/* TODO: Encode one dimension at a time. */
|
|
|
|
return btf_elf__add_array(btfe, ref_type_id, array_index_id, array_type__nelems(tag));
|
2018-03-06 01:53:50 +01:00
|
|
|
case DW_TAG_enumeration_type:
|
2019-02-14 14:47:32 +01:00
|
|
|
return enumeration_type__encode(btfe, tag);
|
2018-03-06 01:53:50 +01:00
|
|
|
case DW_TAG_subroutine_type:
|
2019-02-14 14:47:32 +01:00
|
|
|
return btf_elf__add_func_proto(btfe, tag__ftype(tag), type_id_off);
|
2018-03-06 01:53:50 +01:00
|
|
|
default:
|
|
|
|
fprintf(stderr, "Unsupported DW_TAG_%s(0x%x)\n",
|
|
|
|
dwarf_tag_name(tag->tag), tag->tag);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FIXME: Its in the DWARF loader, we have to find a better handoff
|
|
|
|
* mechanizm...
|
|
|
|
*/
|
|
|
|
extern struct strings *strings;
|
|
|
|
|
2019-02-14 14:47:32 +01:00
|
|
|
static struct btf_elf *btfe;
|
2018-12-18 23:09:39 +01:00
|
|
|
static uint32_t array_index_id;
|
|
|
|
|
|
|
|
int btf_encoder__encode()
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
2019-02-14 14:47:32 +01:00
|
|
|
err = btf_elf__encode(btfe, 0);
|
2019-02-14 15:03:31 +01:00
|
|
|
btf_elf__delete(btfe);
|
2019-02-14 14:47:32 +01:00
|
|
|
btfe = NULL;
|
2018-12-18 23:09:39 +01:00
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2018-03-06 01:53:50 +01:00
|
|
|
int cu__encode_btf(struct cu *cu, int verbose)
|
|
|
|
{
|
2018-12-18 23:09:39 +01:00
|
|
|
bool add_index_type = false;
|
|
|
|
uint32_t type_id_off;
|
|
|
|
uint32_t core_id;
|
2018-03-06 01:53:50 +01:00
|
|
|
struct tag *pos;
|
2018-12-18 23:09:39 +01:00
|
|
|
int err = 0;
|
2018-03-06 01:53:50 +01:00
|
|
|
|
2019-02-14 14:47:32 +01:00
|
|
|
if (btfe && strcmp(btfe->filename, cu->filename)) {
|
2018-12-18 23:09:39 +01:00
|
|
|
err = btf_encoder__encode();
|
|
|
|
if (err)
|
|
|
|
goto out;
|
2018-03-06 01:53:50 +01:00
|
|
|
|
2018-12-18 23:09:39 +01:00
|
|
|
/* Finished one file, add one empty line */
|
|
|
|
if (verbose)
|
|
|
|
printf("\n");
|
|
|
|
}
|
2018-03-06 01:53:50 +01:00
|
|
|
|
2019-02-14 14:47:32 +01:00
|
|
|
if (!btfe) {
|
|
|
|
btfe = btf_elf__new(cu->filename, cu->elf);
|
|
|
|
if (!btfe)
|
2018-12-18 23:09:39 +01:00
|
|
|
return -1;
|
2019-02-14 14:47:32 +01:00
|
|
|
btf_elf__set_strings(btfe, &strings->gb);
|
2018-03-06 01:53:50 +01:00
|
|
|
|
2019-03-07 22:04:20 +01:00
|
|
|
/* cu__find_base_type_by_name() takes "type_id_t *id" */
|
|
|
|
type_id_t id;
|
2018-12-18 23:09:39 +01:00
|
|
|
if (!cu__find_base_type_by_name(cu, "int", &id)) {
|
|
|
|
add_index_type = true;
|
|
|
|
id = cu->types_table.nr_entries;
|
|
|
|
}
|
|
|
|
array_index_id = id;
|
|
|
|
|
|
|
|
if (verbose)
|
2019-02-14 14:47:32 +01:00
|
|
|
printf("File %s:\n", btfe->filename);
|
2018-12-18 23:09:39 +01:00
|
|
|
}
|
|
|
|
|
2019-02-14 14:47:32 +01:00
|
|
|
btf_elf__verbose = verbose;
|
|
|
|
type_id_off = btfe->type_index;
|
2018-03-06 01:53:50 +01:00
|
|
|
|
|
|
|
cu__for_each_type(cu, core_id, pos) {
|
2019-02-14 14:47:32 +01:00
|
|
|
int32_t btf_type_id = tag__encode_btf(pos, core_id, btfe, array_index_id, type_id_off);
|
2018-03-06 01:53:50 +01:00
|
|
|
|
|
|
|
if (btf_type_id < 0 ||
|
2018-12-18 23:09:39 +01:00
|
|
|
tag__check_id_drift(pos, core_id, btf_type_id, type_id_off)) {
|
2018-03-06 01:53:50 +01:00
|
|
|
err = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-18 23:09:39 +01:00
|
|
|
if (add_index_type) {
|
2018-03-06 01:53:50 +01:00
|
|
|
struct base_type bt = {};
|
|
|
|
|
|
|
|
bt.name = 0;
|
|
|
|
bt.bit_size = 32;
|
2019-02-14 14:47:32 +01:00
|
|
|
btf_elf__add_base_type(btfe, &bt);
|
2018-03-06 01:53:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (err)
|
2019-02-14 15:03:31 +01:00
|
|
|
btf_elf__delete(btfe);
|
2018-03-06 01:53:50 +01:00
|
|
|
return err;
|
|
|
|
}
|