Commit Graph

35 Commits

Author SHA1 Message Date
Ilya Leoshkevich de708b3311 btf: Add support for the floating-point types
Some BPF programs compiled on s390 fail to load, because s390
arch-specific linux headers contain float and double types.

Fix as follows:

- Make the DWARF loader fill base_type.float_type.

- Introduce the --btf_gen_floats command-line parameter, so that
  pahole could be used to build both the older and the newer kernels.

- libbpf introduced the support for the floating-point types in commit
  986962fade5, so update the libbpf submodule to that version and use
  the new btf__add_float() function in order to emit the floating-point
  types when not in the compatibility mode.

- Make the BTF loader recognize the new BTF kind.

Example of the resulting entry in the vmlinux BTF:

    [7164] FLOAT 'double' size=8

when building with:

    LLVM_OBJCOPY=${OBJCOPY} ${PAHOLE} -J ${1} --btf_gen_floats

Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com>
Acked-by: Andrii Nakryiko <andrii@kernel.org>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Heiko Carstens <hca@linux.ibm.com>
Cc: Vasily Gorbik <gor@linux.ibm.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>
2021-03-11 14:45:38 -03:00
Arnaldo Carvalho de Melo 7943374ac5 Revert "libbpf: allow to use packaged version"
This reverts commit 82749180b2.

Getting in the way of releasing 1.20, breaking the build of a dwarves
rpm when a libbpf package is installed in a fedora 33 system:

In file included from /home/acme/rpmbuild/BUILD/dwarves-1.20/strings.c:7:
/home/acme/rpmbuild/BUILD/dwarves-1.20/pahole_strings.h:9:10: fatal error: bpf/btf.h: No such file or directory
    9 | #include <bpf/btf.h>
      |          ^~~~~~~~~~~

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2021-02-03 21:45:00 -03:00
Luca Boccassi 82749180b2 libbpf: allow to use packaged version
Add a new CMake option, LIBBPF_EMBEDDED, to switch between the
embedded version and the system version (searched via pkg-config)
of libbpf. Set the embedded version as the default.

Signed-off-by: Luca Boccassi <bluca@debian.org>
Cc: dwarves@vger.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2021-01-21 10:26:25 -03:00
Andrii Nakryiko ace05ba941 btf: Add support for split BTF loading and encoding
Add support for generating split BTF, in which there is a designated base
BTF, containing a base set of types, and a split BTF, which extends main BTF
with extra types, that can reference types and strings from the main BTF.

This is going to be used to generate compact BTFs for kernel modules, with
vmlinux BTF being a main BTF, which all kernel modules are based off of.

These changes rely on patch set [0] to be present in libbpf submodule.

  [0] https://patchwork.kernel.org/project/netdevbpf/list/?series=377859&state=*

Committer notes:

Fixed up wrt ARGP_numeric_version and added a man page entry.

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Cc: bpf@vger.kernel.org
Cc: dwarves@vger.kernel.org
Cc: kernel-team@fb.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-11-11 09:12:44 -03:00
Andrii Nakryiko 5e7ab5b9e0 btf_loader: Handle union forward declaration properly
Differentiate between struct and union forwards in BTF. BTF type
representing forward declaration (BTF_KIND_FWD) encodes whether the
forward declaration is for struct or union type by using kflag. So use
that kflag to create a proper internal representation of forward
declaration.

Tested with btfdiff on vmlinux:

  $ PAHOLE=build/pahole ./btfdiff ~/linux-build/default/vmlinux | wc -l
  0
  $

Also tested manually:

  $ cat test.c
  struct struct_fwd;

  union union_fwd;

  struct s {
          struct struct_fwd *f1;
          union union_fwd *f2;
  };

  int func(struct s *s) {
          return !!s->f1 + !!s->f2;
  }
  $ clang -g -target bpf -c test.c -o test.o
  $ bpftool btf dump file test.o | grep fwd_kind
  [6] FWD 'struct_fwd' fwd_kind=struct
  [8] FWD 'union_fwd' fwd_kind=union
  $ build/pahole -F btf test.o
  struct s {
          struct struct_fwd *        f1;                   /*     0     8 */
          union union_fwd *          f2;                   /*     8     8 */

          /* size: 16, cachelines: 1, members: 2 */
          /* last cacheline: 16 bytes */
  };

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: bpf@vger.kernel.org
Cc: dwarves@vger.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-10-27 08:52:37 -03:00
Andrii Nakryiko 5d863aa7ce btf_loader: Use libbpf to load BTF
Switch BTF loading to completely use libbpf's own struct btf and related
APIs.

BTF encoding is still happening with pahole's own code, so these two
code paths are not sharing anything now. String fetching is happening
based on whether btfe->strings were set to non-NULL pointer by
btf_encoder.

Committer testing:

  $ cp ~/git/build/bpf-next-v5.9.0-rc8+/vmlinux .
  $ readelf -SW vmlinux  | grep BTF
    [24] .BTF      PROGBITS  ffffffff82494ac0 1694ac0 340207 00   A  0  0  1
    [25] .BTF_ids  PROGBITS  ffffffff827d4cc8 19d4cc8 0000a4 00   A  0  0  1
  $ ./btfdiff vmlinux
  $

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Andrii Nakryiko <andrii@kernel.org>
Cc: bpf@vger.kernel.org
Cc: dwarves@vger.kernel.org
Cc: kernel-team@fb.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-10-09 12:43:42 -03:00
Arnaldo Carvalho de Melo aee6808c47 btf_loader: Initialize function->lexblock.tags to fix segfault in pdwtags
pdwtags -F btf vmlinux

Was segfaulting when trying to iterate on the function main lexblock,
which was zeroed instead of INIT_LIST_HEAD'ed, fix it.

This also made pfunct misbehave when used with BTF. pahole unnafected as
it doesn't try to go thru functions in most cases.

Fixes: ccf3eebfcd ("btf_loader: Add support for BTF_KIND_FUNC")
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-10-02 16:23:24 -03:00
Arnaldo Carvalho de Melo dd3c0e9eb0 dwarves: Move the common initialization of fields for 'struct type'
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo 4ece15c37b dwarves: Find common enumerators prefix
Allowing for filters such as 'type==MMAP', equivalent to
'type==PERF_RECORD_MMAP'.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo e0773683fa dwarves: Allow setting a struct/class member as the source of sizeof()
This one just allows that to be set, i.e.:

  $ pahole -C perf_event_header ~/bin/perf
  struct perf_event_header {
  	__u32                      type;                 /*     0     4 */
  	__u16                      misc;                 /*     4     2 */
  	__u16                      size;                 /*     6     2 */

  	/* size: 8, cachelines: 1, members: 3 */
  	/* last cacheline: 8 bytes */
  };
  $ pahole -C 'perf_event_header(sizeof=size)' --seek_bytes=0x348 --count 1 ~/bin/perf < perf.data
  {
  	.type = 0xa,
  	.misc = 0x2,
  	.size = 0x68,
  },
  $

But:

  $ pahole -C 'perf_event_header(sizeof=bla)' --seek_bytes=0x348 --count 1 ~/bin/perf < perf.data
  pahole: the sizeof member 'bla' not found in the 'perf_event_header' type
  $

And:

  $ pahole -C 'perf_event_header(size=misc)' --seek_bytes=0x348 --count 1 ~/bin/perf < perf.data
  pahole: invalid arg 'size' in 'perf_event_header(size=misc)' (known args: sizeof=member)
  $

The next cset will implement sizeof(type) with that modifier, using the
stdin bytes to obtain the size (0x68) in the above case, and then we'll
be able to print a sequence of variable-sized records correctly.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-08-05 15:16:19 -03:00
Arnaldo Carvalho de Melo ccf3eebfcd btf_loader: Add support for BTF_KIND_FUNC
Some changes to the fprintf routines were needed, as BTF has as the
function type just a BTF_KIND_FUNC_PROTO, while DWARF has as the type
for a function its return value type. With a function->btf flag this was
overcome and all the other goodies in pfunct are present, for instance:

  $ pahole -JV examples/tcp.o | grep -w FUNC | head
  [4068] FUNC tcp_init type_id=4067
  [4070] FUNC tcp_abort type_id=4069
  [4072] FUNC tcp_done type_id=4071
  [4074] FUNC tcp_md5_hash_key type_id=4073
  [4076] FUNC tcp_md5_hash_skb_data type_id=4075
  [4078] FUNC tcp_get_md5sig_pool type_id=4077
  [4080] FUNC tcp_alloc_md5sig_pool type_id=4079
  [4082] FUNC compat_tcp_getsockopt type_id=4081
  [4084] FUNC tcp_getsockopt type_id=4083
  [4086] FUNC tcp_get_timestamping_opt_stats type_id=4085
  $

  $ pfunct -F btf examples/tcp.o  | head
  memset
  memcpy
  tcp_enter_memory_pressure
  tcp_leave_memory_pressure
  tcp_init_sock
  tcp_init_transfer
  tcp_poll
  tcp_ioctl
  tcp_splice_read
  sk_stream_alloc_skb
  $

  $ pfunct --prototype -F btf examples/tcp.o | head
  void * memset(void * p, int c, __kernel_size_t size);
  void * memcpy(void * p, const void  * q, __kernel_size_t size);
  void tcp_enter_memory_pressure(struct sock * sk);
  void tcp_leave_memory_pressure(struct sock * sk);
  void tcp_init_sock(struct sock * sk);
  void tcp_init_transfer(struct sock * sk, int bpf_op);
  __poll_t tcp_poll(struct file * file, struct socket * sock, poll_table * wait);
  int tcp_ioctl(struct sock * sk, int cmd, long unsigned int arg);
  ssize_t tcp_splice_read(struct socket * sock, loff_t * ppos, struct pipe_inode_info * pipe, size_t len, unsigned int flags);
  struct sk_buff * sk_stream_alloc_skb(struct sock * sk, int size, gfp_t gfp, bool force_schedule);
  $

Now to ask just for the 'struct sock' 'methods', i.e. functions that
have as one of its arguments a pointer to the given 'class' name:

  $ pfunct --class sock -F btf examples/tcp.o | head
  tcp_abort
  tcp_done
  compat_tcp_getsockopt
  tcp_getsockopt
  tcp_get_info
  compat_tcp_setsockopt
  tcp_setsockopt
  tcp_disconnect
  tcp_write_queue_purge
  tcp_close
  $

Then ask for the prototypes, which requires -V, should have that fixed:

  $ pfunct -V --prototypes --class sock -F btf examples/tcp.o | head
  int tcp_abort(struct sock * sk, int err);
  void tcp_done(struct sock * sk);
  int compat_tcp_getsockopt(struct sock * sk, int level, int optname, char * optval, int * optlen);
  int tcp_getsockopt(struct sock * sk, int level, int optname, char * optval, int * optlen);
  void tcp_get_info(struct sock * sk, struct tcp_info * info);
  int compat_tcp_setsockopt(struct sock * sk, int level, int optname, char * optval, unsigned int optlen);
  int tcp_setsockopt(struct sock * sk, int level, int optname, char * optval, unsigned int optlen);
  int tcp_disconnect(struct sock * sk, int flags);
  void tcp_write_queue_purge(struct sock * sk);
  void tcp_close(struct sock * sk, long int timeout);
  $

Don't like prototypes with parm names, got you covered:

  $ pfunct --no_parm_names -V --prototypes --class sock -F btf examples/tcp.o | head
  int tcp_abort(struct sock *, int);
  void tcp_done(struct sock *);
  int compat_tcp_getsockopt(struct sock *, int, int, char *, int *);
  int tcp_getsockopt(struct sock *, int, int, char *, int *);
  void tcp_get_info(struct sock *, struct tcp_info *);
  int compat_tcp_setsockopt(struct sock *, int, int, char *, unsigned int);
  int tcp_setsockopt(struct sock *, int, int, char *, unsigned int);
  int tcp_disconnect(struct sock *, int);
  void tcp_write_queue_purge(struct sock *);
  void tcp_close(struct sock *, long int);
  $

Don't like long options and want just one function?

  $ pfunct -f tcp_setsockopt -F btf examples/tcp.o
  int tcp_setsockopt(struct sock * sk, int level, int optname, char * optval, unsigned int optlen);
  $

Want to generate compileable code for all of those functions, full with
the necessary types, etc?

  $ pfunct -F btf --compile examples/tcp.o  > a.c
  $ gcc -c -o a.o a.c
  $ pfunct -F dwarf --prototypes --class sock a.o | head
  pfunct: a.o: No debugging information found
  $ gcc -g -c -o a.o a.c
  $ pfunct -V -F dwarf --prototypes --class sock a.o | head
  void tcp_enter_memory_pressure(struct sock * sk);
  void tcp_leave_memory_pressure(struct sock * sk);
  void tcp_init_sock(struct sock * sk);
  void tcp_init_transfer(struct sock * sk, int bpf_op);
  int tcp_ioctl(struct sock * sk, int cmd, long unsigned int arg);
  struct sk_buff * sk_stream_alloc_skb(struct sock * sk, int size, gfp_t gfp, bool force_schedule);
  ssize_t do_tcp_sendpages(struct sock * sk, struct page * page, int offset, size_t size, int flags);
  int tcp_sendpage_locked(struct sock * sk, struct page * page, int offset, size_t size, int flags);
  int tcp_sendpage(struct sock * sk, struct page * page, int offset, size_t size, int flags);
  int tcp_sendmsg_locked(struct sock * sk, struct msghdr * msg, size_t size);
  $

Now lets go full circle and encode BTF for this a.o generated from
source code generated from the original BTF info in that examples/tcp.o
file:

  $ pahole -JV a.o | tail
  [465] FUNC_PROTO (anon) return=35 args=(392 hp, 393 skb, 5 header_len)
  [466] FUNC tcp_md5_hash_skb_data type_id=465
  [467] FUNC_PROTO (anon) return=35 args=(392 hp, 394 key)
  [468] FUNC tcp_md5_hash_key type_id=467
  [469] FUNC_PROTO (anon) return=0 args=(49 sk)
  [470] FUNC tcp_done type_id=469
  [471] FUNC_PROTO (anon) return=35 args=(49 sk, 35 err)
  [472] FUNC tcp_abort type_id=471
  [473] FUNC_PROTO (anon) return=0 args=(void)
  [474] FUNC tcp_init type_id=473
  $

  $ pfunct -F btf -V --prototypes --class=sock a.o | head
  void tcp_enter_memory_pressure(struct sock * sk);
  void tcp_leave_memory_pressure(struct sock * sk);
  void tcp_init_sock(struct sock * sk);
  void tcp_init_transfer(struct sock * sk, int bpf_op);
  int tcp_ioctl(struct sock * sk, int cmd, long unsigned int arg);
  struct sk_buff * sk_stream_alloc_skb(struct sock * sk, int size, gfp_t gfp, bool force_schedule);
  ssize_t do_tcp_sendpages(struct sock * sk, struct page * page, int offset, size_t size, int flags);
  int tcp_sendpage_locked(struct sock * sk, struct page * page, int offset, size_t size, int flags);
  int tcp_sendpage(struct sock * sk, struct page * page, int offset, size_t size, int flags);
  int tcp_sendmsg_locked(struct sock * sk, struct msghdr * msg, size_t size);
  $

Curious about the code generated by 'pfunct -F btf --compile examples/tcp.o?

  http://vger.kernel.org/~acme/pahole/pfunct-F-BTF--compile-examples-tcp.o.txt

Cc: Alexei Starovoitov <ast@fb.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Cc: Andrii Nakryiko <andriin@fb.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Yonghong Song <yhs@fb.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-11-05 12:04:23 -03:00
Arnaldo Carvalho de Melo 910e060b5c btf_loader: Skip BTF_KIND_DATASEC entries
Just skip them, we have no use for them so far, skipping them will allow
us to process the other kinds we have use for.

Cc: Andrii Nakryiko <andriin@fb.com>
Cc: Daniel Bristot de Oliveira <bristot@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Yonghong Song <yhs@fb.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-10-24 21:02:33 -03:00
Arnaldo Carvalho de Melo 96235a74a3 btf_loader: Replace if/else series with a proper switch {}
A switch is the right thing here, use it.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-10-24 20:56:31 -03:00
Arnaldo Carvalho de Melo a4ba2234ff btf_loader: Support BTF_KIND_VAR
Now the output for DWARF and BTF match, modulo warnings about some
BTF_KIND_ still not supported:

  [root@quaco tracebuffer]# pglobal -F dwarf -v bristot.o
  struct ____btf_map_tracebuffer__bristot ____btf_map_tracebuffer__bristot;; /* 0 */

  char                       _license[4];; /* 0 */

  int                        _version;; /* 0 */

  struct bpf_map     tracebuffer__bristot;; /* 0 */

  [root@quaco tracebuffer]# pglobal -F btf -v bristot.o
  BTF: idx: 17, off: 352, Unknown kind 15
  BTF: idx: 18, off: 364, Unknown kind 0
  BTF: idx: 19, off: 376, Unknown kind 15
  BTF: idx: 20, off: 388, Unknown kind 0
  BTF: idx: 21, off: 400, Unknown kind 15
  BTF: idx: 22, off: 412, Unknown kind 0
  BTF: idx: 23, off: 424, Unknown kind 15
  BTF: idx: 24, off: 436, Unknown kind 0
  struct ____btf_map_tracebuffer__bristot ____btf_map_tracebuffer__bristot;; /* 0 */

  char                       _license[4];; /* 0 */

  int                        _version;; /* 0 */

  struct bpf_map     tracebuffer__bristot;; /* 0 */

  [root@quaco tracebuffer]#

Cc: Andrii Nakryiko <andriin@fb.com>
Cc: Daniel Bristot de Oliveira <bristot@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Yonghong Song <yhs@fb.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-10-24 17:14:00 -03:00
Arnaldo Carvalho de Melo 173911ac38 btf_loader: Show the unknown kind numbers
Instead of just telling that some unknown BTF_KIND_ was found in a file,
show the number to help in debugging.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-10-24 16:45:00 -03:00
Arnaldo Carvalho de Melo 092fffe567 btf_loader: Enum values are s32, cast before calling btf_elf__get32()
Addressing this compiler warning:

  /home/acme/git/pahole/btf_loader.c:279:41: error: pointer targets in passing argument 2 of ‘btf_elf__get32’ differ in signedness [-Werror=pointer-sign]
     uint32_t value = btf_elf__get32(btfe, &ep[i].val);
                                           ^~~~~~~~~~
  In file included from /home/acme/git/pahole/btf_loader.c:27:
  /home/acme/git/pahole/libbtf.h:63:10: note: expected ‘uint32_t *’ {aka ‘unsigned int *’} but argument is of type ‘__s32 *’ {aka ‘int *’}
   uint32_t btf_elf__get32(struct btf_elf *btf, uint32_t *p);
            ^~~~~~~~~~~~~~
  cc1: all warnings being treated as errors

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-07-02 12:21:59 -03:00
Arnaldo Carvalho de Melo de5e72bc15 btf_loader: Plug leak when bailing out due to unknown tag
Fixes this coverity report:

  Error: RESOURCE_LEAK (CWE-772): [#def2]
  dwarves-1.13/btf_loader.c:342: alloc_fn: Storage is returned from allocation function "zalloc".
  dwarves-1.13/btf_loader.c:342: var_assign: Assigning: "tag" = storage returned from "zalloc(40UL)".
  dwarves-1.13/btf_loader.c:354: leaked_storage: Variable "tag" going out of scope leaks the storage it points to.
  #  352|   	default:
  #  353|   		printf("%s: FOO %d\n\n", __func__, type);
  #  354|-> 		return 0;
  #  355|   	}
  #  356|

Reported-by: William Cohen <wcohen@redhat.com>
Fixes: 472256d3c5 ("btf_loader: Introduce a loader for the BTF format")
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-07-01 16:30:43 -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 4abc595539 btf_loader: Adjust negative bitfield offsets early on
Bitfield offsets can be negative, if field "borrows" few bits from
following aligned field. This causes a bunch of surprises down the line
in pahole's logic (e.g., for hole calculation logic), so instead of
waiting till printf routines adjust this for display, adjust them early
and keep less surprising semantics.

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>
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 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
Andrii Nakryiko 3526ebebd3 pahole: Use 32-bit integers for type ID iterations within CU
Existing code base assumes that single CU doesn't have more than 65535
types per each CU, which might be a reasonable assumption for DWARF
data. With BTF, though, all we get is single, potentially huge, CU which
can easily have more than 65k types. For example, this is the case for
allyesconfig version of Linux kernel, which has >200k types.

Due to this assumption, libdwarves and other parts of pahole are using
16-bit counters to iterate over entities within CU. This can cause
infinite loops when iterating BTF data, if there are more than 65535
types. This patch changes non-public variables to use 32-bit integers,
where appropriate.

This still leads to invalid reported data when using BTF loader (due to using
(X & 0xFFFF) type ID, instead of X, when X > 65535) and loading huge files,
but at least it's not stuck in an infinite loop anymore.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Reported-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
[ Removed non type ID conversions, for instance for the kind of tag, like in type->namespace.tag.tag, that can remain a uint16_t ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-03-07 11:16:10 -03:00
Andrii Nakryiko 8f4f280163 btf_loader: Simplify fixup code by relying on BTF data more
btf_loader relies on guessing base integral type size for enums and
integers, which is unreliable. There doesn't seem to be a need for that,
as all this information could be extracted from BTF information.

  Before:
  $ PAHOLE=~/local/pahole/build/pahole ./btfdiff ~/local/btf/libc-2.28.so.debug
  base_type__name_to_size: base_type _Float128
  class__fixup_btf_bitfields: unknown base type name "_Float128"!
  base_type__name_to_size: base_type _Float128
  class__fixup_btf_bitfields: unknown base type name "_Float128"!
  base_type__name_to_size: base_type _Float128
  class__fixup_btf_bitfields: unknown base type name "_Float128"!
  base_type__name_to_size: base_type _Float128
  class__fixup_btf_bitfields: unknown base type name "_Float128"!
  base_type__name_to_size: base_type _Float128
  class__fixup_btf_bitfields: unknown base type name "_Float128"!
  base_type__name_to_size: base_type _Float128
  class__fixup_btf_bitfields: unknown base type name "_Float128"!
  --- /tmp/btfdiff.dwarf.aV4wSL   2019-02-25 13:31:54.787923673 -0800
  +++ /tmp/btfdiff.btf.22NQmJ     2019-02-25 13:31:54.802923668 -0800
  @@ -461,9 +461,15 @@ struct La_x86_64_retval {
          uint64_t                   lrv_rdx;              /*     8     8 */
          La_x86_64_xmm              lrv_xmm0;             /*    16    16 */
          La_x86_64_xmm              lrv_xmm1;             /*    32    16 */
  -       long double                lrv_st0;              /*    48    16 */
  +       long double                lrv_st0;              /*    48     8 */
  +
  +       /* XXX 8 bytes hole, try to pack */
  +
          /* --- cacheline 1 boundary (64 bytes) --- */
  -       long double                lrv_st1;              /*    64    16 */
  +       long double                lrv_st1;              /*    64     8 */
  +
  +       /* XXX 8 bytes hole, try to pack */
  +
          La_x86_64_vector           lrv_vector0;          /*    80    64 */
          /* --- cacheline 2 boundary (128 bytes) was 16 bytes ago --- */
          La_x86_64_vector           lrv_vector1;          /*   144    64 */
  @@ -472,6 +478,7 @@ struct La_x86_64_retval {
          __int128                   lrv_bnd1;             /*   224    16 */

          /* size: 240, cachelines: 4, members: 10 */
  +       /* sum members: 224, holes: 2, sum holes: 16 */
          /* last cacheline: 48 bytes */
   };
   struct r_debug {
  @@ -2044,7 +2051,7 @@ union ieee754_float {
          } ieee_nan;                                    /*     0     4 */
   };
   union ieee854_long_double {
  -       long double                d;                  /*     0    16 */
  +       long double                d;                  /*     0     8 */
          struct {
                  unsigned int       mantissa1:32;       /*     0: 0  4 */
                  unsigned int       mantissa0:32;       /*     4: 0  4 */
  @@ -2141,7 +2148,7 @@ struct ucontext_t {
          /* last cacheline: 8 bytes */
   };
   union ieee854_float128 {
  -       _Float128                  d;                  /*     0    16 */
  +       _Float128                  d;                  /*     0     0 */
          struct {
                  unsigned int       mantissa3:32;       /*     0: 0  4 */
                  unsigned int       mantissa2:32;       /*     4: 0  4 */
  @@ -2219,7 +2226,7 @@ union printf_arg {
          long unsigned int          pa_u_long_int;      /*     0     8 */
          long long unsigned int     pa_u_long_long_int; /*     0     8 */
          double                     pa_double;          /*     0     8 */
  -       long double                pa_long_double;     /*     0    16 */
  +       long double                pa_long_double;     /*     0     8 */
          const char  *              pa_string;          /*     0     8 */
          const wchar_t  *           pa_wstring;         /*     0     8 */
          void *                     pa_pointer;         /*     0     8 */

  $ PAHOLE=~/local/pahole/build/pahole ./btfdiff ~/local/btf/libc-2.28.so.debug
  <empty output>

  Still good for kernel image:
  $ PAHOLE=~/local/pahole/build/pahole ./btfdiff ~/local/btf/vmlinux4
  <empty output>

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-02-26 10:41:44 -03:00
Andrii Nakryiko 6586e423d4 btf_loader: Fix bitfield fixup code
Existing code assumes alignment of any integer type, which breaks for
packed structs.

This patch fixes all the current discrepanies between dwarf and btf
loader, when compared using btfdiff.

It preserves bit_offset of non-bitfield members, while for bitfield ones
it re-calculates initial byte/bit offset using natural alignment of the
underlying integer type, which seems to be always the case for
bitfields.

I've tested this on toy examples for both x86-64 and arm targets, there
were no differences reported by btfdiff.

Testing on vmlinux on x86-64 shows only these discrepancies, which are
unrelated to bit offsets:

  $ ./btfdiff /tmp/vmlinux4
  --- /tmp/btfdiff.dwarf.GIVfpr   2019-02-20 12:18:29.138788970 -0800
  +++ /tmp/btfdiff.btf.c3x2KY     2019-02-20 12:18:29.351786365 -0800
  @@ -16884,7 +16884,7 @@ struct pebs_record_nhm {
   };
   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 */
  @@ -26154,7 +26154,7 @@ struct acpi_device_power {
          /* last cacheline: 40 bytes */
   };
   struct acpi_device_perf_flags {
  -       unsigned char              reserved:8;           /*     0: 0  1 */
  +       u8                         reserved:8;           /*     0: 0  1 */

          /* size: 1, cachelines: 1, members: 1 */
          /* last cacheline: 1 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-02-22 18:10:34 -03:00
Arnaldo Carvalho de Melo fe4e1f799c btf_elf: Rename btf_elf__free() to btf_elf__delete()
That is the idiom for free its members and then free itself, 'free' is
just to free its members.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-02-14 17:06:40 -03:00
Arnaldo Carvalho de Melo 6780c4334d btf: Rename 'struct btf' to 'struct btf_elf'
So that we don't clash with libbpf's 'struct btf', in time more internal
state now in 'struct btf_elf' will refer to the equivalent internal
state in libbpf's 'struct btf', as they have lots in common.

Requested-by: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Acked-by: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Martin Lau <kafai@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-02-14 17:06:23 -03:00
Andrii Nakryiko ca86e9416b pahole: use btf.h directly from libbpf
Now that libbpf is a submodule, we don't need to copy/paste btf.h header
with BTF type definitions.

This is a first step in migrating parts of libbtf, btf_encoder and
btf_loader to use libbpf and starting to use btf__dedup().

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-02-11 12:57:06 -03:00
Arnaldo Carvalho de Melo c86960dce5 btf_loader: We can set class_member->type_offset earlier
Because we don't have to lookup the member type that is encoded after
it, i.e. some member fields depend on first processing everything, then
lookup the types to get the sizes, etc.

But the:

    member->byte_offset = member->bit_offset / 8;

Can be done soon after we have member->bit_offset loaded from the BTF
section.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-01-17 15:42:02 -03:00
Arnaldo Carvalho de Melo de3459cc0e btf_loader: BTF encodes the size of enums as bytes not bits
I wrote the BTF loader from the CTF loader, where the size of
enumerations is expressed in bits, while BTF encodes it in bytes, fix
it.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-01-10 15:26:53 -03:00
Yonghong Song f2092f5658 btf: recognize BTF_KIND_FUNC in btf_loader
BTF_KIND_FUNC is generated by llvm (latest trunk, 8.0 or later).
Without BTF_KIND_FUNC support, we will see the following errors,

  -bash-4.4$ cat t.c
  struct t {
    int a;
    char b1:1;
    char b2:3;
    int c;
  } g;
  int main() { return 0; }
  -bash-4.4$ clang -O2 -target bpf -g -c t.c -Xclang -target-feature -Xclang +dwarfris

  -bash-4.4$ pahole -F btf t.o
  BTF: idx: 3, off: 28, Unknown
  struct t {
        int                        a;                    /*     0     4 */

        /* Bitfield combined with previous fields */

        <ERROR(__class__fprintf:1342): 5 not found!>

        /* Bitfield combined with previous fields */

        <ERROR(__class__fprintf:1342): 5 not found!>
        int                        c;                    /*     8     4 */

        /* size: 12, cachelines: 1, members: 4 */
        /* last cacheline: 12 bytes */

        /* BRAIN FART ALERT! 12 != 8 + 0(holes), diff = 4 */

  };
  -bash-4.4$

The reason is that llvm generates BTF_KIND_FUNC which btf_loader does not
recognize.

This patch added support for BTF_KIND_FUNC. Since BTF_KIND_FUNC represents
a defined subprogram and not a real type. A null type is used to
represent BTF_KIND_FUNC to avoid skipping type index.

With this fix:

  -bash-4.4$ pahole -F btf t.o
  struct t {
        int                        a;                    /*     0     4 */
        char                       b1:1;                 /*     4: 0  1 */
        char                       b2:3;                 /*     4: 1  1 */
        int                        c;                    /*     8     4 */

        /* size: 12, cachelines: 1, members: 4 */
        /* last cacheline: 12 bytes */

        /* BRAIN FART ALERT! 12 != 9 + 0(holes), diff = 3 */

  };

Signed-off-by: Yonghong Song <yhs@fb.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Martin KaFai Lau <kafai@fb.com>
Cc: dwarves@vger.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-01-10 15:26:53 -03:00
Yonghong Song 1176661409 btf: Fix kind_flag usage in btf_loader
Commit 2a82d593be ("btf: Add kind_flag support for btf_loader") added
kind_flag supported in btf_loader to get correct bitfield_size and
bit_offset for struct/union members.  The commit unnecessarily stored
the bit in the structure type which is not used later.

So let us remove the kind_flag from the struct type and any other
changes whose purpose is to set this bit.

Signed-off-by: Yonghong Song <yhs@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Martin KaFai Lau <kafai@fb.com>
Fixes: 2a82d593be ("btf: Add kind_flag support for btf_loader")
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-01-10 15:26:53 -03:00
Arnaldo Carvalho de Melo ab0cb33e54 btf_loader: Fixup class_member->bit_offset for !big_endian files
Yonghong explains:

<quote>
The bitfield offset in BTF starts from lower number to bigger one. That
is, it is always following the big endian convention, bitfield_offset
will be smaller if close to the top of structure.

This is different from what dwarf is doing, which will show different
things on little endian vs. big endian.

You can make simple adjustment based on all available info. In
btf_encoder.s, we did similar adjustment for little endian from
dwarf to btf.
</>

So fix it up while loading, so that the rebuilt C output shows the same
thing from BTF and from DWARF.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-01-10 15:26:53 -03:00
Yonghong Song 2a82d593be btf: Add kind_flag support for btf_loader
For struct/union members, the struct/union type info kind_flag is needed
to calculate correct bitfield_size and bit_offset.

  if (kind_flag) {
    bitfield_size = BTF_MEMBER_BITFIELD_SIZE(member->offset);
    bit_offset = BTF_MEMBER_BIT_OFFSET(member->offset);
  } else {
    bitfield_size = 0;
    bit_offset = member->offset;
  }

Note that bitfield_size and bit_offset will not depend on the member
type. The member type will help calculate correct bitfield_offset,
byte_size, byte_offset, bit_size.

For example, with the fix, we will be able to display
bit offset and bitfield size properly.

  -bash-4.4$ cat t.c
  struct t {
    int a:2;
    int b:3;
    int c:2;
  } g;
  -bash-4.4$ gcc -c -O2 -g t.c
  -bash-4.4$ pahole -JV t.o
  File t.o:
  [1] STRUCT t kind_flag=1 size=4 vlen=3
          a type_id=2 bitfield_size=2 bits_offset=0
          b type_id=2 bitfield_size=3 bits_offset=2
          c type_id=2 bitfield_size=2 bits_offset=5
  [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
  -bash-4.4$ pahole -F btf t.o
  struct t {
          int                        a:2;                  /*     0: 0  4 */
          int                        b:3;                  /*     0: 2  4 */
          int                        c:2;                  /*     0: 5  4 */

          /* size: 4, cachelines: 1, members: 3 */
          /* bit_padding: 25 bits */
          /* last cacheline: 4 bytes */
  };

Note that the above offset showing is different from the below dwarf as
BTF bitfield_offset is always the offset from the start of structure,
kindly like big endian encoding. This may need adjustment to be
conforming to the dwarf dump format.

  -bash-4.4$ pahole -F dwarf t.o
  struct t {
          int                        a:2;                  /*     0:30  4 */
          int                        b:3;                  /*     0:27  4 */
          int                        c:2;                  /*     0:25  4 */

          /* size: 4, cachelines: 1, members: 3 */
          /* bit_padding: 25 bits */
          /* last cacheline: 4 bytes */
  };

Signed-off-by: Yonghong Song <yhs@fb.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Martin KaFai Lau <kafai@fb.com>
Cc: dwarves@vger.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-12-21 09:51:12 -03:00
Arnaldo Carvalho de Melo 472256d3c5 btf_loader: Introduce a loader for the BTF format
Show 'struct list_head' from DWARF info:

  $ pahole -C list_head ~/git/build/v4.20-rc5+/net/ipv4/tcp.o
  struct list_head {
	  struct list_head *         next;                 /*     0     8 */
	  struct list_head *         prev;                 /*     8     8 */

	  /* size: 16, cachelines: 1, members: 2 */
	  /* last cacheline: 16 bytes */
  };

Try to show it from BTF, on a file without it:

  $ pahole -F btf -C list_head ~/git/build/v4.20-rc5+/net/ipv4/tcp.o
  pahole: /home/acme/git/build/v4.20-rc5+/net/ipv4/tcp.o: No debugging information found

Encode BTF from the DWARF info:

  $ pahole -J ~/git/build/v4.20-rc5+/net/ipv4/tcp.o

Check that it is there:
  $ readelf -SW ~/git/build/v4.20-rc5+/net/ipv4/tcp.o  | grep BTF
  readelf: /home/acme/git/build/v4.20-rc5+/net/ipv4/tcp.o: Warning: possibly corrupt ELF header - it has a non-zero program header offset, but no program headers
    [136] .BTF              PROGBITS        0000000000000000 101d0e 042edf 00      0   0  1

Now try again printing 'struct list_head' from the BTF info just
encoded:

  $ pahole -F btf -C list_head ~/git/build/v4.20-rc5+/net/ipv4/tcp.o  2> /dev/null
  struct list_head {
	  struct list_head *         next;                 /*     0     8 */
	  struct list_head *         prev;                 /*     8     8 */

	  /* size: 16, cachelines: 1, members: 2 */
	  /* last cacheline: 16 bytes */
  };
  $

There is the bitfields case that BTF desn't have the bit_size info for
bitfield members that makes the output from dwarf to be different than
the one from BTF:

  $ pahole -F btf -C sk_buff ~/git/build/v4.20-rc5+/net/ipv4/tcp.o > /tmp/sk_buff.btf
  $ pahole -F dwarf -C sk_buff ~/git/build/v4.20-rc5+/net/ipv4/tcp.o > /tmp/sk_buff.dwarf
  $ diff -u /tmp/sk_buff.dwarf /tmp/sk_buff.btf
  --- /tmp/sk_buff.dwarf	2018-12-20 14:50:51.428653046 -0300
  +++ /tmp/sk_buff.btf	2018-12-20 14:50:46.302601516 -0300
  @@ -38,45 +38,45 @@
   	__u16                      hdr_len;              /*   138     2 */
   	__u16                      queue_mapping;        /*   140     2 */
   	__u8                       __cloned_offset[0];   /*   142     0 */
  -	__u8                       cloned:1;             /*   142: 7  1 */
  -	__u8                       nohdr:1;              /*   142: 6  1 */
  -	__u8                       fclone:2;             /*   142: 4  1 */
  -	__u8                       peeked:1;             /*   142: 3  1 */
  -	__u8                       head_frag:1;          /*   142: 2  1 */
  -	__u8                       xmit_more:1;          /*   142: 1  1 */
  -	__u8                       pfmemalloc:1;         /*   142: 0  1 */
  +	__u8                       cloned;               /*   142     1 */
  +	__u8                       nohdr;                /*   142     1 */
  +	__u8                       fclone;               /*   142     1 */
  +	__u8                       peeked;               /*   142     1 */
  +	__u8                       head_frag;            /*   142     1 */
  +	__u8                       xmit_more;            /*   142     1 */
  +	__u8                       pfmemalloc;           /*   142     1 */

   	/* XXX 1 byte hole, try to pack */

   	__u32                      headers_start[0];     /*   144     0 */
   	__u8                       __pkt_type_offset[0]; /*   144     0 */
  -	__u8                       pkt_type:3;           /*   144: 5  1 */
  -	__u8                       ignore_df:1;          /*   144: 4  1 */
  -	__u8                       nf_trace:1;           /*   144: 3  1 */
  -	__u8                       ip_summed:2;          /*   144: 1  1 */
  -	__u8                       ooo_okay:1;           /*   144: 0  1 */
  -	__u8                       l4_hash:1;            /*   145: 7  1 */
  -	__u8                       sw_hash:1;            /*   145: 6  1 */
  -	__u8                       wifi_acked_valid:1;   /*   145: 5  1 */
  -	__u8                       wifi_acked:1;         /*   145: 4  1 */
  -	__u8                       no_fcs:1;             /*   145: 3  1 */
  -	__u8                       encapsulation:1;      /*   145: 2  1 */
  -	__u8                       encap_hdr_csum:1;     /*   145: 1  1 */
  -	__u8                       csum_valid:1;         /*   145: 0  1 */
  -	__u8                       csum_complete_sw:1;   /*   146: 7  1 */
  -	__u8                       csum_level:2;         /*   146: 5  1 */
  -	__u8                       csum_not_inet:1;      /*   146: 4  1 */
  -	__u8                       dst_pending_confirm:1; /*   146: 3  1 */
  -	__u8                       ndisc_nodetype:2;     /*   146: 1  1 */
  -	__u8                       ipvs_property:1;      /*   146: 0  1 */
  -	__u8                       inner_protocol_type:1; /*   147: 7  1 */
  -	__u8                       remcsum_offload:1;    /*   147: 6  1 */
  -	__u8                       offload_fwd_mark:1;   /*   147: 5  1 */
  -	__u8                       offload_mr_fwd_mark:1; /*   147: 4  1 */
  -	__u8                       tc_skip_classify:1;   /*   147: 3  1 */
  -	__u8                       tc_at_ingress:1;      /*   147: 2  1 */
  -	__u8                       tc_redirected:1;      /*   147: 1  1 */
  -	__u8                       tc_from_ingress:1;    /*   147: 0  1 */
  +	__u8                       pkt_type;             /*   144     1 */
  +	__u8                       ignore_df;            /*   144     1 */
  +	__u8                       nf_trace;             /*   144     1 */
  +	__u8                       ip_summed;            /*   144     1 */
  +	__u8                       ooo_okay;             /*   144     1 */
  +	__u8                       l4_hash;              /*   145     1 */
  +	__u8                       sw_hash;              /*   145     1 */
  +	__u8                       wifi_acked_valid;     /*   145     1 */
  +	__u8                       wifi_acked;           /*   145     1 */
  +	__u8                       no_fcs;               /*   145     1 */
  +	__u8                       encapsulation;        /*   145     1 */
  +	__u8                       encap_hdr_csum;       /*   145     1 */
  +	__u8                       csum_valid;           /*   145     1 */
  +	__u8                       csum_complete_sw;     /*   146     1 */
  +	__u8                       csum_level;           /*   146     1 */
  +	__u8                       csum_not_inet;        /*   146     1 */
  +	__u8                       dst_pending_confirm;  /*   146     1 */
  +	__u8                       ndisc_nodetype;       /*   146     1 */
  +	__u8                       ipvs_property;        /*   146     1 */
  +	__u8                       inner_protocol_type;  /*   147     1 */
  +	__u8                       remcsum_offload;      /*   147     1 */
  +	__u8                       offload_fwd_mark;     /*   147     1 */
  +	__u8                       offload_mr_fwd_mark;  /*   147     1 */
  +	__u8                       tc_skip_classify;     /*   147     1 */
  +	__u8                       tc_at_ingress;        /*   147     1 */
  +	__u8                       tc_redirected;        /*   147     1 */
  +	__u8                       tc_from_ingress;      /*   147     1 */
   	__u16                      tc_index;             /*   148     2 */

   	/* XXX 2 bytes hole, try to pack */
  $

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-12-20 15:23:35 -03:00