8c92fd2981
365 Commits
Author | SHA1 | Message | Date | |
---|---|---|---|---|
Arnaldo Carvalho de Melo
|
fda1825f0b |
dwarves: Introduce tag_cu_node, so that we can have the leaner tag_cu
With just tag + cu pointers. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
c50b6d37e9 |
pahole: Add infrastructure to have multiple concatenated type_enum
As sometimes we have multiple enums to represent some struct type, like with perf_event_attr->type, that has 'enum perf_event_type' in Linux's UAPI and 'enum perf_user_event_type' for purely userspace types, like the ones synthesized for Intel PT, like PERF_RECORD_AUXTRACE, etc. This patch just transforms type->type_enum into a list, the support for multiple types comes next. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
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> |
||
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> |
||
Arnaldo Carvalho de Melo
|
7c12b234ee |
dwarves: Introduce cus__find_type_by_name()
To find a type in all CUs. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
163c330d31 |
dwarves: Introduce cu__find_enumeration_by_name()
We'll use it in an upcoming feature, to pretty print fields that albeit not declared as an enum, have values coming from one. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
1b2cdda38c |
dwarves: Introduce tag__is_array()
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
cc65946e30 |
dwarves: Adopt tag__is_base_type() from ctrace.c
We'll need it in pahole when pretty printing raw data as structs, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
0b2621d426 |
dwarves: Avoid truncation when concatenating paths for dir entries
To cope with this warning on s390x: /usr/bin/cc -DDWARVES_VERSION=\"v1.16\" -D_GNU_SOURCE -Ddwarves_EXPORTS -I/builddir/build/BUILD/dwarves-1.16 -I/builddir/build/BUILD/dwarves-1.16/lib/bpf/include/uapi -O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -march=zEC12 -mtune=z13 -fasynchronous-unwind-tables -fstack-clash-protection -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -Werror -ggdb -O2 -fPIC -o CMakeFiles/dwarves.dir/libctf.c.o -c /builddir/build/BUILD/dwarves-1.16/libctf.c /builddir/build/BUILD/dwarves-1.16/dwarves.h: In function 'cus__load_dir': /builddir/build/BUILD/dwarves-1.16/dwarves.c:1663:44: error: '%s' directive output may be truncated writing up to 255 bytes into a region of size between 0 and 4095 [-Werror=format-truncation=] 1663 | snprintf(pathname, sizeof(pathname), "%s/%s", | ^~ In file included from /usr/include/stdio.h:867, from /builddir/build/BUILD/dwarves-1.16/dwarves.c:18: /usr/include/bits/stdio2.h:67:10: note: '__builtin___snprintf_chk' output between 2 and 4352 bytes into a destination of size 4096 67 | return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 68 | __bos (__s), __fmt, __va_arg_pack ()); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /builddir/build/BUILD/dwarves-1.16/dwarves.c:1663:44: error: '%s' directive output may be truncated writing up to 255 bytes into a region of size between 0 and 4095 [-Werror=format-truncation=] 1663 | snprintf(pathname, sizeof(pathname), "%s/%s", | ^~ In file included from /usr/include/stdio.h:867, from /builddir/build/BUILD/dwarves-1.16/dwarves.c:18: /usr/include/bits/stdio2.h:67:10: note: '__builtin___snprintf_chk' output between 2 and 4352 bytes into a destination of size 4096 Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
d7b3510795 |
dwarves: Don't use conf if its NULL in cus__load_running_kernel()
Don't use it if NULL, fixing a segfault with dtagnames as reported in: https://bugzilla.redhat.com/show_bug.cgi?id=1795379 Reported-by: Jan Pokorný <jpokorny@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
dde3eb086d |
dwarves: Make list__for_all_tags() more robust
Return if passed a zeroed list or an empty one. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
ded5d36f9c |
dwarves: Introduce cu__find_type_by_name
To look at all the 'struct type' descendants, like enums, typedefs, structs, unions, etc. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
617f5ac2e6 |
dwarves: Move BTF loader ahead of the CTF one
When we call the tools without specifying a file format, all the available format loaders are called, in sequence, till we find the relevant information, now that we support raw BTF we better move it in front of the CTF one, as it is way more common in the Linux community. With this we will not see this warning anymore: $ pahole -C list_head /sys/kernel/btf/vmlinux ctf__new: cannot get elf header. 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 */ }; $ That warning has to go, so that other formats, if present after the CTF one, can be tried. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
cdd5e1399b |
btf loader: Support raw BTF as available in /sys/kernel/btf/vmlinux
Be it automatically when no -F option is passed and /sys/kernel/btf/vmlinux is available, or when /sys/kernel/btf/vmlinux is passed as the filename to the tool, i.e.: $ pahole -C list_head 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 */ }; $ strace -e openat pahole -C list_head |& grep /sys/kernel/btf/ openat(AT_FDCWD, "/sys/kernel/btf/vmlinux", O_RDONLY) = 3 $ $ pahole -C list_head /sys/kernel/btf/vmlinux 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 */ }; $ If one wants to grab the matching vmlinux to use its DWARF info instead, which is useful to compare the results with what we have from BTF, for instance, its just a matter of using '-F dwarf'. This in turn shows something that at first came as a surprise, but then has a simple explanation: For very common data structures, that will probably appear in all of the DWARF CUs (Compilation Units), like 'struct list_head', using '-F dwarf' is faster: [acme@quaco pahole]$ perf stat -e cycles pahole -F btf -C list_head > /dev/null Performance counter stats for 'pahole -F btf -C list_head': 45,722,518 cycles:u 0.023717300 seconds time elapsed 0.016474000 seconds user 0.007212000 seconds sys [acme@quaco pahole]$ perf stat -e cycles pahole -F dwarf -C list_head > /dev/null Performance counter stats for 'pahole -F dwarf -C list_head': 14,170,321 cycles:u 0.006668904 seconds time elapsed 0.005562000 seconds user 0.001109000 seconds sys [acme@quaco pahole]$ But for something that is more specific to a subsystem, the DWARF loader will have to process way more stuff till it gets to that struct: $ perf stat -e cycles pahole -F dwarf -C tcp_sock > /dev/null Performance counter stats for 'pahole -F dwarf -C tcp_sock': 31,579,795,238 cycles:u 8.332272930 seconds time elapsed 8.032124000 seconds user 0.286537000 seconds sys $ While using the BTF loader the time should be constant, as it loads everything from /sys/kernel/btf/vmlinux: $ perf stat -e cycles pahole -F btf -C tcp_sock > /dev/null Performance counter stats for 'pahole -F btf -C tcp_sock': 48,823,488 cycles:u 0.024102760 seconds time elapsed 0.012035000 seconds user 0.012046000 seconds sys $ Above I used '-F btf' just to show that it can be used, but its not really needed, i.e. those are equivalent: $ strace -e openat pahole -F btf -C list_head |& grep /sys/kernel/btf/vmlinux openat(AT_FDCWD, "/sys/kernel/btf/vmlinux", O_RDONLY) = 3 $ strace -e openat pahole -C list_head |& grep /sys/kernel/btf/vmlinux openat(AT_FDCWD, "/sys/kernel/btf/vmlinux", O_RDONLY) = 3 $ The btf_raw__load() function that ends up being grafted into the preexisting btf_elf routines was based on libbpf's btf_load_raw(). Acked-by: Alexei Starovoitov <ast@fb.com> Cc: Andrii Nakryiko <andriin@fb.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
5c590fc29d |
ptr_table: Zero out new id ranges
Since we iterate over these ptr_tables entry by entry, we better zero out the new ranges, i.e. whatever is after the previous allocated space up to the new one, returned by realloc. Fixes a bug found using pahole -F btf on a vmlinux file, now btfdiff on this same file comes clean, i.e. the output from its BTF tags is the same as with its DWARF ones, for the features present in both type information formats. Tested-by: Alexei Starovoitov <ast@fb.com> Cc: Andrii Nakryiko <andrii.nakryiko@gmail.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Gareth Lloyd
|
be37b64aef |
dwarves: Ignore static members for alignment
It was possible to have an infinite loop trying to determine the alignment of a type that had a static data member of its own type. Example code illustrating the problem. struct X { static X thing; }; Committer testing: Before: $ pwd /home/acme/git/pahole/examples/gareth $ cat static_member.cpp struct X { static X thing; }; static struct X f; $ g++ -g -c static_member.cpp -o static_member.o $ pahole static_member.o Segmentation fault (core dumped) $ After: $ pahole static_member.o struct X { static struct X thing; /* 0 0 */ /* XXX last struct has 1 byte of padding */ /* size: 1, cachelines: 0, members: 0, static members: 1 */ /* padding: 1 */ /* paddings: 1, sum paddings: 1 */ /* last cacheline: 1 bytes */ }; $ Signed-off-by: Gareth Lloyd <gareth.lloyd@uk.ibm.com> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
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> |
||
Arnaldo Carvalho de Melo
|
5965ce015e |
dwarves: Fix ptr_table__add_with_id() handling of pt->nr_entries
To agree with what ptr_table__add() does, i.e. the number of entries (pt->nr_entries) is in fact the max id number in the table + 1, we can't allow to add an id that is not contiguous and simply increment nr_entries, as cu__for_each_variable(cu, id, pos) will iterate based on nr_entries, skipping NULL buckets. Detected when adding support for BTF variables, that share the id space with types, but end up in different ptr_tables, so we end up with: cu__table_add_tag(BTF_KIND_VAR) ptr_table__add_with_id(cu->tags_table) but: cu__table_add_tag() ptr_table__add_with_id(cu->types_table) for all the other BTF_KIND_s, fix it so that ptr->nr_entries copes with not receiving strictly monotonically incremented by one ids. We'll have to fix this more properly by defining which tags are supported by some debugging format and this what should go to a ptr_table so that we have all its entries monononically incremented by one and avoid tag__check_id_drift() returning true in the per debugging format encoders (e.g.: btf_encoder.c). This patch allows us to move forward and apply the BTF_KIND_VAR patch where we'll at least produce, for global variables and a simple BPF program with both DWARF and BTF tags the same results: [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> |
||
Arnaldo Carvalho de Melo
|
fe87354c31 |
dwarves: Ditch unused asprintf() function
Not used at all, brought when adding vmlinux searching, but ended up not being used, ditch it. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
60c73a7698 |
dwarves: We need to consistently check if 'conf was specified
Addresses this coverity report entry: Error: FORWARD_NULL (CWE-476): [#def12] dwarves-1.13/dwarves.c:1708: var_compare_op: Comparing "conf" to null implies that "conf" might be null. dwarves-1.13/dwarves.c:1743: var_deref_op: Dereferencing null pointer "conf". # 1741| # 1742| while (debug_fmt_table[i] != NULL) { # 1743|-> if (conf->conf_fprintf) # 1744| conf->conf_fprintf->has_alignment_info = debug_fmt_table[i]->has_alignment_info; # 1745| if (debug_fmt_table[i]->load_file(cus, conf, filename) == 0) The first check setting added by |
||
Arnaldo Carvalho de Melo
|
5fdfd09a6b |
dwarves: Fix check in type__find_first_biggest_size_base_type_member()
We stopped scrubbing type when looking for type->type by assigning the
result of cu__type(cu, type->type) to a temporary 'tag' variable but
continued to check the result of cu__type() looking at type... Check
'tag' instead.
Addresses these coverity report entries:
Error: NULL_RETURNS (CWE-476): [#def9]
dwarves-1.13/dwarves.c:333: returned_null: "cu__type" returns "NULL" (checked 54 out of 62 times).
dwarves-1.13/dwarves.c:333: var_assigned: Assigning: "tag" = "NULL" return value from "cu__type".
dwarves-1.13/dwarves.c:338: alias: Assigning: "type" = "tag". Both pointers are now "NULL".
dwarves-1.13/dwarves.c:312: dereference: Dereferencing "type", which is known to be "NULL".
dwarves-1.13/codiff.c:137: example_assign: Example 1: Assigning: "old_type" = return value from "cu__type(old_cu, old->tag.type)".
dwarves-1.13/codiff.c:141: example_checked: Example 1 (cont.): "old_type" has its value checked in "old_type == NULL".
dwarves-1.13/ctracer.c:356: example_assign: Example 2: Assigning: "type" = return value from "cu__type(cu, tag->type)".
dwarves-1.13/ctracer.c:358: example_checked: Example 2 (cont.): "type" has its value checked in "type == NULL".
dwarves-1.13/dwarves.c:914: example_assign: Example 3: Assigning: "type" = return value from "cu__type(cu, tag->type)".
dwarves-1.13/dwarves.c:916: example_checked: Example 3 (cont.): "type" has its value checked in "type == NULL".
dwarves-1.13/dwarves.c:941: example_assign: Example 4: Assigning: "tag" = return value from "cu__type(cu, var->ip.tag.type)".
dwarves-1.13/dwarves.c:942: example_checked: Example 4 (cont.): "tag" has its value checked in "tag != NULL".
dwarves-1.13/dwarves_emit.c:139: example_assign: Example 5: Assigning: "ptr_type" = return value from "cu__type(cu, type->type)".
dwarves-1.13/dwarves_emit.c:141: example_checked: Example 5 (cont.): "ptr_type" has its value checked in "ptr_type == NULL".
# 310| }
# 311| reevaluate:
# 312|-> switch (type->tag) {
# 313| case DW_TAG_base_type:
# 314|
Error: REVERSE_INULL (CWE-476): [#def10]
dwarves-1.13/dwarves.c:312: deref_ptr: Directly dereferencing pointer "type".
dwarves-1.13/dwarves.c:334: check_after_deref: Null-checking "type" suggests that it may be null, but it has already been dereferenced on all paths leading to the check.
# 332| case DW_TAG_volatile_type: {
# 333| struct tag *tag = cu__type(cu, type->type);
# 334|-> if (type == NULL) {
# 335| tag__id_not_found_fprintf(stderr, type->type);
# 336| continue;
Looking at this last one shows the problem in detail, check for NULL to
then deref it, phew.
This is just used in 'pahole --show_first_biggest_size_base_type_member'
and when fixing up alignment in the --reorganize code tho.
Reported-by: William Cohen <wcohen@redhat.com>
Fixes:
|
||
Arnaldo Carvalho de Melo
|
511a791294 |
dwarves: Remove unused variable
The 'last' variable is not being used at all, ditch it. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
c8fc6f5a7a |
core: Use unnatural alignment of struct embedded in another to infer __packed__
Since we don't have something like DW_AT_alignment for __attribute__((__packed__)), we need to use whatever hints that are there in the alignments to figure out if a naturally packed struct has the __attribute__((packed)) in the original sources, because that is needed to waiver its natural alignment requisites. For instance, /* Used at: btrfs.c */ /* <1e7b> /home/acme/git/pahole/btrfs.c:199 */ struct btrfs_block_group_cache { struct btrfs_key key; /* 0 17 */ struct btrfs_block_group_item item; /* 17 24 */ /* XXX 7 bytes hole, try to pack */ struct btrfs_fs_info * fs_info; /* 48 8 */ struct inode * inode; /* 56 8 */ In the original source code, btrfs_block_group_item is marked __packed__, and being so, even seemingly unnecessarily, makes it, when embedded in another struct, like the above, forfeit its natural alingment, that would be 8 bytes, and instead appear right at the 17th byte offset... struct btrfs_block_group_item { __le64 used; /* 0 8 */ __le64 chunk_objectid; /* 8 8 */ __le64 flags; /* 16 8 */ /* size: 24, cachelines: 1, members: 3 */ /* last cacheline: 24 bytes */ } __attribute__((__packed__)); So we need to, seeing its use at a unnatural offset, go backwards to the btrfs_block_group_item pahole internal data structure, 'struct type' and mark is_packed field as 'true', despite it not looking like a packed struct. Same thing with: struct ieee80211_mcs_info { u8 rx_mask[10]; /* 0 10 */ __le16 rx_highest; /* 10 2 */ u8 tx_params; /* 12 1 */ u8 reserved[3]; /* 13 3 */ /* size: 16, cachelines: 1, members: 4 */ /* last cacheline: 16 bytes */ }; That is naturally aligned and as 16 bytes, a power of two, then when it appears at the end of: $ pahole -IC ieee80211_sta_ht_cap vht.o /* Used at: vht.c */ /* <31ea> /home/acme/git/pahole/vht.c:1769 */ struct ieee80211_sta_ht_cap { u16 cap; /* 0 2 */ bool ht_supported; /* 2 1 */ u8 ampdu_factor; /* 3 1 */ u8 ampdu_density; /* 4 1 */ /* XXX 1 byte hole, try to pack */ struct ieee80211_mcs_info mcs; /* 6 16 */ /* size: 22, cachelines: 1, members: 5 */ /* sum members: 21, holes: 1, sum holes: 1 */ /* last cacheline: 22 bytes */ }; $ We get that one byte hole if ieee80211_mcs_info isn't marked __packed__, as soon as we mark it: $ pahole -IC ieee80211_sta_ht_cap vht.o /* Used at: vht.c */ /* <31ea> /home/acme/git/pahole/vht.c:1769 */ struct ieee80211_sta_ht_cap { u16 cap; /* 0 2 */ bool ht_supported; /* 2 1 */ u8 ampdu_factor; /* 3 1 */ u8 ampdu_density; /* 4 1 */ struct ieee80211_mcs_info mcs; /* 5 16 */ /* size: 22, cachelines: 1, members: 5 */ /* padding: 1 */ /* last cacheline: 22 bytes */ }; [acme@quaco pahole]$ It works, so __packed__ in this case just says: trow away the natural alignment, make it 1 in whatever container structs. So, before emitting the types for some struct, we go back looking at each of its members and checking for such unnatural offsets, marking the types as __packed__. Now: $ pfunct --compile /home/acme/git/build/v5.1-rc4+/net/mac80211/vht.o | grep "^struct ieee80211_mcs_info" -A8 struct ieee80211_mcs_info { u8 rx_mask[10]; /* 0 10 */ __le16 rx_highest; /* 10 2 */ u8 tx_params; /* 12 1 */ u8 reserved[3]; /* 13 3 */ /* size: 16, cachelines: 1, members: 4 */ /* last cacheline: 16 bytes */ } __attribute__((__packed__)); $ $ pfunct --compile /home/acme/git/build/v5.1-rc4+/fs/btrfs/free-space-tree.o | grep "^struct btrfs_block_group_item" -A7 struct btrfs_block_group_item { __le64 used; /* 0 8 */ __le64 chunk_objectid; /* 8 8 */ __le64 flags; /* 16 8 */ /* size: 24, cachelines: 1, members: 3 */ /* last cacheline: 24 bytes */ } __attribute__((__packed__)); $ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
8471736f3c |
core: Cope with zero sized types when looking for natural alignment
We'll use it in a division, so make it 1, solving cases like:
$ pahole examples/tcp.o -C u64_stats_sync
struct u64_stats_sync {
/* size: 0, cachelines: 0, members: 0 */
};
$
$ pahole -C bpf_prog_stats examples/tcp.o
struct bpf_prog_stats {
u64 cnt; /* 0 8 */
u64 nsecs; /* 8 8 */
struct u64_stats_sync syncp; /* 16 0 */
/* size: 16, cachelines: 1, members: 3 */
/* last cacheline: 16 bytes */
};
$
That were sefaulting.
Fixes:
|
||
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> |
||
Arnaldo Carvalho de Melo
|
dc6b9437a3 |
emit: Handle structs with DW_AT_alignment=1 meaning __packed__
In the following struct the ceph_entity_addr entries all appear marked with a __attribute__((__aligned__(8)), which, for the first two members of this type, 'peer_addr' and 'peer_addr_for_me', don't cause the regenerated struct to differ in layout from the original layout put in place by the compiler as per the original source code. But the third member of this type, 'actual_peer_addr' ends up in a different offset, even in a different cacheline, here is how it looks like in the code generated from the original source code, at offset 568. char in_banner[30]; /* 472 30 */ struct ceph_msg_connect out_connect; /* 502 33 */ /* --- cacheline 8 boundary (512 bytes) was 23 bytes ago --- */ struct ceph_msg_connect_reply in_reply; /* 535 26 */ struct ceph_entity_addr actual_peer_addr __attribute__((__aligned__(1))); /* 561 136 */ /* --- cacheline 10 boundary (640 bytes) was 57 bytes ago --- */ struct ceph_msg_header out_hdr; /* 697 53 */ /* XXX 2 bytes hole, try to pack */ /* --- cacheline 11 boundary (704 bytes) was 48 bytes ago --- */ And here is how it looks like when built from the regenerated source code, at offset 568: $ pfunct --compile /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o > ceph.c $ gcc -g -c ceph.c $ pahole -C ceph_connection ceph.o | head -46 struct ceph_connection { void * private; /* 0 8 */ const struct ceph_connection_operations * ops; /* 8 8 */ struct ceph_messenger * msgr; /* 16 8 */ atomic_t sock_state; /* 24 4 */ /* XXX 4 bytes hole, try to pack */ struct socket * sock; /* 32 8 */ struct ceph_entity_addr peer_addr __attribute__((__aligned__(8))); /* 40 136 */ /* --- cacheline 2 boundary (128 bytes) was 48 bytes ago --- */ struct ceph_entity_addr peer_addr_for_me __attribute__((__aligned__(8))); /* 176 136 */ /* --- cacheline 4 boundary (256 bytes) was 56 bytes ago --- */ long unsigned int flags; /* 312 8 */ /* --- cacheline 5 boundary (320 bytes) --- */ long unsigned int state; /* 320 8 */ const char * error_msg; /* 328 8 */ struct ceph_entity_name peer_name; /* 336 9 */ /* XXX 7 bytes hole, try to pack */ u64 peer_features; /* 352 8 */ u32 connect_seq; /* 360 4 */ u32 peer_global_seq; /* 364 4 */ struct ceph_auth_handshake * auth; /* 368 8 */ int auth_retry; /* 376 4 */ /* XXX 4 bytes hole, try to pack */ /* --- cacheline 6 boundary (384 bytes) --- */ struct mutex mutex; /* 384 32 */ struct list_head out_queue; /* 416 16 */ struct list_head out_sent; /* 432 16 */ /* --- cacheline 7 boundary (448 bytes) --- */ u64 out_seq; /* 448 8 */ u64 in_seq; /* 456 8 */ u64 in_seq_acked; /* 464 8 */ char in_banner[30]; /* 472 30 */ struct ceph_msg_connect out_connect; /* 502 33 */ /* --- cacheline 8 boundary (512 bytes) was 23 bytes ago --- */ struct ceph_msg_connect_reply in_reply; /* 535 26 */ /* XXX 7 bytes hole, try to pack */ struct ceph_entity_addr actual_peer_addr __attribute__((__aligned__(8))); /* 568 136 */ /* --- cacheline 11 boundary (704 bytes) --- */ $ That happens because 'struct ceph_entity_addr' has that __attribute__ ((__aligned__(8)) in the regenerated source code, above, now look at how it gets regenerated: $ pahole -C ceph_entity_addr ceph.o struct ceph_entity_addr { __le32 type; /* 0 4 */ __le32 nonce; /* 4 4 */ struct __kernel_sockaddr_storage in_addr __attribute__((__aligned__(8))); /* 8 128 */ /* size: 136, cachelines: 3, members: 3 */ /* forced alignments: 1 */ /* last cacheline: 8 bytes */ } __attribute__((__aligned__(8))); $ While when looking at the original DWARF: $ pahole -C ceph_entity_addr /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o struct ceph_entity_addr { __le32 type; /* 0 4 */ __le32 nonce; /* 4 4 */ struct __kernel_sockaddr_storage in_addr __attribute__((__aligned__(1))); /* 8 128 */ /* size: 136, cachelines: 3, members: 3 */ /* forced alignments: 1 */ /* last cacheline: 8 bytes */ } __attribute__((__aligned__(1))); $ The confusion may further come from the fact that 'struct __kernel_sockaddr_storage' has, in the regenerated source code, the __attribute__((__aligned__8))) $ pahole -C __kernel_sockaddr_storage ceph.o struct __kernel_sockaddr_storage { __kernel_sa_family_t ss_family; /* 0 2 */ char __data[126]; /* 2 126 */ /* size: 128, cachelines: 2, members: 2 */ } __attribute__((__aligned__(8))); $ Which is the same as in the original DWARF: $ pahole -C __kernel_sockaddr_storage /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o struct __kernel_sockaddr_storage { __kernel_sa_family_t ss_family; /* 0 2 */ char __data[126]; /* 2 126 */ /* size: 128, cachelines: 2, members: 2 */ } __attribute__((__aligned__(8))); $ Looking at the original original source code for 'struct ceph_entity_addr' helps here, as it reads: include/linux/ceph/msgr.h, line 63: /* * entity_addr -- network address */ struct ceph_entity_addr { __le32 type; __le32 nonce; /* unique id for process (e.g. pid) */ struct sockaddr_storage in_addr; } __attribute__ ((packed)); So the original code has no __attribute__((__aligned__(1))), so, lets look at what the compiler generates for 'struct ceph_entity_addr': $ readelf -wi /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o | grep ceph_entity_addr -A7 <193a6> DW_AT_name : (indirect string, offset: 0x1586): ceph_entity_addr <193aa> DW_AT_byte_size : 136 <193ab> DW_AT_alignment : 1 <193ac> DW_AT_decl_file : 296 <193ae> DW_AT_decl_line : 63 <193af> DW_AT_decl_column : 8 <193b0> DW_AT_sibling : <0x193e0> <2><193b4>: Abbrev Number: 5 (DW_TAG_member) $ So the natural alignment for 'struct ceph_entity_addr' ends up being the natural alignment for 'struct __kernel_sockaddr_storage', which is 8, but since 'struct ceph_entity_addr' was marked in the original source code as __packed__, the compiler added the DW_AT_alignment: 1 to override that. The heuristic in pahole, so far, took that __attribute__((__aligned__(1))) literally: $ pahole -C ceph_entity_addr /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o struct ceph_entity_addr { __le32 type; /* 0 4 */ __le32 nonce; /* 4 4 */ struct __kernel_sockaddr_storage in_addr __attribute__((__aligned__(1))); /* 8 128 */ /* size: 136, cachelines: 3, members: 3 */ /* forced alignments: 1 */ /* last cacheline: 8 bytes */ } __attribute__((__aligned__(1))); $ which ends up making the regenerated source code (with the __aligned__((1))), generate a different layout, the __aligned__((8)) in one of its members overrode that __aligned__((1)). Take this into account and when faced with a structure which natural alignment is not one and that has a DW_AT_alignment:1 to mean it really is __packed__. Doing that makes the regenerated source code match the original structure layouts, i.e. after the patch we get: $ pahole -C ceph_entity_addr /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o struct ceph_entity_addr { __le32 type; /* 0 4 */ __le32 nonce; /* 4 4 */ struct __kernel_sockaddr_storage in_addr __attribute__((__aligned__(1))); /* 8 128 */ /* size: 136, cachelines: 3, members: 3 */ /* forced alignments: 1 */ /* last cacheline: 8 bytes */ } __attribute__((__packed__)); $ And that member in 'struct ceph_connection', in the original, continues to read: $ pahole -C ceph_connection /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o | grep -w actual_peer_addr -B4 -A6 char in_banner[30]; /* 472 30 */ struct ceph_msg_connect out_connect; /* 502 33 */ /* --- cacheline 8 boundary (512 bytes) was 23 bytes ago --- */ struct ceph_msg_connect_reply in_reply; /* 535 26 */ struct ceph_entity_addr actual_peer_addr __attribute__((__aligned__(1))); /* 561 136 */ /* --- cacheline 10 boundary (640 bytes) was 57 bytes ago --- */ struct ceph_msg_header out_hdr; /* 697 53 */ /* XXX 2 bytes hole, try to pack */ /* --- cacheline 11 boundary (704 bytes) was 48 bytes ago --- */ $ While in the regenerated DWARF from the regenerated source code reads: $ pfunct --compile /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o > ceph.c $ gcc -g -c ceph.c $ pahole -C ceph_connection ceph.o | grep -w actual_peer_addr -B4 -A6 char in_banner[30]; /* 472 30 */ struct ceph_msg_connect out_connect; /* 502 33 */ /* --- cacheline 8 boundary (512 bytes) was 23 bytes ago --- */ struct ceph_msg_connect_reply in_reply; /* 535 26 */ struct ceph_entity_addr actual_peer_addr __attribute__((__aligned__(1))); /* 561 136 */ /* --- cacheline 10 boundary (640 bytes) was 57 bytes ago --- */ struct ceph_msg_header out_hdr; /* 697 53 */ /* XXX 2 bytes hole, try to pack */ /* --- cacheline 11 boundary (704 bytes) was 48 bytes ago --- */ $ I.e. it now matches. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
f78633cfb9 |
core: Infer __packed__ for union struct members
I.e. check that all the structs that are embedded in a union have their natural alignment satisfied by the size of the array they are contained in, Before this change we ended up not marking union struct members that didn't had natural alignment violations as __packed__ even tho they had to be to be in a struct that didn't satisfied their natural alignment requirements, which would violate them when said union was in an array, i.e. the second entry would have the non __packed__ union struct member in a bad position. E.g. Before: $ pahole -C ceph_osd_op /home/acme/git/build/v5.1-rc4+/net/ceph/osd_client.o struct ceph_osd_op { __le16 op; /* 0 2 */ __le32 flags; /* 2 4 */ union { struct { __le64 offset; /* 6 8 */ __le64 length; /* 14 8 */ __le64 truncate_size; /* 22 8 */ __le32 truncate_seq; /* 30 4 */ } __attribute__((__packed__)) extent; /* 6 28 */ struct { __le32 name_len; /* 6 4 */ __le32 value_len; /* 10 4 */ __u8 cmp_op; /* 14 1 */ __u8 cmp_mode; /* 15 1 */ } __attribute__((__packed__)) xattr; /* 6 10 */ struct { __u8 class_len; /* 6 1 */ __u8 method_len; /* 7 1 */ __u8 argc; /* 8 1 */ __le32 indata_len; /* 9 4 */ } __attribute__((__packed__)) cls; /* 6 7 */ struct { __le64 cookie; /* 6 8 */ __le64 count; /* 14 8 */ } pgls; /* 6 16 */ struct { __le64 snapid; /* 6 8 */ } snap; /* 6 8 */ struct { __le64 cookie; /* 6 8 */ __le64 ver; /* 14 8 */ __u8 op; /* 22 1 */ __le32 gen; /* 23 4 */ } __attribute__((__packed__)) watch; /* 6 21 */ struct { __le64 cookie; /* 6 8 */ } notify; /* 6 8 */ struct { __le64 offset; /* 6 8 */ __le64 length; /* 14 8 */ __le64 src_offset; /* 22 8 */ } clonerange; /* 6 24 */ struct { __le64 expected_object_size; /* 6 8 */ __le64 expected_write_size; /* 14 8 */ } alloc_hint; /* 6 16 */ struct { __le64 snapid; /* 6 8 */ __le64 src_version; /* 14 8 */ __u8 flags; /* 22 1 */ __le32 src_fadvise_flags; /* 23 4 */ } __attribute__((__packed__)) copy_from; /* 6 21 */ }; /* 6 28 */ __le32 payload_len; /* 34 4 */ /* size: 38, cachelines: 1, members: 4 */ /* last cacheline: 38 bytes */ } __attribute__((__packed__)); After: $ pahole -C ceph_osd_op /home/acme/git/build/v5.1-rc4+/net/ceph/osd_client.o struct ceph_osd_op { __le16 op; /* 0 2 */ __le32 flags; /* 2 4 */ union { struct { __le64 offset; /* 6 8 */ __le64 length; /* 14 8 */ __le64 truncate_size; /* 22 8 */ __le32 truncate_seq; /* 30 4 */ } __attribute__((__packed__)) extent; /* 6 28 */ struct { __le32 name_len; /* 6 4 */ __le32 value_len; /* 10 4 */ __u8 cmp_op; /* 14 1 */ __u8 cmp_mode; /* 15 1 */ } __attribute__((__packed__)) xattr; /* 6 10 */ struct { __u8 class_len; /* 6 1 */ __u8 method_len; /* 7 1 */ __u8 argc; /* 8 1 */ __le32 indata_len; /* 9 4 */ } __attribute__((__packed__)) cls; /* 6 7 */ struct { __le64 cookie; /* 6 8 */ __le64 count; /* 14 8 */ } pgls; /* 6 16 */ struct { __le64 snapid; /* 6 8 */ } snap; /* 6 8 */ struct { __le64 cookie; /* 6 8 */ __le64 ver; /* 14 8 */ __u8 op; /* 22 1 */ __le32 gen; /* 23 4 */ } __attribute__((__packed__)) watch; /* 6 21 */ struct { __le64 cookie; /* 6 8 */ } notify; /* 6 8 */ struct { __le64 offset; /* 6 8 */ __le64 length; /* 14 8 */ __le64 src_offset; /* 22 8 */ } clonerange; /* 6 24 */ struct { __le64 expected_object_size; /* 6 8 */ __le64 expected_write_size; /* 14 8 */ } alloc_hint; /* 6 16 */ struct { __le64 snapid; /* 6 8 */ __le64 src_version; /* 14 8 */ __u8 flags; /* 22 1 */ __le32 src_fadvise_flags; /* 23 4 */ } __attribute__((__packed__)) copy_from; /* 6 21 */ }; /* 6 28 */ __le32 payload_len; /* 34 4 */ /* size: 38, cachelines: 1, members: 4 */ /* last cacheline: 38 bytes */ } __attribute__((__packed__)); $ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
75c52de9c6 |
core: Move packed_attribute_inferred from 'class' to 'type' class
Since we need to infer the attributes of union members too, so move there. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
dc3d441961 |
core: Improve the natural alignment calculation
We need to take more than just arrays into account when figuring out the
natural alignment of struct members, looking recursively at types till
we get to basic types and pointers.
Before this patch the 'new' struct field in the 'v' union was considered
__packed__, when in fact it is not, as the natural alignment for the
'state_id' typedef is 4, so it can start at offset 36 (or 4 considering
just its container struct), see below:
$ pahole -IC nfsd4_lock /home/acme/git/build/v5.1-rc4+/fs/nfsd/nfs4xdr.o
/* Used at: /home/acme/git/linux/fs/nfsd/nfs4xdr.c */
/* <1717a> /home/acme/git/linux/fs/nfsd/xdr4.h:156 */
struct nfsd4_lock {
u32 lk_type; /* 0 4 */
u32 lk_reclaim; /* 4 4 */
u64 lk_offset; /* 8 8 */
u64 lk_length; /* 16 8 */
u32 lk_is_new; /* 24 4 */
/* XXX 4 bytes hole, try to pack */
union {
struct {
u32 open_seqid; /* 32 4 */
stateid_t open_stateid; /* 36 16 */
u32 lock_seqid; /* 52 4 */
clientid_t clientid; /* 56 8 */
/* --- cacheline 1 boundary (64 bytes) --- */
struct xdr_netobj owner; /* 64 16 */
} __attribute__((__packed__)) new; /* 32 48 */
struct {
stateid_t lock_stateid; /* 32 16 */
u32 lock_seqid; /* 48 4 */
} __attribute__((__packed__)) old; /* 32 20 */
} v; /* 32 48 */
/* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */
union {
struct {
stateid_t stateid; /* 80 16 */
} ok; /* 80 16 */
struct nfsd4_lock_denied denied; /* 80 48 */
} u; /* 80 48 */
/* size: 128, cachelines: 2, members: 7 */
/* sum members: 124, holes: 1, sum holes: 4 */
};
$
Asking for -rEIC, i.e. relative offsets, expand types we can see that
stateid_t opaque type:
struct {
/* typedef u32 -> __u32 */ unsigned int open_seqid; /* 0 4 */
/* typedef stateid_t */ struct {
/* typedef u32 -> __u32 */ unsigned int si_generation; /* 0 4 */
/* typedef stateid_opaque_t */ struct {
/* typedef clientid_t */ struct {
/* typedef u32 -> __u32 */ unsigned int cl_boot; /* 0 4 */
/* typedef u32 -> __u32 */ unsigned int cl_id; /* 4 4 */
} so_clid; /* 0 8 */
/* typedef u32 -> __u32 */ unsigned int so_id; /* 8 4 */
} si_opaque; /* 4 12 */
} open_stateid; /* 4 16 */
With the algorithm implemented in this patch we get it correctly as not
packed:
$ pahole -IC nfsd4_lock /home/acme/git/build/v5.1-rc4+/fs/nfsd/nfs4xdr.o
/* Used at: /home/acme/git/linux/fs/nfsd/nfs4xdr.c */
/* <1717a> /home/acme/git/linux/fs/nfsd/xdr4.h:156 */
struct nfsd4_lock {
u32 lk_type; /* 0 4 */
u32 lk_reclaim; /* 4 4 */
u64 lk_offset; /* 8 8 */
u64 lk_length; /* 16 8 */
u32 lk_is_new; /* 24 4 */
/* XXX 4 bytes hole, try to pack */
union {
struct {
u32 open_seqid; /* 32 4 */
stateid_t open_stateid; /* 36 16 */
u32 lock_seqid; /* 52 4 */
clientid_t clientid; /* 56 8 */
/* --- cacheline 1 boundary (64 bytes) --- */
struct xdr_netobj owner; /* 64 16 */
} new; /* 32 48 */
struct {
stateid_t lock_stateid; /* 32 16 */
u32 lock_seqid; /* 48 4 */
} old; /* 32 20 */
} v; /* 32 48 */
/* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */
union {
struct {
stateid_t stateid; /* 80 16 */
} ok; /* 80 16 */
struct nfsd4_lock_denied denied; /* 80 48 */
} u; /* 80 48 */
/* size: 128, cachelines: 2, members: 7 */
/* sum members: 124, holes: 1, sum holes: 4 */
};
Fixes:
|
||
Arnaldo Carvalho de Melo
|
f2641ce169 |
core: Take arrays into account when inferring if a struct is packed
Before: $ pahole -C qrwlock /home/acme/git/build/v5.1-rc4+/fs/ceph/dir.o struct qrwlock { union { atomic_t cnts; /* 0 4 */ struct { u8 wlocked; /* 0 1 */ u8 __lstate[3]; /* 1 3 */ } __attribute__((__packed__)); /* 0 4 */ }; /* 0 4 */ arch_spinlock_t wait_lock; /* 4 4 */ /* size: 8, cachelines: 1, members: 2 */ /* last cacheline: 8 bytes */ }; I.e. __lstate's class_member->byte_size is 3, causing the misinference that that struct was packed, it is naturally aligned, we need to look at the size of the array's entries to figure out its natural alignment: After: $ pahole -C qrwlock /home/acme/git/build/v5.1-rc4+/fs/ceph/dir.o struct qrwlock { union { atomic_t cnts; /* 0 4 */ struct { u8 wlocked; /* 0 1 */ u8 __lstate[3]; /* 1 3 */ }; /* 0 4 */ }; /* 0 4 */ arch_spinlock_t wait_lock; /* 4 4 */ /* size: 8, cachelines: 1, members: 2 */ /* last cacheline: 8 bytes */ }; $ To further test: $ cat packed_array_struct.c struct sarray { short array[3]; long long first; } __attribute__((__packed__)); void foo(struct sarray *s) {} $ gcc -g -c packed_array_struct.c $ pahole packed_array_struct.o struct sarray { short int array[3]; /* 0 6 */ long long int first; /* 6 8 */ /* size: 14, cachelines: 1, members: 2 */ /* last cacheline: 14 bytes */ } __attribute__((__packed__)); $ cat packed_array_struct.c struct sarray { short array[3]; long long first; }; void foo(struct sarray *s) {} $ gcc -g -c packed_array_struct.c $ pahole packed_array_struct.o struct sarray { short int array[3]; /* 0 6 */ /* XXX 2 bytes hole, try to pack */ long long int first; /* 8 8 */ /* size: 16, cachelines: 1, members: 2 */ /* sum members: 14, holes: 1, sum holes: 2 */ /* last cacheline: 16 bytes */ }; $ One more test: $ cat packed_array_struct.c struct sarray { short a; short array[3]; long long b; }; void foo(struct sarray *s) {} $ Before this patch: $ gcc -g -c packed_array_struct.c $ pahole packed_array_struct.o struct sarray { short int a; /* 0 2 */ short int array[3]; /* 2 6 */ long long int b; /* 8 8 */ /* size: 16, cachelines: 1, members: 3 */ /* last cacheline: 16 bytes */ } __attribute__((__packed__)); After: $ pahole packed_array_struct.o struct sarray { short int a; /* 0 2 */ short int array[3]; /* 2 6 */ long long int b; /* 8 8 */ /* size: 16, cachelines: 1, members: 3 */ /* last cacheline: 16 bytes */ }; $ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
3247a777dc |
core: Infer if a struct is packed by the offsets/natural alignments
As DWARF (nor BTF) provides explicit attributes, we need to look at the natural alignments, a byte is always alignted, etc. This probably fails with things like __attribute__(__aligned(power-of-two)), but with it most of the kernel data structures are full circled, i.e. 'pfunct --compile' regenerates source code from debug info that when compiled generats debug info that end up matching the original sources. $ cat a.c #define __packed __attribute__((__packed__)) struct filename { const char * name; const char * uptr; int refcnt; }; void m(struct filename *f) {} $ gcc -g -c a.c $ pahole a.o struct filename { const char * name; /* 0 8 */ const char * uptr; /* 8 8 */ int refcnt; /* 16 4 */ /* size: 24, cachelines: 1, members: 3 */ /* padding: 4 */ /* last cacheline: 24 bytes */ }; $ cat a.c #define __packed __attribute__((__packed__)) struct filename { const char * name; const char * uptr; int refcnt; } __packed; void m(struct filename *f) {} $ gcc -g -c a.c $ pahole a.o struct filename { const char * name; /* 0 8 */ const char * uptr; /* 8 8 */ int refcnt; /* 16 4 */ /* size: 20, cachelines: 1, members: 3 */ /* last cacheline: 20 bytes */ } __attribute__((__packed__)); $ cat a.c #define __packed __attribute__((__packed__)) struct filename { const char * name; int refcnt; const char * uptr; }; void m(struct filename *f) {} $ gcc -g -c a.c $ pahole a.o struct filename { const char * name; /* 0 8 */ int refcnt; /* 8 4 */ /* XXX 4 bytes hole, try to pack */ const char * uptr; /* 16 8 */ /* size: 24, cachelines: 1, members: 3 */ /* sum members: 20, holes: 1, sum holes: 4 */ /* last cacheline: 24 bytes */ }; $ cat a.c #define __packed __attribute__((__packed__)) struct filename { const char * name; int refcnt; const char * uptr; } __packed; void m(struct filename *f) {} $ gcc -g -c a.c $ pahole a.o struct filename { const char * name; /* 0 8 */ int refcnt; /* 8 4 */ const char * uptr; /* 12 8 */ /* size: 20, cachelines: 1, members: 3 */ /* last cacheline: 20 bytes */ } __attribute__((__packed__)); $ cat a.c #define __packed __attribute__((__packed__)) struct filename { const char * name; const char * uptr; unsigned char refcnt; }; void m(struct filename *f) {} $ gcc -g -c a.c $ pahole a.o struct filename { const char * name; /* 0 8 */ const char * uptr; /* 8 8 */ unsigned char refcnt; /* 16 1 */ /* size: 24, cachelines: 1, members: 3 */ /* padding: 7 */ /* last cacheline: 24 bytes */ }; $ cat a.c #define __packed __attribute__((__packed__)) struct filename { const char * name; const char * uptr; unsigned char refcnt; } __packed; void m(struct filename *f) {} $ gcc -g -c a.c $ pahole a.o struct filename { const char * name; /* 0 8 */ const char * uptr; /* 8 8 */ unsigned char refcnt; /* 16 1 */ /* size: 17, cachelines: 1, members: 3 */ /* last cacheline: 17 bytes */ } __attribute__((__packed__)); $ cat a.c #define __packed __attribute__((__packed__)) struct filename { const char * name; unsigned char refcnt; const char * uptr; }; void m(struct filename *f) {} $ gcc -g -c a.c $ pahole a.o struct filename { const char * name; /* 0 8 */ unsigned char refcnt; /* 8 1 */ /* XXX 7 bytes hole, try to pack */ const char * uptr; /* 16 8 */ /* size: 24, cachelines: 1, members: 3 */ /* sum members: 17, holes: 1, sum holes: 7 */ /* last cacheline: 24 bytes */ }; $ cat a.c #define __packed __attribute__((__packed__)) struct filename { const char * name; unsigned char refcnt; const char * uptr; } __packed; void m(struct filename *f) {} $ gcc -g -c a.c $ pahole a.o struct filename { const char * name; /* 0 8 */ unsigned char refcnt; /* 8 1 */ const char * uptr; /* 9 8 */ /* size: 17, cachelines: 1, members: 3 */ /* last cacheline: 17 bytes */ } __attribute__((__packed__)); $ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
15a754f224 |
core: Add nr_entries member to 'struct cus'
Will be used when considering comparing multiple CU entries in a struct cus to the sole compile unit in a second file, like when comparing the types in a multi-CU DWARF file like vmlinux against the combined, deduplicated entries in that vmlinux .BTF section. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
45ad545944 |
tag: Introduce tag__is_pointer()
For the usual idiom to ask if a tag is a pointer, removing a bit of DWARFism and shortening the operation. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Andrii Nakryiko
|
fe590758cb |
class__find_holes: Zero out bit_hole/hole on member
pahole --reorganize is calling class__find_holes() multiple times on the same struct to re-calculate holes. If it so happens that after reorg last struct's member had hole previously, we are not going to clear it out, which will lead to weird output and BFA, like this: $ pahole -F btf --reorganize -C netns_frags ~/tmp/vmlinux-default struct netns_frags { long int high_thresh; /* 0 8 */ long int low_thresh; /* 8 8 */ int timeout; /* 16 4 */ int max_dist; /* 20 4 */ struct inet_frags * f; /* 24 8 */ atomic_long_t mem; /* 32 8 */ /* XXX 24 bytes hole, try to pack */ /* --- cacheline 1 boundary (64 bytes) --- */ struct rhashtable rhashtable; /* 64 136 */ /* XXX 56 bytes hole, try to pack */ /* size: 200, cachelines: 4, members: 7 */ /* sum members: 176, holes: 1, sum holes: 80 */ /* last cacheline: 8 bytes */ /* BRAIN FART ALERT! 200 bytes != 176 (member bytes) + 0 (member bits) + 80 (byte holes) + 0 (bit holes), diff = -448 bits */ }; /* saved 120 bytes and 1 cacheline! */ After this change: $ pahole -F btf --reorganize -C netns_frags ~/tmp/vmlinux-defaultstruct netns_frags { long int high_thresh; /* 0 8 */ long int low_thresh; /* 8 8 */ int timeout; /* 16 4 */ int max_dist; /* 20 4 */ struct inet_frags * f; /* 24 8 */ atomic_long_t mem; /* 32 8 */ /* XXX 24 bytes hole, try to pack */ /* --- cacheline 1 boundary (64 bytes) --- */ struct rhashtable rhashtable; /* 64 136 */ /* size: 200, cachelines: 4, members: 7 */ /* sum members: 176, holes: 1, sum holes: 24 */ /* last cacheline: 8 bytes */ }; /* saved 120 bytes and 1 cacheline! */ Signed-off-by: Andrii Nakryiko <andriin@fb.com> Reported-by: Arnaldo Carvalho de Melo <acme@redhat.com> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Alexei Starovoitov <ast@fb.com> Cc: Yonghong Song <yhs@fb.com> Cc: dwarves@vger.kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Andrii Nakryiko
|
8ce85a1ad7 |
reorganize: Use class__find_holes() to recalculate holes
Instead of relying on error-prone adjustment of bit/byte holes, use class__find_holes() to re-calculate them after members are moved around. As part of that change, fix bug with not adjusting bit_offset, when changing byte_offset. Signed-off-by: Andrii Nakryiko <andriin@fb.com> Cc: Alexei Starovoitov <ast@fb.com> Cc: Yonghong Song <yhs@fb.com> Cc: dwarves@vger.kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Andrii Nakryiko
|
5d1c4029bd |
dwarves: Fix classification of byte/bit hole for aligned bitfield
This patch fixes a bug in class__find_holes() with determining byte hole as a bit hole in case where previous member is not bitfield, but current one is aligned bitfield. See example below, notice hole classification hw_stopped field.. $ cat test/bit_test.c struct s { long unused: 62; /* 2 bit hole */ int hw_stopped; /* 4 byte hole */ long unused2: 55; /* 9 bit padding */ }; int main() { static struct s s; return 0; } $ clang -g test/bit_test.c -o test/bit_test $ pahole -JV test/bit_test File test/bit_test: [1] STRUCT s kind_flag=1 size=24 vlen=3 unused type_id=2 bitfield_size=62 bits_offset=0 hw_stopped type_id=3 bitfield_size=0 bits_offset=64 unused2 type_id=2 bitfield_size=55 bits_offset=128 [2] INT long int size=8 bit_offset=0 nr_bits=64 encoding=SIGNED [3] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED BEFORE: $ pahole -F btf test/bit_test struct s { long int unused:62; /* 0: 0 8 */ /* XXX 2 bits hole, try to pack */ int hw_stopped; /* 8 4 */ /* XXX 32 bits hole, try to pack */ long int unused2:55; /* 16: 0 8 */ /* size: 24, cachelines: 1, members: 3 */ /* sum members: 4 */ /* sum bitfield members: 117 bits, bit holes: 2, sum bit holes: 34 bits */ /* bit_padding: 9 bits */ /* last cacheline: 24 bytes */ }; AFTER: $ pahole -F btf test/bit_test struct s { long int unused:62; /* 0: 0 8 */ /* XXX 2 bits hole, try to pack */ int hw_stopped; /* 8 4 */ /* XXX 4 bytes hole, try to pack */ long int unused2:55; /* 16: 0 8 */ /* size: 24, cachelines: 1, members: 3 */ /* sum members: 4, holes: 1, sum holes: 4 */ /* sum bitfield members: 117 bits, bit holes: 1, sum bit holes: 2 bits */ /* bit_padding: 9 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: dwarves@vger.kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Andrii Nakryiko
|
78c110a7ea |
dwarves: Revert semantics of member bit/byte hole
pahole --reorganize heavily depends on member's bit_hole and hole fields to denote bit/byte holes *after* member. Previous commit "dwarves: use bit sizes and bit/byte hole info in __class__fprintf" changed its meaning to bit/byte hole *before* member to accomodate possible bit/byte holes at the beginning of a struct. This change broke reorganization algorithm, though, which is quite involved and isn't trivially modifiable to accomodate new semantics. This patch reverts the meaning of bit_hole and hole, but also introduces per class pre_bit_hole/pre_hole to record initial bit/byte hole of a struct. This allows to fix reorg code more easily and still handle initial holes cases, if at the expense of being not as elegant. Committer testing: $ time pahole -F btf --packable vmlinux | sort -nr -k4 | head bts_ctx 12288 8192 4096 swsusp_info 4096 432 3664 vc_data 792 496 296 pci_dev 2488 2320 168 rcu_state 3392 3240 152 ptr_ring 192 40 152 xdp_sock 960 840 120 zone 1664 1552 112 rcu_data 576 472 104 rcu_node 576 480 96 real 0m0.038s user 0m0.029s sys 0m0.017s $ Signed-off-by: Andrii Nakryiko <andriin@fb.com> Reported-by: Arnaldo Carvalho de Melo <acme@redhat.com> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Alexei Starovoitov <ast@fb.com> Cc: Yonghong Song <yhs@fb.com> Cc: dwarves@vger.kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
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> |
||
Andrii Nakryiko
|
975757bc88 |
dwarves: Use bit sizes and bit/byte hole info in __class__fprintf
This patch changes __class__fprintf to rely on bit_size/bitfield_size, calculated in corresponding loaders, instead of trying to guess correct sizes on its own. Bit and byte hole information is now stored in current field member and stores information about holes *before* field. Previously hole information was stored in previous field and was meant to represent "holes after a field". Such approach makes it hard to report bit holes at the very beginning of the struct: struct s { int:4; /* this is bit hole, not a field in DWARF/BTF */ int x:8; }; With this change and previous bitfield calculation/fixup logic fixes, there are no more discrepancies between DWARF/BTF for allyesconfig kernel. There are also no more BRAIN FART ALERTS! 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> |
||
Andrii Nakryiko
|
1838d3d762 |
dwarves: Revamp bit/byte holes detection logic
This patch rewrites hole detection logic. Now many crazy combinations of bitfields and normal fields are handled correctly. This was tested on allyesconfig kernel and differences before/after were always in favor of new algorithm. With subsequent change in next patch, there are no more BRAIN FART ALERTs for allyesconfig and DWARF and BTF outputs have no discrepanies. Example: $ cat test.c struct s { short : 4; /* this one is not emitted in DWARF/BTF */ short a : 4; int x; int : 10; int y : 4; short zz; short zzz : 4; long z; short : 4; }; int main() { struct s s; return 0; } $ gcc -g test.c -o test $ ~/pahole/build/pahole -J test $ ~/pahole/build/pahole -F dwarf test struct s { /* XXX 4 bits hole, try to pack */ short int a:4; /* 0: 8 2 */ /* XXX 8 bits hole, try to pack */ /* XXX 2 bytes hole, try to pack */ int x; /* 4 4 */ /* XXX 10 bits hole, try to pack */ int y:4; /* 8:18 4 */ /* Bitfield combined with next fields */ /* XXX 2 bits hole, try to pack */ short int zz; /* 10 2 */ short int zzz:4; /* 12:12 2 */ /* XXX 12 bits hole, try to pack */ /* XXX 2 bytes hole, try to pack */ long int z; /* 16 8 */ /* size: 32, cachelines: 1, members: 6 */ /* sum members (bits): 124, holes: 2, sum holes: 4 */ /* bit holes: 5, sum bit holes: 36 bits */ /* padding: 8 */ /* last cacheline: 32 bytes */ }; No discrepanies between BTF/DWARF: $ ../btfdiff test $ Signed-off-by: Andrii Nakryiko <andriin@fb.com> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.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> |
||
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> |
||
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> |
||
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> |
||
Arnaldo Carvalho de Melo
|
762e7b58f4 |
dwarves: Change ptr_table__add() signature to allow for uint32_t returns
We were using 'long', so that we could return -ENOMEM, but since we need struct ptr_table members are already uint32_t, meaning we can use the entire range, make the return be just an int and be just for error reporting and pass a uint32_t pointer to return the index used for the new entry. 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> |
||
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> |
||
Arnaldo Carvalho de Melo
|
be5173b4df |
dwarves: Fixup sizeof(long double) in bits in base_type_name_to_size table
We were erroneously setting it to 64 bits everywhere, i.e. 8 bytes, when it in fact is 16 bytes, 128 bits, as we can see with: $ btfdiff libc-2.28.so.debug --- /tmp/btfdiff.dwarf.RuqA9q 2019-02-26 10:25:49.828150761 -0300 +++ /tmp/btfdiff.btf.t3XqV6 2019-02-26 10:25:49.835150835 -0300 @@ -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 */ And: $ cat long_double.c #include <stdio.h> int main(void) { return printf("sizeof(long double)=%zd\n", sizeof(long double)); } $ ./long_double sizeof(long double)=16 $ file long_double long_double: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=c876603879d49905fbd7fc5907dc65ad3bca422f, not stripped $ So use sizeof to at least match the running machine, this is insufficient for cross-platform analysis, so a new fix is needed to cover those cases, this at least improves the current situation a bit. Reported-by: Andrii Nakryiko <andriin@fb.com> Cc: Alexei Starovoitov <ast@fb.com> Cc: Yonghong Song <yhs@fb.com> link: https://lore.kernel.org/bpf/20190226131156.GA26786@kernel.org/ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Arnaldo Carvalho de Melo
|
5081ed5070 |
dwarves: Add _Float128 base_type
Without it we end up with these messages and a zero sized _Float128 type. $ btfdiff 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.M7kavg 2019-02-26 10:13:06.633184595 -0300 +++ /tmp/btfdiff.btf.vlWt5u 2019-02-26 10:13:06.640184669 -0300 @@ -2142,7 +2149,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 */ After this patch these messages are gone and for pahole's needs we have enough information, no need to wait for BTF to have explicit support for floating point base types. Reported-by: Andrii Nakryiko <andriin@fb.com> Cc: Alexei Starovoitov <ast@fb.com> Cc: Yonghong Song <yhs@fb.com> Link: https://lore.kernel.org/bpf/20190226131156.GA26786@kernel.org/ Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> |
||
Andrii Nakryiko
|
7daa4300d2 |
pahole: Complete list of base type names
clang seems to generate base type with name "short", instead of "short in", but it also isn't inconceivable to imagine other compilers generating just "long" and/or "long long". This patch adds all those short forms to a list of base type names. $ cat type_test.c struct s { short x1; long x2; long long x3; }; int main() { struct s s; return 0; } $ clang -g type_test.c -o type_test && ~/local/pahole/build/pahole -JV type_test File type_test: [1] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED [2] STRUCT s kind_flag=0 size=24 vlen=3 x1 type_id=3 bits_offset=0 x2 type_id=4 bits_offset=64 x3 type_id=5 bits_offset=128 [3] INT short size=2 bit_offset=0 nr_bits=16 encoding=SIGNED [4] INT long int size=8 bit_offset=0 nr_bits=64 encoding=SIGNED [5] INT long long int size=8 bit_offset=0 nr_bits=64 encoding=SIGNED Before: $ ~/local/pahole/build/pahole -F btf type_test base_type__name_to_size: base_type short class__fixup_btf_bitfields: unknown base type name "short"! struct s { short x1; /* 0 0 */ /* XXX 8 bytes hole, try to pack */ long int x2; /* 8 8 */ long long int x3; /* 16 8 */ /* size: 24, cachelines: 1, members: 3 */ /* sum members: 16, holes: 1, sum holes: 8 */ /* last cacheline: 24 bytes */ }; After: $ ~/local/pahole/build/pahole -F btf type_test struct s { short x1; /* 0 2 */ /* XXX 6 bytes hole, try to pack */ long int x2; /* 8 8 */ long long int x3; /* 16 8 */ /* size: 24, cachelines: 1, members: 3 */ /* sum members: 18, holes: 1, sum holes: 6 */ /* 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> |
||
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> |
||
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> |