Commit Graph

125 Commits

Author SHA1 Message Date
Andrii Nakryiko 3c5f2a224a btf_encoder: Preserve and encode exported functions as BTF_KIND_FUNC
Add encoding of DWARF's DW_TAG_subprogram_type into BTF's BTF_KIND_FUNC
(plus corresponding BTF_KIND_FUNC_PROTO). Only exported functions are
converted for now. This allows to capture all the exported kernel
functions, same subset that's exposed through /proc/kallsyms.

Committer testing:

Before:

  $ readelf -SW vmlinux  | grep BTF
    [78] .BTF              PROGBITS        0000000000000000 26a27da9 1e5543 00      0   0  1
  $

After:

  $ pahole -J vmlinux
  $ readelf -SW vmlinux  | grep BTF
    [78] .BTF              PROGBITS        0000000000000000 26a27da9 2d5f47 00      0   0  1
  $

  >>> 0x2d5f47 - 0x1e5543
  985604

The kernel has a lot of functions! :-)

  $ pahole -VJ vmlinux > /tmp/pahole-btf-encoding-verbose-output.txt

  $ grep -w FUNC /tmp/pahole-btf-encoding-verbose-output.txt | wc -l
  22871
  [acme@quaco pahole]$ grep -w FUNC /tmp/pahole-btf-encoding-verbose-output.txt | tail
  [4511543] FUNC copy_from_user_nmi type_id=4511542
  [4512934] FUNC memcpy_page_flushcache type_id=4512933
  [4512936] FUNC __memcpy_flushcache type_id=4512935
  [4512938] FUNC __copy_user_flushcache type_id=4512937
  [4512940] FUNC arch_wb_cache_pmem type_id=4512939
  [4512942] FUNC mcsafe_handle_tail type_id=4512941
  [4512944] FUNC copy_user_handle_tail type_id=4512943
  [4512946] FUNC clear_user type_id=4512945
  [4512948] FUNC __clear_user type_id=4512947
  [4512950] FUNC memcpy type_id=4512949
  $ grep -w FUNC_PROTO /tmp/pahole-btf-encoding-verbose-output.txt | tail
  [4512902] FUNC_PROTO (anon) return=4511725 args=(4512097 (anon), 4511544 (anon))
  [4512933] FUNC_PROTO (anon) return=0 args=(4511598 to, 4511725 page, 4511610 offset, 4511610 len)
  [4512935] FUNC_PROTO (anon) return=0 args=(4511638 _dst, 4511759 _src, 4511610 size)
  [4512937] FUNC_PROTO (anon) return=4511585 args=(4511638 dst, 4511759 src, 4511552 size)
  [4512939] FUNC_PROTO (anon) return=0 args=(4511638 addr, 4511610 size)
  [4512941] FUNC_PROTO (anon) return=4511544 args=(4511598 to, 4511598 from, 4511552 len)
  [4512943] FUNC_PROTO (anon) return=4511544 args=(4511598 to, 4511598 from, 4511552 len)
  [4512945] FUNC_PROTO (anon) return=4511544 args=(4511638 to, 4511544 n)
  [4512947] FUNC_PROTO (anon) return=4511544 args=(4511638 addr, 4511544 size)
  [4512949] FUNC_PROTO (anon) return=4511638 args=(4511638 p, 4511759 q, 4511591 size)
  $ grep -w FUNC_PROTO /tmp/pahole-btf-encoding-verbose-output.txt |grep 4511542
  [4511542] FUNC_PROTO (anon) return=4510159 args=(4510254 to, 4510374 from, 4510159 n)
  $

With a little change to pdwtags to see DW_TAG_subroutine_type, which is
what BTF's KIND_FUNC_PROTO maps to, we see some of those last
prototypes:

[acme@quaco pahole]$ pdwtags -F btf vmlinux  | grep '()(' | tail
void ()(struct insn * insn); /* size: 45404744 */
int ()(struct insn * insn); /* size: 4 */
void ()(struct insn * insn, const void  * kaddr, int buf_len, int x86_64); /* size: 45405032 */
long unsigned int ()(const char  * purpose); /* size: 8 */
void ()(char * to, struct page * page, size_t offset, size_t len); /* size: 45405864 */
void ()(void * _dst, const void  * _src, size_t size); /* size: 45406200 */
long int ()(void * dst, const void  * src, unsigned int size); /* size: 8 */
long unsigned int ()(char * to, char * from, unsigned int len); /* size: 8 */
long unsigned int ()(void * to, long unsigned int n); /* size: 8 */
long unsigned int ()(void * addr, long unsigned int size); /* size: 8 */
[acme@quaco pahole]$

I.e.:

  [4512941] FUNC_PROTO (anon) return=4511544 args=(4511598 to, 4511598 from, 4511552 len)

gets decoded by pdwtags as:

  long unsigned int ()(char * to, char * from, unsigned int len); /* size: 8 */

  $ grep '\[\(4511544\|4511598\|4511550\|4511552\)\]' /tmp/pahole-btf-encoding-verbose-output.txt
  [4511544] INT long unsigned int size=8 bit_offset=0 nr_bits=64 encoding=(none)
  [4511550] INT char size=1 bit_offset=0 nr_bits=8 encoding=(none)
  [4511552] INT unsigned int size=4 bit_offset=0 nr_bits=32 encoding=(none)
  [4511598] PTR (anon) type_id=4511550
  $

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Tested-by: Alexei Starovoitov <ast@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Cc: Yonghong Song <yhs@fb.com>
Cc: dwarves@vger.kernel.org
Cc: kernel-team@fb.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-11-05 09:27:58 -03:00
Arnaldo Carvalho de Melo 24ced5be8a dwarf_loader: Fix array overrun access
Fixing this coverity report:

  Error: OVERRUN (CWE-119): [#def7]
  dwarves-1.13/dwarf_loader.c:54: cond_at_most: Checking "tag < DW_TAG_MIPS_loop" implies that "tag" may be up to 16512 on the true branch.
  dwarves-1.13/dwarf_loader.c:55: overrun-local: Overrunning array "dwarf_tags_warned" of 66 bytes at byte offset 16512 using index "tag" (which evaluates to 16512).
  #   53|
  #   54|   	if (tag < DW_TAG_MIPS_loop) {
  #   55|-> 		if (dwarf_tags_warned[tag])
  #   56|   			return;
  #   57|   		dwarf_tags_warned[tag] = true;

Reported-by: William Cohen <wcohen@redhat.com>
Fixes: c7fa6a9a40 ("dwarf loader: Catch some more template tags")
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-07-01 22:20:14 -03:00
Arnaldo Carvalho de Melo 49c27bdd66 core: Allow the loaders to advertise features they have
For instance, DWARF has DW_AT_alignment, and some output features
require that, so let loaders advertise such things, next patch will use
this info.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-15 14:45:27 -03:00
Arnaldo Carvalho de Melo 1c9c1d6bbd dwarf_loader: Store DW_AT_alignment if available in DW_TAG_{structure,union,class}_type
That is not just for DW_TAG_class_member :-)

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-09 16:57:52 -03:00
Arnaldo Carvalho de Melo d83d9f578f dwarf_loader: Handle DW_TAG_label in inline expansions
Just add it to the current lexblock.

This removes the warnings that started to appear with the fix in the
previous patch, i.e. these:

      die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3aefafe> not handled!
      die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3aeff8a> not handled!
      die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af02e0> not handled!

Reported-by: Jiri Olsa <jolsa@kernel.org>
Cc: Andrii Nakryiko <andriin@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-03 21:34:01 -03:00
Arnaldo Carvalho de Melo 73e545b144 dwarf_loader: Handle unsupported_tag in die__process_inline_expansion
It calls die__process_tag() that can return &unsupported_tag, for
instance for DW_TAG_label, as reported by Jiri, so handle that, which
ends up causing these warnings to appear in pahole:

  struct userfaultfd_wake_range {
  	long unsigned int          start;                /*     0     8 */
  	long unsigned int          len;                  /*     8     8 */

  	/* size: 16, cachelines: 1, members: 2 */
  	/* last cacheline: 16 bytes */
  };
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3aefafe> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3aeff8a> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af02e0> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af1903> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af19bf> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af358d> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af3e87> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af4268> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af46ec> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af4bd3> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af4f8c> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af551e> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af5815> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af5c15> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af5cad> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af79e0> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af7b34> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af7df3> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af806e> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af812c> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3afa8da> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3afaba5> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3afb22e> not handled!
  die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3afb45e> not handled!
  struct kioctx_table {
  	struct callback_head       rcu __attribute__((__aligned__(8)); /*     0    16 */
  	unsigned int               nr;                   /*    16     4 */

  	/* XXX 4 bytes hole, try to pack */

  	struct kioctx *            table[0];             /*    24     0 */

  	/* size: 24, cachelines: 1, members: 3 */
  	/* sum members: 20, holes: 1, sum holes: 4 */
  	/* forced alignments: 1 */
  	/* last cacheline: 24 bytes */
  };
  ^C
  [acme@quaco pahole]$

But at least no segfault takes place. Next csets should take care of it
more properly.

Reported-by: Jiri Olsa <jolsa@kernel.org>
Cc: Andrii Nakryiko <andriin@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-03 21:23:08 -03:00
Arnaldo Carvalho de Melo f31ea292e3 dwarf_loader: Store the DW_AT_alignment if available
DWARF got a DW_AT_alignment as described in:

  http://dwarfstd.org/ShowIssue.php?issue=140528.1

This appeared first in DWARF5:

  http://dwarfstd.org/doc/DWARF5.pdf

In:

 ----------------------------------------------------------------------

Chapter 2.

General Description

2.24    Alignment

A debugging information entry may have a DW_AT_alignment attribute whose
value of class constant is a positive, non-zero, integer describing the
alignment of the entity.

For example, an alignment attribute whose value is 8 indicates that the
entity to which it applies occurs at an address that is a multiple of
eight (not a multiple of 8 or 256)

 ----------------------------------------------------------------------

Use it on a struct present in the running kernel, i.e. not specifying
which ELF file to look for the DWARF info to use:

  $ pahole -C inet_timewait_death_row
  struct inet_timewait_death_row {
  	atomic_t                   tw_count;             /*     0     4 */

  	/* XXX 60 bytes hole, try to pack */

  	/* --- cacheline 1 boundary (64 bytes) --- */
  	struct inet_hashinfo *     hashinfo __attribute__((__aligned__(64)); /*    64     8 */
  	int                        sysctl_max_tw_buckets; /*    72     4 */

  	/* size: 128, cachelines: 2, members: 3 */
  	/* sum members: 16, holes: 1, sum holes: 60 */
  	/* padding: 52 */
  };
  $

Now to do some tweaking to get that "__attribute..." part nicely, hum,
aligned in the pahole output :-)

BTW: the original struct is in the kernel sources:

  include/net/netns/ipv4.h

  struct inet_timewait_death_row {
          atomic_t                tw_count;

          struct inet_hashinfo    *hashinfo ____cacheline_aligned_in_smp;
          int                     sysctl_max_tw_buckets;
  };

Reported-by: Mark Wielaard <mark@klomp.org>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Andrii Nakryiko <andriin@fb.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Yonghong Song <yhs@fb.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-03 18:10:16 -03:00
Andrii Nakryiko c0fdc5e685 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-29 15:55:37 -03:00
Andrii Nakryiko 5104d1bef3 loaders: Record CU's endianness in dwarf/btf/ctf loaders
This patch records for each CU whether it's in little-endian or
big-endian data format. This flag will be used in subsequent commits to
adjust bit offsets where necessary, to make them uniform across
endianness. This patch doesn't have any effect on pahole's output.

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-29 15:55:37 -03:00
Andrii Nakryiko 03d9b6ebca dwarf_loader: Fix bitfield fixup logic for DWARF
This patch uses similar approach to btf_loader's one and
adjusts/calculates bitfield parameters in such a way, that their byte
offset is always naturally aligned according to underlying base type
alignment requirement. This is consistent with btf_loader behavior and
helps to get closer to zero discrepancies between DWARF/BTF.

We also make sure that bitfield_offset never stays negative, which can
surprise some other parts of pahole logic.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: dwarves@vger.kernel.org
Cc: Mark Wielaard <mark@klomp.org>
Cc: Martin KaFai Lau <kafai@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-03-29 15:55:37 -03:00
Andrii Nakryiko 41cf0e3cba dwarf_loader: Don't recode enums and use real enum size in calculations
This patch disables bitfield recoding logic in DWARF loader.
This logic is already disabled during DWARF->BTF conversion and without
it pahole reliably produces correct BTF bitfield offsets.

If this functionality is enabled, we are losing correct enum size
information. So let's disable and probably eventually remove it altogether.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
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-29 15:55:37 -03:00
Andrii Nakryiko 55c96aaed8 loaders: Strip away volatile/const/restrict when fixing bitfields
btf_loader and ctf_loader didn't remove const/volatile/restrict, so
bitfields using modifiers were not adjusted properly.

This patch abstracts logic of stripping aways typedefs and access
modifiers into tag__strip_typedefs_and_modifiers, which handles any
interleaving of typedefs and modifiers. dwarf_loader was adapter to
reuse this function as well, instead of custom goto loop.

REPRO:

  $ cat vc_map.c
  typedef unsigned int u32;
  typedef volatile u32 vu32;
  typedef vu32 vu32_t;

  typedef struct vc_map {
          volatile unsigned int tx: 1;
          vu32_t rx: 1;
          void *x1, *x2;
  } vc_map;

  int main() {
          struct vc_map s;
          return 0;
  }

BEFORE:

  $ ~/pahole/build/pahole -F btf vc_map
  struct vc_map {
          volatile unsigned int      tx:1;                 /*     0: 0  4 */
          vu32_t                     rx:1;                 /*     0: 0  4 */

          /* XXX 30 bits hole, try to pack */
          /* XXX 4 bytes hole, try to pack */

          void *                     x1;                   /*     8     8 */
          void *                     x2;                   /*    16     8 */

          /* size: 24, cachelines: 1, members: 4 */
          /* sum members: 20, holes: 1, sum holes: 4 */
          /* bit holes: 1, sum bit holes: 30 bits */
          /* last cacheline: 24 bytes */
  };

AFTER:

  $ ~/pahole/build/pahole -F btf vc_map
  struct vc_map {
          volatile unsigned int      tx:1;                 /*     0:31  4 */
          vu32_t                     rx:1;                 /*     0:30  4 */

          /* XXX 30 bits hole, try to pack */
          /* XXX 4 bytes hole, try to pack */

          void *                     x1;                   /*     8     8 */
          void *                     x2;                   /*    16     8 */

          /* size: 24, cachelines: 1, members: 4 */
          /* sum members: 20, holes: 1, sum holes: 4 */
          /* bit holes: 1, sum bit holes: 30 bits */
          /* last cacheline: 24 bytes */
  };

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Cc: bpf@vger.kernel.org
Cc: dwarves@vger.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-03-29 15:55:37 -03:00
Arnaldo Carvalho de Melo 5375d06faf dwarves: Introduce type_id_t for use with the type IDs
This is just a prep patch, marking uint16_t IDs as type_id_t, that
points to uint16_t, so no change in the resulting code.

Cc: Andrii Nakryiko <andriin@fb.com>
Tested-by:Andrii Nakryiko <andrii.nakryiko@gmail.com>
Link: https://lore.kernel.org/bpf/CAEf4Bzb0SpvXdDKMMnUof==kp4Y0AP54bKFjeCzX_AsmDm7k7g@mail.gmail.com/
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-03-11 11:44:53 -03:00
Arnaldo Carvalho de Melo c9b2ef034f dwarf: Add cu__add_tag_with_id() to stop using id == -1 to allocate id
the CTF and BTF loaders come already with the id to use, while the DWARF
loader comes with a Dwarf_Off that needs to be converted into the
ptr_table index.

So keep the cu__add_tag(cu, tag, &id) method to ask ask for the index to
be allocated in the ptr_table and the result to come back via the 'id'
parameter, now a uint32_t and introduce a cu__add_tag_with_id(cu, tag, id)
method to indicate that the 'uint32_t id' is the one to use.

With this we can use a uint32_t for the id both on 32-bit and 64-bit
arches.

Reported-by: Andrii Nakryiko <andriin@fb.com>
Tested-by:Andrii Nakryiko <andrii.nakryiko@gmail.com>
Link: https://lore.kernel.org/bpf/CAEf4Bzb0SpvXdDKMMnUof==kp4Y0AP54bKFjeCzX_AsmDm7k7g@mail.gmail.com/
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-03-11 11:44:45 -03:00
Arnaldo Carvalho de Melo 079e6890b7 dwarf_loader: Mark tag__recode_dwarf_bitfield() static
Not used outside the DWARF loader.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-03-07 11:35:17 -03:00
Arnaldo Carvalho de Melo d52c9f9b94 dwarf_loader: Fixup bitfield entry with same number of bits as its base_type
For instance:

  $ cat examples/andrii/hsw_tsx_tuning.c
  // https://lore.kernel.org/bpf/CAEf4BzYoJgxYWX9iS7pda62cnGr5VOC70NGgqPurMNWP_w1daA@mail.gmail.com/

  typedef unsigned long long u64;
  typedef unsigned int u32;

  union hsw_tsx_tuning {
          struct {
                  u32 cycles_last_block     : 32,
                      hle_abort             : 1,
                      rtm_abort             : 1,
                      instruction_abort     : 1,
                      non_instruction_abort : 1,
                      retry                 : 1,
                      data_conflict         : 1,
                      capacity_writes       : 1,
                      capacity_reads        : 1;
          };
          u64        value;
  };

  int main() {
          union hsw_tsx_tuning t;
          return 0;
  }
  $

When recoding the DWARF bitfield sizes we look for a base type that has
32 bits and that has the same name, in this case:

  $ pahole --expand_types examples/andrii/hsw_tsx_tuning-gcc  | grep cycles_last_block
		/* typedef u32 */ unsigned int       cycles_last_block:32;               /*     0: 0  4 */
  $

We look for a 'unsigned int' with 32 bits, the same way as we do for all
the other bitfield entries. That worked well when the bit_size wasn't
the same as existing base type like 'unsigned int', which ended up
making that field get recoded to point to 'unsigned int', effectively
removing the original name, 'u32' in the case above, with the base type
it ultimately points to, 'unsigned int'.

Fix it making it use the same logic as for bit sizes that are different
from the base_type it points to, i.e. go ahead and create a new typedef
pointing to to a new base_type that has the right bit_size.

So, to sum up, before this fix:

$ btfdiff hsw_tsx_tuning-gcc
--- /tmp/btfdiff.dwarf.ErXffk   2019-02-25 10:26:56.863625697 -0300
+++ /tmp/btfdiff.btf.Y5EDdM     2019-02-25 10:26:56.864625706 -0300
@@ -1,6 +1,6 @@
 union hsw_tsx_tuning {
        struct {
-               unsigned int       cycles_last_block:32; /*     0: 0  4 */
+               u32                cycles_last_block:32; /*     0: 0  4 */
                u32                hle_abort:1;        /*     4:31  4 */
                u32                rtm_abort:1;        /*     4:30  4 */
                u32                instruction_abort:1; /*     4:29  4 */
$

Now btfdiff shows there are no differences, DWARF and BTF output produce
the same output, that matches the original struct.

The same tests were performed with vmlinux files produced by me and
Andrii.

Reported-by: Andrii Nakryiko <andriin@fb.com>
Reviewed-by: Andrii Nakryiko <andriin@fb.com>
Tested-by: Andrii Nakryiko <andriin@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Cc: bpf@vger.kernel.org
Cc: dwarves@vger.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-02-25 16:49:10 -03:00
Domenico Andreoli e714d2eaa1 Adopt SPDX-License-Identifier
Signed-off-by: Domenico Andreoli <domenico.andreoli@linux.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-01-18 15:41:48 -03:00
Yonghong Song 8630ce4042 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 b18354f64c ("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-20 11:27:20 -03:00
Arnaldo Carvalho de Melo bfdea37668 dwarves_fprintf: Print the scope of variables
E.g.:

$ pfunct -iVT ../build/v4.18.0+/kernel/sched/core.o  -f tg_cfs_schedulable_down
int tg_cfs_schedulable_down(struct task_group * tg, void * data);
{ /* low_pc=0x10340 */
	struct cfs_schedulable_data * d; /* scope: optimized */       //  6681
	struct cfs_bandwidth * cfs_b; /* scope: optimized */          //  6682
	s64 quota; /* scope: optimized */                             //  6683
	s64 parent_quota; /* scope: register */                       //  6683
	{
		struct cfs_bandwidth * parent_b; /* scope: optimized */ //  6688
		{
			bool branch; /* scope: optimized */           //  6698
			arch_static_branch(struct static_key * key,
						bool branch); /* size=30, low_pc=0x103a3 */ //  6698
		} /* lexblock size=0 */
		{
			s64 __UNIQUE_ID___x272; /* scope: optimized *///  6699
			s64 __UNIQUE_ID___y273; /* scope: optimized *///  6699
		} /* lexblock size=0 */
		normalize_cfs_quota(struct task_group * tg,
					struct cfs_schedulable_data * d); /* size=94, low_pc=0x10355 */ //  6690
	} /* lexblock size=0 */
}/* size: 240, variables: 4 */

This is part of an investigation on how to lookup the struct types associated
to registers in instructions with offsets from registers.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-09-26 16:45:25 -03:00
Arnaldo Carvalho de Melo 465110ec99 dwarves: Add the DWARF location to struct variable
This is DWARF specific, we don't have in CTF, AFAIK, info about where a
variable is put, i.e. in a register? in the stack? etc.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-09-26 16:45:25 -03:00
Arnaldo Carvalho de Melo c65f2cf436 dwarves: Rename variable->location to ->scope
We'll use location in the DWARF sense, i.e. location lists, etc, i.e.
where is this variable? In a register? The stack? etc.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-09-26 16:45:25 -03:00
Yonghong Song eb6bd05766 dwarf_loader: Process DW_AT_count in DW_TAG_subrange_type
For array type, gcc and clang generates dwarf info with different tags.  For
example, with existing pahole,

  $ cat test.c
    int a[5][5];
  $ gcc -c -g test.c
  $ llvm-dwarfdump test.o
    ...
    0x0000001d:   DW_TAG_array_type
                  DW_AT_type    (0x0000003a "int")
                  DW_AT_sibling (0x00000033)

    0x00000026:     DW_TAG_subrange_type
                      DW_AT_type        (0x00000033 "long unsigned int")
                      DW_AT_upper_bound (0x04)

    0x0000002c:     DW_TAG_subrange_type
                      DW_AT_type        (0x00000033 "long unsigned int")
                      DW_AT_upper_bound (0x04)
  $ pahole -JV test.o
    [1] ARRAY (anon) type_id=3 index_type_id=3 nr_elems=25
    [2] INT long unsigned int size=8 bit_offset=0 nr_bits=64 encoding=(none)
    [3] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
  $ clang -c -g test.c
  $ llvm-dwarfdump test.o
    ...
    0x00000033:   DW_TAG_array_type
                    DW_AT_type  (0x00000045 "int")

    0x00000038:     DW_TAG_subrange_type
                      DW_AT_type        (0x0000004c "__ARRAY_SIZE_TYPE__")
                      DW_AT_count       (0x05)

    0x0000003e:     DW_TAG_subrange_type
                      DW_AT_type        (0x0000004c "__ARRAY_SIZE_TYPE__")
                      DW_AT_count       (0x05)
  $ pahole -JV test.o
    [1] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=0
    [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
    [3] INT __ARRAY_SIZE_TYPE__ size=8 bit_offset=0 nr_bits=64 encoding=(none)

Current pahole processed DW_AT_upper_bound under DW_TAG_subrange_type to
get array range, but it did not process DW_AT_count so during pahole
dwarf2btf conversion, the flattened array size is 0.

This patch fixed the issue by processing DW_AT_count properly.
With the change, for clang generated test.o, pahole btf conversion output is:
  $ pahole -JV test.o
    [1] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=25
    [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
    [3] INT __ARRAY_SIZE_TYPE__ size=8 bit_offset=0 nr_bits=64 encoding=(none)

Committer testing:

Before:

  # pahole -C augmented_enter_connect_args augmented_syscalls.bpf.o
  struct augmented_enter_connect_args {
          struct syscall_enter_connect_args args;          /*     0    40 */
          char                       addr[0];              /*    40     0 */

          /* size: 56, cachelines: 1, members: 2 */
          /* padding: 16 */
          /* last cacheline: 56 bytes */
  };
  # file augmented_syscalls.bpf.o
  augmented_syscalls.bpf.o: ELF 64-bit LSB relocatable, *unknown arch 0xf7* version 1 (SYSV), with debug_info, not stripped
  #

After:

  # pahole -C augmented_enter_connect_args augmented_syscalls.bpf.o
  struct augmented_enter_connect_args {
	  struct syscall_enter_connect_args args;          /*     0    40 */
	  char                       addr[14];             /*    40    14 */

	  /* size: 56, cachelines: 1, members: 2 */
	  /* padding: 2 */
	  /* last cacheline: 56 bytes */
  };
  #

Signed-off-by: Yonghong Song <yhs@fb.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Martin KaFai Lau <kafai@fb.com>
Cc: Okash Khawaja <osk@fb.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-08-28 12:40:10 -03:00
Arnaldo Carvalho de Melo f727c22191 dwarf_loader: Initial support for DW_TAG_partial_unit
This allows processing DW_TAG_partial_unit sections, which gets us from
no tags processed in files contained such tags to at least showing the
tags present in those sections.

Further work is required to support DW_TAG_compile_unit sections using
DW_TAG_imported_unit to import those DW_TAG_partial_unit sections, which
will be done by basically readding the contents of the
DW_TAG_partial_unit sections to the DW_TAG_compile_unit sections
importing them and then recoding as if all the tags in the partial units
were in the compile units.

This will make sure we have a contiguous series of types used in a
compile unit so that the converting routines to CTF and BTF can work
just as before.

On a fedora 27 system:

Before:

  $ pahole /usr/lib/debug/usr/lib64/libgtk-3.so.0.2200.19.debug
  die__process: DW_TAG_compile_unit or DW_TAG_type_unit expected got partial_unit!
  $

After:

  $ pahole /usr/lib/debug/usr/lib64/libgtk-3.so.0.2200.19.debug
  struct _GTimeVal {
          glong                      tv_sec;         /*     0     8 */
          glong                      tv_usec;        /*     8     8 */

          /* size: 16, cachelines: 1, members: 2 */
          /* last cacheline: 16 bytes */
  };
  struct _GError {
          GQuark                     domain;         /*     0     4 */
          gint                       code;           /*     4     4 */
          gchar *                    message;        /*     8     8 */

          /* size: 16, cachelines: 1, members: 3 */
          /* last cacheline: 16 bytes */
  };
  struct _GCond {
          gpointer                   p;              /*     0     8 */
          guint                      i[2];           /*     8     8 */

          /* size: 16, cachelines: 1, members: 2 */
          /* last cacheline: 16 bytes */
  };
<SNIP some more structs found in DW_TAG_partial unit sections...>
  struct _GSourceFuncs {
          gboolean                   (*prepare)(GSource *, gint *); /*     0     8 */
          gboolean                   (*check)(GSource *);  /*     8     8 */
          gboolean                   (*dispatch)(GSource *, GSourceFunc, gpointer); /*    16     8 */
          void                       (*finalize)(GSource *); /*    24     8 */
          GSourceFunc                closure_callback;     /*    32     8 */
          GSourceDummyMarshal        closure_marshal;      /*    40     8 */

          /* size: 48, cachelines: 1, members: 6 */
          /* last cacheline: 48 bytes */
  };
  struct _GThreadFunctions {
          GMutex *                   (*mutex_new)(void);   /*     0     8 */
          void                       (*mutex_lock)(GMutex *); /*     8     8 */
          gboolean                   (*mutex_trylock)(GMutex *)tag__recode_dwarf_type: couldn't find 0x74 type for 0x7fc (typedef)!
  tag__recode_dwarf_type: couldn't find 0x7e type for 0x829 (pointer_type)!
  tag__recode_dwarf_type: couldn't find 0x829 type for 0x844 (variable)!
  tag__recode_dwarf_type: couldn't find 0x7e type for 0x850 (variable)!
  tag__recode_dwarf_type: couldn't find 0x22 type for 0x85b (variable)!
  tag__recode_dwarf_type: couldn't find 0x22 type for 0x866 (variable)!
  tag__recode_dwarf_type: couldn't find 0x22 type for 0x871 (variable)!
  namespace__recode_dwarf_types: couldn't find 0x7fc type for 0x8a4 (member)!
  tag__recode_dwarf_type: couldn't find 0xfa type for 0x8e8 (volatile_type)!
  namespace__recode_dwarf_types: couldn't find 0x8b2 type for 0x90d (member)!
  tag__recode_dwarf_type: couldn't find 0x1b type for 0x941 (typedef)!
<SNIP>
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-08-13 16:07:23 -03:00
Arnaldo Carvalho de Melo 327757975b dwarf_loader: Add URL for template tags description
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-05-19 10:31:21 -03:00
Arnaldo Carvalho de Melo f4d5e21fd1 dwarf_loader: Tidy up template tags usage
We need to get it under some ifdefs to avoid breaking the build in
distros where these tags are not defined.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-05-19 10:28:59 -03:00
Arnaldo Carvalho de Melo e12bf99999 dwarf_loader: Do not hash unsupported tags
This causes a segfault:

    at /home/teuf/freesoftware/pahole/dwarf_loader.c:141
    at /home/teuf/freesoftware/pahole/dwarf_loader.c:170
    cu=0x66bc70) at /home/teuf/freesoftware/pahole/dwarf_loader.c:1535
    at /home/teuf/freesoftware/pahole/dwarf_loader.c:1552
    fn=0x7ffff79cd160 <__FUNCTION__.8133> "die__process_class")
    at /home/teuf/freesoftware/pahole/dwarf_loader.c:1593
    at /home/teuf/freesoftware/pahole/dwarf_loader.c:1288

We're not supposed to hash those unsupported tags, so just check for it and
avoid hashing.

Reported-by: Christophe Fergeau <cfergeau@redhat.com>
Cc: Dodji Seketeli <dodji@redhat.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-05-19 10:23:11 -03:00
Arnaldo Carvalho de Melo 3afcfbec9e dwarf_loader: Add DW_TAG_GNU_formal_parameter_pack stub in process_function
Template support is a big TODO entry...

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-05-19 10:21:17 -03:00
Arnaldo Carvalho de Melo 55d9b20dba dwarf_loader: Ignore DW_TAG_dwarf_procedure when processing functions
We have no use for these location expressions yet...

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-05-19 10:09:06 -03:00
Arnaldo Carvalho de Melo 45618c7ec1 dwarf_loader: Initial support for DW_TAG_unspecified_type
Still need to check what to fprintf for this, but at least have it in
the type lists so that we can find it.

Reported-by: Christophe Fergeau <cfergeau@redhat.com>
Cc: Dodji Seketeli <dodji@redhat.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-05-17 10:05:34 -03:00
Arnaldo Carvalho de Melo 658a238b98 dwarf_loader: Stop emitting warnings about DW_TAG_call_site
No tool in this suite has any use for that, so just leave the comments
and pointers to the documentation for these tags and stop bothering the
user.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-05-06 15:20:16 -03:00
Arnaldo Carvalho de Melo 0fbb39291d dwarf_loader: Add support for DW_TAG_restrict_type
I.e. supporting the 'restrict' keyword, emitted by recent compilers:

  [acme@jouet pahole]$ pfunct -P ~/bin/perf |& grep -w restrict
  inline int vprintf(const char  * restrict  __fmt, struct __va_list_tag * __ap);
  inline size_t fread(void * restrict  __ptr, size_t __size, size_t __n, FILE * restrict  __stream);
  inline int vfprintf(FILE * restrict  __stream, const char  * restrict  __fmt, struct __va_list_tag * __ap);
  inline int vasprintf(char * * restrict  __ptr, const char  * restrict  __fmt, struct __va_list_tag * __ap);
  inline char * realpath(const char  * restrict  __name, char * restrict  __resolved);
  inline ssize_t readlink(const char  * restrict  __path, char * restrict  __buf, size_t __len);
  inline char * strcat(char * restrict  __dest, const char  * restrict  __src);
  inline char * fgets(char * restrict  __s, int __n, FILE * restrict  __stream);
  inline int snprintf(char * restrict  __s, size_t __n, const char  * restrict  __fmt, ...);
  inline int sprintf(char * restrict  __s, const char  * restrict  __fmt, ...);
  inline char * strcpy(char * restrict  __dest, const char  * restrict  __src);
  inline int asprintf(char * * restrict  __ptr, const char  * restrict  __fmt, ...);
  inline char * strncpy(char * restrict  __dest, const char  * restrict  __src, size_t __len);
  inline int fprintf(FILE * restrict  __stream, const char  * restrict  __fmt, ...);
  inline int vsnprintf(char * restrict  __s, size_t __n, const char  * restrict  __fmt, struct __va_list_tag * __ap);
  inline int printf(const char  * restrict  __fmt, ...);
  [acme@jouet pahole]$

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-05-06 15:02:17 -03:00
Arnaldo Carvalho de Melo 9df42c6826 dwarves: Initial support for rvalue_reference_type
Need to read more on http://www.artima.com/cppsource/rvalue.html, but
handling it mostly like DW_TAG_typedef so that at least references to it
are resolved, we can get its byte size, etc.

FIXME: look at the vtable parameters, some are resolving to "(null)".

Reported-by: Benjamin Kosnik <bkoz@redhat.com>
Reported-by: Mark Wieelard <mjw@redhat.com>
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=962571
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-03-15 16:37:48 -03:00
Arnaldo Carvalho de Melo 1decb1bc4a dwarf_loader: Check cu__find_type_by_ref result
In class_member__dwarf_recode_bitfield, making it more robust, as
now we're in the process of supporting DW_TAG_partial_unit, where
some types seem to be in some ther unit and thus not being found
at the moment.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-06-05 15:10:39 -03:00
Arnaldo Carvalho de Melo e71353c3fa dwarf_loader: Ignore DW_TAG_dwarf_procedure
It just about having a location expression that is referenced by other
tags to save space, since we don't have use for loc exprs so far, just
ignore it instead of spamming the user with "tag not supported"
warnings.

First seen on binaries generated by gcc 4.9

Reported-by: Diego Elio Pettenò <flameeyes@flameeyes.eu>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2014-11-19 18:15:26 -03:00
Arnaldo Carvalho de Melo c23eab4b12 dwarf_loader: Print unknown tags as an hex number
So that we can look up at /usr/include/dwarf.h for new tags, like.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2014-11-19 17:51:11 -03:00
Masatake YAMATO a8e562a157 dwarf_loader: Use obstack_zalloc when allocating tag
obstack_alloc was used in __tag__alloc to allocate tag object. As the
result some fields of the tag object are not initialized.

Signed-off-by: Masatake YAMATO <yamato@redhat.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2014-07-03 12:32:16 -03:00
Arnaldo Carvalho de Melo 5ecf1aab9e dwarf_loader: Support DW_FORM_data{4,8} for reading class member offsets
Finally found really _large_ structs, like this one in the Linux kernel:

[acme@zoo pahole]$ pahole -C cmp_data /home/acme/git/build/v3.11.0-rc3+/kernel/power/swap.o
struct cmp_data {
	struct task_struct *       thr;                  /*     0     8 */
	atomic_t                   ready;                /*     8     4 */
	atomic_t                   stop;                 /*    12     4 */
	int                        ret;                  /*    16     4 */

	/* XXX 4 bytes hole, try to pack */

	wait_queue_head_t          go;                   /*    24    24 */
	wait_queue_head_t          done;                 /*    48    24 */
	/* --- cacheline 1 boundary (64 bytes) was 8 bytes ago --- */
	size_t                     unc_len;              /*    72     8 */
	size_t                     cmp_len;              /*    80     8 */
	unsigned char              unc[131072];          /*    88 131072 */
	/* --- cacheline 2049 boundary (131136 bytes) was 24 bytes ago --- */
	unsigned char              cmp[143360];          /* 131160 143360 */
	/* --- cacheline 4289 boundary (274496 bytes) was 24 bytes ago --- */
	unsigned char              wrk[16384];           /* 274520 16384 */
	/* --- cacheline 4545 boundary (290880 bytes) was 24 bytes ago --- */

	/* size: 290904, cachelines: 4546, members: 11 */
	/* sum members: 290900, holes: 1, sum holes: 4 */
	/* last cacheline: 24 bytes */
};

The last two members have offsets that takes more than a u16, using
DW_FORM_data4. For extra future proof reasons, add DW_FORM_data8 too ;-)

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2013-08-15 15:06:48 -03:00
Arnaldo Carvalho de Melo 8c6378fd88 dwarves: Support static class data members
Fixes the following BFA:

[acme@sandy pahole]$ pahole brainfart.o
class ios_base {
	enum _Ios_Openmodeconst    in;                   /*     0     4 */
	typedef enum _Ios_Fmtflags fmtflags;

	/* size: 1, cachelines: 1, members: 1 */
	/* padding: 65533 */
	/* last cacheline: 1 bytes */

	/* BRAIN FART ALERT! 1 != 4 + 0(holes), diff = -3 */

};

That now produces:

[acme@sandy pahole]$ build/pahole brainfart.o
class ios_base {
	static enum _Ios_Openmodeconst    in = 8;        /*     0     0 */
	typedef enum _Ios_Fmtflags fmtflags;

	/* size: 1, cachelines: 0, members: 0, static members: 1 */
	/* last cacheline: 1 bytes */
};
[acme@sandy pahole]$

Reported-by: Nicolas <nikos42@gmail.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2012-08-20 14:42:17 -03:00
Arnaldo Carvalho de Melo a54515fa6e dwarves: Stop using 'self'
As Thomas Gleixner wisely pointed out, using 'self' is stupid, it
doesn't convey useful information, so use sensible names.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2012-08-17 18:47:15 -03:00
Arnaldo Carvalho de Melo be7b691756 dwarf_loader: Don't stop processing after finding unsupported tag
After emitting a warning that a tag is not supported __die__process_tag
was returning NULL, making die__process_unit think that the problem
was insufficient memory.

Introduce a global variable 'unsupported_tag' and return it instead,
that way die__process_unit can distinguish ENOMEM from unsupported tags.

Reported-by: Thiago Macieira <thiago.macieira@intel.com>
Tested-by: Thiago Macieira <thiago.macieira@intel.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2012-08-16 14:55:14 -03:00
Arnaldo Carvalho de Melo 50d4bd4a5b dwarf_loader: Stub for DW_TAG_GNU_call_site{_parameter} in inline expansion too
Need to study
http://www.dwarfstd.org/ShowIssue.php?issue=100909.2&type=open to
implement proper support.

Reported-by: Masatake YAMATO <yamato@redhat.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2012-05-15 10:47:29 -03:00
Arnaldo Carvalho de Melo 109dcf9bd0 dwarf loader: Fix some type/tag searches
The 2938a70 changeset incorectly changed some searches that needed to be
done on the all tags hashtable to the type only one.

This lead to problems such as:

[acme@sandy pahole]$ build/pahole /usr/lib/debug/usr/libexec/pt_chown.debug > /dev/null
tag__recode_dwarf_type: couldn't find 0xf7a type for 0x1133 (inlined_subroutine)!
tag__recode_dwarf_type: couldn't find 0x1047 type for 0x1166 (inlined_subroutine)!
tag__recode_dwarf_type: couldn't find 0xf7a type for 0x11a8 (inlined_subroutine)!
lexblock__recode_dwarf_types: couldn't find 0xf7a type for 0x1133 (inlined_subroutine)!
lexblock__recode_dwarf_types: couldn't find 0x1047 type for 0x1166 (inlined_subroutine)!
lexblock__recode_dwarf_types: couldn't find 0xf7a type for 0x11a8 (inlined_subroutine)!
[acme@sandy pahole]$

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2012-05-14 19:41:49 -03:00
Arnaldo Carvalho de Melo 8bd68e0f24 Merge branch 'debug-types' of https://github.com/tromey/dwarves 2012-05-14 14:39:49 -03:00
Arnaldo Carvalho de Melo 851daa958e Merge branch 'remove-unneeded-call' of https://github.com/tromey/dwarves 2012-05-14 14:38:35 -03:00
Arnaldo Carvalho de Melo edc0917ca3 dwarf_loader: Add stub for DW_TAG_GNU_call_site{_parameter}
Need to study http://www.dwarfstd.org/ShowIssue.php?issue=100909.2&type=open
to implement proper support.

Reported-by: Jakub Jelinek <jakub@redhat.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2012-05-09 15:47:02 -03:00
Tom Tromey 2938a70e1e Add support for .debug_types sections.
.debug_types is a new DWARF 4 feature that adds some simple
compression to DWARF.  The basic idea is that identical type
definitions are merged by the linker and put into a new .debug_types
section.

This introduces 'dwarf_off_ref', which is like Dwarf_Off, but carries
a flag indicating if the DIE came from .debug_types.  This allows
future uses to find the DIE properly.

Type units are read a little differently from ordinary CUs.  All type
units are read at once, then types are recoded for them.  (I think
something like this is needed more generally, to support inter-CU
references, but I have not tried that.)

The type unit is also kept around longer, because any other CU may
refer to it.  This necessitated a change to load_steal_kind to replace
the notion of a "stolen" CU with a request by the "stealer" for the
caller to delete the CU when appropriate.

I need elfutils patch 581c3f60e2b1fc7ddaf4260bb5a9cb59f8e3f0d0
to make this work properly; without it I see crashes.

You can make test cases by compiling with '-gdwarf-4 -fdebug-types-section'.
2012-03-22 12:51:20 -06:00
Tom Tromey 5ef26a053d Remove unused field from debug_fmt_ops
Nothing seemed to use the 'tag__orig_type' method in debug_fmt_ops.
It was simpler to remove it than to try to fix it for the next patch.
2012-03-22 12:43:59 -06:00
Tom Tromey 7defe84d73 Correctly handle DW_FORM_flag and add DW_FORM_flag_present support
Before this patch, running pahole on an executable compiled
with -gdwarf-4 gave:

DW_AT_<0x3c>=0x19

0x19 is DW_FORM_flag_present.
Also, DW_FORM_flag takes an argument.
Changing attr_numeric to call dwarf_formflag fixes both problems.
2012-03-20 12:31:08 -06:00
Tom Tromey 4df110dcc4 Remove unnecessary call to dwarf_diecu 2012-03-20 12:30:05 -06:00
Arnaldo Carvalho de Melo 76f687bf49 dwarf_loader: Fix the build on older systems
RHEL5 and Fedora 11 were not building due to the GNU attributes stuff,
cope with that using a define we know is not present in both RHEL5 and
Fedora 11 to #ifdef those parts. Ugly, but _ELFUTILS_PREREQ, i.e.
elfutils/version.h is not present in RHEL5 either.

Reported-by: Jon Stanley <jstanley@fedoraproject.org>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-17 15:33:28 -03:00