diff options
author | Connor O'Brien <connoro@google.com> | 2021-12-21 14:07:11 -0800 |
---|---|---|
committer | Connor O'Brien <connoro@google.com> | 2022-01-05 16:20:35 -0800 |
commit | e017fe3016a192e30959cd3cd6271e5a134bd8cd (patch) | |
tree | 2f2e97ca10417fcfb99df398cdde3e72bab6f07d | |
parent | 6e27fd1e21f3f01577db8b533550cf0e2d559011 (diff) | |
parent | c2b7b8c20877d267159ace36119f6340b9d12823 (diff) | |
download | dwarves-e017fe3016a192e30959cd3cd6271e5a134bd8cd.tar.gz |
Merge branch 'upstream-master' into 'master'
* upstream-master:
pahole: Prep 1.23
Revert "fprintf: Allow making struct/enum/union anonymous"
Revert "pahole: Add --inner_anon option"
pahole: Add --inner_anon option
fprintf: Allow making struct/enum/union anonymous
btf_encoder: Support btf_type_tag attribute
man pages: Add missing --skip_encoding_btf_decl_tag entry
man pages: Add missing --skip_encoding_btf_type_tag entry
dwarf_loader: Support btf_type_tag attribute
dutil: Move DW_TAG_LLVM_annotation definition to dutil.h
libbpf: Sync with latest libbpf repo to pick support for BTF_KIND_TYPE_TAG
dwarf_loader: Warn about DW_TAG_skeleton_unit and give a workaround
fprintf: Add DWARF5 tags added in elfutils 0.170
dwarf_loader: Print the hexadecimal value for unexpected tags in die__process()
btf_encoder: generate BTF_KIND_DECL_TAGs for typedef btf_decl_tag attributes
dwarf_loader: support typedef DW_TAG_LLVM_annotation
btf_loader: Use cacheline size to infer alignment
btf_loader: Propagate struct conf_load
dwarves_fprintf: Move cacheline_size into struct conf_fprintf
btfdiff: Suppress alignment tags with BTF as well as with DWARF
btf_loader: Infer alignment info
core: Export tag__natural_alignment()
fprintf: Fix __attribute__((__aligned__(N)) handling for struct members
btf: Rename btf_tag to btf_decl_tag
manpages: Minor fixes
btf_loader: Refactor class__fixup_btf_bitfields
pahole: Add --skip_missing option
fprintf: Fix nested struct printing wrt attributes
btf_encoder: Fix handling of percpu symbols on s390
dwarf_loader: Fix heap overflow when accessing variable specification
dwarves: Introduce conf_load->thread_exit() callback
Update libbpf to get API to combine BTF
btf_encoder: Generate BTF_KIND_TAG from llvm annotations
dwarf_loader: Parse DWARF tag DW_TAG_LLVM_annotation
CMakeList.txt: Don't download libbpf source when system library is used
libbpf: Get latest libbpf
CMakeList.txt: Make python optional
pahole: Prep 1.22
core: Bump the chunk size for ptr_table uses in types, tags, functions tables
pahole: Introduce --ptr_table_stats
core: Add CU stats printer
pahole: Fix races in accessing type information in live CUs
pahole: Add missing limits.h include to get ULLONG_MAX definition
CMakeList.txt: Look for obstack outside libc
cmake: Add a module to find if obstack is in a separate library
CMakeList.txt: Look for argp outside libc
cmake: Add a module to find if argp is in a separate library
fprintf: Add alternative method for reading the data cacheline size
core: Include missing limits.h to get PATH_MAX definition
dutil: Include linux/stddef.h to build on Alpine Linux systems
README.tarball: Overcome --transform problem with symlinks
MANIFEST: Add lib/include/bpf
spec: Remove deleted libbtf.h from the RPM file list
README.tarball: Remove hardcoded version, get it from 'git tag'
MANIFEST: Remove long gone strings header files
MANIFEST: Add buildcmd.sh
buildcmd.sh: Add single build script for use in CI
pahole: Consider type members's names when comparing unions, structs
pahole: Consider type members's types when comparing unions, structs
core: Introduce helpers for getting the first and next members of a type
pahole: Improve the type sorting routine to consider multiple types with same name
core: Remove extra ; in 'print_numeric_version' extern declaration
btf_loader: Mark the 'level' arg to the libbpf error callback as __maybe_unused
btf_loader: Mark create_new_datasec() args as __maybe_unused
dwarf_loader: cus__load_debug_types() doesn't use its 'cus' arg, remove it
dwarf_loader: Rename finalize_cu_immediately() to cus__finalize() to follow convention
dwarf_loader: Remove unused 'dcu' argument from finalize_cu_immediately()
dwarf_loader: Remove unused 'dcus' argument from cu__finalize()
dwarf_loader: Remove unused 'cus' argument from finalize_cu()
dwarf_loader: Fix signed/unsigned comparision in tag__recode_dwarf_bitfield()
core: cus__fprintf_load_files_err() doesn't use its 'cus' argument
core: Change last_seen_bit to uint32_t in class__find_holes()
core: Change aligned_start to uint32_t in class__find_holes()
core: Change cur_bitfield_end to uint32_t in class__find_holes()
core: Change bit_start and bit_end to uint32_t in class__find_holes()
btf_encoder: Fix signed/unsigned comparision
btf_encoder: has_arg_names() doesn't need the 'cu' pointer
btf_encoder: btf_encoder__encode_tag() doesn't need the 'core_id' pointer
btf_encoder: btf_encoder__encode_tag() doesn't need the 'cu' pointer
btf_encoder: btf_encoder__add_struct_type() doesn't need the 'cu' pointer
btf_encoder: btf_encoder__add_func_proto() doesn't need the 'cu' pointer
btf_encoder: No need to read the ehdr in btf_encoder__write_elf(), ditch it
ctracer: No need to read the ehdr, ditch it
btf_encoder: No need to store the ehdr in the instance
elf_symtab: Remove needless GElf_Ehdr pointer argument from the constructor
dutil: elf_symtab__new() doesn't need the GElf_Ehdr *ep argument
pahole: Move case fallthru comment to after the statement
elfcreator: elfcreator_copy_scn() doesn't need the 'elf' arg
syscse: zero_extend() doesn't need a 'cu' arg
pahole: Fix signedness of ternary expression operator
ctracer: Remove a bunch of unused 'cu' pointers
pahole: Use the 'prototypes' parameter in prototypes__load()
codiff: class__find_pair_member() doesn't need 'cu' args
core: class__find_member_by_name() doesn't need a cu pointer
core: Document type->node member usage
core: Fix nnr_members typo on 'struct type' comment docs
man-pages: Improve the --nr_methods/-m pahole man page entry
pahole: Clarify that currently --nr_methods doesn't work together witn -C
pahole: No need to store the class name in 'struct structure'
pahole: Multithreaded DWARF loading requires elfutils >= 0.178
btf_encoder: Add methods to maintain a list of btf encoders
list: Adopt list_next_entry() from the Linux kernel
dwarf_loader: Make hash table size default to 12, faster than 15
pahole: Allow tweaking the size of the loader hash tables
core: Allow sizing the loader hash table
hash: Remove unused hash_32(), hash_ptr()
dwarf_loader: Use a per-CU frontend cache for the latest lookup result
core: Use obstacks: take 2
dwarf_loader: Add comment on why we can't ignore lexblocks
pahole: Ignore DW_TAG_label when encoding BTF
core: Allow ignoring DW_TAG_label
pahole: Ignore DW_TAG_inline_expansion when encoding BTF
core: Allow ignoring DW_TAG_inline_expansion
pahole: Allow encoding BTF with parallel DWARF loading
core: Zero out unused entries when extending ptr_table array in ptr_table__add()
pahole: No need to read DW_AT_alignment when encoding BTF
dwarf_loader: Allow asking not to read the DW_AT_alignment attribute
dwarf_loader: Do not look for non-C DWARF attributes in C CUs
core: Add cu__is_c() to check if the CU language is C
dwarf_loader: Add a lock around dwarf_decl_file() and dwarf_decl_line() calls
btfdiff: Use multithreaded DWARF loading
btfdiff: Use --sort for pretty printing from both BTF and DWARF
pahole: Introduce --sort
pahole: Store the class id in 'struct structure' as well
dwarf_loader: Defer freeing libdw Dwfl handler
core: Provide a way to store per loader info in cus and an exit function
pahole: Keep class + cu in tree of structures
dwarf_loader: Parallel DWARF loading
pahole: Disable parallell BTF encoding for now
pahole: Add locking for the structures list and rbtree
dwarf_loader: Introduce 'dwarf_cus' to group all the DWARF specific per-cus state
dwarf_loader: Factor common bits for creating and processing CU
fprintf: class__vtable_fprintf() doesn't need a 'cu' arg
fprintf: string_type__fprintf() doesn't need a 'cu' arg
core: Ditch tag__free_orig_info(), unused
core: variable__name() doesn't need a 'cu' arg
core: base_type__name() doesn't need a 'cu' arg
core: namespace__delete() doesn't need a 'cu' arg
core: {tag,function,lexblock}__delete() doesn't need a 'cu' arg
core: ftype__delete() doesn't need a 'cu' arg
core: enumeration__delete() doesn't need a 'cu' arg
core: type__delete() doesn't need a 'cu' arg
core: class__clone() doesn't need a 'cu' arg
core: class__delete() doesn't need a 'cu' arg
core: type__delete_class_members() doesn't need a 'cu' arg
core: class_member__delete() doesn't need a 'cu' arg
core: type__clone_members() doesn't need a 'cu' arg
core: class_member__clone() doesn't need a 'cu' arg
pahole: Introduce --prettify option
pahole: Try harder to resolve the --header type when pretty printing
pahole: Make prototype__stdio_fprintf_value() receive a FILE to read raw data from
man-page: Move the PRETTY PRINTING header earlier
pahole: Make pipe_seek() honour the 'fp' arg instead of hardcoding stdin
pahole: Rename 'fp' to 'output' in prototype__stdio_fprintf_value()
pahole: Use the supplied 'fp' argument in type__instance_read_once()
core: Use namespace->name in class__clone()
core: Remove strings.c, unused
ctf_loader: Use uint32_t instead of strings_t, that is going away
core: Remove base_type_name_to_size_table.sname, unused
pahole: Add missing bpf/btf.h include
libctf: Comment out unused CTF encoding functions
btf_loader: Add missing bpf/btf.h include
btf_encoder: Add missing bpf/btf.h include
pahole: Use conf_load.kabi_prefix
dwarf_loader: Make attr_suffix() handle kabi_prefix
dwarf_loader: Pass conf_load to functions calling attr_string()
ctf_loader: No need for the 'strings' extern, not used
btf_loader: No need for the 'strings' extern, not used
dwarf_loader: Remove unused strings variable and debug_fmt_ops->{init,exit}()
core: Remove unused debug_fmt_ops->dwarf__strings_ptr()
core: Remove unused cu__string() method
dwarf_loader: No need to strdup() what dwarf_formstring() returns
core: No need for debug_fmt_ops->variable_name() anymore
pahole: function__name() doesn't need a 'struct cu *' argument
core: Make variable->name a real string
core: Make label->name a real string
pahole: class_member_filter__new() doesn't need a 'struct cu *' argument
pahole: class_member_filter__parse() doesn't need a 'struct cu *' argument
pahole: tag__real_sizeof() doesn't need a 'struct cu *' argument
pahole: Rename tag__fprintf_hexdump_value() to instance__fprintf_hexdump_value()
pahole: enumerations__lookup_entry_from_value() doesn't need to return a CU anymore
pahole: enumeration__lookup_entry_from_value() doesn't need a 'cu' argument
core: Ditch unused enumeration__prefix_len() method
core: Ditch unused enumeration__prefix() method
pahole: enumeration__lookup_value() doesn't need a 'cu' argument
pahole: enumeration__lookup_enumerator() doesn't need a 'cu' argument
core: enumeration__emit_definitions() doesn't need a 'cu' argument
core: enumeration__fprintf() doesn't need a 'cu' argument
core: Make enumeration__max_entry_name_len() static
core: enumeration__max_entry_name_len() doesn't need a 'cu' argument
core: Make enumeration__calc_prefix() static
core: enumeration__calc_prefix doesn't need a 'cu' argument
btf_encoder: btf_encoder__add_enum_type() doesn't need a 'cu' argument, ditch it
core: enumerator__name() doesn't need a 'cu' argument, ditch it
core: Ditch dwarves__active_loader, unused
core: Make enumerator->name a real string
emit: type__emit_fwd_decl() isn't used outside emit.c, make it static
emit: type__emit_fwd_decl() doesn't need a cu arg
emit: type_emissions__find_definition() doesn't need a cu arg
core: class__name() doesn't need a cu arg
core: type__name() doesn't need a cu arg
core: Make namespace->name a real string
core: Make class_member->name a real string
core: Make parameter->name a real string
core: Make base_type->name a real string
pahole: Disable incomplete CTF encoder
core: Ditch unused cu__find_struct_by_sname()
core: Convert cu__find_base_type_by_sname_and_size to search for a string
core: Convert cu__find_enumeration_by_sname_and_size to search for a string
dwarf_loader: Rename strings_t 'name' to 'sname' to clarify usage
core: Make function->name a real string
core: Make function->linkage_name a real string
dwarf_loader: Make dwarf_tag->decl_file a real string
pahole: Allow specifying the number of threads to use while loading files
pahole: Make '-j' available for use as number of jobs (threads)
core: Protect cus->cus with a mutex
core: Make 'struct cus' opaque, only visible in dwarves.c
core: Introduce helper to return number of cu entries in a 'struct cus'
core: Move cus__find_pair() from codiff to the core
core: Introduce helper to return if there is no cu entries in a 'struct cus'
core: Prepare cus__find_cu_by_name() for locking
core: Prepare cus__find_function_at_addr() for locking
core: Prepare __cus__find_struct_by_name() for locking
core: Prepare cus__find_type_by_name() for locking
core: Initialize cu->priv in cu__new()
CMakeList.txt: Bump version to the upcoming 1.22 release, not out of the door yet
Revert "btf_encoder: Reduce the size of encode_cu() by moving function encoding to separate method"
dwarf_loader: Separate non-LTO per CU processing + BTF encoding part
btf_encoder: Reduce the size of encode_cu() by moving function encoding to separate method
btf_encoder: Reduce the size of encode_cu() by moving var encoding to separate method
btf_encoder: No need to set the endianness twice when encoding into an ELF file
btf_encoder: Pass detached_filename to the constructor
btf_encoder: Adopt writing to raw file method
btf_encoder: Rename btf__encode_in_elf to btf_encoder__write_elf, shortening function signature
btf_encoder: Combine btf__write_elf() with btf__encode_in_elf()
btf_encoder: Move duplicate code to btf_encoder__encode()
btf_encoder: Pass the 'skip_encoding_vars' to the constructor
btf_encoder: No need to export the 'struct btf_definition', make it opaque
libbpf: bump dependency to >= 0.4.0
libbpf: If LIBBPF_EMBEDDED=OFF and libbpf-dev/pkgconfig is not available, fail the build
headers: Rebame __unused to __maybe_unused to avoid clashes with system headers
libbpf: Fixup patch to allow to use packaged version
README: Add documentation for -DBUILD_SHARED_LIBS
CMakeLists.txt: Enable SHARED and STATIC lib creation
libbpf: Allow to use packaged version
btf_encoder: Move libbtf.c to btf_encoder.c, the only user of its functions
btf_encoder: Move PERCPU_SECTION define to the only source file using it
btf_encoder: Move library global variable btf_gen_floats to btf_encoder class
btf_encoder: Ditch unused 'btf_encoder__verbose' global variable
btf_encoder: Use encoder->verbose instead of btf_encoder__verbose
btf_encoder: Adopt btf__log_func_param() and use encoder->verbose
btf_encoder: Adopt btf__log_member() and use encoder->verbose
btf_encoder: Adopt btf__log_type() and use encoder->verbose
btf_encoder: No need to pass encoder->percpu_secinfo to btf_encoder__add_datasec()
btf_encoder: Make btf_encoder__add_var_secinfo() receive a btf_encoder pointer
btf_encoder: Adopt btf__encode_var_secinfo() as btf_encoder__add_var_secinfo()
btf_encoder: Adopt btf__encode_datasec_type() as btf_encoder__add_datasec()
btf_encoder: Adopt btf__encode_var_type() as btf_encoder__add_var()
btf_encoder: Adopt btf__encode_enum_val() as btf_encoder__add_enum_val()
btf_encoder: Adopt btf__encode_enum() as btf_encoder__add_enum()
btf_encoder: Adopt btf__encode_enumeration_type() as btf_encoder__add_enum_type()
btf_encoder: Adopt btf__encode_func_proto_param() as btf_encoder__add_func_param()
btf_encoder: Adopt btf__encode_func_proto() as btf_encoder__add_func_proto()
btf_encoder: Adopt btf__encode_array() as btf_encoder__add_array()
btf_encoder: Adopt btf__encode_struct() as btf_encoder__add_struct()
btf_encoder: Adopt btf__encode_member() as btf_encoder__add_field()
btf_encoder: Adopt btf__encode_struct_type() as btf_encoder__add_struct_type()
btf_encoder: Adopt btf__encode_ref_type() as btf_encoder__add_ref_type()
btf_encoder: Adopt btf__encode_float_type() as btf_encoder__add_float()
btf_encoder: Adopt btf__encode_base_type() as btf_encoder__add_base_type()
libbtf: Ditch unused btf_elf__verbose_log() macro
libbtf: Ditch btf_elf class, unused
btf_encoder: Use 'struct btf' directly, stop using btf_elf
libbtf: Remove unused ELF fields and calls from btf_elf
btf_encoder: Phagocytize percpu_secinfo from btf_elf
pahole: Rename 'encoder' to 'btf_encoder' as we support multiple formats
btf_encoder: Move the global btf_encoder to its users, like pahole
btf_encoder: Adopt the cu__encode_btf() method, as btf_encoder__encode_cu()
btf_encoder: Set btf_encoder__verbose in just one place, its users
btf_encoder: Move btf_elf__verbose var to btf_encoder class
btf_encoder: Adopt btf_elf__force, its only used in btf_encoder methods
pahole: Adopt btf_encoder object instantiation from btf_encoder
btf_encoder: Move the function encode() to be a btf_encoder method
btf_encoder: Temporarily expose the 'encoder' variable
btf_encoder: Move 'filename' member from btf_elf to btf_encoder
btfdiff: Support diffing DWARF vs detached BTF
btf_encoder: No need for calling btf_encoder__encode() from cu__encode_btf()
btf_encoder: Move btf_elf based encode_in_elf and write_elf methods to btf_encoder
btf_encoder: Move collect_symbols() call to btf_encoder constructor
btf_encoder: Remove needless hash.h include, not used anymore
btf_encoder: Move global elf_functions table to the btf_encoder class
btf_encoder: Move ELF's ehdr from btf_elf to btf_encoder
btf_encoder: Move 'symtab' from btf_elf to btf_encoder
btf_encoder: Move percpu members from btf_elf to btf_encoder
btf_encoder: Add a 'verbose' member for encoder specific logging requests
btf_encoder: Move 'array_index_id' global variable to 'struct btf_encoder'
btf_encoder: Move percpu vars global variables to btf_encoder class
btf_encoder: Adopt collect_function() as a btf_encoder method
btf_encoder: Adopt collect_per_cpu_var() as a btf_encoder method
btf_encoder: Adopt collect_symbol() as a btf_encoder method
btf_encoder: Move 'need_index_type' global variable to 'struct btf_encoder'
btf_encoder: Make tag__encode_btf() its encode_tag() method
btf_encoder: Move 'has_index_type' global variable to 'struct btf_encoder'
btf_encoder: Introduce 'struct btf_encoder' to have all the current globals
btf_encoder: Replace btfe with btf in tag__encode_btf()
btf_encoder: Move encode_enumeration_type() from btf_elf to btf
btf_encoder: Move encode_struct_type() from btf_elf to btf
btf_encoder: Move add_datasec_type() from btf_elf to btf encode_datasec_type()
btf_encoder: Move add_var_secinfo() from btf_elf to btf encode_var_secinfo()
btf_encoder: Move add_var_type() from btf_elf to btf encode_var_type()
btf_encoder: Move add_func_proto() from btf_elf to btf encode_func_proto()
btf_encoder: Move add_enum() from btf_elf to btf encode_enum()
btf_encoder: Move add_struct() from btf_elf to btf encode_struct()
btf_encoder: Move add_array() from btf_elf to btf encode_array()
btf_encoder: Move add_ref_type() from btf_elf to btf encode_ref_type()
btf_encoder: Move add_member() from btf_elf to btf encode_member()
btf_encoder: Move add_base_type() from btf_elf to btf encode_base_type()
btf_encoder: Move add_float_type() from btf_elf to btf encode_float_type()
btf_encoder: bpf__log_func_param() doesn't need the btfe arg
btf_encoder: Move log_member() from btf_elf to btf
btf_encoder: Move log_type() from btf_elf to btf
btf_encoder: Move log_err() from btf_elf to btf
btf_encoder: Remove 'elf' from btf_elf__int_encoding_str() name
btf_elf: Remove base_btf member, used only in the constructor
btf_elf: No point in falling back to raw BTF in btf_new()
btf_elf: No point in looking at the filename for /sys/kernel/btf in btf_elf__new()
libbtf: Ditch unused btf_elf__string() method
libbtf: Remove unused btf_elf__load()
btf_encoder: Move printable_name() from btfe to btf
btf_loader: Stop using libbtf.h and the btf_elf class
btf_loader: cu__fixup_btf_bitfields doesn'n need btfe parameter, ditch it
btf_loader: class__fixup_btf_bitfields doesn'n need btfe parameter, ditch it
btf_elf: Remove unused btfe->priv member
btf_loader: Add a local 'btf' var to prep next patches
btf_loader: Move load_sections() from btf_elf to btf
btf_loader: Move load_types() from btf_elf to btf
btf_loader: Move create_new_tag() from btfe to cu
btf_loader: Move create_new_datasec() from btfe to cu
btf_loader: Move create_new_variable() from btfe to cu
btf_loader: Move create_new_typedef() from btfe to cu
btf_loader: Move create_new_forward_decl() from btfe to cu
btf_loader: Move create_new_subroutine_type() from btfe to cu
btf_loader: Move create_new_enumeration() from btfe to cu
btf_loader: Move create_new_union() from btfe to cu
btf_loader: Move create_new_class() from btfe to cu
btf_loader: The create_members() function doesn't need the btfe arg
btf_loader: Move create_new_array() from btfe to cu
btf_loader: Move create_new_float_type() from btfe to cu
btf_loader: Move create_new_int_type() from btfe to cu
btf_loader: Move create_new_function() from btfe to cu
btf_loader: Move load_ftype() from btfe to cu
btf_loader: Pass cu to the load_sections/types routines
libbtf: Remove the 'base_btf' global var, now unused
pahole: Use conf_load->base_btf to call cu__encode_btf()
btf_loader: Use conf_load->base_btf instead of the global btf_base var
pahole: Set conf_load.btf_base when applicable
core: Allow passing the base_btf object via 'struct conf_load'
btf_encoder: Pass the base BTF object to the BTF encoder
btf_elf: Remove unused 'wordsize' member
btf_loader: Use btf__pointer_size() to set cu->addr_size
btf_elf: Remove unused is_big_endian member
btf_loader: Use btf__endianness() instead of to be removed btfe member
libbtf: Remove unused btf_elf->raw_btf member
pahole: Allow encoding BTF into a detached file
btf_loader: Use btf__parse_split()
dwarf_loader: Add define for DW_OP_addrx
libbtf: Fix typo
pahole: Add --kabi_prefix flag
btf_encoder: fix and complete filtering out zero-sized per-CPU variables
dwarves: Make handling of NULL by destructos consistent
dutil: Make handling of NULL by destructos consistent
pahole: Make handling of NULL by destructos consistent
elf_symtab: Use zfree() where applicable
codiff: Use zfree() where applicable
dutil: Adopt the zalloc()/zfree() from perf
libctf: Use zfree() where applicable
pahole: Use zfree() where applicable
dwarf_loader: Use zfree() instead of ad-hoc implementation
gobuffer: Use zfree() and make delete accept NULL, like free()
dwarves: Use zfree()
libbtf: Use zfree()
dutil: Adopt zfree(), to use it more pervasively
dwarves: Plug leaks in cu__new() found by covscan
dwarf_loader: Call dwarf_cu__delete() when aborting the load
dwarf_loader: Delete the allocated CU when aborting
dwarf_loader: Make all ABORT returns go thru a single exit label
dwarf_loader: Use zalloc() to allocate dwarf_cu
dwarf_loader: Make dwarf_cu__delete() accept NULL, just like free()
dwarves: Accept NULL in cu__delete(), just like free() accepts
dwarf_loader: Check if we have a CU after the loop in cus__merge_and_process_cu()
dwarf_loader: Check tag__recode_dwarf_bitfield() return, may be NULL
ctracer: Initialize the 'parm_list' variable, detected by covscan
codiff: Fix usage of negative errno values with strerror(), reported by covscan
btf_loader: Fix some memory leaks found by covscan
loaders: Plug enumeration__delete() leak detected by covscan
dwarves: Stop using obstacks
CMakeLists.txt: Specify the file extension of srcs
README: Mention how to specify another prefix
btf: Remove ftrace filter
pahole: Fix error message when --header couldn't be read
pahole: Introduce --with_flexible_array option to show just types ending in a flexible array
pahole: Prep 1.21
spec: Fix dates in RPM spec
dwarf_loader: Handle DWARF5 DW_OP_addrx properly
dwarf_loader: Handle subprogram ret type with abstract_origin properly
dwarf_loader: Check .notes section for LTO build info
dwarf_loader: Check .debug_abbrev for cross-CU references
dwarf_loader: Permit merging all DWARF CU's for clang LTO built binary
dwarf_loader: Factor out common code to initialize a cu
dwarf_loader: Permit a flexible HASHTAGS__BITS
btf: Add --btf_gen_all flag
btf: Add support for the floating-point types
fprintf: Honour conf_fprintf.hex when printing enumerations
Avoid warning when building with NDEBUG
btf_encoder: Match ftrace addresses within ELF functions
dwarf_loader: Use a better hashing function, from libbpf
btf_encoder: Funnel ELF error reporting through a macro
btf_encoder: Sanitize non-regular int base type
pahole: Prep 1.20
Revert "libbpf: allow to use packaged version"
dwarf_loader: Support DWARF_TAG_call_site{_parameter} also in die__process_inline_expansion
dwarf_loader: Add conditional DW_FORM_implicit_const definition for older systems
dtagnames: Stop using the deprecated mallinfo() function
cmake: Bump minimum required version to 2.8.12 as per upstream support warning
dwarves: Make enum prefix search more robust
dwarf_loader: Handle DWARF5 DW_TAG_call_site like DW_TAG_GNU_call_site
dwarf_loader: Support DW_FORM_implicit_const in __attr_offset()
dwarf_loader: Support DW_AT_data_bit_offset
dwarf_loader: Optimize a bit the reading of DW_AT_data_member_location
dwarf_loader: Fix typo
dwarf_loader: Introduce __attr_offset() to reuse call to dwarf_attr()
dwarf_loader: Support DW_FORM_implicit_const in attr_numeric()
btf_encoder: Improve ELF error reporting
bpf_encoder: Translate SHN_XINDEX in symbol's st_shndx values
elf_symtab: Handle SHN_XINDEX index in elf_section_by_name()
btf_encoder: Add extra checks for symbol names
libbpf: allow to use packaged version
btf_encoder: Improve error-handling around objcopy
btf_encoder: Fix handling of restrict qualifier
Bug: 203823368
Test: pahole builds after follow-up Android.bp change
Signed-off-by: Connor O'Brien <connoro@google.com>
Change-Id: I598528959a21e5f070edd1de23061401f28ba31d
-rw-r--r-- | CMakeLists.txt | 85 | ||||
-rw-r--r-- | MANIFEST | 12 | ||||
-rw-r--r-- | METADATA | 4 | ||||
-rw-r--r-- | NEWS | 459 | ||||
-rw-r--r-- | README | 13 | ||||
-rw-r--r-- | README.tarball | 2 | ||||
-rw-r--r-- | btf_encoder.c | 1556 | ||||
-rw-r--r-- | btf_encoder.h | 19 | ||||
-rw-r--r-- | btf_loader.c | 317 | ||||
-rwxr-xr-x | btfdiff | 18 | ||||
-rwxr-xr-x | buildcmd.sh | 7 | ||||
-rw-r--r-- | changes-v1.20 | 66 | ||||
-rw-r--r-- | changes-v1.21 | 70 | ||||
-rw-r--r-- | changes-v1.22 | 95 | ||||
-rw-r--r-- | changes-v1.23 | 56 | ||||
-rw-r--r-- | cmake/modules/Findargp.cmake | 41 | ||||
-rw-r--r-- | cmake/modules/Findobstack.cmake | 41 | ||||
-rw-r--r-- | codiff.c | 87 | ||||
-rw-r--r-- | ctf_loader.c | 87 | ||||
-rw-r--r-- | ctracer.c | 98 | ||||
-rw-r--r-- | dtagnames.c | 20 | ||||
-rw-r--r-- | dutil.c | 29 | ||||
-rw-r--r-- | dutil.h | 16 | ||||
-rw-r--r-- | dwarf_loader.c | 1405 | ||||
-rw-r--r-- | dwarves.c | 589 | ||||
-rw-r--r-- | dwarves.h | 282 | ||||
-rw-r--r-- | dwarves_emit.c | 56 | ||||
-rw-r--r-- | dwarves_emit.h | 3 | ||||
-rw-r--r-- | dwarves_fprintf.c | 267 | ||||
-rw-r--r-- | dwarves_reorganize.c | 32 | ||||
-rw-r--r-- | elf_symtab.c | 47 | ||||
-rw-r--r-- | elf_symtab.h | 31 | ||||
-rw-r--r-- | elfcreator.c | 2 | ||||
-rw-r--r-- | elfcreator.h | 2 | ||||
-rw-r--r-- | gobuffer.c | 5 | ||||
-rw-r--r-- | hash.h | 48 | ||||
l--------- | lib/include/bpf | 1 | ||||
-rw-r--r-- | libbtf.c | 826 | ||||
-rw-r--r-- | libbtf.h | 72 | ||||
-rw-r--r-- | libctf.c | 14 | ||||
-rw-r--r-- | list.h | 8 | ||||
-rw-r--r-- | man-pages/pahole.1 | 107 | ||||
-rw-r--r-- | pahole.c | 790 | ||||
-rw-r--r-- | pahole_strings.h | 31 | ||||
-rw-r--r-- | pdwtags.c | 11 | ||||
-rw-r--r-- | pfunct.c | 51 | ||||
-rw-r--r-- | pglobal.c | 18 | ||||
-rw-r--r-- | prefcnt.c | 10 | ||||
-rw-r--r-- | rpm/SPECS/dwarves.spec | 83 | ||||
-rw-r--r-- | scncopy.c | 2 | ||||
-rw-r--r-- | strings.c | 88 | ||||
-rw-r--r-- | syscse.c | 21 |
52 files changed, 5079 insertions, 3021 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 857487a..c0363b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,25 @@ project(pahole C) -cmake_minimum_required(VERSION 2.8.8) +cmake_minimum_required(VERSION 2.8.12) cmake_policy(SET CMP0005 NEW) +option(LIBBPF_EMBEDDED "Use the embedded version of libbpf instead of searching it via pkg-config" ON) +if (NOT LIBBPF_EMBEDDED) + find_package(PkgConfig REQUIRED) + if(PKGCONFIG_FOUND) + pkg_check_modules(LIBBPF REQUIRED libbpf>=0.4.0) + endif() +endif() + INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/lib/bpf/include/uapi) + ${CMAKE_CURRENT_SOURCE_DIR}) +if(NOT LIBBPF_FOUND) + # Allows to use 'system' style #include with both embedded and system libbpf + INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/lib/include) + INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/lib/bpf/include/uapi) +else() + INCLUDE_DIRECTORIES(${LIBBPF_INCLUDE_DIRS}) + LINK_DIRECTORIES(${LIBBPF_LIBRARY_DIRS}) +endif() # Try to parse this later, Helio just showed me a KDE4 example to support # x86-64 builds. @@ -31,17 +46,26 @@ endif (NOT CMAKE_BUILD_TYPE) set(CMAKE_C_FLAGS_DEBUG "-Wall -Werror -ggdb -O0") set(CMAKE_C_FLAGS_RELEASE "-Wall -O2") +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread") + +if (NOT DEFINED BUILD_SHARED_LIBS) + set (BUILD_SHARED_LIBS ON) + message(STATUS "Setting BUILD_SHARED_LIBS = ${BUILD_SHARED_LIBS}") +endif (NOT DEFINED BUILD_SHARED_LIBS) # Just for grepping, DWARVES_VERSION isn't used anywhere anymore -# add_definitions(-D_GNU_SOURCE -DDWARVES_VERSION="v1.19") +# add_definitions(-D_GNU_SOURCE -DDWARVES_VERSION="v1.23") add_definitions(-D_GNU_SOURCE -DDWARVES_MAJOR_VERSION=1) -add_definitions(-D_GNU_SOURCE -DDWARVES_MINOR_VERSION=19) +add_definitions(-D_GNU_SOURCE -DDWARVES_MINOR_VERSION=23) find_package(DWARF REQUIRED) find_package(ZLIB REQUIRED) +find_package(argp REQUIRED) +find_package(obstack REQUIRED) +find_package(Python3 QUIET) # make sure git submodule(s) are checked out find_package(Git QUIET) -if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") +if(LIBBPF_EMBEDDED AND GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") # Update submodules as needed option(GIT_SUBMODULE "Check submodules during build" ON) if(GIT_SUBMODULE) @@ -56,7 +80,7 @@ if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") endif() endif() endif() -if(NOT EXISTS "${PROJECT_SOURCE_DIR}/lib/bpf/src/btf.h") +if(NOT LIBBPF_FOUND AND NOT EXISTS "${PROJECT_SOURCE_DIR}/lib/bpf/src/btf.h") message(FATAL_ERROR "The submodules were not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.") endif() @@ -81,30 +105,33 @@ endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64") -file(GLOB libbpf_sources "lib/bpf/src/*.c") -add_library(bpf OBJECT ${libbpf_sources}) -set_property(TARGET bpf PROPERTY POSITION_INDEPENDENT_CODE 1) -target_include_directories(bpf PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/lib/bpf/include - ${CMAKE_CURRENT_SOURCE_DIR}/lib/bpf/include/uapi) +if (NOT LIBBPF_FOUND) + file(GLOB libbpf_sources "lib/bpf/src/*.c") + add_library(bpf OBJECT ${libbpf_sources}) + set_property(TARGET bpf PROPERTY POSITION_INDEPENDENT_CODE 1) + target_include_directories(bpf PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/lib/bpf/include + ${CMAKE_CURRENT_SOURCE_DIR}/lib/bpf/include/uapi) +endif() -set(dwarves_LIB_SRCS dwarves.c dwarves_fprintf.c gobuffer strings - ctf_encoder.c ctf_loader.c libctf.c btf_encoder.c btf_loader.c libbtf.c +set(dwarves_LIB_SRCS dwarves.c dwarves_fprintf.c gobuffer.c + ctf_loader.c libctf.c btf_encoder.c btf_loader.c dwarf_loader.c dutil.c elf_symtab.c rbtree.c) -add_library(dwarves SHARED ${dwarves_LIB_SRCS} $<TARGET_OBJECTS:bpf>) +if (NOT LIBBPF_FOUND) + list(APPEND dwarves_LIB_SRCS $<TARGET_OBJECTS:bpf>) +endif() +add_library(dwarves ${dwarves_LIB_SRCS}) set_target_properties(dwarves PROPERTIES VERSION 1.0.0 SOVERSION 1) set_target_properties(dwarves PROPERTIES INTERFACE_LINK_LIBRARIES "") -target_include_directories(dwarves PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/lib/bpf/include/uapi) -target_link_libraries(dwarves ${DWARF_LIBRARIES} ${ZLIB_LIBRARIES}) +target_link_libraries(dwarves ${DWARF_LIBRARIES} ${ZLIB_LIBRARIES} ${LIBBPF_LIBRARIES} ${ARGP_LIBRARY} ${OBSTACK_LIBRARY}) set(dwarves_emit_LIB_SRCS dwarves_emit.c) -add_library(dwarves_emit SHARED ${dwarves_emit_LIB_SRCS}) +add_library(dwarves_emit ${dwarves_emit_LIB_SRCS}) set_target_properties(dwarves_emit PROPERTIES VERSION 1.0.0 SOVERSION 1) target_link_libraries(dwarves_emit dwarves) set(dwarves_reorganize_LIB_SRCS dwarves_reorganize.c) -add_library(dwarves_reorganize SHARED ${dwarves_reorganize_LIB_SRCS}) +add_library(dwarves_reorganize ${dwarves_reorganize_LIB_SRCS}) set_target_properties(dwarves_reorganize PROPERTIES VERSION 1.0.0 SOVERSION 1) target_link_libraries(dwarves_reorganize dwarves) @@ -151,16 +178,18 @@ target_link_libraries(syscse dwarves) install(TARGETS codiff ctracer dtagnames pahole pdwtags pfunct pglobal prefcnt scncopy syscse RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) -install(TARGETS dwarves LIBRARY DESTINATION ${LIB_INSTALL_DIR}) -install(TARGETS dwarves dwarves_emit dwarves_reorganize LIBRARY DESTINATION ${LIB_INSTALL_DIR}) +install(TARGETS dwarves LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR}) +install(TARGETS dwarves dwarves_emit dwarves_reorganize LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR}) install(FILES dwarves.h dwarves_emit.h dwarves_reorganize.h - dutil.h gobuffer.h list.h rbtree.h pahole_strings.h - btf_encoder.h config.h ctf_encoder.h ctf.h - elfcreator.h elf_symtab.h hash.h libbtf.h libctf.h + dutil.h gobuffer.h list.h rbtree.h + btf_encoder.h config.h ctf.h + elfcreator.h elf_symtab.h hash.h libctf.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include/dwarves/) install(FILES man-pages/pahole.1 DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1/) -install(PROGRAMS ostra/ostra-cg DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) +if(Python3_FOUND) + install(PROGRAMS ostra/ostra-cg DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) + install(FILES ostra/python/ostra.py DESTINATION ${CMAKE_INSTALL_PREFIX}/share/dwarves/runtime/python) +endif() install(PROGRAMS btfdiff fullcircle DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) -install(FILES ostra/python/ostra.py DESTINATION ${CMAKE_INSTALL_PREFIX}/share/dwarves/runtime/python) install(FILES lib/Makefile lib/ctracer_relay.c lib/ctracer_relay.h lib/linux.blacklist.cu DESTINATION ${CMAKE_INSTALL_PREFIX}/share/dwarves/runtime) @@ -15,6 +15,8 @@ dwarves_fprintf.c dwarves_reorganize.c dwarves_reorganize.h cmake/modules/FindDWARF.cmake +cmake/modules/Findargp.cmake +cmake/modules/Findobstack.cmake CMakeLists.txt codiff.c ctracer.c @@ -27,8 +29,6 @@ fullcircle gobuffer.c gobuffer.h hash.h -libbtf.c -libbtf.h list.h MANIFEST man-pages/pahole.1 @@ -41,8 +41,6 @@ rbtree.c rbtree.h scncopy.c syscse.c -strings.c -pahole_strings.h dutil.c dutil.h changes-v1.13 @@ -50,6 +48,11 @@ changes-v1.16 changes-v1.17 changes-v1.18 changes-v1.19 +changes-v1.20 +changes-v1.21 +changes-v1.22 +changes-v1.23 +buildcmd.sh COPYING NEWS README @@ -69,3 +72,4 @@ libctf.c libctf.h regtest lib/bpf/ +lib/include/bpf @@ -11,7 +11,7 @@ third_party { type: GIT value: "https://git.kernel.org/pub/scm/devel/pahole/pahole" } - version: "b688e35970600c15d18d5eb7c19139253437a2aa" - last_upgrade_date { year: 2021 month: 1 day: 18 } + version: "c2b7b8c20877d267159ace36119f6340b9d12823" + last_upgrade_date { year: 2022 month: 1 day: 5 } license_type: RESTRICTED } @@ -1,3 +1,462 @@ +v1.23 + +Wed Dec 8 2021 + +54ae2f7f5e4f6328 Revert "fprintf: Allow making struct/enum/union anonymous" +69fb1861de35120c Revert "pahole: Add --inner_anon option" +005236c3e40eeb64 pahole: Add --inner_anon option +7c5e35b63bd26f1d fprintf: Allow making struct/enum/union anonymous +d99d551930cf4b4b btf_encoder: Support btf_type_tag attribute +3da248c3284ef905 man pages: Add missing --skip_encoding_btf_decl_tag entry +a58ecca0a89b142b man pages: Add missing --skip_encoding_btf_type_tag entry +b488c8d328845bd8 dwarf_loader: Support btf_type_tag attribute +a0cc68687f1281cd dutil: Move DW_TAG_LLVM_annotation definition to dutil.h +76401e9e46f07753 libbpf: Sync with latest libbpf repo to pick support for BTF_KIND_TYPE_TAG +0135ccd632796ab3 dwarf_loader: Warn about DW_TAG_skeleton_unit and give a workaround +433dc780ca48944b fprintf: Add DWARF5 tags added in elfutils 0.170 +7af9ed4aed182869 dwarf_loader: Print the hexadecimal value for unexpected tags in die__process() +ec62499774c4bcac btf_encoder: generate BTF_KIND_DECL_TAGs for typedef btf_decl_tag attributes +468b4196f65458e8 dwarf_loader: support typedef DW_TAG_LLVM_annotation +696c62180455fd29 btf_loader: Use cacheline size to infer alignment +48f4086b766d2214 btf_loader: Propagate struct conf_load +772725a77d3323c6 dwarves_fprintf: Move cacheline_size into struct conf_fprintf +cdd088c05cfed082 btfdiff: Suppress alignment tags with BTF as well as with DWARF +836c139fdf6f2b13 btf_loader: Infer alignment info +4db65fe0cd02b3cc core: Export tag__natural_alignment() +43e8216c25ee767f fprintf: Fix __attribute__((__aligned__(N)) handling for struct members +c52f6421f21146ad btf: Rename btf_tag to btf_decl_tag +3433c67bbd158e1d manpages: Minor fixes +e975d0fba833d02d btf_loader: Refactor class__fixup_btf_bitfields +5282feee6d4d3a88 pahole: Add --skip_missing option +6931e393f8f6d81f fprintf: Fix nested struct printing wrt attributes +16a7acaba4466121 btf_encoder: Fix handling of percpu symbols on s390 +3cde0135ca51de73 dwarf_loader: Fix heap overflow when accessing variable specification +a9c99e98815f06bd dwarves: Introduce conf_load->thread_exit() callback +cc6c7d473d518324 Update libbpf to get API to combine BTF +e38e89e8539b144a btf_encoder: Generate BTF_KIND_TAG from llvm annotations +aa8c494e65a77fa5 dwarf_loader: Parse DWARF tag DW_TAG_LLVM_annotation +3d20210d84f61ee2 CMakeList.txt: Don't download libbpf source when system library is used +38fad22d669ab044 libbpf: Get latest libbpf +88431099950ab3e8 CMakeList.txt: Make python optional + +v1.22: + +Mon Aug 23 2021 + +40a40df961e74aac core: Bump the chunk size for ptr_table uses in types, tags, functions tables +9f0809e6a8e790a1 pahole: Introduce --ptr_table_stats +f035871495435e58 core: Add CU stats printer +c59e996c97eb5569 pahole: Fix races in accessing type information in live CUs +c34b6c6cc96b9669 pahole: Add missing limits.h include to get ULLONG_MAX definition +5e8ad60d1f33bed6 CMakeList.txt: Look for obstack outside libc +739bc50b90694ffe cmake: Add a module to find if obstack is in a separate library +5244b47a883bee96 CMakeList.txt: Look for argp outside libc +3f70d31571d47c64 cmake: Add a module to find if argp is in a separate library +9f9588dc2bd8c955 fprintf: Add alternative method for reading the data cacheline size +71867afd666f2945 core: Include missing limits.h to get PATH_MAX definition +0d8d457fa325fb07 dutil: Include linux/stddef.h to build on Alpine Linux systems +bb02fec203463b9f README.tarball: Overcome --transform problem with symlinks +9fed416b380a7a17 MANIFEST: Add lib/include/bpf +cb1cf166769a169e spec: Remove deleted libbtf.h from the RPM file list +d0c3dd18c7d81cff README.tarball: Remove hardcoded version, get it from 'git tag' +b5140d5e6c8cade8 MANIFEST: Remove long gone strings header files +02fd228f6af7b04e MANIFEST: Add buildcmd.sh +06e41eccc2d19263 buildcmd.sh: Add single build script for use in CI +eba3e874add53187 pahole: Consider type members's names when comparing unions, structs +f61d458c9126467e pahole: Consider type members's types when comparing unions, structs +589a298829760a14 core: Introduce helpers for getting the first and next members of a type +815041d6dc4d62bf pahole: Improve the type sorting routine to consider multiple types with same name +93e26698693aca95 core: Remove extra ; in 'print_numeric_version' extern declaration +19798784d7a715d0 btf_loader: Mark the 'level' arg to the libbpf error callback as __maybe_unused +22763a8fc7ee5f79 btf_loader: Mark create_new_datasec() args as __maybe_unused +38df86db2b65dd43 dwarf_loader: cus__load_debug_types() doesn't use its 'cus' arg, remove it +16d646c07e98d66c dwarf_loader: Rename finalize_cu_immediately() to cus__finalize() to follow convention +90599e6177f20696 dwarf_loader: Remove unused 'dcu' argument from finalize_cu_immediately() +6fd4377a0db1e8d3 dwarf_loader: Remove unused 'dcus' argument from cu__finalize() +2bb04ecf79880a97 dwarf_loader: Remove unused 'cus' argument from finalize_cu() +9ada372a21d0abc2 dwarf_loader: Fix signed/unsigned comparision in tag__recode_dwarf_bitfield() +e4e9267c3a177ffa core: cus__fprintf_load_files_err() doesn't use its 'cus' argument +df92cb6b8e0f1577 core: Change last_seen_bit to uint32_t in class__find_holes() +789d8b3e1a7210d1 core: Change aligned_start to uint32_t in class__find_holes() +182cdcaed9055c76 core: Change cur_bitfield_end to uint32_t in class__find_holes() +5900f43f10844971 core: Change bit_start and bit_end to uint32_t in class__find_holes() +8634d8535f3fb25d btf_encoder: Fix signed/unsigned comparision +8d2efa2b6c147c37 btf_encoder: has_arg_names() doesn't need the 'cu' pointer +05f737076f81eab9 btf_encoder: btf_encoder__encode_tag() doesn't need the 'core_id' pointer +dc30e82b26dc98ef btf_encoder: btf_encoder__encode_tag() doesn't need the 'cu' pointer +4360359e434b41db btf_encoder: btf_encoder__add_struct_type() doesn't need the 'cu' pointer +6e1e4881a584ca54 btf_encoder: btf_encoder__add_func_proto() doesn't need the 'cu' pointer +9fbfcee7d9708876 btf_encoder: No need to read the ehdr in btf_encoder__write_elf(), ditch it +898cc490279c794b ctracer: No need to read the ehdr, ditch it +dee83e27dd69cb98 btf_encoder: No need to store the ehdr in the instance +24404190b80aa6bf elf_symtab: Remove needless GElf_Ehdr pointer argument from the constructor +74c2078e04755b6a dutil: elf_symtab__new() doesn't need the GElf_Ehdr *ep argument +23ea62817c3c9d1d pahole: Move case fallthru comment to after the statement +7a8e75cd9a99f618 elfcreator: elfcreator_copy_scn() doesn't need the 'elf' arg +3925a5bd53d68edc syscse: zero_extend() doesn't need a 'cu' arg +21b2933f018a954d pahole: Fix signedness of ternary expression operator +4e11c13895556982 ctracer: Remove a bunch of unused 'cu' pointers +54c1e93b8eada3db pahole: Use the 'prototypes' parameter in prototypes__load() +8b495918e6b52c4c codiff: class__find_pair_member() doesn't need 'cu' args +057be3d9936f78d9 core: class__find_member_by_name() doesn't need a cu pointer +ce9de90364d0fe98 core: Document type->node member usage +cead526d6b96e1d7 core: Fix nnr_members typo on 'struct type' comment docs +7cfc9be1f27876f4 man-pages: Improve the --nr_methods/-m pahole man page entry +3895127ce674cfb6 pahole: Clarify that currently --nr_methods doesn't work together witn -C +2ea46285aca0c639 pahole: No need to store the class name in 'struct structure' +4d8551396df5ce5a pahole: Multithreaded DWARF loading requires elfutils >= 0.178 +e57e23c72a7c0446 btf_encoder: Add methods to maintain a list of btf encoders +e9b83dba79a87935 list: Adopt list_next_entry() from the Linux kernel +6edae3e768e6bc0d dwarf_loader: Make hash table size default to 12, faster than 15 +d2d83be1e2124785 pahole: Allow tweaking the size of the loader hash tables +ff7bd7083f36db80 core: Allow sizing the loader hash table +3068ff36b7ff943d hash: Remove unused hash_32(), hash_ptr() +8eebf70d05d881cf dwarf_loader: Use a per-CU frontend cache for the latest lookup result +a2f1e698487b783c core: Use obstacks: take 2 +dca86fb8c2220f9a dwarf_loader: Add comment on why we can't ignore lexblocks +9d0a7ee0c38ebac5 pahole: Ignore DW_TAG_label when encoding BTF +d40c5f1e20795938 core: Allow ignoring DW_TAG_label +51ba83192979916e pahole: Ignore DW_TAG_inline_expansion when encoding BTF +903863889131a7fa core: Allow ignoring DW_TAG_inline_expansion +20757745f04ace54 pahole: Allow encoding BTF with parallel DWARF loading +5a85d9a450819500 core: Zero out unused entries when extending ptr_table array in ptr_table__add() +d133569bd0f0b422 pahole: No need to read DW_AT_alignment when encoding BTF +21a41e5386938db1 dwarf_loader: Allow asking not to read the DW_AT_alignment attribute +1ef1639039b56005 dwarf_loader: Do not look for non-C DWARF attributes in C CUs +88265eab35666f34 core: Add cu__is_c() to check if the CU language is C +1caed1c443d4a0dc dwarf_loader: Add a lock around dwarf_decl_file() and dwarf_decl_line() calls +dd13708f2fd7557b btfdiff: Use multithreaded DWARF loading +f95f7838499f94c3 btfdiff: Use --sort for pretty printing from both BTF and DWARF +3e1c7a20770ec432 pahole: Introduce --sort +967290bc7176e3c2 pahole: Store the class id in 'struct structure' as well +2b45e1b6d04b147c dwarf_loader: Defer freeing libdw Dwfl handler +35845e7e41872158 core: Provide a way to store per loader info in cus and an exit function +5365c45177643f35 pahole: Keep class + cu in tree of structures +fb99cad539e58638 dwarf_loader: Parallel DWARF loading +75d4748861ad98cf pahole: Disable parallell BTF encoding for now +1c60f71daacbe4d0 pahole: Add locking for the structures list and rbtree +46ad8c01585df768 dwarf_loader: Introduce 'dwarf_cus' to group all the DWARF specific per-cus state +d963af9fd8148ba0 dwarf_loader: Factor common bits for creating and processing CU +0c5bf70cc1259162 fprintf: class__vtable_fprintf() doesn't need a 'cu' arg +38ff86b149e1aa9a fprintf: string_type__fprintf() doesn't need a 'cu' arg +a75c342ac2fc6c54 core: Ditch tag__free_orig_info(), unused +80fe32fd294ac15e core: variable__name() doesn't need a 'cu' arg +caa219dffcb41422 core: base_type__name() doesn't need a 'cu' arg +7569e46d3515f93e core: namespace__delete() doesn't need a 'cu' arg +de4e8b7f178257d5 core: {tag,function,lexblock}__delete() doesn't need a 'cu' arg +789ed4e3a2c34b54 core: ftype__delete() doesn't need a 'cu' arg +6340cb462767aa95 core: enumeration__delete() doesn't need a 'cu' arg +33e44f5295702d95 core: type__delete() doesn't need a 'cu' arg +0f54ca9c827e9ef7 core: class__clone() doesn't need a 'cu' arg +2b2014187b811243 core: class__delete() doesn't need a 'cu' arg +f40900eba656db3b core: type__delete_class_members() doesn't need a 'cu' arg +50916756d59ad7f9 core: class_member__delete() doesn't need a 'cu' arg +2e50463c3aa149d3 core: type__clone_members() doesn't need a 'cu' arg +a66208355ea8108c core: class_member__clone() doesn't need a 'cu' arg +33e0d5f874dc22c4 pahole: Introduce --prettify option +bc36e94f32e662aa pahole: Try harder to resolve the --header type when pretty printing +fcfa2141c375849b pahole: Make prototype__stdio_fprintf_value() receive a FILE to read raw data from +1a65d232b040fae6 man-page: Move the PRETTY PRINTING header earlier +2d35630fa55de2bc pahole: Make pipe_seek() honour the 'fp' arg instead of hardcoding stdin +9aa01472d9655e1b pahole: Rename 'fp' to 'output' in prototype__stdio_fprintf_value() +472b94018055873d pahole: Use the supplied 'fp' argument in type__instance_read_once() +63992cb02a69485b core: Use namespace->name in class__clone() +ced4c34c37acd185 core: Remove strings.c, unused +adbb66c2957d9eef ctf_loader: Use uint32_t instead of strings_t, that is going away +ad707445b3fcaf11 core: Remove base_type_name_to_size_table.sname, unused +f8d571934b96ea35 pahole: Add missing bpf/btf.h include +deb6a4a4921b33ec libctf: Comment out unused CTF encoding functions +82155bc92f726c43 btf_loader: Add missing bpf/btf.h include +05f1f9dece4ed54d btf_encoder: Add missing bpf/btf.h include +f4a77d03909c1cf8 pahole: Use conf_load.kabi_prefix +2b9bd83e63bacb59 dwarf_loader: Make attr_suffix() handle kabi_prefix +daaafeb35f168fd1 dwarf_loader: Pass conf_load to functions calling attr_string() +def39099c1920346 ctf_loader: No need for the 'strings' extern, not used +2499920ac8d88b82 btf_loader: No need for the 'strings' extern, not used +a388aaf489f945d4 dwarf_loader: Remove unused strings variable and debug_fmt_ops->{init,exit}() +3d3b7b3287617b8f core: Remove unused debug_fmt_ops->dwarf__strings_ptr() +05687c547e9f22e8 core: Remove unused cu__string() method +a201149e181be613 dwarf_loader: No need to strdup() what dwarf_formstring() returns +6b7f1b72f9a958e4 core: No need for debug_fmt_ops->variable_name() anymore +9d0e3ab9a29c2e81 pahole: function__name() doesn't need a 'struct cu *' argument +a7d789a4f841b0ea core: Make variable->name a real string +b5694280ec5b1f1f core: Make label->name a real string +e974d1b2404154ea pahole: class_member_filter__new() doesn't need a 'struct cu *' argument +0275e8d24975c531 pahole: class_member_filter__parse() doesn't need a 'struct cu *' argument +90183e8e4da43785 pahole: tag__real_sizeof() doesn't need a 'struct cu *' argument +5cb91927385d7b75 pahole: Rename tag__fprintf_hexdump_value() to instance__fprintf_hexdump_value() +75c769a900185faf pahole: enumerations__lookup_entry_from_value() doesn't need to return a CU anymore +1edca2655284d968 pahole: enumeration__lookup_entry_from_value() doesn't need a 'cu' argument +f8d98eff756bf406 core: Ditch unused enumeration__prefix_len() method +5cc365164a4cba46 core: Ditch unused enumeration__prefix() method +e18c60d793935ab6 pahole: enumeration__lookup_value() doesn't need a 'cu' argument +4b877c8e6762c42f pahole: enumeration__lookup_enumerator() doesn't need a 'cu' argument +3ff11828fee01a91 core: enumeration__emit_definitions() doesn't need a 'cu' argument +0947d6e7954d752d core: enumeration__fprintf() doesn't need a 'cu' argument +bb22f5bb0a137342 core: Make enumeration__max_entry_name_len() static +dc83336171d1157c core: enumeration__max_entry_name_len() doesn't need a 'cu' argument +ec1667c76db61e2f core: Make enumeration__calc_prefix() static +45ec63ed20a56557 core: enumeration__calc_prefix doesn't need a 'cu' argument +2fae84e2f79726fb btf_encoder: btf_encoder__add_enum_type() doesn't need a 'cu' argument, ditch it +96243fdd79c5c45d core: enumerator__name() doesn't need a 'cu' argument, ditch it +ee5c12893b2e8aa8 core: Ditch dwarves__active_loader, unused +713239bc008910ff core: Make enumerator->name a real string +7721cc17aca7020d emit: type__emit_fwd_decl() isn't used outside emit.c, make it static +9aa5db7acd420d55 emit: type__emit_fwd_decl() doesn't need a cu arg +c46f91ef6f9cd0a1 emit: type_emissions__find_definition() doesn't need a cu arg +c127d25daf88c1a2 core: class__name() doesn't need a cu arg +00e8c5fe216755d6 core: type__name() doesn't need a cu arg +b99c4008acca8794 core: Make namespace->name a real string +379a73c6eb4633d4 core: Make class_member->name a real string +3280cb41768a7130 core: Make parameter->name a real string +f009162fd133d155 core: Make base_type->name a real string +e2ee753fa74ca570 pahole: Disable incomplete CTF encoder +0d13bc50ee8dbdf8 core: Ditch unused cu__find_struct_by_sname() +46f3f372416d7f9f core: Convert cu__find_base_type_by_sname_and_size to search for a string +f84e8777eada296a core: Convert cu__find_enumeration_by_sname_and_size to search for a string +a16c82f7110467b7 dwarf_loader: Rename strings_t 'name' to 'sname' to clarify usage +82e5b5101a6adf73 core: Make function->name a real string +4f73cac85321bd5b core: Make function->linkage_name a real string +a93160df5335ed2e dwarf_loader: Make dwarf_tag->decl_file a real string +a3fcbcacf79787d6 pahole: Allow specifying the number of threads to use while loading files +d70b2562ee5d90f2 pahole: Make '-j' available for use as number of jobs (threads) +41a283c65d300945 core: Protect cus->cus with a mutex +972065482a7f714d core: Make 'struct cus' opaque, only visible in dwarves.c +3895b29060fdc21e core: Introduce helper to return number of cu entries in a 'struct cus' +874e750fb8bddcad core: Move cus__find_pair() from codiff to the core +bf74fc1fcf1ab78e core: Introduce helper to return if there is no cu entries in a 'struct cus' +7020f9214328ac8d core: Prepare cus__find_cu_by_name() for locking +4bf7285b37108222 core: Prepare cus__find_function_at_addr() for locking +73a2fd1e5a70c7f2 core: Prepare __cus__find_struct_by_name() for locking +5020bf721b204673 core: Prepare cus__find_type_by_name() for locking +d124926baf2366a2 core: Initialize cu->priv in cu__new() +3ec54ee72ff7c5b1 CMakeList.txt: Bump version to the upcoming 1.22 release, not out of the door yet +1ef87b26fd268b52 Revert "btf_encoder: Reduce the size of encode_cu() by moving function encoding to separate method" +7869cc113b0b5a3d dwarf_loader: Separate non-LTO per CU processing + BTF encoding part +de3a7f912559433c btf_encoder: Reduce the size of encode_cu() by moving function encoding to separate method +d348b37ed16249e0 btf_encoder: Reduce the size of encode_cu() by moving var encoding to separate method +f62196d3be5a8765 btf_encoder: No need to set the endianness twice when encoding into an ELF file +819f83bd9736b9c8 btf_encoder: Pass detached_filename to the constructor +7ed4ddc46826538e btf_encoder: Adopt writing to raw file method +1498094d335f697a btf_encoder: Rename btf__encode_in_elf to btf_encoder__write_elf, shortening function signature +cd2b8978c43ea00a btf_encoder: Combine btf__write_elf() with btf__encode_in_elf() +18a3e9711ccc6887 btf_encoder: Move duplicate code to btf_encoder__encode() +9eb3d7a29de32bf4 btf_encoder: Pass the 'skip_encoding_vars' to the constructor +707101ec38d76958 btf_encoder: No need to export the 'struct btf_definition', make it opaque +f1feaa94c7a92e5c libbpf: bump dependency to >= 0.4.0 +1b50808c2958dde3 libbpf: If LIBBPF_EMBEDDED=OFF and libbpf-dev/pkgconfig is not available, fail the build +790dfbda79444d11 headers: Rebame __unused to __maybe_unused to avoid clashes with system headers +82756ea3eaf1f6e7 libbpf: Fixup patch to allow to use packaged version +ba953a53be25765e README: Add documentation for -DBUILD_SHARED_LIBS +aa2027708659f172 CMakeLists.txt: Enable SHARED and STATIC lib creation +ae2581647e849488 libbpf: Allow to use packaged version +743f2536d8b876a4 btf_encoder: Move libbtf.c to btf_encoder.c, the only user of its functions +6f72dddbed0e1bf8 btf_encoder: Move PERCPU_SECTION define to the only source file using it +f0f5b4f2bc34b029 btf_encoder: Move library global variable btf_gen_floats to btf_encoder class +09970b03bd309d83 btf_encoder: Ditch unused 'btf_encoder__verbose' global variable +1bf2e3511a1386c4 btf_encoder: Use encoder->verbose instead of btf_encoder__verbose +4ffa484b6a04530b btf_encoder: Adopt btf__log_func_param() and use encoder->verbose +729da7613b399f9c btf_encoder: Adopt btf__log_member() and use encoder->verbose +be75b76b6b9e479f btf_encoder: Adopt btf__log_type() and use encoder->verbose +9f19e96001f72c49 btf_encoder: No need to pass encoder->percpu_secinfo to btf_encoder__add_datasec() +869c177f8e13ce12 btf_encoder: Make btf_encoder__add_var_secinfo() receive a btf_encoder pointer +f0e8cd68f5dcb20b btf_encoder: Adopt btf__encode_var_secinfo() as btf_encoder__add_var_secinfo() +cc646d93d18270e9 btf_encoder: Adopt btf__encode_datasec_type() as btf_encoder__add_datasec() +b0f71fabdeee329b btf_encoder: Adopt btf__encode_var_type() as btf_encoder__add_var() +1aece1e7e6d593db btf_encoder: Adopt btf__encode_enum_val() as btf_encoder__add_enum_val() +4371b3cd5b9b18e6 btf_encoder: Adopt btf__encode_enum() as btf_encoder__add_enum() +b536947512efcb7e btf_encoder: Adopt btf__encode_enumeration_type() as btf_encoder__add_enum_type() +1bc29591ec010fa8 btf_encoder: Adopt btf__encode_func_proto_param() as btf_encoder__add_func_param() +393febe592e94f4a btf_encoder: Adopt btf__encode_func_proto() as btf_encoder__add_func_proto() +4eb4c03578b29791 btf_encoder: Adopt btf__encode_array() as btf_encoder__add_array() +06cd9f00c69d11bd btf_encoder: Adopt btf__encode_struct() as btf_encoder__add_struct() +0b29d5a65ccef5f6 btf_encoder: Adopt btf__encode_member() as btf_encoder__add_field() +3e2a1f7ddc930d3c btf_encoder: Adopt btf__encode_struct_type() as btf_encoder__add_struct_type() +2c7a2f270ef7bcea btf_encoder: Adopt btf__encode_ref_type() as btf_encoder__add_ref_type() +07d4ec9cef0c1619 btf_encoder: Adopt btf__encode_float_type() as btf_encoder__add_float() +68ed8af85908e432 btf_encoder: Adopt btf__encode_base_type() as btf_encoder__add_base_type() +14ab2e036147b6bc libbtf: Ditch unused btf_elf__verbose_log() macro +539c94fee9315254 libbtf: Ditch btf_elf class, unused +282a8a2187bde9bd btf_encoder: Use 'struct btf' directly, stop using btf_elf +dd0b01f56837794e libbtf: Remove unused ELF fields and calls from btf_elf +a0c5f49b0c405b1b btf_encoder: Phagocytize percpu_secinfo from btf_elf +c288e29d06c380b5 pahole: Rename 'encoder' to 'btf_encoder' as we support multiple formats +46a3e3a87a8963ca btf_encoder: Move the global btf_encoder to its users, like pahole +fa849010b4565015 btf_encoder: Adopt the cu__encode_btf() method, as btf_encoder__encode_cu() +3be437c5e7d2777e btf_encoder: Set btf_encoder__verbose in just one place, its users +e1e787af4496b51f btf_encoder: Move btf_elf__verbose var to btf_encoder class +66f4054252ab996e btf_encoder: Adopt btf_elf__force, its only used in btf_encoder methods +5e1207754eb67249 pahole: Adopt btf_encoder object instantiation from btf_encoder +0208952e8ffcfdc3 btf_encoder: Move the function encode() to be a btf_encoder method +bcc5f95364da96bb btf_encoder: Temporarily expose the 'encoder' variable +e27be59f10c7c6e3 btf_encoder: Move 'filename' member from btf_elf to btf_encoder +da9d70a16ff01492 btfdiff: Support diffing DWARF vs detached BTF +6bc135c8f49696a9 btf_encoder: No need for calling btf_encoder__encode() from cu__encode_btf() +48a0fa2ef7b546a3 btf_encoder: Move btf_elf based encode_in_elf and write_elf methods to btf_encoder +cc8eae604dc07b5e btf_encoder: Move collect_symbols() call to btf_encoder constructor +96e59c55686bccaf btf_encoder: Remove needless hash.h include, not used anymore +92f8852d8d178966 btf_encoder: Move global elf_functions table to the btf_encoder class +aa48ed55a790c2d5 btf_encoder: Move ELF's ehdr from btf_elf to btf_encoder +b3701a8346a6ea71 btf_encoder: Move 'symtab' from btf_elf to btf_encoder +be8dac48691f6578 btf_encoder: Move percpu members from btf_elf to btf_encoder +7f70877b198200ac btf_encoder: Add a 'verbose' member for encoder specific logging requests +ef8a48b3bc7cda1f btf_encoder: Move 'array_index_id' global variable to 'struct btf_encoder' +f3e6edffd7210265 btf_encoder: Move percpu vars global variables to btf_encoder class +14053c490bf68a49 btf_encoder: Adopt collect_function() as a btf_encoder method +049675f4a5401ac2 btf_encoder: Adopt collect_per_cpu_var() as a btf_encoder method +21b5e9b368228502 btf_encoder: Adopt collect_symbol() as a btf_encoder method +e40bc12ddf42b4a8 btf_encoder: Move 'need_index_type' global variable to 'struct btf_encoder' +cccfeec3c7d68f89 btf_encoder: Make tag__encode_btf() its encode_tag() method +65b3c598ab24a14a btf_encoder: Move 'has_index_type' global variable to 'struct btf_encoder' +a5c732e712564798 btf_encoder: Introduce 'struct btf_encoder' to have all the current globals +bd4e585d81a35862 btf_encoder: Replace btfe with btf in tag__encode_btf() +de2676159d8472b7 btf_encoder: Move encode_enumeration_type() from btf_elf to btf +80dec71c934fc96a btf_encoder: Move encode_struct_type() from btf_elf to btf +6d7790a5d4fc68a4 btf_encoder: Move add_datasec_type() from btf_elf to btf encode_datasec_type() +12d340c321e31564 btf_encoder: Move add_var_secinfo() from btf_elf to btf encode_var_secinfo() +d90b38c1f3ca9171 btf_encoder: Move add_var_type() from btf_elf to btf encode_var_type() +bce82c1a00a9beee btf_encoder: Move add_func_proto() from btf_elf to btf encode_func_proto() +188f25b74130d131 btf_encoder: Move add_enum() from btf_elf to btf encode_enum() +f82bd80a5c175425 btf_encoder: Move add_struct() from btf_elf to btf encode_struct() +1216aebd27ecd4e6 btf_encoder: Move add_array() from btf_elf to btf encode_array() +a96c91261e731602 btf_encoder: Move add_ref_type() from btf_elf to btf encode_ref_type() +a97ed8080a354487 btf_encoder: Move add_member() from btf_elf to btf encode_member() +56e0c2d4ccc7a9bd btf_encoder: Move add_base_type() from btf_elf to btf encode_base_type() +adeeb9ebcf7ef8bd btf_encoder: Move add_float_type() from btf_elf to btf encode_float_type() +37d3b8a1cedac677 btf_encoder: bpf__log_func_param() doesn't need the btfe arg +64d216f57e7a229f btf_encoder: Move log_member() from btf_elf to btf +20bb260428c0a1ad btf_encoder: Move log_type() from btf_elf to btf +16d026ce5db765c9 btf_encoder: Move log_err() from btf_elf to btf +c5f594eaa9b5ab8c btf_encoder: Remove 'elf' from btf_elf__int_encoding_str() name +c81d11b8bb80bed1 btf_elf: Remove base_btf member, used only in the constructor +69f5fff50e442cad btf_elf: No point in falling back to raw BTF in btf_new() +3fcf804a7e69aa4f btf_elf: No point in looking at the filename for /sys/kernel/btf in btf_elf__new() +c9eb447502b771c5 libbtf: Ditch unused btf_elf__string() method +a355a245fb48b05f libbtf: Remove unused btf_elf__load() +3de65d433ab18301 btf_encoder: Move printable_name() from btfe to btf +7fb31d787d3deec1 btf_loader: Stop using libbtf.h and the btf_elf class +e431c18b80650545 btf_loader: cu__fixup_btf_bitfields doesn'n need btfe parameter, ditch it +8c1919c10df8a79f btf_loader: class__fixup_btf_bitfields doesn'n need btfe parameter, ditch it +4d2ae0d9049e47c5 btf_elf: Remove unused btfe->priv member +b54b74edfcd0dc8c btf_loader: Add a local 'btf' var to prep next patches +52bedef0fddaa227 btf_loader: Move load_sections() from btf_elf to btf +ac3f2952a854fccc btf_loader: Move load_types() from btf_elf to btf +1a33deae144dbd22 btf_loader: Move create_new_tag() from btfe to cu +8323cb33bc73817a btf_loader: Move create_new_datasec() from btfe to cu +6fb41bbf8e31f3cf btf_loader: Move create_new_variable() from btfe to cu +9122424dda37ccff btf_loader: Move create_new_typedef() from btfe to cu +dd3e9ab4a00f8cf9 btf_loader: Move create_new_forward_decl() from btfe to cu +cca6b51d292df6f1 btf_loader: Move create_new_subroutine_type() from btfe to cu +ba1b77d01dda3ac0 btf_loader: Move create_new_enumeration() from btfe to cu +a58bc8300f13c93f btf_loader: Move create_new_union() from btfe to cu +367f5fe74cbb35ec btf_loader: Move create_new_class() from btfe to cu +2460bead8a8fc02f btf_loader: The create_members() function doesn't need the btfe arg +1136795fec7ff6bf btf_loader: Move create_new_array() from btfe to cu +ab2f5028916c7822 btf_loader: Move create_new_float_type() from btfe to cu +6b452a1cc5f2f22b btf_loader: Move create_new_int_type() from btfe to cu +073a5f69ef19700a btf_loader: Move create_new_function() from btfe to cu +29ad464f73286067 btf_loader: Move load_ftype() from btfe to cu +407693e2cabfa7a3 btf_loader: Pass cu to the load_sections/types routines +89b9c4f8851ebc8f libbtf: Remove the 'base_btf' global var, now unused +6f70d0accaf17f74 pahole: Use conf_load->base_btf to call cu__encode_btf() +0e77be409969631b btf_loader: Use conf_load->base_btf instead of the global btf_base var +6ee61b161dbcc42c pahole: Set conf_load.btf_base when applicable +b8255beb12c02d76 core: Allow passing the base_btf object via 'struct conf_load' +f93e05d8bd45d4b3 btf_encoder: Pass the base BTF object to the BTF encoder +81797a00fbdd1017 btf_elf: Remove unused 'wordsize' member +ecc888b855c249a6 btf_loader: Use btf__pointer_size() to set cu->addr_size +e6b69ffc74dcb3f0 btf_elf: Remove unused is_big_endian member +81fe7d688de85fa2 btf_loader: Use btf__endianness() instead of to be removed btfe member +9f8ce7e1137f22c1 libbtf: Remove unused btf_elf->raw_btf member +89be5646a03435bf pahole: Allow encoding BTF into a detached file +22a76fbc8b33a3cd btf_loader: Use btf__parse_split() +c9d4c106ab95cd8c dwarf_loader: Add define for DW_OP_addrx +76f7844c817eeac5 libbtf: Fix typo +3d510406ff363056 pahole: Add --kabi_prefix flag +0d17503db0580a66 btf_encoder: fix and complete filtering out zero-sized per-CPU variables +fb418f9d8384d3a9 dwarves: Make handling of NULL by destructos consistent +f049fe9ebf7aa9c2 dutil: Make handling of NULL by destructos consistent +1512ab8ab6fe76a9 pahole: Make handling of NULL by destructos consistent +1105b7dad2d0978b elf_symtab: Use zfree() where applicable +131275fa20437dec codiff: Use zfree() where applicable +6784b03fd447f507 dutil: Adopt the zalloc()/zfree() from perf +d7f5824a9e1895d1 libctf: Use zfree() where applicable +45d9eb8602b42a6b pahole: Use zfree() where applicable +f48cc78e8e6e56df dwarf_loader: Use zfree() instead of ad-hoc implementation +d0f6a4a1da524873 gobuffer: Use zfree() and make delete accept NULL, like free() +db37185d16d4a56d dwarves: Use zfree() +e7e86c75e149c161 libbtf: Use zfree() +972001e58e9f5ed9 dutil: Adopt zfree(), to use it more pervasively +5847901abf13e8bd dwarves: Plug leaks in cu__new() found by covscan +32114e611a93b046 dwarf_loader: Call dwarf_cu__delete() when aborting the load +26bd4c41646a64b9 dwarf_loader: Delete the allocated CU when aborting +d8940df90b4a952b dwarf_loader: Make all ABORT returns go thru a single exit label +3ba54ee43a301419 dwarf_loader: Use zalloc() to allocate dwarf_cu +f3957627bb3e3431 dwarf_loader: Make dwarf_cu__delete() accept NULL, just like free() +8772c9d827813d4d dwarves: Accept NULL in cu__delete(), just like free() accepts +25cc0c77545cefbc dwarf_loader: Check if we have a CU after the loop in cus__merge_and_process_cu() +2f30062b546de1ef dwarf_loader: Check tag__recode_dwarf_bitfield() return, may be NULL +d68fd2bbc59291f3 ctracer: Initialize the 'parm_list' variable, detected by covscan +5b0fb9745e1e9597 codiff: Fix usage of negative errno values with strerror(), reported by covscan +cba940fb86a46fa9 btf_loader: Fix some memory leaks found by covscan +674063b1ea4156d2 loaders: Plug enumeration__delete() leak detected by covscan +505a1f5615c5064a dwarves: Stop using obstacks +872658b88021b487 CMakeLists.txt: Specify the file extension of srcs +aa8519378a37c9a5 README: Mention how to specify another prefix +58a98f76ac95b1bb btf: Remove ftrace filter +7c60b0443cb01795 pahole: Fix error message when --header couldn't be read +7eea706c14997b4f pahole: Introduce --with_flexible_array option to show just types ending in a flexible array + +v1.21: + +Fri Apr 9 2021 + +ae0b7dde1fd50b12 dwarf_loader: Handle DWARF5 DW_OP_addrx properly +9adb014930f31c66 dwarf_loader: Handle subprogram ret type with abstract_origin properly +5752d1951d081a80 dwarf_loader: Check .notes section for LTO build info +209e45424ff4a22d dwarf_loader: Check .debug_abbrev for cross-CU references +39227909db3cc2c2 dwarf_loader: Permit merging all DWARF CU's for clang LTO built binary +763475ca1101ccfe dwarf_loader: Factor out common code to initialize a cu +d0d3fbd4744953e8 dwarf_loader: Permit a flexible HASHTAGS__BITS +ffe0ef4d73906c18 btf: Add --btf_gen_all flag +de708b33114d42c2 btf: Add support for the floating-point types +4b7f8c04d009942b fprintf: Honour conf_fprintf.hex when printing enumerations +f2889ff163726336 Avoid warning when building with NDEBUG +8e1f8c904e303d5d btf_encoder: Match ftrace addresses within ELF functions +9fecc77ed82d429f dwarf_loader: Use a better hashing function, from libbpf +0125de3a4c055cdf btf_encoder: Funnel ELF error reporting through a macro +7d8e829f636f47ab btf_encoder: Sanitize non-regular int base type + +v1.20: + +Tue Feb 2 2021 + +8d6f06f053a06829 (HEAD -> master, seventh/master, quaco/master, origin/master, origin/HEAD) dwarf_loader: Add conditional DW_FORM_implicit_const definition for older systems +66d12e4790b7c5e5 dtagnames: Stop using the deprecated mallinfo() function +1279e439b622aeb5 cmake: Bump minimum required version to 2.8.12 as per upstream support warning +b1eaf0da6d1f72b2 dwarves: Make enum prefix search more robust +d783117162c0212d dwarf_loader: Handle DWARF5 DW_TAG_call_site like DW_TAG_GNU_call_site +3ff98a6396e91d0a dwarf_loader: Support DW_FORM_implicit_const in __attr_offset() +b91b19840b0062b8 dwarf_loader: Support DW_AT_data_bit_offset +c692e8ac5ccbab99 dwarf_loader: Optimize a bit the reading of DW_AT_data_member_location +65917b24942ce620 dwarf_loader: Fix typo +77205a119c85e396 dwarf_loader: Introduce __attr_offset() to reuse call to dwarf_attr() +8ec231f6b0c8aaef dwarf_loader: Support DW_FORM_implicit_const in attr_numeric() +7453895e01edb535 btf_encoder: Improve ELF error reporting +1bb49897dd2b65b0 bpf_encoder: Translate SHN_XINDEX in symbol's st_shndx values +3f8aad340bf1a188 elf_symtab: Handle SHN_XINDEX index in elf_section_by_name() +e32b9800e650a6eb btf_encoder: Add extra checks for symbol names +82749180b23d3c9c libbpf: allow to use packaged version +452dbcf35f1a7bf9 btf_encoder: Improve error-handling around objcopy +cf381f9a3822d68b btf_encoder: Fix handling of restrict qualifier +b688e35970600c15 btf_encoder: fix skipping per-CPU variables at offset 0 +8c009d6ce762dfc9 btf_encoder: fix BTF variable generation for kernel modules +b94e97e015a94e6b dwarves: Fix compilation on 32-bit architectures +17df51c700248f02 btf_encoder: Detect kernel module ftrace addresses +06ca639505fc56c6 btf_encoder: Use address size based on ELF's class +aff60970d16b909e btf_encoder: Factor filter_functions function +1e6a3fed6e52d365 rpm: Fix changelog date + v1.19: Fri Nov 20 2020 @@ -6,8 +6,17 @@ Build instructions: 4. cmake -D__LIB=lib .. 5. make install -Default is to be installed on /usr/local, see rpm spec file for -installing on other places. +cmake Options: + -DBUILD_SHARED_LIBS + By default SHARED libraries are created and applications are linked to it. + Use -DBUILD_SHARED_LIBS=OFF while invoking cmake to create STATIC libraries + and link applications to it. + + Ex. cmake -D__LIB=lib -DBUILD_SHARED_LIBS=OFF .. + + -DCMAKE_INSTALL_PREFIX + Default is to install to /usr/local, use -DCMAKE_INSTALL_PREFIX= + when invoking cmake to specify another install location. Known to work scenarios: diff --git a/README.tarball b/README.tarball index a3db289..bade4a8 100644 --- a/README.tarball +++ b/README.tarball @@ -1 +1 @@ -tar cvfJ ~/rpmbuild/SOURCES/dwarves-1.17.tar.xz --transform 's,^,dwarves-1.17/,' `cat MANIFEST` +v=1.$(($(git tag | sort -V | tail -1 | cut -d. -f2) + 1)) ; tar cvfJ ~/rpmbuild/SOURCES/dwarves-${v}.tar.xz --transform "s,^pahole/,dwarves-${v}/," `sed s%^%../pahole/%g MANIFEST` diff --git a/btf_encoder.c b/btf_encoder.c index 3339730..9d015f3 100644 --- a/btf_encoder.c +++ b/btf_encoder.c @@ -10,307 +10,719 @@ */ #include "dwarves.h" -#include "libbtf.h" -#include "lib/bpf/include/uapi/linux/btf.h" -#include "lib/bpf/src/libbpf.h" -#include "hash.h" #include "elf_symtab.h" #include "btf_encoder.h" +#include "gobuffer.h" +#include <linux/btf.h> +#include <bpf/btf.h> +#include <bpf/libbpf.h> #include <ctype.h> /* for isalpha() and isalnum() */ #include <stdlib.h> /* for qsort() and bsearch() */ #include <inttypes.h> +#include <limits.h> -/* - * This corresponds to the same macro defined in - * include/linux/kallsyms.h - */ -#define KSYM_NAME_LEN 128 +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> -struct funcs_layout { - unsigned long mcount_start; - unsigned long mcount_stop; - unsigned long mcount_sec_idx; -}; +#include <unistd.h> + +#include <errno.h> +#include <stdint.h> struct elf_function { const char *name; - unsigned long addr; - unsigned long sh_addr; bool generated; }; -static struct elf_function *functions; -static int functions_alloc; -static int functions_cnt; +#define MAX_PERCPU_VAR_CNT 4096 -static int functions_cmp(const void *_a, const void *_b) +struct var_info { + uint64_t addr; + const char *name; + uint32_t sz; +}; + +struct btf_encoder { + struct list_head node; + struct btf *btf; + struct gobuffer percpu_secinfo; + const char *filename; + struct elf_symtab *symtab; + bool has_index_type, + need_index_type, + skip_encoding_vars, + raw_output, + verbose, + force, + gen_floats, + is_rel; + uint32_t array_index_id; + struct { + struct var_info vars[MAX_PERCPU_VAR_CNT]; + int var_cnt; + uint32_t shndx; + uint64_t base_addr; + uint64_t sec_sz; + } percpu; + struct { + struct elf_function *entries; + int allocated; + int cnt; + } functions; +}; + +void btf_encoders__add(struct list_head *encoders, struct btf_encoder *encoder) { - const struct elf_function *a = _a; - const struct elf_function *b = _b; + list_add_tail(&encoder->node, encoders); +} - return strcmp(a->name, b->name); +struct btf_encoder *btf_encoders__first(struct list_head *encoders) +{ + return list_first_entry(encoders, struct btf_encoder, node); } -static void delete_functions(void) +struct btf_encoder *btf_encoders__next(struct btf_encoder *encoder) { - free(functions); - functions_alloc = functions_cnt = 0; - functions = NULL; + return list_next_entry(encoder, node); } -#ifndef max -#define max(x, y) ((x) < (y) ? (y) : (x)) -#endif +#define PERCPU_SECTION ".data..percpu" -static int collect_function(struct btf_elf *btfe, GElf_Sym *sym) +/* + * This depends on the GNU extension to eliminate the stray comma in the zero + * arguments case. + * + * The difference between elf_errmsg(-1) and elf_errmsg(elf_errno()) is that the + * latter clears the current error. + */ +#define elf_error(fmt, ...) \ + fprintf(stderr, "%s: " fmt ": %s.\n", __func__, ##__VA_ARGS__, elf_errmsg(-1)) + +/* + * This depends on the GNU extension to eliminate the stray comma in the zero + * arguments case. + * + * The difference between elf_errmsg(-1) and elf_errmsg(elf_errno()) is that the + * latter clears the current error. + */ +#define elf_error(fmt, ...) \ + fprintf(stderr, "%s: " fmt ": %s.\n", __func__, ##__VA_ARGS__, elf_errmsg(-1)) + +static int btf_var_secinfo_cmp(const void *a, const void *b) { - struct elf_function *new; - static GElf_Shdr sh; - static int last_idx; - int idx; + const struct btf_var_secinfo *av = a; + const struct btf_var_secinfo *bv = b; - if (elf_sym__type(sym) != STT_FUNC) - return 0; + return av->offset - bv->offset; +} - if (functions_cnt == functions_alloc) { - functions_alloc = max(1000, functions_alloc * 3 / 2); - new = realloc(functions, functions_alloc * sizeof(*functions)); - if (!new) { - /* - * The cleanup - delete_functions is called - * in cu__encode_btf error path. - */ - return -1; - } - functions = new; +#define BITS_PER_BYTE 8 +#define BITS_PER_BYTE_MASK (BITS_PER_BYTE - 1) +#define BITS_PER_BYTE_MASKED(bits) ((bits) & BITS_PER_BYTE_MASK) +#define BITS_ROUNDDOWN_BYTES(bits) ((bits) >> 3) +#define BITS_ROUNDUP_BYTES(bits) (BITS_ROUNDDOWN_BYTES(bits) + !!BITS_PER_BYTE_MASKED(bits)) + +static const char * const btf_kind_str[NR_BTF_KINDS] = { + [BTF_KIND_UNKN] = "UNKNOWN", + [BTF_KIND_INT] = "INT", + [BTF_KIND_PTR] = "PTR", + [BTF_KIND_ARRAY] = "ARRAY", + [BTF_KIND_STRUCT] = "STRUCT", + [BTF_KIND_UNION] = "UNION", + [BTF_KIND_ENUM] = "ENUM", + [BTF_KIND_FWD] = "FWD", + [BTF_KIND_TYPEDEF] = "TYPEDEF", + [BTF_KIND_VOLATILE] = "VOLATILE", + [BTF_KIND_CONST] = "CONST", + [BTF_KIND_RESTRICT] = "RESTRICT", + [BTF_KIND_FUNC] = "FUNC", + [BTF_KIND_FUNC_PROTO] = "FUNC_PROTO", + [BTF_KIND_VAR] = "VAR", + [BTF_KIND_DATASEC] = "DATASEC", + [BTF_KIND_FLOAT] = "FLOAT", + [BTF_KIND_DECL_TAG] = "DECL_TAG", + [BTF_KIND_TYPE_TAG] = "TYPE_TAG", +}; + +static const char *btf__printable_name(const struct btf *btf, uint32_t offset) +{ + if (!offset) + return "(anon)"; + else + return btf__str_by_offset(btf, offset); +} + +static const char * btf__int_encoding_str(uint8_t encoding) +{ + if (encoding == 0) + return "(none)"; + else if (encoding == BTF_INT_SIGNED) + return "SIGNED"; + else if (encoding == BTF_INT_CHAR) + return "CHAR"; + else if (encoding == BTF_INT_BOOL) + return "BOOL"; + else + return "UNKN"; +} + +__attribute ((format (printf, 5, 6))) +static void btf__log_err(const struct btf *btf, int kind, const char *name, + bool output_cr, const char *fmt, ...) +{ + fprintf(stderr, "[%u] %s %s", btf__get_nr_types(btf) + 1, + btf_kind_str[kind], name ?: "(anon)"); + + if (fmt && *fmt) { + va_list ap; + + fprintf(stderr, " "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); } - idx = elf_sym__section(sym); + if (output_cr) + fprintf(stderr, "\n"); +} - if (idx != last_idx) { - if (!elf_section_by_idx(btfe->elf, &sh, idx)) - return 0; - last_idx = idx; +__attribute ((format (printf, 5, 6))) +static void btf_encoder__log_type(const struct btf_encoder *encoder, const struct btf_type *t, + bool err, bool output_cr, const char *fmt, ...) +{ + const struct btf *btf = encoder->btf; + uint8_t kind; + FILE *out; + + if (!encoder->verbose && !err) + return; + + kind = BTF_INFO_KIND(t->info); + out = err ? stderr : stdout; + + fprintf(out, "[%u] %s %s", + btf__get_nr_types(btf), btf_kind_str[kind], + btf__printable_name(btf, t->name_off)); + + if (fmt && *fmt) { + va_list ap; + + fprintf(out, " "); + va_start(ap, fmt); + vfprintf(out, fmt, ap); + va_end(ap); } - functions[functions_cnt].name = elf_sym__name(sym, btfe->symtab); - functions[functions_cnt].addr = elf_sym__value(sym); - functions[functions_cnt].sh_addr = sh.sh_addr; - functions[functions_cnt].generated = false; - functions_cnt++; - return 0; + if (output_cr) + fprintf(out, "\n"); } -static int addrs_cmp(const void *_a, const void *_b) +__attribute ((format (printf, 5, 6))) +static void btf_encoder__log_member(const struct btf_encoder *encoder, const struct btf_type *t, + const struct btf_member *member, bool err, const char *fmt, ...) { - const __u64 *a = _a; - const __u64 *b = _b; + const struct btf *btf = encoder->btf; + FILE *out; - if (*a == *b) - return 0; - return *a < *b ? -1 : 1; + if (!encoder->verbose && !err) + return; + + out = err ? stderr : stdout; + + if (btf_kflag(t)) + fprintf(out, "\t%s type_id=%u bitfield_size=%u bits_offset=%u", + btf__printable_name(btf, member->name_off), + member->type, + BTF_MEMBER_BITFIELD_SIZE(member->offset), + BTF_MEMBER_BIT_OFFSET(member->offset)); + else + fprintf(out, "\t%s type_id=%u bits_offset=%u", + btf__printable_name(btf, member->name_off), + member->type, + member->offset); + + if (fmt && *fmt) { + va_list ap; + + fprintf(out, " "); + va_start(ap, fmt); + vfprintf(out, fmt, ap); + va_end(ap); + } + + fprintf(out, "\n"); } -static int get_vmlinux_addrs(struct btf_elf *btfe, struct funcs_layout *fl, - __u64 **paddrs, __u64 *pcount) +__attribute ((format (printf, 6, 7))) +static void btf_encoder__log_func_param(struct btf_encoder *encoder, const char *name, uint32_t type, + bool err, bool is_last_param, const char *fmt, ...) { - __u64 *addrs, count, offset; - unsigned int addr_size, i; - Elf_Data *data; - GElf_Shdr shdr; - Elf_Scn *sec; + FILE *out; - /* Initialize for the sake of all error paths below. */ - *paddrs = NULL; - *pcount = 0; + if (!encoder->verbose && !err) + return; - if (!fl->mcount_start || !fl->mcount_stop) - return 0; + out = err ? stderr : stdout; - /* - * Find mcount addressed marked by __start_mcount_loc - * and __stop_mcount_loc symbols and load them into - * sorted array. - */ - sec = elf_getscn(btfe->elf, fl->mcount_sec_idx); - if (!sec || !gelf_getshdr(sec, &shdr)) { - fprintf(stderr, "Failed to get section(%lu) header.\n", - fl->mcount_sec_idx); - return -1; + if (is_last_param && !type) + fprintf(out, "vararg)\n"); + else + fprintf(out, "%u %s%s", type, name, is_last_param ? ")\n" : ", "); + + if (fmt && *fmt) { + va_list ap; + + fprintf(out, " "); + va_start(ap, fmt); + vfprintf(out, fmt, ap); + va_end(ap); } +} - /* Get address size from processed file's ELF class. */ - addr_size = gelf_getclass(btfe->elf) == ELFCLASS32 ? 4 : 8; +static int32_t btf_encoder__add_float(struct btf_encoder *encoder, const struct base_type *bt, const char *name) +{ + int32_t id = btf__add_float(encoder->btf, name, BITS_ROUNDUP_BYTES(bt->bit_size)); - offset = fl->mcount_start - shdr.sh_addr; - count = (fl->mcount_stop - fl->mcount_start) / addr_size; + if (id < 0) { + btf__log_err(encoder->btf, BTF_KIND_FLOAT, name, true, "Error emitting BTF type"); + } else { + const struct btf_type *t; - data = elf_getdata(sec, 0); - if (!data) { - fprintf(stderr, "Failed to get section(%lu) data.\n", - fl->mcount_sec_idx); - return -1; + t = btf__type_by_id(encoder->btf, id); + btf_encoder__log_type(encoder, t, false, true, "size=%u nr_bits=%u", t->size, bt->bit_size); } - addrs = malloc(count * sizeof(addrs[0])); - if (!addrs) { - fprintf(stderr, "Failed to allocate memory for ftrace addresses.\n"); + return id; +} + +static int32_t btf_encoder__add_base_type(struct btf_encoder *encoder, const struct base_type *bt, const char *name) +{ + const struct btf_type *t; + uint8_t encoding = 0; + uint16_t byte_sz; + int32_t id; + + if (bt->is_signed) { + encoding = BTF_INT_SIGNED; + } else if (bt->is_bool) { + encoding = BTF_INT_BOOL; + } else if (bt->float_type && encoder->gen_floats) { + /* + * Encode floats as BTF_KIND_FLOAT if allowed, otherwise (in + * compatibility mode) encode them as BTF_KIND_INT - that's not + * fully correct, but that's what it used to be. + */ + if (bt->float_type == BT_FP_SINGLE || + bt->float_type == BT_FP_DOUBLE || + bt->float_type == BT_FP_LDBL) + return btf_encoder__add_float(encoder, bt, name); + fprintf(stderr, "Complex, interval and imaginary float types are not supported\n"); return -1; } - if (addr_size == sizeof(__u64)) { - memcpy(addrs, data->d_buf + offset, count * addr_size); + /* dwarf5 may emit DW_ATE_[un]signed_{num} base types where + * {num} is not power of 2 and may exceed 128. Such attributes + * are mostly used to record operation for an actual parameter + * or variable. + * For example, + * DW_AT_location (indexed (0x3c) loclist = 0x00008fb0: + * [0xffffffff82808812, 0xffffffff82808817): + * DW_OP_breg0 RAX+0, + * DW_OP_convert (0x000e97d5) "DW_ATE_unsigned_64", + * DW_OP_convert (0x000e97df) "DW_ATE_unsigned_8", + * DW_OP_stack_value, + * DW_OP_piece 0x1, + * DW_OP_breg0 RAX+0, + * DW_OP_convert (0x000e97d5) "DW_ATE_unsigned_64", + * DW_OP_convert (0x000e97da) "DW_ATE_unsigned_32", + * DW_OP_lit8, + * DW_OP_shr, + * DW_OP_convert (0x000e97da) "DW_ATE_unsigned_32", + * DW_OP_convert (0x000e97e4) "DW_ATE_unsigned_24", + * DW_OP_stack_value, DW_OP_piece 0x3 + * DW_AT_name ("ebx") + * DW_AT_decl_file ("/linux/arch/x86/events/intel/core.c") + * + * In the above example, at some point, one unsigned_32 value + * is right shifted by 8 and the result is converted to unsigned_32 + * and then unsigned_24. + * + * BTF does not need such DW_OP_* information so let us sanitize + * these non-regular int types to avoid libbpf/kernel complaints. + */ + byte_sz = BITS_ROUNDUP_BYTES(bt->bit_size); + if (!byte_sz || (byte_sz & (byte_sz - 1))) { + name = "__SANITIZED_FAKE_INT__"; + byte_sz = 4; + } + + id = btf__add_int(encoder->btf, name, byte_sz, encoding); + if (id < 0) { + btf__log_err(encoder->btf, BTF_KIND_INT, name, true, "Error emitting BTF type"); } else { - for (i = 0; i < count; i++) - addrs[i] = (__u64) *((__u32 *) (data->d_buf + offset + i * addr_size)); + t = btf__type_by_id(encoder->btf, id); + btf_encoder__log_type(encoder, t, false, true, "size=%u nr_bits=%u encoding=%s%s", + t->size, bt->bit_size, btf__int_encoding_str(encoding), + id < 0 ? " Error in emitting BTF" : "" ); } - *paddrs = addrs; - *pcount = count; - return 0; + return id; } -static int -get_kmod_addrs(struct btf_elf *btfe, __u64 **paddrs, __u64 *pcount) +static int32_t btf_encoder__add_ref_type(struct btf_encoder *encoder, uint16_t kind, uint32_t type, + const char *name, bool kind_flag) { - __u64 *addrs, count; - unsigned int addr_size, i; - GElf_Shdr shdr_mcount; - Elf_Data *data; - Elf_Scn *sec; + struct btf *btf = encoder->btf; + const struct btf_type *t; + int32_t id; + + switch (kind) { + case BTF_KIND_PTR: + id = btf__add_ptr(btf, type); + break; + case BTF_KIND_VOLATILE: + id = btf__add_volatile(btf, type); + break; + case BTF_KIND_CONST: + id = btf__add_const(btf, type); + break; + case BTF_KIND_RESTRICT: + id = btf__add_restrict(btf, type); + break; + case BTF_KIND_TYPEDEF: + id = btf__add_typedef(btf, name, type); + break; + case BTF_KIND_TYPE_TAG: + id = btf__add_type_tag(btf, name, type); + break; + case BTF_KIND_FWD: + id = btf__add_fwd(btf, name, kind_flag); + break; + case BTF_KIND_FUNC: + id = btf__add_func(btf, name, BTF_FUNC_STATIC, type); + break; + default: + btf__log_err(btf, kind, name, true, "Unexpected kind for reference"); + return -1; + } - /* Initialize for the sake of all error paths below. */ - *paddrs = NULL; - *pcount = 0; + if (id > 0) { + t = btf__type_by_id(btf, id); + if (kind == BTF_KIND_FWD) + btf_encoder__log_type(encoder, t, false, true, "%s", kind_flag ? "union" : "struct"); + else + btf_encoder__log_type(encoder, t, false, true, "type_id=%u", t->type); + } else { + btf__log_err(btf, kind, name, true, "Error emitting BTF type"); + } + return id; +} - /* get __mcount_loc */ - sec = elf_section_by_name(btfe->elf, &btfe->ehdr, &shdr_mcount, - "__mcount_loc", NULL); - if (!sec) { - if (btf_elf__verbose) { - printf("%s: '%s' doesn't have __mcount_loc section\n", __func__, - btfe->filename); - } - return 0; +static int32_t btf_encoder__add_array(struct btf_encoder *encoder, uint32_t type, uint32_t index_type, uint32_t nelems) +{ + struct btf *btf = encoder->btf; + const struct btf_type *t; + const struct btf_array *array; + int32_t id; + + id = btf__add_array(btf, index_type, type, nelems); + if (id > 0) { + t = btf__type_by_id(btf, id); + array = btf_array(t); + btf_encoder__log_type(encoder, t, false, true, "type_id=%u index_type_id=%u nr_elems=%u", + array->type, array->index_type, array->nelems); + } else { + btf__log_err(btf, BTF_KIND_ARRAY, NULL, true, + "type_id=%u index_type_id=%u nr_elems=%u Error emitting BTF type", + type, index_type, nelems); } + return id; +} + +static int btf_encoder__add_field(struct btf_encoder *encoder, const char *name, uint32_t type, uint32_t bitfield_size, uint32_t offset) +{ + struct btf *btf = encoder->btf; + const struct btf_type *t; + const struct btf_member *m; + int err; - data = elf_getdata(sec, NULL); - if (!data) { - fprintf(stderr, "Failed to data for __mcount_loc section.\n"); + err = btf__add_field(btf, name, type, offset, bitfield_size); + t = btf__type_by_id(btf, btf__get_nr_types(btf)); + if (err) { + fprintf(stderr, "[%u] %s %s's field '%s' offset=%u bit_size=%u type=%u Error emitting field\n", + btf__get_nr_types(btf), btf_kind_str[btf_kind(t)], + btf__printable_name(btf, t->name_off), + name, offset, bitfield_size, type); + } else { + m = &btf_members(t)[btf_vlen(t) - 1]; + btf_encoder__log_member(encoder, t, m, false, NULL); + } + return err; +} + +static int32_t btf_encoder__add_struct(struct btf_encoder *encoder, uint8_t kind, const char *name, uint32_t size) +{ + struct btf *btf = encoder->btf; + const struct btf_type *t; + int32_t id; + + switch (kind) { + case BTF_KIND_STRUCT: + id = btf__add_struct(btf, name, size); + break; + case BTF_KIND_UNION: + id = btf__add_union(btf, name, size); + break; + default: + btf__log_err(btf, kind, name, true, "Unexpected kind of struct"); return -1; } - /* Get address size from processed file's ELF class. */ - addr_size = gelf_getclass(btfe->elf) == ELFCLASS32 ? 4 : 8; + if (id < 0) { + btf__log_err(btf, kind, name, true, "Error emitting BTF type"); + } else { + t = btf__type_by_id(btf, id); + btf_encoder__log_type(encoder, t, false, true, "size=%u", t->size); + } + + return id; +} + +static int32_t btf_encoder__add_enum(struct btf_encoder *encoder, const char *name, uint32_t bit_size) +{ + struct btf *btf = encoder->btf; + const struct btf_type *t; + int32_t id, size; + + size = BITS_ROUNDUP_BYTES(bit_size); + id = btf__add_enum(btf, name, size); + if (id > 0) { + t = btf__type_by_id(btf, id); + btf_encoder__log_type(encoder, t, false, true, "size=%u", t->size); + } else { + btf__log_err(btf, BTF_KIND_ENUM, name, true, + "size=%u Error emitting BTF type", size); + } + return id; +} + +static int btf_encoder__add_enum_val(struct btf_encoder *encoder, const char *name, int32_t value) +{ + int err = btf__add_enum_value(encoder->btf, name, value); - count = data->d_size / addr_size; + if (!err) { + if (encoder->verbose) + printf("\t%s val=%d\n", name, value); + } else { + fprintf(stderr, "\t%s val=%d Error emitting BTF enum value\n", + name, value); + } + return err; +} + +static int32_t btf_encoder__add_func_param(struct btf_encoder *encoder, const char *name, uint32_t type, bool is_last_param) +{ + int err = btf__add_func_param(encoder->btf, name, type); - addrs = malloc(count * sizeof(addrs[0])); - if (!addrs) { - fprintf(stderr, "Failed to allocate memory for ftrace addresses.\n"); + if (!err) { + btf_encoder__log_func_param(encoder, name, type, false, is_last_param, NULL); + return 0; + } else { + btf_encoder__log_func_param(encoder, name, type, true, is_last_param, "Error adding func param"); return -1; } +} + +static int32_t btf_encoder__add_func_proto(struct btf_encoder *encoder, struct ftype *ftype, uint32_t type_id_off) +{ + struct btf *btf = encoder->btf; + const struct btf_type *t; + struct parameter *param; + uint16_t nr_params, param_idx; + int32_t id, type_id; - if (addr_size == sizeof(__u64)) { - memcpy(addrs, data->d_buf, count * addr_size); + /* add btf_type for func_proto */ + nr_params = ftype->nr_parms + (ftype->unspec_parms ? 1 : 0); + type_id = ftype->tag.type == 0 ? 0 : type_id_off + ftype->tag.type; + + id = btf__add_func_proto(btf, type_id); + if (id > 0) { + t = btf__type_by_id(btf, id); + btf_encoder__log_type(encoder, t, false, false, "return=%u args=(%s", t->type, !nr_params ? "void)\n" : ""); } else { - for (i = 0; i < count; i++) - addrs[i] = (__u64) *((__u32 *) (data->d_buf + i * addr_size)); + btf__log_err(btf, BTF_KIND_FUNC_PROTO, NULL, true, + "return=%u vlen=%u Error emitting BTF type", + type_id, nr_params); + return id; } - /* - * We get Elf object from dwfl_module_getelf function, - * which performs all possible relocations, including - * __mcount_loc section. - * - * So addrs array now contains relocated values, which - * we need take into account when we compare them to - * functions values, see comment in setup_functions - * function. - */ - *paddrs = addrs; - *pcount = count; - return 0; + /* add parameters */ + param_idx = 0; + ftype__for_each_parameter(ftype, param) { + const char *name = parameter__name(param); + + type_id = param->tag.type == 0 ? 0 : type_id_off + param->tag.type; + ++param_idx; + if (btf_encoder__add_func_param(encoder, name, type_id, param_idx == nr_params)) + return -1; + } + + ++param_idx; + if (ftype->unspec_parms) + if (btf_encoder__add_func_param(encoder, NULL, 0, param_idx == nr_params)) + return -1; + + return id; } -static int setup_functions(struct btf_elf *btfe, struct funcs_layout *fl) +static int32_t btf_encoder__add_var(struct btf_encoder *encoder, uint32_t type, const char *name, uint32_t linkage) { - __u64 *addrs, count, i; - int functions_valid = 0; - bool kmod = false; + struct btf *btf = encoder->btf; + const struct btf_type *t; + int32_t id; + + id = btf__add_var(btf, name, linkage, type); + if (id > 0) { + t = btf__type_by_id(btf, id); + btf_encoder__log_type(encoder, t, false, true, "type=%u linkage=%u", t->type, btf_var(t)->linkage); + } else { + btf__log_err(btf, BTF_KIND_VAR, name, true, + "type=%u linkage=%u Error emitting BTF type", + type, linkage); + } + return id; +} - /* - * Check if we are processing vmlinux image and - * get mcount data if it's detected. - */ - if (get_vmlinux_addrs(btfe, fl, &addrs, &count)) - return -1; +static int32_t btf_encoder__add_var_secinfo(struct btf_encoder *encoder, uint32_t type, + uint32_t offset, uint32_t size) +{ + struct btf_var_secinfo si = { + .type = type, + .offset = offset, + .size = size, + }; + return gobuffer__add(&encoder->percpu_secinfo, &si, sizeof(si)); +} - /* - * Check if we are processing kernel module and - * get mcount data if it's detected. - */ - if (!addrs) { - if (get_kmod_addrs(btfe, &addrs, &count)) +static int32_t btf_encoder__add_datasec(struct btf_encoder *encoder, const char *section_name) +{ + struct gobuffer *var_secinfo_buf = &encoder->percpu_secinfo; + struct btf *btf = encoder->btf; + size_t sz = gobuffer__size(var_secinfo_buf); + uint16_t nr_var_secinfo = sz / sizeof(struct btf_var_secinfo); + struct btf_var_secinfo *last_vsi, *vsi; + const struct btf_type *t; + uint32_t datasec_sz; + int32_t err, id, i; + + qsort(var_secinfo_buf->entries, nr_var_secinfo, + sizeof(struct btf_var_secinfo), btf_var_secinfo_cmp); + + last_vsi = (struct btf_var_secinfo *)var_secinfo_buf->entries + nr_var_secinfo - 1; + datasec_sz = last_vsi->offset + last_vsi->size; + + id = btf__add_datasec(btf, section_name, datasec_sz); + if (id < 0) { + btf__log_err(btf, BTF_KIND_DATASEC, section_name, true, + "size=%u vlen=%u Error emitting BTF type", + datasec_sz, nr_var_secinfo); + } else { + t = btf__type_by_id(btf, id); + btf_encoder__log_type(encoder, t, false, true, "size=%u vlen=%u", t->size, nr_var_secinfo); + } + + for (i = 0; i < nr_var_secinfo; i++) { + vsi = (struct btf_var_secinfo *)var_secinfo_buf->entries + i; + err = btf__add_datasec_var_info(btf, vsi->type, vsi->offset, vsi->size); + if (!err) { + if (encoder->verbose) + printf("\ttype=%u offset=%u size=%u\n", + vsi->type, vsi->offset, vsi->size); + } else { + fprintf(stderr, "\ttype=%u offset=%u size=%u Error emitting BTF datasec var info\n", + vsi->type, vsi->offset, vsi->size); return -1; - kmod = true; + } } - if (!addrs) { - if (btf_elf__verbose) - printf("ftrace symbols not detected, falling back to DWARF data\n"); - delete_functions(); - return 0; + return id; +} + +static int32_t btf_encoder__add_decl_tag(struct btf_encoder *encoder, const char *value, uint32_t type, + int component_idx) +{ + struct btf *btf = encoder->btf; + const struct btf_type *t; + int32_t id; + + id = btf__add_decl_tag(btf, value, type, component_idx); + if (id > 0) { + t = btf__type_by_id(btf, id); + btf_encoder__log_type(encoder, t, false, true, "type_id=%u component_idx=%d", + t->type, component_idx); + } else { + btf__log_err(btf, BTF_KIND_DECL_TAG, value, true, "component_idx=%d Error emitting BTF type", + component_idx); } - qsort(addrs, count, sizeof(addrs[0]), addrs_cmp); - qsort(functions, functions_cnt, sizeof(functions[0]), functions_cmp); + return id; +} - /* - * Let's got through all collected functions and filter - * out those that are not in ftrace. - */ - for (i = 0; i < functions_cnt; i++) { - struct elf_function *func = &functions[i]; - /* - * For vmlinux image both addrs[x] and functions[x]::addr - * values are final address and are comparable. - * - * For kernel module addrs[x] is final address, but - * functions[x]::addr is relative address within section - * and needs to be relocated by adding sh_addr. - */ - __u64 addr = kmod ? func->addr + func->sh_addr : func->addr; +/* + * This corresponds to the same macro defined in + * include/linux/kallsyms.h + */ +#define KSYM_NAME_LEN 128 + +static int functions_cmp(const void *_a, const void *_b) +{ + const struct elf_function *a = _a; + const struct elf_function *b = _b; - /* Make sure function is within ftrace addresses. */ - if (bsearch(&addr, addrs, count, sizeof(addrs[0]), addrs_cmp)) { + return strcmp(a->name, b->name); +} + +#ifndef max +#define max(x, y) ((x) < (y) ? (y) : (x)) +#endif + +static int btf_encoder__collect_function(struct btf_encoder *encoder, GElf_Sym *sym) +{ + struct elf_function *new; + const char *name; + + if (elf_sym__type(sym) != STT_FUNC) + return 0; + name = elf_sym__name(sym, encoder->symtab); + if (!name) + return 0; + + if (encoder->functions.cnt == encoder->functions.allocated) { + encoder->functions.allocated = max(1000, encoder->functions.allocated * 3 / 2); + new = realloc(encoder->functions.entries, encoder->functions.allocated * sizeof(*encoder->functions.entries)); + if (!new) { /* - * We iterate over sorted array, so we can easily skip - * not valid item and move following valid field into - * its place, and still keep the 'new' array sorted. + * The cleanup - delete_functions is called + * in btf_encoder__encode_cu error path. */ - if (i != functions_valid) - functions[functions_valid] = functions[i]; - functions_valid++; + return -1; } + encoder->functions.entries = new; } - functions_cnt = functions_valid; - free(addrs); - - if (btf_elf__verbose) - printf("Found %d functions!\n", functions_cnt); + encoder->functions.entries[encoder->functions.cnt].name = name; + encoder->functions.entries[encoder->functions.cnt].generated = false; + encoder->functions.cnt++; return 0; } -static struct elf_function *find_function(const struct btf_elf *btfe, - const char *name) +static struct elf_function *btf_encoder__find_function(const struct btf_encoder *encoder, const char *name) { struct elf_function key = { .name = name }; - return bsearch(&key, functions, functions_cnt, sizeof(functions[0]), - functions_cmp); + return bsearch(&key, encoder->functions.entries, encoder->functions.cnt, sizeof(key), functions_cmp); } static bool btf_name_char_ok(char c, bool first) @@ -355,8 +767,6 @@ static void dump_invalid_symbol(const char *msg, const char *sym, fprintf(stderr, "PAHOLE: Error: Use '--btf_encode_force' to ignore such symbols and force emit the btf.\n"); } -extern struct debug_fmt_ops *dwarves__active_loader; - static int tag__check_id_drift(const struct tag *tag, uint32_t core_id, uint32_t btf_type_id, uint32_t type_id_off) @@ -372,19 +782,18 @@ static int tag__check_id_drift(const struct tag *tag, return 0; } -static int32_t structure_type__encode(struct btf_elf *btfe, struct cu *cu, struct tag *tag, uint32_t type_id_off) +static int32_t btf_encoder__add_struct_type(struct btf_encoder *encoder, struct tag *tag, uint32_t type_id_off) { struct type *type = tag__type(tag); struct class_member *pos; - const char *name; + const char *name = type__name(type); int32_t type_id; uint8_t kind; kind = (tag->tag == DW_TAG_union_type) ? BTF_KIND_UNION : BTF_KIND_STRUCT; - name = dwarves__active_loader->strings__ptr(cu, type->namespace.name); - type_id = btf_elf__add_struct(btfe, kind, name, type->size); + type_id = btf_encoder__add_struct(encoder, kind, name, type->size); if (type_id < 0) return type_id; @@ -394,8 +803,8 @@ static int32_t structure_type__encode(struct btf_elf *btfe, struct cu *cu, struc * scheme, which conforms to BTF requirement, so no conversion * is required. */ - name = dwarves__active_loader->strings__ptr(cu, pos->name); - if (btf_elf__add_member(btfe, name, type_id_off + pos->tag.type, pos->bitfield_size, pos->bit_offset)) + name = class_member__name(pos); + if (btf_encoder__add_field(encoder, name, type_id_off + pos->tag.type, pos->bitfield_size, pos->bit_offset)) return -1; } @@ -414,67 +823,68 @@ static uint32_t array_type__nelems(struct tag *tag) return nelem; } -static int32_t enumeration_type__encode(struct btf_elf *btfe, struct cu *cu, struct tag *tag) +static int32_t btf_encoder__add_enum_type(struct btf_encoder *encoder, struct tag *tag) { struct type *etype = tag__type(tag); struct enumerator *pos; - const char *name; + const char *name = type__name(etype); int32_t type_id; - name = dwarves__active_loader->strings__ptr(cu, etype->namespace.name); - type_id = btf_elf__add_enum(btfe, name, etype->size); + type_id = btf_encoder__add_enum(encoder, name, etype->size); if (type_id < 0) return type_id; type__for_each_enumerator(etype, pos) { - name = dwarves__active_loader->strings__ptr(cu, pos->name); - if (btf_elf__add_enum_val(btfe, name, pos->value)) + name = enumerator__name(pos); + if (btf_encoder__add_enum_val(encoder, name, pos->value)) return -1; } return type_id; } -static bool need_index_type; - -static int tag__encode_btf(struct cu *cu, struct tag *tag, uint32_t core_id, struct btf_elf *btfe, - uint32_t array_index_id, uint32_t type_id_off) +static int btf_encoder__encode_tag(struct btf_encoder *encoder, struct tag *tag, uint32_t type_id_off) { /* single out type 0 as it represents special type "void" */ uint32_t ref_type_id = tag->type == 0 ? 0 : type_id_off + tag->type; + struct base_type *bt; const char *name; switch (tag->tag) { case DW_TAG_base_type: - name = dwarves__active_loader->strings__ptr(cu, tag__base_type(tag)->name); - return btf_elf__add_base_type(btfe, tag__base_type(tag), name); + bt = tag__base_type(tag); + name = __base_type__name(bt); + return btf_encoder__add_base_type(encoder, bt, name); case DW_TAG_const_type: - return btf_elf__add_ref_type(btfe, BTF_KIND_CONST, ref_type_id, NULL, false); + return btf_encoder__add_ref_type(encoder, BTF_KIND_CONST, ref_type_id, NULL, false); case DW_TAG_pointer_type: - return btf_elf__add_ref_type(btfe, BTF_KIND_PTR, ref_type_id, NULL, false); + return btf_encoder__add_ref_type(encoder, BTF_KIND_PTR, ref_type_id, NULL, false); case DW_TAG_restrict_type: - return btf_elf__add_ref_type(btfe, BTF_KIND_RESTRICT, ref_type_id, NULL, false); + return btf_encoder__add_ref_type(encoder, BTF_KIND_RESTRICT, ref_type_id, NULL, false); case DW_TAG_volatile_type: - return btf_elf__add_ref_type(btfe, BTF_KIND_VOLATILE, ref_type_id, NULL, false); + return btf_encoder__add_ref_type(encoder, BTF_KIND_VOLATILE, ref_type_id, NULL, false); case DW_TAG_typedef: - name = dwarves__active_loader->strings__ptr(cu, tag__namespace(tag)->name); - return btf_elf__add_ref_type(btfe, BTF_KIND_TYPEDEF, ref_type_id, name, false); + name = namespace__name(tag__namespace(tag)); + return btf_encoder__add_ref_type(encoder, BTF_KIND_TYPEDEF, ref_type_id, name, false); + case DW_TAG_LLVM_annotation: + name = tag__btf_type_tag(tag)->value; + return btf_encoder__add_ref_type(encoder, BTF_KIND_TYPE_TAG, ref_type_id, name, false); case DW_TAG_structure_type: case DW_TAG_union_type: case DW_TAG_class_type: - name = dwarves__active_loader->strings__ptr(cu, tag__namespace(tag)->name); + name = namespace__name(tag__namespace(tag)); if (tag__type(tag)->declaration) - return btf_elf__add_ref_type(btfe, BTF_KIND_FWD, 0, name, tag->tag == DW_TAG_union_type); + return btf_encoder__add_ref_type(encoder, BTF_KIND_FWD, 0, name, tag->tag == DW_TAG_union_type); else - return structure_type__encode(btfe, cu, tag, type_id_off); + return btf_encoder__add_struct_type(encoder, tag, type_id_off); case DW_TAG_array_type: /* TODO: Encode one dimension at a time. */ - need_index_type = true; - return btf_elf__add_array(btfe, ref_type_id, array_index_id, array_type__nelems(tag)); + encoder->need_index_type = true; + return btf_encoder__add_array(encoder, ref_type_id, encoder->array_index_id, array_type__nelems(tag)); case DW_TAG_enumeration_type: - return enumeration_type__encode(btfe, cu, tag); + return btf_encoder__add_enum_type(encoder, tag); case DW_TAG_subroutine_type: - return btf_elf__add_func_proto(btfe, cu, tag__ftype(tag), type_id_off); + return btf_encoder__add_func_proto(encoder, tag__ftype(tag), type_id_off); default: fprintf(stderr, "Unsupported DW_TAG_%s(0x%x)\n", dwarf_tag_name(tag->tag), tag->tag); @@ -482,35 +892,172 @@ static int tag__encode_btf(struct cu *cu, struct tag *tag, uint32_t core_id, str } } -static struct btf_elf *btfe; -static uint32_t array_index_id; -static bool has_index_type; +static int btf_encoder__write_raw_file(struct btf_encoder *encoder) +{ + const char *filename = encoder->filename; + uint32_t raw_btf_size; + const void *raw_btf_data; + int fd, err; + + raw_btf_data = btf__get_raw_data(encoder->btf, &raw_btf_size); + if (raw_btf_data == NULL) { + fprintf(stderr, "%s: btf__get_raw_data failed!\n", __func__); + return -1; + } + + fd = open(filename, O_WRONLY | O_CREAT, 0640); + if (fd < 0) { + fprintf(stderr, "%s: Couldn't open %s for writing the raw BTF info: %s\n", __func__, filename, strerror(errno)); + return -1; + } + err = write(fd, raw_btf_data, raw_btf_size); + if (err < 0) + fprintf(stderr, "%s: Couldn't write the raw BTF info to %s: %s\n", __func__, filename, strerror(errno)); + + close(fd); -int btf_encoder__encode() + if ((uint32_t)err != raw_btf_size) { + fprintf(stderr, "%s: Could only write %d bytes to %s of raw BTF info out of %d, aborting\n", __func__, err, filename, raw_btf_size); + unlink(filename); + err = -1; + } else { + /* go from bytes written == raw_btf_size to an indication that all went fine */ + err = 0; + } + + return err; +} + +static int btf_encoder__write_elf(struct btf_encoder *encoder) { - int err; + struct btf *btf = encoder->btf; + const char *filename = encoder->filename; + GElf_Shdr shdr_mem, *shdr; + Elf_Data *btf_data = NULL; + Elf_Scn *scn = NULL; + Elf *elf = NULL; + const void *raw_btf_data; + uint32_t raw_btf_size; + int fd, err = -1; + size_t strndx; + + fd = open(filename, O_RDWR); + if (fd < 0) { + fprintf(stderr, "Cannot open %s\n", filename); + return -1; + } + + if (elf_version(EV_CURRENT) == EV_NONE) { + elf_error("Cannot set libelf version"); + goto out; + } + + elf = elf_begin(fd, ELF_C_RDWR, NULL); + if (elf == NULL) { + elf_error("Cannot update ELF file"); + goto out; + } - if (gobuffer__size(&btfe->percpu_secinfo) != 0) - btf_elf__add_datasec_type(btfe, PERCPU_SECTION, &btfe->percpu_secinfo); + elf_flagelf(elf, ELF_C_SET, ELF_F_DIRTY); - err = btf_elf__encode(btfe, 0); - delete_functions(); - btf_elf__delete(btfe); - btfe = NULL; + /* + * First we look if there was already a .BTF section to overwrite. + */ + elf_getshdrstrndx(elf, &strndx); + while ((scn = elf_nextscn(elf, scn)) != NULL) { + shdr = gelf_getshdr(scn, &shdr_mem); + if (shdr == NULL) + continue; + char *secname = elf_strptr(elf, strndx, shdr->sh_name); + if (strcmp(secname, ".BTF") == 0) { + btf_data = elf_getdata(scn, btf_data); + break; + } + } + + raw_btf_data = btf__get_raw_data(btf, &raw_btf_size); + + if (btf_data) { + /* Existing .BTF section found */ + btf_data->d_buf = (void *)raw_btf_data; + btf_data->d_size = raw_btf_size; + elf_flagdata(btf_data, ELF_C_SET, ELF_F_DIRTY); + + if (elf_update(elf, ELF_C_NULL) >= 0 && + elf_update(elf, ELF_C_WRITE) >= 0) + err = 0; + else + elf_error("elf_update failed"); + } else { + const char *llvm_objcopy; + char tmp_fn[PATH_MAX]; + char cmd[PATH_MAX * 2]; + + llvm_objcopy = getenv("LLVM_OBJCOPY"); + if (!llvm_objcopy) + llvm_objcopy = "llvm-objcopy"; + + /* Use objcopy to add a .BTF section */ + snprintf(tmp_fn, sizeof(tmp_fn), "%s.btf", filename); + close(fd); + fd = creat(tmp_fn, S_IRUSR | S_IWUSR); + if (fd == -1) { + fprintf(stderr, "%s: open(%s) failed!\n", __func__, + tmp_fn); + goto out; + } + + if (write(fd, raw_btf_data, raw_btf_size) != raw_btf_size) { + fprintf(stderr, "%s: write of %d bytes to '%s' failed: %d!\n", + __func__, raw_btf_size, tmp_fn, errno); + goto unlink; + } + + snprintf(cmd, sizeof(cmd), "%s --add-section .BTF=%s %s", + llvm_objcopy, tmp_fn, filename); + if (system(cmd)) { + fprintf(stderr, "%s: failed to add .BTF section to '%s': %d!\n", + __func__, filename, errno); + goto unlink; + } + + err = 0; + unlink: + unlink(tmp_fn); + } + +out: + if (fd != -1) + close(fd); + if (elf) + elf_end(elf); return err; } -#define MAX_PERCPU_VAR_CNT 4096 +int btf_encoder__encode(struct btf_encoder *encoder) +{ + int err; -struct var_info { - uint64_t addr; - uint32_t sz; - const char *name; -}; + if (gobuffer__size(&encoder->percpu_secinfo) != 0) + btf_encoder__add_datasec(encoder, PERCPU_SECTION); -static struct var_info percpu_vars[MAX_PERCPU_VAR_CNT]; -static int percpu_var_cnt; + /* Empty file, nothing to do, so... done! */ + if (btf__get_nr_types(encoder->btf) == 0) + return 0; + + if (btf__dedup(encoder->btf, NULL, NULL)) { + fprintf(stderr, "%s: btf__dedup failed!\n", __func__); + return -1; + } + + if (encoder->raw_output) + err = btf_encoder__write_raw_file(encoder); + else + err = btf_encoder__write_elf(encoder); + + return err; +} static int percpu_var_cmp(const void *_a, const void *_b) { @@ -522,14 +1069,11 @@ static int percpu_var_cmp(const void *_a, const void *_b) return a->addr < b->addr ? -1 : 1; } -static bool percpu_var_exists(uint64_t addr, uint32_t *sz, const char **name) +static bool btf_encoder__percpu_var_exists(struct btf_encoder *encoder, uint64_t addr, uint32_t *sz, const char **name) { - const struct var_info *p; struct var_info key = { .addr = addr }; - - p = bsearch(&key, percpu_vars, percpu_var_cnt, - sizeof(percpu_vars[0]), percpu_var_cmp); - + const struct var_info *p = bsearch(&key, encoder->percpu.vars, encoder->percpu.var_cnt, + sizeof(encoder->percpu.vars[0]), percpu_var_cmp); if (!p) return false; @@ -538,14 +1082,14 @@ static bool percpu_var_exists(uint64_t addr, uint32_t *sz, const char **name) return true; } -static int collect_percpu_var(struct btf_elf *btfe, GElf_Sym *sym) +static int btf_encoder__collect_percpu_var(struct btf_encoder *encoder, GElf_Sym *sym, size_t sym_sec_idx) { const char *sym_name; uint64_t addr; uint32_t size; /* compare a symbol's shndx to determine if it's a percpu variable */ - if (elf_sym__section(sym) != btfe->percpu_shndx) + if (sym_sec_idx != encoder->percpu.shndx) return 0; if (elf_sym__type(sym) != STT_OBJECT) return 0; @@ -556,238 +1100,125 @@ static int collect_percpu_var(struct btf_elf *btfe, GElf_Sym *sym) if (!size) return 0; /* ignore zero-sized symbols */ - sym_name = elf_sym__name(sym, btfe->symtab); + sym_name = elf_sym__name(sym, encoder->symtab); if (!btf_name_valid(sym_name)) { dump_invalid_symbol("Found symbol of invalid name when encoding btf", - sym_name, btf_elf__verbose, btf_elf__force); - if (btf_elf__force) + sym_name, encoder->verbose, encoder->force); + if (encoder->force) return 0; return -1; } - if (btf_elf__verbose) + if (encoder->verbose) printf("Found per-CPU symbol '%s' at address 0x%" PRIx64 "\n", sym_name, addr); - if (percpu_var_cnt == MAX_PERCPU_VAR_CNT) { + /* Make sure addr is section-relative. For kernel modules (which are + * ET_REL files) this is already the case. For vmlinux (which is an + * ET_EXEC file) we need to subtract the section address. + */ + if (!encoder->is_rel) + addr -= encoder->percpu.base_addr; + + if (encoder->percpu.var_cnt == MAX_PERCPU_VAR_CNT) { fprintf(stderr, "Reached the limit of per-CPU variables: %d\n", MAX_PERCPU_VAR_CNT); return -1; } - percpu_vars[percpu_var_cnt].addr = addr; - percpu_vars[percpu_var_cnt].sz = size; - percpu_vars[percpu_var_cnt].name = sym_name; - percpu_var_cnt++; + encoder->percpu.vars[encoder->percpu.var_cnt].addr = addr; + encoder->percpu.vars[encoder->percpu.var_cnt].sz = size; + encoder->percpu.vars[encoder->percpu.var_cnt].name = sym_name; + encoder->percpu.var_cnt++; return 0; } -static void collect_symbol(GElf_Sym *sym, struct funcs_layout *fl) +static int btf_encoder__collect_symbols(struct btf_encoder *encoder, bool collect_percpu_vars) { - if (!fl->mcount_start && - !strcmp("__start_mcount_loc", elf_sym__name(sym, btfe->symtab))) { - fl->mcount_start = sym->st_value; - fl->mcount_sec_idx = sym->st_shndx; - } - - if (!fl->mcount_stop && - !strcmp("__stop_mcount_loc", elf_sym__name(sym, btfe->symtab))) - fl->mcount_stop = sym->st_value; -} - -static int collect_symbols(struct btf_elf *btfe, bool collect_percpu_vars) -{ - struct funcs_layout fl = { }; + Elf32_Word sym_sec_idx; uint32_t core_id; GElf_Sym sym; /* cache variables' addresses, preparing for searching in symtab. */ - percpu_var_cnt = 0; + encoder->percpu.var_cnt = 0; /* search within symtab for percpu variables */ - elf_symtab__for_each_symbol(btfe->symtab, core_id, sym) { - if (collect_percpu_vars && collect_percpu_var(btfe, &sym)) + elf_symtab__for_each_symbol_index(encoder->symtab, core_id, sym, sym_sec_idx) { + if (collect_percpu_vars && btf_encoder__collect_percpu_var(encoder, &sym, sym_sec_idx)) return -1; - if (collect_function(btfe, &sym)) + if (btf_encoder__collect_function(encoder, &sym)) return -1; - collect_symbol(&sym, &fl); } if (collect_percpu_vars) { - if (percpu_var_cnt) - qsort(percpu_vars, percpu_var_cnt, sizeof(percpu_vars[0]), percpu_var_cmp); + if (encoder->percpu.var_cnt) + qsort(encoder->percpu.vars, encoder->percpu.var_cnt, sizeof(encoder->percpu.vars[0]), percpu_var_cmp); - if (btf_elf__verbose) - printf("Found %d per-CPU variables!\n", percpu_var_cnt); + if (encoder->verbose) + printf("Found %d per-CPU variables!\n", encoder->percpu.var_cnt); } - if (functions_cnt && setup_functions(btfe, &fl)) { - fprintf(stderr, "Failed to filter DWARF functions\n"); - return -1; + if (encoder->functions.cnt) { + qsort(encoder->functions.entries, encoder->functions.cnt, sizeof(encoder->functions.entries[0]), + functions_cmp); + if (encoder->verbose) + printf("Found %d functions!\n", encoder->functions.cnt); } return 0; } -static bool has_arg_names(struct cu *cu, struct ftype *ftype) +static bool ftype__has_arg_names(const struct ftype *ftype) { struct parameter *param; - const char *name; ftype__for_each_parameter(ftype, param) { - name = dwarves__active_loader->strings__ptr(cu, param->name); - if (name == NULL) + if (parameter__name(param) == NULL) return false; } return true; } -int cu__encode_btf(struct cu *cu, int verbose, bool force, - bool skip_encoding_vars) +static int btf_encoder__encode_cu_variables(struct btf_encoder *encoder, struct cu *cu, uint32_t type_id_off) { - uint32_t type_id_off; uint32_t core_id; - struct variable *var; - struct function *fn; struct tag *pos; - int err = 0; - - btf_elf__verbose = verbose; - btf_elf__force = force; + int err = -1; - if (btfe && strcmp(btfe->filename, cu->filename)) { - err = btf_encoder__encode(); - if (err) - goto out; - - /* Finished one file, add one empty line */ - if (verbose) - printf("\n"); - } - - if (!btfe) { - btfe = btf_elf__new(cu->filename, cu->elf, base_btf); - if (!btfe) - return -1; - - err = collect_symbols(btfe, !skip_encoding_vars); - if (err) - goto out; - - has_index_type = false; - need_index_type = false; - array_index_id = 0; - - if (verbose) - printf("File %s:\n", btfe->filename); - } - - type_id_off = btf__get_nr_types(btfe->btf); - - if (!has_index_type) { - /* cu__find_base_type_by_name() takes "type_id_t *id" */ - type_id_t id; - if (cu__find_base_type_by_name(cu, "int", &id)) { - has_index_type = true; - array_index_id = type_id_off + id; - } else { - has_index_type = false; - array_index_id = type_id_off + cu->types_table.nr_entries; - } - } - - cu__for_each_type(cu, core_id, pos) { - int32_t btf_type_id = tag__encode_btf(cu, pos, core_id, btfe, array_index_id, type_id_off); - - if (btf_type_id < 0 || - tag__check_id_drift(pos, core_id, btf_type_id, type_id_off)) { - err = -1; - goto out; - } - } - - if (need_index_type && !has_index_type) { - struct base_type bt = {}; - - bt.name = 0; - bt.bit_size = 32; - btf_elf__add_base_type(btfe, &bt, "__ARRAY_SIZE_TYPE__"); - has_index_type = true; - } - - cu__for_each_function(cu, core_id, fn) { - int btf_fnproto_id, btf_fn_id; - const char *name; - - /* - * Skip functions that: - * - are marked as declarations - * - do not have full argument names - * - are not in ftrace list (if it's available) - * - are not external (in case ftrace filter is not available) - */ - if (fn->declaration) - continue; - if (!has_arg_names(cu, &fn->proto)) - continue; - if (functions_cnt) { - struct elf_function *func; - - func = find_function(btfe, function__name(fn, cu)); - if (!func || func->generated) - continue; - func->generated = true; - } else { - if (!fn->external) - continue; - } - - btf_fnproto_id = btf_elf__add_func_proto(btfe, cu, &fn->proto, type_id_off); - name = dwarves__active_loader->strings__ptr(cu, fn->name); - btf_fn_id = btf_elf__add_ref_type(btfe, BTF_KIND_FUNC, btf_fnproto_id, name, false); - if (btf_fnproto_id < 0 || btf_fn_id < 0) { - err = -1; - printf("error: failed to encode function '%s'\n", function__name(fn, cu)); - goto out; - } - } - - if (skip_encoding_vars) - goto out; - - if (btfe->percpu_shndx == 0 || !btfe->symtab) - goto out; + if (encoder->percpu.shndx == 0 || !encoder->symtab) + return 0; - if (verbose) + if (encoder->verbose) printf("search cu '%s' for percpu global variables.\n", cu->name); cu__for_each_variable(cu, core_id, pos) { + struct variable *var = tag__variable(pos); uint32_t size, type, linkage; const char *name, *dwarf_name; + struct llvm_annotation *annot; + const struct tag *tag; uint64_t addr; int id; - var = tag__variable(pos); if (var->declaration && !var->spec) continue; + /* percpu variables are allocated in global space */ if (variable__scope(var) != VSCOPE_GLOBAL && !var->spec) continue; /* addr has to be recorded before we follow spec */ addr = var->ip.addr; + dwarf_name = variable__name(var); - /* DWARF takes into account .data..percpu section offset - * within its segment, which for vmlinux is 0, but for kernel - * modules is >0. ELF symbols, on the other hand, don't take - * into account these offsets (as they are relative to the - * section start), so to match DWARF and ELF symbols we need - * to negate the section base address here. + /* Make sure addr is section-relative. DWARF, unlike ELF, + * always contains virtual symbol addresses, so subtract + * the section address unconditionally. */ - if (addr < btfe->percpu_base_addr || addr >= btfe->percpu_base_addr + btfe->percpu_sec_sz) + if (addr < encoder->percpu.base_addr || addr >= encoder->percpu.base_addr + encoder->percpu.sec_sz) continue; - addr -= btfe->percpu_base_addr; + addr -= encoder->percpu.base_addr; - if (!percpu_var_exists(addr, &size, &name)) + if (!btf_encoder__percpu_var_exists(encoder, addr, &size, &name)) continue; /* not a per-CPU variable */ /* A lot of "special" DWARF variables (e.g, __UNIQUE_ID___xxx) @@ -808,7 +1239,6 @@ int cu__encode_btf(struct cu *cu, int verbose, bool force, * per-CPU symbols have non-zero values. */ if (var->ip.addr == 0) { - dwarf_name = variable__name(var, cu); if (!dwarf_name || strcmp(dwarf_name, name)) continue; } @@ -819,47 +1249,283 @@ int cu__encode_btf(struct cu *cu, int verbose, bool force, if (var->ip.tag.type == 0) { fprintf(stderr, "error: found variable '%s' in CU '%s' that has void type\n", name, cu->name); - if (force) + if (encoder->force) continue; err = -1; break; } + tag = cu__type(cu, var->ip.tag.type); + if (tag__size(tag, cu) == 0) { + if (encoder->verbose) + fprintf(stderr, "Ignoring zero-sized per-CPU variable '%s'...\n", dwarf_name ?: "<missing name>"); + continue; + } + type = var->ip.tag.type + type_id_off; linkage = var->external ? BTF_VAR_GLOBAL_ALLOCATED : BTF_VAR_STATIC; - if (btf_elf__verbose) { + if (encoder->verbose) { printf("Variable '%s' from CU '%s' at address 0x%" PRIx64 " encoded\n", name, cu->name, addr); } - /* add a BTF_KIND_VAR in btfe->types */ - id = btf_elf__add_var_type(btfe, type, name, linkage); + /* add a BTF_KIND_VAR in encoder->types */ + id = btf_encoder__add_var(encoder, type, name, linkage); if (id < 0) { - err = -1; fprintf(stderr, "error: failed to encode variable '%s' at addr 0x%" PRIx64 "\n", name, addr); - break; + goto out; + } + + list_for_each_entry(annot, &var->annots, node) { + int tag_type_id = btf_encoder__add_decl_tag(encoder, annot->value, id, annot->component_idx); + if (tag_type_id < 0) { + fprintf(stderr, "error: failed to encode tag '%s' to variable '%s' with component_idx %d\n", + annot->value, name, annot->component_idx); + goto out; + } } /* - * add a BTF_VAR_SECINFO in btfe->percpu_secinfo, which will be added into - * btfe->types later when we add BTF_VAR_DATASEC. + * add a BTF_VAR_SECINFO in encoder->percpu_secinfo, which will be added into + * encoder->types later when we add BTF_VAR_DATASEC. */ - id = btf_elf__add_var_secinfo(&btfe->percpu_secinfo, id, addr, size); + id = btf_encoder__add_var_secinfo(encoder, id, addr, size); if (id < 0) { - err = -1; fprintf(stderr, "error: failed to encode section info for variable '%s' at addr 0x%" PRIx64 "\n", name, addr); - break; + goto out; } } + err = 0; out: - if (err) { - delete_functions(); - btf_elf__delete(btfe); - btfe = NULL; + return err; +} + +struct btf_encoder *btf_encoder__new(struct cu *cu, const char *detached_filename, struct btf *base_btf, bool skip_encoding_vars, bool force, bool gen_floats, bool verbose) +{ + struct btf_encoder *encoder = zalloc(sizeof(*encoder)); + + if (encoder) { + encoder->raw_output = detached_filename != NULL; + encoder->filename = strdup(encoder->raw_output ? detached_filename : cu->filename); + if (encoder->filename == NULL) + goto out_delete; + + encoder->btf = btf__new_empty_split(base_btf); + if (encoder->btf == NULL) + goto out_delete; + + encoder->force = force; + encoder->gen_floats = gen_floats; + encoder->skip_encoding_vars = skip_encoding_vars; + encoder->verbose = verbose; + encoder->has_index_type = false; + encoder->need_index_type = false; + encoder->array_index_id = 0; + + GElf_Ehdr ehdr; + + if (gelf_getehdr(cu->elf, &ehdr) == NULL) { + if (encoder->verbose) + elf_error("cannot get ELF header"); + goto out_delete; + } + + encoder->is_rel = ehdr.e_type == ET_REL; + + switch (ehdr.e_ident[EI_DATA]) { + case ELFDATA2LSB: + btf__set_endianness(encoder->btf, BTF_LITTLE_ENDIAN); + break; + case ELFDATA2MSB: + btf__set_endianness(encoder->btf, BTF_BIG_ENDIAN); + break; + default: + fprintf(stderr, "%s: unknown ELF endianness.\n", __func__); + goto out_delete; + } + + encoder->symtab = elf_symtab__new(NULL, cu->elf); + if (!encoder->symtab) { + if (encoder->verbose) + printf("%s: '%s' doesn't have symtab.\n", __func__, cu->filename); + goto out; + } + + /* find percpu section's shndx */ + + GElf_Shdr shdr; + Elf_Scn *sec = elf_section_by_name(cu->elf, &shdr, PERCPU_SECTION, NULL); + + if (!sec) { + if (encoder->verbose) + printf("%s: '%s' doesn't have '%s' section\n", __func__, cu->filename, PERCPU_SECTION); + } else { + encoder->percpu.shndx = elf_ndxscn(sec); + encoder->percpu.base_addr = shdr.sh_addr; + encoder->percpu.sec_sz = shdr.sh_size; + } + + if (btf_encoder__collect_symbols(encoder, !encoder->skip_encoding_vars)) + goto out_delete; + + if (encoder->verbose) + printf("File %s:\n", cu->filename); + } +out: + return encoder; + +out_delete: + btf_encoder__delete(encoder); + return NULL; +} + +void btf_encoder__delete(struct btf_encoder *encoder) +{ + if (encoder == NULL) + return; + + __gobuffer__delete(&encoder->percpu_secinfo); + zfree(&encoder->filename); + btf__free(encoder->btf); + encoder->btf = NULL; + elf_symtab__delete(encoder->symtab); + + encoder->functions.allocated = encoder->functions.cnt = 0; + free(encoder->functions.entries); + encoder->functions.entries = NULL; + + free(encoder); +} + +int btf_encoder__encode_cu(struct btf_encoder *encoder, struct cu *cu) +{ + uint32_t type_id_off = btf__get_nr_types(encoder->btf); + struct llvm_annotation *annot; + int btf_type_id, tag_type_id; + uint32_t core_id; + struct function *fn; + struct tag *pos; + int err = 0; + + + if (!encoder->has_index_type) { + /* cu__find_base_type_by_name() takes "type_id_t *id" */ + type_id_t id; + if (cu__find_base_type_by_name(cu, "int", &id)) { + encoder->has_index_type = true; + encoder->array_index_id = type_id_off + id; + } else { + encoder->has_index_type = false; + encoder->array_index_id = type_id_off + cu->types_table.nr_entries; + } + } + + cu__for_each_type(cu, core_id, pos) { + btf_type_id = btf_encoder__encode_tag(encoder, pos, type_id_off); + + if (btf_type_id < 0 || + tag__check_id_drift(pos, core_id, btf_type_id, type_id_off)) { + err = -1; + goto out; + } + } + + if (encoder->need_index_type && !encoder->has_index_type) { + struct base_type bt = {}; + + bt.name = 0; + bt.bit_size = 32; + btf_encoder__add_base_type(encoder, &bt, "__ARRAY_SIZE_TYPE__"); + encoder->has_index_type = true; + } + + cu__for_each_type(cu, core_id, pos) { + struct namespace *ns; + const char *tag_name; + + switch (pos->tag) { + case DW_TAG_structure_type: + tag_name = "struct"; + break; + case DW_TAG_union_type: + tag_name = "union"; + break; + case DW_TAG_typedef: + tag_name = "typedef"; + break; + default: + continue; + } + + btf_type_id = type_id_off + core_id; + ns = tag__namespace(pos); + list_for_each_entry(annot, &ns->annots, node) { + tag_type_id = btf_encoder__add_decl_tag(encoder, annot->value, btf_type_id, annot->component_idx); + if (tag_type_id < 0) { + fprintf(stderr, "error: failed to encode tag '%s' to %s '%s' with component_idx %d\n", + annot->value, tag_name, namespace__name(ns), annot->component_idx); + goto out; + } + } + } + + cu__for_each_function(cu, core_id, fn) { + int btf_fnproto_id, btf_fn_id; + const char *name; + + /* + * Skip functions that: + * - are marked as declarations + * - do not have full argument names + * - are not in ftrace list (if it's available) + * - are not external (in case ftrace filter is not available) + */ + if (fn->declaration) + continue; + if (!ftype__has_arg_names(&fn->proto)) + continue; + if (encoder->functions.cnt) { + struct elf_function *func; + const char *name; + + name = function__name(fn); + if (!name) + continue; + + func = btf_encoder__find_function(encoder, name); + if (!func || func->generated) + continue; + func->generated = true; + } else { + if (!fn->external) + continue; + } + + btf_fnproto_id = btf_encoder__add_func_proto(encoder, &fn->proto, type_id_off); + name = function__name(fn); + btf_fn_id = btf_encoder__add_ref_type(encoder, BTF_KIND_FUNC, btf_fnproto_id, name, false); + if (btf_fnproto_id < 0 || btf_fn_id < 0) { + err = -1; + printf("error: failed to encode function '%s'\n", function__name(fn)); + goto out; + } + + list_for_each_entry(annot, &fn->annots, node) { + tag_type_id = btf_encoder__add_decl_tag(encoder, annot->value, btf_fn_id, annot->component_idx); + if (tag_type_id < 0) { + fprintf(stderr, "error: failed to encode tag '%s' to func %s with component_idx %d\n", + annot->value, name, annot->component_idx); + goto out; + } + } } + + if (!encoder->skip_encoding_vars) + err = btf_encoder__encode_cu_variables(encoder, cu, type_id_off); +out: return err; } diff --git a/btf_encoder.h b/btf_encoder.h index 46fb231..f133b0d 100644 --- a/btf_encoder.h +++ b/btf_encoder.h @@ -9,11 +9,24 @@ Copyright (C) Arnaldo Carvalho de Melo <acme@redhat.com> */ +#include <stdbool.h> + +struct btf_encoder; +struct btf; struct cu; +struct list_head; + +struct btf_encoder *btf_encoder__new(struct cu *cu, const char *detached_filename, struct btf *base_btf, bool skip_encoding_vars, bool force, bool gen_floats, bool verbose); +void btf_encoder__delete(struct btf_encoder *encoder); + +int btf_encoder__encode(struct btf_encoder *encoder); + +int btf_encoder__encode_cu(struct btf_encoder *encoder, struct cu *cu); + +void btf_encoders__add(struct list_head *encoders, struct btf_encoder *encoder); -int btf_encoder__encode(); +struct btf_encoder *btf_encoders__first(struct list_head *encoders); -int cu__encode_btf(struct cu *cu, int verbose, bool force, - bool skip_encoding_vars); +struct btf_encoder *btf_encoders__next(struct btf_encoder *encoder); #endif /* _BTF_ENCODER_H_ */ diff --git a/btf_loader.c b/btf_loader.c index ec286f4..7a5b16f 100644 --- a/btf_loader.c +++ b/btf_loader.c @@ -20,20 +20,20 @@ #include <string.h> #include <limits.h> #include <libgen.h> +#include <linux/btf.h> +#include <bpf/btf.h> +#include <bpf/libbpf.h> #include <zlib.h> #include <gelf.h> -#include "libbtf.h" -#include "lib/bpf/include/uapi/linux/btf.h" #include "dutil.h" #include "dwarves.h" -/* - * FIXME: We should just get the table from the BTF ELF section - * and use it directly - */ -extern struct strings *strings; +static const char *cu__btf_str(struct cu *cu, uint32_t offset) +{ + return offset ? btf__str_by_offset(cu->priv, offset) : NULL; +} static void *tag__alloc(const size_t size) { @@ -45,8 +45,7 @@ static void *tag__alloc(const size_t size) return tag; } -static int btf_elf__load_ftype(struct btf_elf *btfe, struct ftype *proto, uint32_t tag, - const struct btf_type *tp, uint32_t id) +static int cu__load_ftype(struct cu *cu, struct ftype *proto, uint32_t tag, const struct btf_type *tp, uint32_t id) { const struct btf_param *param = btf_params(tp); int i, vlen = btf_vlen(tp); @@ -65,20 +64,20 @@ static int btf_elf__load_ftype(struct btf_elf *btfe, struct ftype *proto, uint32 goto out_free_parameters; p->tag.tag = DW_TAG_formal_parameter; p->tag.type = param->type; - p->name = param->name_off; + p->name = cu__btf_str(cu, param->name_off); ftype__add_parameter(proto, p); } } - cu__add_tag_with_id(btfe->priv, &proto->tag, id); + cu__add_tag_with_id(cu, &proto->tag, id); return 0; out_free_parameters: - ftype__delete(proto, btfe->priv); + ftype__delete(proto); return -ENOMEM; } -static int create_new_function(struct btf_elf *btfe, const struct btf_type *tp, uint32_t id) +static int create_new_function(struct cu *cu, const struct btf_type *tp, uint32_t id) { struct function *func = tag__alloc(sizeof(*func)); @@ -90,14 +89,14 @@ static int create_new_function(struct btf_elf *btfe, const struct btf_type *tp, func->btf = 1; func->proto.tag.tag = DW_TAG_subprogram; func->proto.tag.type = tp->type; - func->name = tp->name_off; + func->name = cu__btf_str(cu, tp->name_off); INIT_LIST_HEAD(&func->lexblock.tags); - cu__add_tag_with_id(btfe->priv, &func->proto.tag, id); + cu__add_tag_with_id(cu, &func->proto.tag, id); return 0; } -static struct base_type *base_type__new(strings_t name, uint32_t attrs, +static struct base_type *base_type__new(const char *name, uint32_t attrs, uint8_t float_type, size_t size) { struct base_type *bt = tag__alloc(sizeof(*bt)); @@ -113,18 +112,16 @@ static struct base_type *base_type__new(strings_t name, uint32_t attrs, return bt; } -static void type__init(struct type *type, uint32_t tag, - strings_t name, size_t size) +static void type__init(struct type *type, uint32_t tag, const char *name, size_t size) { __type__init(type); INIT_LIST_HEAD(&type->namespace.tags); type->size = size; type->namespace.tag.tag = tag; type->namespace.name = name; - type->namespace.sname = 0; } -static struct type *type__new(uint16_t tag, strings_t name, size_t size) +static struct type *type__new(uint16_t tag, const char *name, size_t size) { struct type *type = tag__alloc(sizeof(*type)); @@ -134,7 +131,7 @@ static struct type *type__new(uint16_t tag, strings_t name, size_t size) return type; } -static struct class *class__new(strings_t name, size_t size, bool is_union) +static struct class *class__new(const char *name, size_t size, bool is_union) { struct class *class = tag__alloc(sizeof(*class)); uint32_t tag = is_union ? DW_TAG_union_type : DW_TAG_structure_type; @@ -147,7 +144,7 @@ static struct class *class__new(strings_t name, size_t size, bool is_union) return class; } -static struct variable *variable__new(strings_t name, uint32_t linkage) +static struct variable *variable__new(const char *name, uint32_t linkage) { struct variable *var = tag__alloc(sizeof(*var)); @@ -160,22 +157,36 @@ static struct variable *variable__new(strings_t name, uint32_t linkage) return var; } -static int create_new_base_type(struct btf_elf *btfe, const struct btf_type *tp, uint32_t id) +static int create_new_int_type(struct cu *cu, const struct btf_type *tp, uint32_t id) { uint32_t attrs = btf_int_encoding(tp); - strings_t name = tp->name_off; + const char *name = cu__btf_str(cu, tp->name_off); struct base_type *base = base_type__new(name, attrs, 0, btf_int_bits(tp)); if (base == NULL) return -ENOMEM; base->tag.tag = DW_TAG_base_type; - cu__add_tag_with_id(btfe->priv, &base->tag, id); + cu__add_tag_with_id(cu, &base->tag, id); return 0; } -static int create_new_array(struct btf_elf *btfe, const struct btf_type *tp, uint32_t id) +static int create_new_float_type(struct cu *cu, const struct btf_type *tp, uint32_t id) +{ + const char *name = cu__btf_str(cu, tp->name_off); + struct base_type *base = base_type__new(name, 0, BT_FP_SINGLE, tp->size * 8); + + if (base == NULL) + return -ENOMEM; + + base->tag.tag = DW_TAG_base_type; + cu__add_tag_with_id(cu, &base->tag, id); + + return 0; +} + +static int create_new_array(struct cu *cu, const struct btf_type *tp, uint32_t id) { struct btf_array *ap = btf_array(tp); struct array_type *array = tag__alloc(sizeof(*array)); @@ -197,13 +208,12 @@ static int create_new_array(struct btf_elf *btfe, const struct btf_type *tp, uin array->tag.tag = DW_TAG_array_type; array->tag.type = ap->type; - cu__add_tag_with_id(btfe->priv, &array->tag, id); + cu__add_tag_with_id(cu, &array->tag, id); return 0; } -static int create_members(struct btf_elf *btfe, const struct btf_type *tp, - struct type *class) +static int create_members(struct cu *cu, const struct btf_type *tp, struct type *class) { struct btf_member *mp = btf_members(tp); int i, vlen = btf_vlen(tp); @@ -216,7 +226,7 @@ static int create_members(struct btf_elf *btfe, const struct btf_type *tp, member->tag.tag = DW_TAG_member; member->tag.type = mp[i].type; - member->name = mp[i].name_off; + member->name = cu__btf_str(cu, mp[i].name_off); member->bit_offset = btf_member_bit_offset(tp, i); member->bitfield_size = btf_member_bitfield_size(tp, i); member->byte_offset = member->bit_offset / 8; @@ -227,39 +237,39 @@ static int create_members(struct btf_elf *btfe, const struct btf_type *tp, return 0; } -static int create_new_class(struct btf_elf *btfe, const struct btf_type *tp, uint32_t id) +static int create_new_class(struct cu *cu, const struct btf_type *tp, uint32_t id) { - struct class *class = class__new(tp->name_off, tp->size, false); - int member_size = create_members(btfe, tp, &class->type); + struct class *class = class__new(cu__btf_str(cu, tp->name_off), tp->size, false); + int member_size = create_members(cu, tp, &class->type); if (member_size < 0) goto out_free; - cu__add_tag_with_id(btfe->priv, &class->type.namespace.tag, id); + cu__add_tag_with_id(cu, &class->type.namespace.tag, id); return 0; out_free: - class__delete(class, btfe->priv); + class__delete(class); return -ENOMEM; } -static int create_new_union(struct btf_elf *btfe, const struct btf_type *tp, uint32_t id) +static int create_new_union(struct cu *cu, const struct btf_type *tp, uint32_t id) { - struct type *un = type__new(DW_TAG_union_type, tp->name_off, tp->size); - int member_size = create_members(btfe, tp, un); + struct type *un = type__new(DW_TAG_union_type, cu__btf_str(cu, tp->name_off), tp->size); + int member_size = create_members(cu, tp, un); if (member_size < 0) goto out_free; - cu__add_tag_with_id(btfe->priv, &un->namespace.tag, id); + cu__add_tag_with_id(cu, &un->namespace.tag, id); return 0; out_free: - type__delete(un, btfe->priv); + type__delete(un); return -ENOMEM; } -static struct enumerator *enumerator__new(strings_t name, uint32_t value) +static struct enumerator *enumerator__new(const char *name, uint32_t value) { struct enumerator *en = tag__alloc(sizeof(*en)); @@ -272,19 +282,19 @@ static struct enumerator *enumerator__new(strings_t name, uint32_t value) return en; } -static int create_new_enumeration(struct btf_elf *btfe, const struct btf_type *tp, uint32_t id) +static int create_new_enumeration(struct cu *cu, const struct btf_type *tp, uint32_t id) { struct btf_enum *ep = btf_enum(tp); uint16_t i, vlen = btf_vlen(tp); struct type *enumeration = type__new(DW_TAG_enumeration_type, - tp->name_off, + cu__btf_str(cu, tp->name_off), tp->size ? tp->size * 8 : (sizeof(int) * 8)); if (enumeration == NULL) return -ENOMEM; for (i = 0; i < vlen; i++) { - strings_t name = ep[i].name_off; + const char *name = cu__btf_str(cu, ep[i].name_off); uint32_t value = ep[i].val; struct enumerator *enumerator = enumerator__new(name, value); @@ -294,66 +304,64 @@ static int create_new_enumeration(struct btf_elf *btfe, const struct btf_type *t enumeration__add(enumeration, enumerator); } - cu__add_tag_with_id(btfe->priv, &enumeration->namespace.tag, id); + cu__add_tag_with_id(cu, &enumeration->namespace.tag, id); return 0; out_free: - enumeration__delete(enumeration, btfe->priv); + enumeration__delete(enumeration); return -ENOMEM; } -static int create_new_subroutine_type(struct btf_elf *btfe, const struct btf_type *tp, uint32_t id) +static int create_new_subroutine_type(struct cu *cu, const struct btf_type *tp, uint32_t id) { struct ftype *proto = tag__alloc(sizeof(*proto)); if (proto == NULL) return -ENOMEM; - return btf_elf__load_ftype(btfe, proto, DW_TAG_subroutine_type, tp, id); + return cu__load_ftype(cu, proto, DW_TAG_subroutine_type, tp, id); } -static int create_new_forward_decl(struct btf_elf *btfe, const struct btf_type *tp, uint32_t id) +static int create_new_forward_decl(struct cu *cu, const struct btf_type *tp, uint32_t id) { - struct class *fwd = class__new(tp->name_off, 0, btf_kflag(tp)); + struct class *fwd = class__new(cu__btf_str(cu, tp->name_off), 0, btf_kflag(tp)); if (fwd == NULL) return -ENOMEM; fwd->type.declaration = 1; - cu__add_tag_with_id(btfe->priv, &fwd->type.namespace.tag, id); + cu__add_tag_with_id(cu, &fwd->type.namespace.tag, id); return 0; } -static int create_new_typedef(struct btf_elf *btfe, const struct btf_type *tp, uint32_t id) +static int create_new_typedef(struct cu *cu, const struct btf_type *tp, uint32_t id) { - struct type *type = type__new(DW_TAG_typedef, tp->name_off, 0); + struct type *type = type__new(DW_TAG_typedef, cu__btf_str(cu, tp->name_off), 0); if (type == NULL) return -ENOMEM; type->namespace.tag.type = tp->type; - cu__add_tag_with_id(btfe->priv, &type->namespace.tag, id); + cu__add_tag_with_id(cu, &type->namespace.tag, id); return 0; } -static int create_new_variable(struct btf_elf *btfe, const struct btf_type *tp, uint32_t id) +static int create_new_variable(struct cu *cu, const struct btf_type *tp, uint32_t id) { struct btf_var *bvar = btf_var(tp); - struct variable *var = variable__new(tp->name_off, bvar->linkage); + struct variable *var = variable__new(cu__btf_str(cu, tp->name_off), bvar->linkage); if (var == NULL) return -ENOMEM; var->ip.tag.type = tp->type; - cu__add_tag_with_id(btfe->priv, &var->ip.tag, id); + cu__add_tag_with_id(cu, &var->ip.tag, id); return 0; } -static int create_new_datasec(struct btf_elf *btfe, const struct btf_type *tp, uint32_t id) +static int create_new_datasec(struct cu *cu __maybe_unused, const struct btf_type *tp __maybe_unused, uint32_t id __maybe_unused) { - //strings_t name = btf_elf__get32(btfe, &tp->name_off); - - //cu__add_tag_with_id(btfe->priv, &datasec->tag, id); + //cu__add_tag_with_id(cu, &datasec->tag, id); /* * FIXME: this will not be used to reconstruct some original C code, @@ -362,7 +370,7 @@ static int create_new_datasec(struct btf_elf *btfe, const struct btf_type *tp, u return 0; } -static int create_new_tag(struct btf_elf *btfe, int type, const struct btf_type *tp, uint32_t id) +static int create_new_tag(struct cu *cu, int type, const struct btf_type *tp, uint32_t id) { struct tag *tag = zalloc(sizeof(*tag)); @@ -381,66 +389,69 @@ static int create_new_tag(struct btf_elf *btfe, int type, const struct btf_type } tag->type = tp->type; - cu__add_tag_with_id(btfe->priv, tag, id); + cu__add_tag_with_id(cu, tag, id); return 0; } -static int btf_elf__load_types(struct btf_elf *btfe) +static int btf__load_types(struct btf *btf, struct cu *cu) { uint32_t type_index; int err; - for (type_index = 1; type_index <= btf__get_nr_types(btfe->btf); type_index++) { - const struct btf_type *type_ptr = btf__type_by_id(btfe->btf, type_index); + for (type_index = 1; type_index <= btf__get_nr_types(btf); type_index++) { + const struct btf_type *type_ptr = btf__type_by_id(btf, type_index); uint32_t type = btf_kind(type_ptr); switch (type) { case BTF_KIND_INT: - err = create_new_base_type(btfe, type_ptr, type_index); + err = create_new_int_type(cu, type_ptr, type_index); break; case BTF_KIND_ARRAY: - err = create_new_array(btfe, type_ptr, type_index); + err = create_new_array(cu, type_ptr, type_index); break; case BTF_KIND_STRUCT: - err = create_new_class(btfe, type_ptr, type_index); + err = create_new_class(cu, type_ptr, type_index); break; case BTF_KIND_UNION: - err = create_new_union(btfe, type_ptr, type_index); + err = create_new_union(cu, type_ptr, type_index); break; case BTF_KIND_ENUM: - err = create_new_enumeration(btfe, type_ptr, type_index); + err = create_new_enumeration(cu, type_ptr, type_index); break; case BTF_KIND_FWD: - err = create_new_forward_decl(btfe, type_ptr, type_index); + err = create_new_forward_decl(cu, type_ptr, type_index); break; case BTF_KIND_TYPEDEF: - err = create_new_typedef(btfe, type_ptr, type_index); + err = create_new_typedef(cu, type_ptr, type_index); break; case BTF_KIND_VAR: - err = create_new_variable(btfe, type_ptr, type_index); + err = create_new_variable(cu, type_ptr, type_index); break; case BTF_KIND_DATASEC: - err = create_new_datasec(btfe, type_ptr, type_index); + err = create_new_datasec(cu, type_ptr, type_index); break; case BTF_KIND_VOLATILE: case BTF_KIND_PTR: case BTF_KIND_CONST: case BTF_KIND_RESTRICT: - err = create_new_tag(btfe, type, type_ptr, type_index); + err = create_new_tag(cu, type, type_ptr, type_index); break; case BTF_KIND_UNKN: - cu__table_nullify_type_entry(btfe->priv, type_index); + cu__table_nullify_type_entry(cu, type_index); fprintf(stderr, "BTF: idx: %d, Unknown kind %d\n", type_index, type); fflush(stderr); err = 0; break; case BTF_KIND_FUNC_PROTO: - err = create_new_subroutine_type(btfe, type_ptr, type_index); + err = create_new_subroutine_type(cu, type_ptr, type_index); break; case BTF_KIND_FUNC: // BTF_KIND_FUNC corresponding to a defined subprogram. - err = create_new_function(btfe, type_ptr, type_index); + err = create_new_function(cu, type_ptr, type_index); + break; + case BTF_KIND_FLOAT: + err = create_new_float_type(cu, type_ptr, type_index); break; default: fprintf(stderr, "BTF: idx: %d, Unknown kind %d\n", type_index, type); @@ -455,15 +466,53 @@ static int btf_elf__load_types(struct btf_elf *btfe) return 0; } -static int btf_elf__load_sections(struct btf_elf *btfe) +static int btf__load_sections(struct btf *btf, struct cu *cu) { - return btf_elf__load_types(btfe); + return btf__load_types(btf, cu); } -static int class__fixup_btf_bitfields(struct tag *tag, struct cu *cu, struct btf_elf *btfe) +static uint32_t class__infer_alignment(const struct conf_load *conf, + uint32_t byte_offset, + uint32_t natural_alignment, + uint32_t smallest_offset) +{ + uint16_t cacheline_size = conf->conf_fprintf->cacheline_size; + uint32_t alignment = 0; + uint32_t offset_delta = byte_offset - smallest_offset; + + if (offset_delta) { + if (byte_offset % 2 == 0) { + /* Find the power of 2 immediately higher than + * offset_delta + */ + alignment = 1 << (8 * sizeof(offset_delta) - + __builtin_clz(offset_delta)); + } else { + alignment = 0; + } + } + + /* Natural alignment, nothing to do */ + if (alignment <= natural_alignment || alignment == 1) + alignment = 0; + /* If the offset is compatible with being aligned on the cacheline size + * and this would only result in increasing the alignment, use the + * cacheline size as it is safe and quite likely to be what was in the + * source. + */ + else if (alignment < cacheline_size && + cacheline_size % alignment == 0 && + byte_offset % cacheline_size == 0) + alignment = cacheline_size; + + return alignment; +} + +static int class__fixup_btf_bitfields(const struct conf_load *conf, struct tag *tag, struct cu *cu) { struct class_member *pos; struct type *tag_type = tag__type(tag); + uint32_t smallest_offset = 0; type__for_each_data_member(tag_type, pos) { struct tag *type = tag__strip_typedefs_and_modifiers(&pos->tag, cu); @@ -475,42 +524,52 @@ static int class__fixup_btf_bitfields(struct tag *tag, struct cu *cu, struct btf pos->byte_size = tag__size(type, cu); pos->bit_size = pos->byte_size * 8; - /* bitfield fixup is needed for enums and base types only */ - if (type->tag != DW_TAG_base_type && type->tag != DW_TAG_enumeration_type) - continue; - /* if BTF data is incorrect and has size == 0, skip field, * instead of crashing */ if (pos->byte_size == 0) { continue; } - if (pos->bitfield_size) { - /* bitfields seem to be always aligned, no matter the packing */ - pos->byte_offset = pos->bit_offset / pos->bit_size * pos->bit_size / 8; - pos->bitfield_offset = pos->bit_offset - pos->byte_offset * 8; - /* re-adjust bitfield offset if it is negative */ - if (pos->bitfield_offset < 0) { - pos->bitfield_offset += pos->bit_size; - pos->byte_offset -= pos->byte_size; - pos->bit_offset = pos->byte_offset * 8 + pos->bitfield_offset; + /* bitfield fixup is needed for enums and base types only */ + if (type->tag == DW_TAG_base_type || type->tag == DW_TAG_enumeration_type) { + if (pos->bitfield_size) { + /* bitfields seem to be always aligned, no matter the packing */ + pos->byte_offset = pos->bit_offset / pos->bit_size * pos->bit_size / 8; + pos->bitfield_offset = pos->bit_offset - pos->byte_offset * 8; + /* re-adjust bitfield offset if it is negative */ + if (pos->bitfield_offset < 0) { + pos->bitfield_offset += pos->bit_size; + pos->byte_offset -= pos->byte_size; + pos->bit_offset = pos->byte_offset * 8 + pos->bitfield_offset; + } + } else { + pos->byte_offset = pos->bit_offset / 8; } - } else { - pos->byte_offset = pos->bit_offset / 8; } + + pos->alignment = class__infer_alignment(conf, + pos->byte_offset, + tag__natural_alignment(type, cu), + smallest_offset); + smallest_offset = pos->byte_offset + pos->byte_size; } + tag_type->alignment = class__infer_alignment(conf, + tag_type->size, + tag__natural_alignment(tag, cu), + smallest_offset); + return 0; } -static int cu__fixup_btf_bitfields(struct cu *cu, struct btf_elf *btfe) +static int cu__fixup_btf_bitfields(const struct conf_load *conf, struct cu *cu) { int err = 0; struct tag *pos; list_for_each_entry(pos, &cu->tags, node) if (tag__is_struct(pos) || tag__is_union(pos)) { - err = class__fixup_btf_bitfields(pos, cu, btfe); + err = class__fixup_btf_bitfields(conf, pos, cu); if (err) break; } @@ -518,48 +577,49 @@ static int cu__fixup_btf_bitfields(struct cu *cu, struct btf_elf *btfe) return err; } -static void btf_elf__cu_delete(struct cu *cu) +static void btf__cu_delete(struct cu *cu) { - btf_elf__delete(cu->priv); + btf__free(cu->priv); cu->priv = NULL; } -static const char *btf_elf__strings_ptr(const struct cu *cu, strings_t s) +static int libbpf_log(enum libbpf_print_level level __maybe_unused, const char *format, va_list args) { - return btf_elf__string(cu->priv, s); + return vfprintf(stderr, format, args); } -struct debug_fmt_ops btf_elf__ops; +struct debug_fmt_ops btf__ops; -int btf_elf__load_file(struct cus *cus, struct conf_load *conf, const char *filename) +static int cus__load_btf(struct cus *cus, struct conf_load *conf, const char *filename) { - int err; - struct btf_elf *btfe = btf_elf__new(filename, NULL, base_btf); - - if (btfe == NULL) - return -1; + int err = -1; - struct cu *cu = cu__new(filename, btfe->wordsize, NULL, 0, filename); + // Pass a zero for addr_size, we'll get it after we load via btf__pointer_size() + struct cu *cu = cu__new(filename, 0, NULL, 0, filename, false); if (cu == NULL) return -1; cu->language = LANG_C; cu->uses_global_strings = false; - cu->little_endian = !btfe->is_big_endian; - cu->dfops = &btf_elf__ops; - cu->priv = btfe; - btfe->priv = cu; - if (btf_elf__load(btfe) != 0) - return -1; + cu->dfops = &btf__ops; - err = btf_elf__load_sections(btfe); + libbpf_set_print(libbpf_log); - if (err != 0) { - cu__delete(cu); - return err; - } + struct btf *btf = btf__parse_split(filename, conf->base_btf); + + err = libbpf_get_error(btf); + if (err) + goto out_free; + + cu->priv = btf; + cu->little_endian = btf__endianness(btf) == BTF_LITTLE_ENDIAN; + cu->addr_size = btf__pointer_size(btf); - err = cu__fixup_btf_bitfields(cu, btfe); + err = btf__load_sections(btf, cu); + if (err != 0) + goto out_free; + + err = cu__fixup_btf_bitfields(conf, cu); /* * The app stole this cu, possibly deleting it, * so forget about it @@ -569,11 +629,14 @@ int btf_elf__load_file(struct cus *cus, struct conf_load *conf, const char *file cus__add(cus, cu); return err; + +out_free: + cu__delete(cu); // will call btf__free(cu->priv); + return err; } -struct debug_fmt_ops btf_elf__ops = { +struct debug_fmt_ops btf__ops = { .name = "btf", - .load_file = btf_elf__load_file, - .strings__ptr = btf_elf__strings_ptr, - .cu__delete = btf_elf__cu_delete, + .load_file = cus__load_btf, + .cu__delete = btf__cu_delete, }; @@ -8,24 +8,34 @@ # this information is not available when loading from BTF. if [ $# -eq 0 ] ; then - echo "Usage: btfdiff <filename_with_BTF_and_DWARF_info>" + echo "Usage: btfdiff <filename_with_DWARF_and_maybe_BTF_info> [<filename_with_BTF_info>]" exit 1 fi -file=$1 +dwarf_input=$1 +btf_input=$dwarf_input + +if [ $# -eq 2 ] ; then + btf_input=$2 +fi + btf_output=$(mktemp /tmp/btfdiff.btf.XXXXXX) dwarf_output=$(mktemp /tmp/btfdiff.dwarf.XXXXXX) pahole_bin=${PAHOLE-"pahole"} ${pahole_bin} -F dwarf \ --flat_arrays \ + --sort \ + --jobs \ --suppress_aligned_attribute \ --suppress_force_paddings \ --suppress_packed \ - --show_private_classes $file > $dwarf_output + --show_private_classes $dwarf_input > $dwarf_output ${pahole_bin} -F btf \ + --sort \ + --suppress_aligned_attribute \ --suppress_packed \ - $file > $btf_output + $btf_input > $btf_output diff -up $dwarf_output $btf_output diff --git a/buildcmd.sh b/buildcmd.sh new file mode 100755 index 0000000..5a15d4b --- /dev/null +++ b/buildcmd.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +mkdir build +cd build +cmake -DCMAKE_BUILD_TYPE=Release .. +cd .. +make -j $(getconf _NPROCESSORS_ONLN) -C build diff --git a/changes-v1.20 b/changes-v1.20 new file mode 100644 index 0000000..0dddfda --- /dev/null +++ b/changes-v1.20 @@ -0,0 +1,66 @@ +BTF encoder: + + - Improve ELF error reporting using elf_errmsg(elf_errno()). + + - Improve objcopy error handling. + + - Fix handling of 'restrict' qualifier, that was being treated as a 'const'. + + - Support SHN_XINDEX in st_shndx symbol indexes, to handle ELF objects with + more than 65534 sections, for instance, which happens with kernels built + with 'KCFLAGS="-ffunction-sections -fdata-sections", Other cases may + include when using FG-ASLR, LTO. + + - Cope with functions without a name, as seen sometimes when building kernel + images with some versions of clang, when a SEGFAULT was taking place. + + - Fix BTF variable generation for kernel modules, not skipping variables at + offset zero. + + - Fix address size to match what is in the ELF file being processed, to fix using + a 64-bit pahole binary to generate BTF for a 32-bit vmlinux image. + + - Use kernel module ftrace addresses when finding which functions to encode, + which increases the number of functions encoded. + +libbpf: + + - Allow use of packaged version, for distros wanting to dynamically link with + the system's libbpf package instead of using the libbpf git submodule shipped + in pahole's source code. + +DWARF loader: + + - Support DW_AT_data_bit_offset + + This appeared in DWARF4 but is supported only in gcc's -gdwarf-5, + support it in a way that makes the output be the same for both cases. + + $ gcc -gdwarf-5 -c examples/dwarf5/bf.c + $ pahole bf.o + struct pea { + long int a:1; /* 0: 0 8 */ + long int b:1; /* 0: 1 8 */ + long int c:1; /* 0: 2 8 */ + + /* XXX 29 bits hole, try to pack */ + /* Bitfield combined with next fields */ + + int after_bitfield; /* 4 4 */ + + /* size: 8, cachelines: 1, members: 4 */ + /* sum members: 4 */ + /* sum bitfield members: 3 bits, bit holes: 1, sum bit holes: 29 bits */ + /* last cacheline: 8 bytes */ + }; + + - DW_FORM_implicit_const in attr_numeric() and attr_offset() + + - Support DW_TAG_GNU_call_site, its the standardized rename of the previously supported + DW_TAG_GNU_call_site. + +build: + + - Fix compilation on 32-bit architectures. + +Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> diff --git a/changes-v1.21 b/changes-v1.21 new file mode 100644 index 0000000..5b148c8 --- /dev/null +++ b/changes-v1.21 @@ -0,0 +1,70 @@ +DWARF loader: + +- Handle DWARF5 DW_OP_addrx properly + + Part of the effort to support the subset of DWARF5 that is generated when building the kernel. + +- Handle subprogram ret type with abstract_origin properly + + Adds a second pass to resolve abstract origin DWARF description of functions to aid + the BTF encoder in getting the right return type. + +- Check .notes section for LTO build info + + When LTO is used, currently only with clang, we need to do extra steps to handle references + from one object (compile unit, aka CU) to another, a way for DWARF to avoid duplicating + information. + +- Check .debug_abbrev for cross-CU references + + When the kernel build process doesn't add an ELF note in vmlinux indicating that LTO was + used and thus intra-CU references are present and thus we need to use a more expensive + way to resolve types and (again) thus to encode BTF, we need to look at DWARF's .debug_abbrev + ELF section to figure out if such intra-CU references are present. + +- Permit merging all DWARF CU's for clang LTO built binary + + Allow not trowing away previously supposedly self contained compile units + (objects, aka CU, aka Compile Units) as they have type descriptions that will + be used in later CUs. + +- Permit a flexible HASHTAGS__BITS + + So that we can use a more expensive algorithm when we need to keep previously processed + compile units that will then be referenced by later ones to resolve types. + +- Use a better hashing function, from libbpf + + Enabling patch to combine compile units when using LTO. + +BTF encoder: + +- Add --btf_gen_all flag + + A new command line to allow asking for the generation of all BTF encodings, so that we + can stop adding new command line options to enable new encodings in the kernel Makefile. + +- Match ftrace addresses within ELF functions + + To cope with differences in how DWARF and ftrace describes function boundaries. + +- Funnel ELF error reporting through a macro + + To use libelf's elf_error() function, improving error messages. + +- Sanitize non-regular int base type + + Cope with clang with dwarf5 non-regular int base types, tricky stuff, see yhs + full explanation in the relevant cset. + +- Add support for the floating-point types + + S/390 has floats'n'doubles in its arch specific linux headers, cope with that. + +Pretty printer: + +- Honour conf_fprintf.hex when printing enumerations + + If the user specifies --hex in the command line, honour it when printing enumerations. + +Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> diff --git a/changes-v1.22 b/changes-v1.22 new file mode 100644 index 0000000..9fd0972 --- /dev/null +++ b/changes-v1.22 @@ -0,0 +1,95 @@ +pahole: + +- Allow encoding BTF to a separate BTF file (detached) instead of to a new + ".BTF" ELF section in the file being encoded (vmlinux usually). + +- Introduce -j/--jobs option to specify the number of threads to use. Without + arguments means one thread per CPU. So far used for the DWARF loader, will + be used as well for the BTF encoder. + +- Show all different types with the same name, not just the first one found. + +- Introduce sorted type output (--sort), needed with multithreaded DWARF loading, + to use with things like 'btfdiff' that expects the output from DWARF and BTF + types to be comparable using 'diff'. + +- Stop assuming that reading from stdin means pretty printing as this broke + pre-existing scripts, introduce a explicit --prettify command line option. + +- Improve type resolution for the --header command line option. + +- Disable incomplete CTF encoder, this needs to be done using the external + libctf library. + +- Do not consider the ftrace filter when encoding BTF for kernel functions. + +- Add --kabi_prefix to avoid deduplication woes when using _RH_KABI_REPLACE(), + +- Add --with_flexible_array to show just types with flexible arrays. + +DWARF Loader: + +- Multithreaded loading, requires elfutils >= 0.178. + +- Lock calls to non-thread safe elfutils' libdw functions (dwarf_decl_file() + and dwarf_decl_line()) + +- Change hash table size to one that performs better with current typical + vmlinux files. + +- Allow tweaking the hash table size from the command line. + +- Stop allocating memory for strings obtained from libdw, just defer freeing + the Dwfl handler so that references to its strings can be safely kept. + +- Use a frontend cache for the latest lookup result. + +- Allow ignoring some DWARF tags when loading for encoding BTF, as BTF doesn't + have equivalents for things like DW_TAG_inline_expansion and DW_TAG_label. + +- Allow ignoring some DWARF tag attributes, such as DW_AT_alignment, not used + when encoding BTF. + +- Do not query for non-C attributes when loading a C language CU (compilation unit). + +BTF encoder: + +- Preparatory work for multithreaded encoding, the focus for 1.23. + +btfdiff: + +- Support diffing against a detached BTF file, e.g.: 'btfdiff vmlinux vmlinux.btf' + +- Support multithreaded DWARF loading, using the new pahole --sort option to have + the output from both BTF and DWARF sorted and thus comparable via 'diff'. + +Build: + +- Support building with libc libraries lacking either obstacks or argp, such + as Alpine Linux's musl libc. + +- Support systems without getconf() to obtain the data cacheline size, such + as musl libc. + +- Add a buildcmd.sh for test builds, tested using the same set of containers + used for testing the Linux kernel perf tools. + +- Enable selecting building with a shared libdwarves library or statically. + +- Allow to use the libbpf package found in distributions instead of with the + accompanying libbpf git submodule. + +Cleanups: + +- Address lots of compiler warnings accumulated by not using -Wextra, it'll + be added in the next release after allowing not to use it to build libbpf. + +- Address covscan report issues. + +Documentation: + +- Improve the --nr_methods/-m pahole man page entry. + +- Clarify that currently --nr_methods doesn't work together witn -C. + +Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> diff --git a/changes-v1.23 b/changes-v1.23 new file mode 100644 index 0000000..426f6f3 --- /dev/null +++ b/changes-v1.23 @@ -0,0 +1,56 @@ +DWARF loader: + +- Read DW_TAG_LLVM_annotation tags, associating it with variables, functions, + types. So far this is only being used by the BTF encoder, but the pretty + printer should use this as well in a future release, printing these + attributes when available. + +- Initial support for DW_TAG_skeleton_unit, so far just suggest looking up a + matching .dwo file to be used instead. Automagically doing this is in the + plans for a future release. + +- Fix heap overflow when accessing variable specification. + +BTF encoder: + +- Support the new BTF type tag attribute, encoding DW_TAG_LLVM_annotation DWARF + tags as BTF_KIND_TYPE_TAG and BTF_KIND_DECL_TAG. + + This allows __attribute__((btf_type_tag("tag1"))) to be used for variables, + functions, typedefs, so that contextual information can be stored in BTF and + used by the kernel BPF verifier for more checks. + + The --skip_encoding_btf_type_tag option can be used to suppress this. + +- Fix handling of percpu symbols on s390. + +BTF loader: + +- Use cacheline size to infer alignment. + +btfdiff: + +- Now that the BTF loader infers struct member alingment, and as that is just + an heuristic, suppress printing the alignment when pretty printing from BTF + info like is done when printing from DWARF. + +pahole: + +- Add --skip_missing so that we don't stop when not finding one of the types passed + to -C. + +Pretty printer: + +- Fix __attribute__((__aligned__(N)) printing alignment for struct members. + +- Fix nested __attribute__(__aligned__(N)) struct printing order, so that + rebuilding from the printed source circles back to the original source code + alignment semantics. + +Build: + +- No need to download libbpf source when using the system library (libbpf-devel). + +- Make python optional + +Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> diff --git a/cmake/modules/Findargp.cmake b/cmake/modules/Findargp.cmake new file mode 100644 index 0000000..db9dc09 --- /dev/null +++ b/cmake/modules/Findargp.cmake @@ -0,0 +1,41 @@ +# - Find argp +# Figure out if argp is in glibc or if it argp-standalone +# +# ARGP_LIBRARY - Library to use argp +# ARGP_FOUND - True if found. + +message(STATUS "Checking availability of argp library") + +INCLUDE(CheckLibraryExists) + +if (ARGP_LIBRARY) + # Already in cache, be silent + set(ARGP_FIND_QUIETLY TRUE) +endif (ARGP_LIBRARY) + +find_library(ARGP_LIBRARY + NAMES argp + PATHS /usr/lib /usr/local/lib /usr/lib64 /usr/local/lib64 ~/usr/local/lib ~/usr/local/lib64 +) + +if (ARGP_LIBRARY) + set(ARGP_FOUND TRUE) + set(ARGP_LIBRARY ${ARGP_LIBRARY}) + set(CMAKE_REQUIRED_LIBRARIES ${ARGP_LIBRARY}) +else (ARGP_LIBRARY) + set(ARGP_LIBRARY "") +endif (ARGP_LIBRARY) + +if (ARGP_FOUND) + if (NOT ARGP_FIND_QUIETLY) + message(STATUS "Found argp library: ${ARGP_LIBRARY}") + endif (NOT ARGP_FIND_QUIETLY) +else (ARGP_FOUND) + set(ARGP_FOUND TRUE) + message(STATUS "Assuming argp is in libc") +endif (ARGP_FOUND) + +mark_as_advanced(ARGP_LIBRARY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_SOURCE_DIR}/config.h) + +message(STATUS "Checking availability of argp library - done") diff --git a/cmake/modules/Findobstack.cmake b/cmake/modules/Findobstack.cmake new file mode 100644 index 0000000..b0f6895 --- /dev/null +++ b/cmake/modules/Findobstack.cmake @@ -0,0 +1,41 @@ +# - Find obstack +# Figure out if obstack is in glibc or if it musl-obstack or elsewhere +# +# OBSTACK_LIBRARY - Library to use obstack +# OBSTACK_FOUND - True if found. + +message(STATUS "Checking availability of obstack library") + +INCLUDE(CheckLibraryExists) + +if (OBSTACK_LIBRARY) + # Already in cache, be silent + set(OBSTACK_FIND_QUIETLY TRUE) +endif (OBSTACK_LIBRARY) + +find_library(OBSTACK_LIBRARY + NAMES obstack + PATHS /usr/lib /usr/local/lib /usr/lib64 /usr/local/lib64 ~/usr/local/lib ~/usr/local/lib64 +) + +if (OBSTACK_LIBRARY) + set(OBSTACK_FOUND TRUE) + set(OBSTACK_LIBRARY ${OBSTACK_LIBRARY}) + set(CMAKE_REQUIRED_LIBRARIES ${OBSTACK_LIBRARY}) +else (OBSTACK_LIBRARY) + set(OBSTACK_LIBRARY "") +endif (OBSTACK_LIBRARY) + +if (OBSTACK_FOUND) + if (NOT OBSTACK_FIND_QUIETLY) + message(STATUS "Found obstack library: ${OBSTACK_LIBRARY}") + endif (NOT OBSTACK_FIND_QUIETLY) +else (OBSTACK_FOUND) + set(OBSTACK_FOUND TRUE) + message(STATUS "Assuming obstack is in libc") +endif (OBSTACK_FOUND) + +mark_as_advanced(OBSTACK_LIBRARY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_SOURCE_DIR}/config.h) + +message(STATUS "Checking availability of obstack library - done") @@ -87,7 +87,7 @@ static void diff_function(const struct cu *new_cu, struct function *function, if (function->inlined || function->abstract_origin != 0) return; - name = function__name(function, cu); + name = function__name(function); new_tag = cu__find_function_by_name(new_cu, name); if (new_tag != NULL) { struct function *new_function = tag__function(new_tag); @@ -173,7 +173,7 @@ static int check_print_change(const struct class_member *old, printf(" %s\n" " from: %-21s /* %5u(%2u) %5zd(%2d) */\n" " to: %-21s /* %5u(%2u) %5zd(%2u) */\n", - class_member__name(old, old_cu), + class_member__name(old), old_type_name, old->byte_offset, old->bitfield_offset, old_size, old->bitfield_size, new_type_name, new->byte_offset, new->bitfield_offset, @@ -182,15 +182,15 @@ static int check_print_change(const struct class_member *old, return changes; } -static struct class_member *class__find_pair_member(const struct class *structure, const struct cu *cu, - const struct class_member *pair_member, const struct cu *pair_cu, +static struct class_member *class__find_pair_member(const struct class *structure, + const struct class_member *pair_member, int *nr_anonymousp) { - const char *member_name = class_member__name(pair_member, pair_cu); + const char *member_name = class_member__name(pair_member); struct class_member *member; if (member_name) - return class__find_member_by_name(structure, cu, member_name); + return class__find_member_by_name(structure, member_name); int nr_anonymous = ++*nr_anonymousp; @@ -198,7 +198,7 @@ static struct class_member *class__find_pair_member(const struct class *structur type__for_each_member(&structure->type, member) { if (member->tag.tag == pair_member->tag.tag && /* Both are class/union/struct (unnamed) */ - class_member__name(member, cu) == member_name && /* Both are NULL? */ + class_member__name(member) == member_name && /* Both are NULL? */ --nr_anonymous == 0) return member; } @@ -217,7 +217,7 @@ static int check_print_members_changes(const struct class *structure, uint16_t nr_twins_found = 0; type__for_each_member(&structure->type, member) { - struct class_member *twin = class__find_pair_member(new_structure, new_cu, member, cu, &nr_anonymous); + struct class_member *twin = class__find_pair_member(new_structure, member, &nr_anonymous); if (twin != NULL) { twin->tag.visited = 1; ++nr_twins_found; @@ -231,7 +231,7 @@ static int check_print_members_changes(const struct class *structure, type = cu__type(cu, member->tag.type); printf(" %s\n" " removed: %-21s /* %5u(%2u) %5zd(%2d) */\n", - class_member__name(member, cu), + class_member__name(member), tag__name(type, cu, name, sizeof(name), NULL), member->byte_offset, member->bitfield_offset, member->byte_size, member->bitfield_size); @@ -254,7 +254,7 @@ static int check_print_members_changes(const struct class *structure, type = cu__type(new_cu, member->tag.type); printf(" %s\n" " added: %-21s /* %5u(%2u) %5zd(%2d) */\n", - class_member__name(member, new_cu), + class_member__name(member), tag__name(type, new_cu, name, sizeof(name), NULL), member->byte_offset, member->bitfield_offset, member->byte_size, member->bitfield_size); @@ -273,11 +273,10 @@ static void diff_struct(const struct cu *new_cu, struct class *structure, assert(class__is_struct(structure)); - if (class__size(structure) == 0 || class__name(structure, cu) == NULL) + if (class__size(structure) == 0 || class__name(structure) == NULL) return; - new_tag = cu__find_struct_by_name(new_cu, - class__name(structure, cu), 0, NULL); + new_tag = cu__find_struct_by_name(new_cu, class__name(structure), 0, NULL); if (new_tag == NULL) return; @@ -299,20 +298,11 @@ static void diff_struct(const struct cu *new_cu, struct class *structure, return; ++cu->nr_structures_changed; - cu__check_max_len_changed_item(cu, class__name(structure, cu), - sizeof("struct")); + cu__check_max_len_changed_item(cu, class__name(structure), sizeof("struct")); structure->priv = diff_info__new(class__tag(new_structure), new_cu, diff); } -static struct cu *cus__find_pair(struct cus *cus, const char *name) -{ - if (cus->nr_entries == 1) - return list_first_entry(&cus->cus, struct cu, node); - - return cus__find_cu_by_name(cus, name); -} - static int cu_find_new_tags_iterator(struct cu *new_cu, void *old_cus) { struct cu *old_cu = cus__find_pair(old_cus, new_cu->name); @@ -330,7 +320,7 @@ static int cu_find_new_tags_iterator(struct cu *new_cu, void *old_cus) if (function->abstract_origin || function->inlined) continue; - const char *name = function__name(function, new_cu); + const char *name = function__name(function); struct tag *old_function = cu__find_function_by_name(old_cu, name); if (old_function != NULL && !tag__function(old_function)->inlined) @@ -346,7 +336,7 @@ static int cu_find_new_tags_iterator(struct cu *new_cu, void *old_cus) struct class *class; cu__for_each_struct(new_cu, id, class) { - const char *name = class__name(class, new_cu); + const char *name = class__name(class); if (name == NULL || class__size(class) == 0 || cu__find_struct_by_name(old_cu, name, 0, NULL)) continue; @@ -386,7 +376,7 @@ static void show_diffs_function(struct function *function, const struct cu *cu, printf(" %-*.*s | %+4d", (int)cu->max_len_changed_item, (int)cu->max_len_changed_item, - function__name(function, cu), di->diff); + function__name(function), di->diff); if (!verbose) { putchar('\n'); @@ -400,12 +390,12 @@ static void show_diffs_function(struct function *function, const struct cu *cu, if (twin->inlined) puts(cookie ? " (uninlined)" : " (inlined)"); - else if (strcmp(function__name(function, cu), - function__name(twin, di->cu)) != 0) + else if (strcmp(function__name(function), + function__name(twin)) != 0) printf("%s: BRAIN FART ALERT: comparing %s to %s, " "should be the same name\n", __FUNCTION__, - function__name(function, cu), - function__name(twin, di->cu)); + function__name(function), + function__name(twin)); else { char proto[1024], twin_proto[1024]; @@ -446,7 +436,7 @@ static void show_changed_member(char change, const struct class_member *member, tag__assert_search_result(type); printf(" %c%-26s %-21s /* %5u %5zd */\n", change, tag__name(type, cu, bf, sizeof(bf), NULL), - class_member__name(member, cu), + class_member__name(member), member->byte_offset, member->byte_size); } @@ -460,7 +450,7 @@ static void show_nr_members_changes(const struct class *structure, /* Find the removed ones */ type__for_each_member(&structure->type, member) { - struct class_member *twin = class__find_pair_member(new_structure, new_cu, member, cu, &nr_anonymous); + struct class_member *twin = class__find_pair_member(new_structure, member, &nr_anonymous); if (twin == NULL) show_changed_member('-', member, cu); } @@ -468,18 +458,17 @@ static void show_nr_members_changes(const struct class *structure, nr_anonymous = 0; /* Find the new ones */ type__for_each_member(&new_structure->type, member) { - struct class_member *twin = class__find_pair_member(structure, cu, member, new_cu, &nr_anonymous); + struct class_member *twin = class__find_pair_member(structure, member, &nr_anonymous); if (twin == NULL) show_changed_member('+', member, new_cu); } } -static void print_terse_type_changes(struct class *structure, - const struct cu *cu) +static void print_terse_type_changes(struct class *structure) { const char *sep = ""; - printf("struct %s: ", class__name(structure, cu)); + printf("struct %s: ", class__name(structure)); if (terse_type_changes & TCHANGEF__SIZE) { fputs("size", stdout); @@ -549,7 +538,7 @@ static void show_diffs_structure(struct class *structure, printf(" struct %-*.*s | %+4d\n", (int)(cu->max_len_changed_item - sizeof("struct")), (int)(cu->max_len_changed_item - sizeof("struct")), - class__name(structure, cu), diff); + class__name(structure), diff); if (diff != 0) terse_type_changes |= TCHANGEF__SIZE; @@ -595,13 +584,13 @@ static void show_diffs_structure(struct class *structure, new_structure, di->cu, 1); } if (show_terse_type_changes) - print_terse_type_changes(structure, cu); + print_terse_type_changes(structure); } static void show_structure_diffs_iterator(struct class *class, struct cu *cu) { if (class->priv != NULL) { - const char *name = class__name(class, cu); + const char *name = class__name(class); if (!strlist__has_entry(structs_printed, name)) { show_diffs_structure(class, cu); strlist__add(structs_printed, name); @@ -672,17 +661,17 @@ static int cu_show_diffs_iterator(struct cu *cu, void *cookie) return 0; } -static int cu_delete_priv(struct cu *cu, void *cookie __unused) +static int cu_delete_priv(struct cu *cu, void *cookie __maybe_unused) { struct class *c; struct function *f; uint32_t id; cu__for_each_struct(cu, id, c) - free(c->priv); + zfree(&c->priv); cu__for_each_function(cu, id, f) - free(f->priv); + zfree(&f->priv); return 0; } @@ -746,8 +735,8 @@ static const struct argp_option codiff__options[] = { } }; -static error_t codiff__options_parser(int key, char *arg __unused, - struct argp_state *state __unused) +static error_t codiff__options_parser(int key, char *arg __maybe_unused, + struct argp_state *state __maybe_unused) { switch (key) { case 'f': show_function_diffs = 1; break; @@ -789,11 +778,13 @@ failure: goto out; } - if (dwarves__init(0)) { + if (dwarves__init()) { fputs("codiff: insufficient memory\n", stderr); goto out; } + dwarves__resolve_cacheline_size(&conf_load, 0); + if (show_function_diffs == 0 && show_struct_diffs == 0 && show_terse_type_changes == 0) show_function_diffs = show_struct_diffs = 1; @@ -814,7 +805,7 @@ failure: /* If old_file is a character device, leave its cus empty */ if (!S_ISCHR(st.st_mode)) { err = cus__load_file(old_cus, &conf_load, old_filename); - if (err != 0) { + if (err < 0) { cus__print_error_msg("codiff", old_cus, old_filename, err); goto out_cus_delete_priv; } @@ -828,7 +819,7 @@ failure: /* If old_file is a character device, leave its cus empty */ if (!S_ISCHR(st.st_mode)) { err = cus__load_file(new_cus, &conf_load, new_filename); - if (err != 0) { + if (err < 0) { cus__print_error_msg("codiff", new_cus, new_filename, err); goto out_cus_delete_priv; } @@ -837,7 +828,7 @@ failure: cus__for_each_cu(old_cus, cu_diff_iterator, new_cus, NULL); cus__for_each_cu(new_cus, cu_find_new_tags_iterator, old_cus, NULL); cus__for_each_cu(old_cus, cu_show_diffs_iterator, NULL, NULL); - if (new_cus->nr_entries > 1) + if (cus__nr_entries(new_cus) > 1) cus__for_each_cu(new_cus, cu_show_diffs_iterator, (void *)1, NULL); if (total_cus_changed > 1) { diff --git a/ctf_loader.c b/ctf_loader.c index 9f03f09..7c34739 100644 --- a/ctf_loader.c +++ b/ctf_loader.c @@ -24,12 +24,6 @@ #include "dutil.h" #include "dwarves.h" -/* - * FIXME: We should just get the table from the CTF ELF section - * and use it directly - */ -extern struct strings *strings; - static void *tag__alloc(const size_t size) { struct tag *tag = zalloc(size); @@ -82,7 +76,7 @@ static int ctf__load_ftype(struct ctf *ctf, struct ftype *proto, uint16_t tag, return vlen; out_free_parameters: - ftype__delete(proto, ctf->priv); + ftype__delete(proto); return -ENOMEM; } @@ -94,7 +88,7 @@ static struct function *function__new(uint16_t **ptr, GElf_Sym *sym, if (func != NULL) { func->lexblock.ip.addr = elf_sym__value(sym); func->lexblock.size = elf_sym__size(sym); - func->name = sym->st_name; + func->name = elf_sym__name(sym, ctf->symtab); func->vtable_entry = -1; func->external = elf_sym__bind(sym) == STB_GLOBAL; INIT_LIST_HEAD(&func->vtable_node); @@ -150,7 +144,7 @@ static int ctf__load_funcs(struct ctf *ctf) return 0; } -static struct base_type *base_type__new(strings_t name, uint32_t attrs, +static struct base_type *base_type__new(const char *name, uint32_t attrs, uint8_t float_type, size_t size) { struct base_type *bt = tag__alloc(sizeof(*bt)); @@ -167,18 +161,16 @@ static struct base_type *base_type__new(strings_t name, uint32_t attrs, return bt; } -static void type__init(struct type *type, uint16_t tag, - strings_t name, size_t size) +static void type__init(struct type *type, uint16_t tag, const char *name, size_t size) { __type__init(type); INIT_LIST_HEAD(&type->namespace.tags); type->size = size; type->namespace.tag.tag = tag; type->namespace.name = name; - type->namespace.sname = 0; } -static struct type *type__new(uint16_t tag, strings_t name, size_t size) +static struct type *type__new(uint16_t tag, const char *name, size_t size) { struct type *type = tag__alloc(sizeof(*type)); @@ -188,7 +180,7 @@ static struct type *type__new(uint16_t tag, strings_t name, size_t size) return type; } -static struct class *class__new(strings_t name, size_t size) +static struct class *class__new(const char *name, size_t size) { struct class *class = tag__alloc(sizeof(*class)); @@ -206,8 +198,8 @@ static int create_new_base_type(struct ctf *ctf, void *ptr, uint32_t *enc = ptr; uint32_t eval = ctf__get32(ctf, enc); uint32_t attrs = CTF_TYPE_INT_ATTRS(eval); - strings_t name = ctf__get32(ctf, &tp->base.ctf_name); - struct base_type *base = base_type__new(name, attrs, 0, + uint32_t name = ctf__get32(ctf, &tp->base.ctf_name); + struct base_type *base = base_type__new(ctf__string(ctf, name), attrs, 0, CTF_TYPE_INT_BITS(eval)); if (base == NULL) return -ENOMEM; @@ -222,9 +214,9 @@ static int create_new_base_type_float(struct ctf *ctf, void *ptr, struct ctf_full_type *tp, uint32_t id) { - strings_t name = ctf__get32(ctf, &tp->base.ctf_name); + uint32_t name = ctf__get32(ctf, &tp->base.ctf_name); uint32_t *enc = ptr, eval = ctf__get32(ctf, enc); - struct base_type *base = base_type__new(name, 0, eval, + struct base_type *base = base_type__new(ctf__string(ctf, name), 0, eval, CTF_TYPE_FP_BITS(eval)); if (base == NULL) return -ENOMEM; @@ -292,7 +284,7 @@ static int create_full_members(struct ctf *ctf, void *ptr, member->tag.tag = DW_TAG_member; member->tag.type = ctf__get16(ctf, &mp[i].ctf_member_type); - member->name = ctf__get32(ctf, &mp[i].ctf_member_name); + member->name = ctf__string(ctf, ctf__get32(ctf, &mp[i].ctf_member_name)); member->bit_offset = (ctf__get32(ctf, &mp[i].ctf_member_offset_high) << 16) | ctf__get32(ctf, &mp[i].ctf_member_offset_low); /* sizes and offsets will be corrected at class__fixup_ctf_bitfields */ @@ -316,7 +308,7 @@ static int create_short_members(struct ctf *ctf, void *ptr, member->tag.tag = DW_TAG_member; member->tag.type = ctf__get16(ctf, &mp[i].ctf_member_type); - member->name = ctf__get32(ctf, &mp[i].ctf_member_name); + member->name = ctf__string(ctf, ctf__get32(ctf, &mp[i].ctf_member_name)); member->bit_offset = ctf__get16(ctf, &mp[i].ctf_member_offset); /* sizes and offsets will be corrected at class__fixup_ctf_bitfields */ @@ -331,7 +323,7 @@ static int create_new_class(struct ctf *ctf, void *ptr, uint64_t size, uint32_t id) { int member_size; - strings_t name = ctf__get32(ctf, &tp->base.ctf_name); + const char *name = ctf__string(ctf, ctf__get32(ctf, &tp->base.ctf_name)); struct class *class = class__new(name, size); if (size >= CTF_SHORT_MEMBER_LIMIT) { @@ -347,7 +339,7 @@ static int create_new_class(struct ctf *ctf, void *ptr, return (vlen * member_size); out_free: - class__delete(class, ctf->priv); + class__delete(class); return -ENOMEM; } @@ -356,7 +348,7 @@ static int create_new_union(struct ctf *ctf, void *ptr, uint64_t size, uint32_t id) { int member_size; - strings_t name = ctf__get32(ctf, &tp->base.ctf_name); + const char *name = ctf__string(ctf, ctf__get32(ctf, &tp->base.ctf_name)); struct type *un = type__new(DW_TAG_union_type, name, size); if (size >= CTF_SHORT_MEMBER_LIMIT) { @@ -372,11 +364,11 @@ static int create_new_union(struct ctf *ctf, void *ptr, return (vlen * member_size); out_free: - type__delete(un, ctf->priv); + type__delete(un); return -ENOMEM; } -static struct enumerator *enumerator__new(strings_t name, uint32_t value) +static struct enumerator *enumerator__new(const char *name, uint32_t value) { struct enumerator *en = tag__alloc(sizeof(*en)); @@ -395,16 +387,14 @@ static int create_new_enumeration(struct ctf *ctf, void *ptr, { struct ctf_enum *ep = ptr; uint16_t i; - struct type *enumeration = type__new(DW_TAG_enumeration_type, - ctf__get32(ctf, - &tp->base.ctf_name), - size ?: (sizeof(int) * 8)); + const char *name = ctf__string(ctf, ctf__get32(ctf, &tp->base.ctf_name)); + struct type *enumeration = type__new(DW_TAG_enumeration_type, name, size ?: (sizeof(int) * 8)); if (enumeration == NULL) return -ENOMEM; for (i = 0; i < vlen; i++) { - strings_t name = ctf__get32(ctf, &ep[i].ctf_enum_name); + const char *name = ctf__string(ctf, ctf__get32(ctf, &ep[i].ctf_enum_name)); uint32_t value = ctf__get32(ctf, &ep[i].ctf_enum_val); struct enumerator *enumerator = enumerator__new(name, value); @@ -418,14 +408,14 @@ static int create_new_enumeration(struct ctf *ctf, void *ptr, return (vlen * sizeof(*ep)); out_free: - enumeration__delete(enumeration, ctf->priv); + enumeration__delete(enumeration); return -ENOMEM; } static int create_new_forward_decl(struct ctf *ctf, struct ctf_full_type *tp, uint64_t size, uint32_t id) { - strings_t name = ctf__get32(ctf, &tp->base.ctf_name); + const char *name = ctf__string(ctf, ctf__get32(ctf, &tp->base.ctf_name)); struct class *fwd = class__new(name, size); if (fwd == NULL) @@ -438,7 +428,7 @@ static int create_new_forward_decl(struct ctf *ctf, struct ctf_full_type *tp, static int create_new_typedef(struct ctf *ctf, struct ctf_full_type *tp, uint64_t size, uint32_t id) { - strings_t name = ctf__get32(ctf, &tp->base.ctf_name); + const char *name = ctf__string(ctf, ctf__get32(ctf, &tp->base.ctf_name)); unsigned int type_id = ctf__get16(ctf, &tp->base.ctf_type); struct type *type = type__new(DW_TAG_typedef, name, size); @@ -560,7 +550,7 @@ static struct variable *variable__new(uint16_t type, GElf_Sym *sym, if (var != NULL) { var->scope = VSCOPE_GLOBAL; var->ip.addr = elf_sym__value(sym); - var->name = sym->st_name; + var->name = ctf->symtab->symstrs->d_buf + sym->st_name; var->external = elf_sym__bind(sym) == STB_GLOBAL; var->ip.tag.tag = DW_TAG_variable; var->ip.tag.type = type; @@ -642,8 +632,7 @@ static int class__fixup_ctf_bitfields(struct tag *tag, struct cu *cu) integral_bit_size = base_type__name_to_size(bt, cu); if (integral_bit_size == 0) fprintf(stderr, "%s: unknown base type name \"%s\"!\n", - __func__, base_type__name(bt, cu, name, - sizeof(name))); + __func__, base_type__name(bt, name, sizeof(name))); } break; default: @@ -691,33 +680,12 @@ static int cu__fixup_ctf_bitfields(struct cu *cu) return err; } -static const char *ctf__function_name(struct function *func, - const struct cu *cu) -{ - struct ctf *ctf = cu->priv; - - return ctf->symtab->symstrs->d_buf + func->name; -} - -static const char *ctf__variable_name(const struct variable *var, - const struct cu *cu) -{ - struct ctf *ctf = cu->priv; - - return ctf->symtab->symstrs->d_buf + var->name; -} - static void ctf__cu_delete(struct cu *cu) { ctf__delete(cu->priv); cu->priv = NULL; } -static const char *ctf__strings_ptr(const struct cu *cu, strings_t s) -{ - return ctf__string(cu->priv, s); -} - struct debug_fmt_ops ctf__ops; int ctf__load_file(struct cus *cus, struct conf_load *conf, @@ -729,7 +697,7 @@ int ctf__load_file(struct cus *cus, struct conf_load *conf, if (state == NULL) return -1; - struct cu *cu = cu__new(filename, state->wordsize, NULL, 0, filename); + struct cu *cu = cu__new(filename, state->wordsize, NULL, 0, filename, false); if (cu == NULL) return -1; @@ -763,9 +731,6 @@ int ctf__load_file(struct cus *cus, struct conf_load *conf, struct debug_fmt_ops ctf__ops = { .name = "ctf", - .function__name = ctf__function_name, .load_file = ctf__load_file, - .variable__name = ctf__variable_name, - .strings__ptr = ctf__strings_ptr, .cu__delete = ctf__cu_delete, }; @@ -130,7 +130,7 @@ static LIST_HEAD(pointers); static const char *structure__name(const struct structure *st) { - return class__name(tag__class(st->class), st->cu); + return class__name(tag__class(st->class)); } static struct structure *structures__find(struct list_head *list, const char *name) @@ -198,7 +198,7 @@ static struct function *function__filter(struct function *function, function->abstract_origin != 0 || !list_empty(&function->tool_node) || !ftype__has_parm_of_type(&function->proto, target_type_id, cu) || - strlist__has_entry(init_blacklist, function__name(function, cu))) { + strlist__has_entry(init_blacklist, function__name(function))) { return NULL; } @@ -309,15 +309,14 @@ static struct class_member *class__remove_member(struct class *class, const stru return list_entry(next, struct class_member, tag.node); } -static size_t class__find_biggest_member_name(const struct class *class, - const struct cu *cu) +static size_t class__find_biggest_member_name(const struct class *class) { struct class_member *pos; size_t biggest_name_len = 0; type__for_each_data_member(&class->type, pos) { const size_t len = pos->name ? - strlen(class_member__name(pos, cu)) : 0; + strlen(class_member__name(pos)) : 0; if (len > biggest_name_len) biggest_name_len = len; @@ -326,23 +325,21 @@ static size_t class__find_biggest_member_name(const struct class *class, return biggest_name_len; } -static void class__emit_class_state_collector(struct class *class, - const struct cu *cu, - struct class *clone) +static void class__emit_class_state_collector(struct class *class, struct class *clone) { struct class_member *pos; - int len = class__find_biggest_member_name(clone, cu); + int len = class__find_biggest_member_name(clone); fprintf(fp_collector, "void ctracer__class_state(const void *from, void *to)\n" "{\n" "\tconst struct %s *obj = from;\n" "\tstruct %s *mini_obj = to;\n\n", - class__name(class, cu), class__name(clone, cu)); + class__name(class), class__name(clone)); type__for_each_data_member(&clone->type, pos) fprintf(fp_collector, "\tmini_obj->%-*s = obj->%s;\n", len, - class_member__name(pos, cu), - class_member__name(pos, cu)); + class_member__name(pos), + class_member__name(pos)); fputs("}\n\n", fp_collector); } @@ -352,7 +349,7 @@ static struct class *class__clone_base_types(const struct tag *tag, { struct class *class = tag__class(tag); struct class_member *pos, *next; - struct class *clone = class__clone(class, new_class_name, cu); + struct class *clone = class__clone(class, new_class_name); if (clone == NULL) return NULL; @@ -363,7 +360,7 @@ static struct class *class__clone_base_types(const struct tag *tag, tag__assert_search_result(member_type); if (!tag__is_base_type(member_type, cu)) { next = class__remove_member(clone, cu, pos); - class_member__delete(pos, cu); + class_member__delete(pos); } } class__fixup_alignment(clone, cu); @@ -390,20 +387,19 @@ static void emit_struct_member_table_entry(FILE *fp, * ostra-cg to preprocess the raw data collected from the debugfs/relay * channel. */ -static int class__emit_ostra_converter(struct tag *tag, - const struct cu *cu) +static int class__emit_ostra_converter(struct tag *tag) { struct class *class = tag__class(tag); struct class_member *pos; struct type *type = &mini_class->type; int field = 0, first = 1; char filename[128]; - char parm_list[1024]; + char parm_list[1024] = ""; char *p = parm_list; size_t n; size_t plen = sizeof(parm_list); FILE *fp_fields, *fp_converter; - const char *name = class__name(class, cu); + const char *name = class__name(class); snprintf(filename, sizeof(filename), "%s/%s.fields", src_dir, name); fp_fields = fopen(filename, "w"); @@ -461,10 +457,10 @@ static int class__emit_ostra_converter(struct tag *tag, plen -= n; p += n; } fprintf(fp_converter, "%%u"); - n = snprintf(p, plen, "obj.%s", class_member__name(pos, cu)); + n = snprintf(p, plen, "obj.%s", class_member__name(pos)); plen -= n; p += n; emit_struct_member_table_entry(fp_fields, field++, - class_member__name(pos, cu), + class_member__name(pos), 1, "entry,exit"); } fprintf(fp_converter, @@ -495,7 +491,7 @@ static struct tag *pointer_filter(struct tag *tag, struct cu *cu, if (type->nr_members == 0) return NULL; - class_name = class__name(tag__class(tag), cu); + class_name = class__name(tag__class(tag)); if (class_name == NULL || structures__find(&pointers, class_name)) return NULL; @@ -539,8 +535,7 @@ static void class__find_pointers(const char *class_name) * We want just the DW_TAG_structure_type tags that have as its first member * a struct of type target. */ -static struct tag *alias_filter(struct tag *tag, const struct cu *cu, - type_id_t target_type_id) +static struct tag *alias_filter(struct tag *tag, type_id_t target_type_id) { struct type *type; struct class_member *first_member; @@ -557,7 +552,7 @@ static struct tag *alias_filter(struct tag *tag, const struct cu *cu, if (first_member->tag.type != target_type_id) return NULL; - if (structures__find(&aliases, class__name(tag__class(tag), cu))) + if (structures__find(&aliases, class__name(tag__class(tag)))) return NULL; return tag; @@ -578,8 +573,8 @@ static int cu_find_aliases_iterator(struct cu *cu, void *class_name) return 0; cu__for_each_type(cu, id, pos) { - if (alias_filter(pos, cu, target_type_id)) { - const char *alias_name = class__name(tag__class(pos), cu); + if (alias_filter(pos, target_type_id)) { + const char *alias_name = class__name(tag__class(pos)); structures__add(&aliases, pos, cu); @@ -607,7 +602,7 @@ static void class__find_aliases(const char *class_name) cus__for_each_cu(methods_cus, cu_find_aliases_iterator, (void *)class_name, cu_filter); } -static void emit_list_of_types(struct list_head *list, const struct cu *cu) +static void emit_list_of_types(struct list_head *list) { struct structure *pos; @@ -617,8 +612,7 @@ static void emit_list_of_types(struct list_head *list, const struct cu *cu) * Lets look at the other CUs, perhaps we have already * emmited this one */ - if (type_emissions__find_definition(&emissions, cu, - structure__name(pos))) { + if (type_emissions__find_definition(&emissions, structure__name(pos))) { type->definition_emitted = 1; continue; } @@ -638,7 +632,7 @@ static int class__emit_classes(struct tag *tag, struct cu *cu) char mini_class_name[128]; snprintf(mini_class_name, sizeof(mini_class_name), "ctracer__mini_%s", - class__name(class, cu)); + class__name(class)); mini_class = class__clone_base_types(tag, cu, mini_class_name); if (mini_class == NULL) @@ -649,15 +643,15 @@ static int class__emit_classes(struct tag *tag, struct cu *cu) type__emit(tag, cu, NULL, NULL, fp_classes); fputs("\n/* class aliases */\n\n", fp_classes); - emit_list_of_types(&aliases, cu); + emit_list_of_types(&aliases); fputs("\n/* class with pointers */\n\n", fp_classes); - emit_list_of_types(&pointers, cu); + emit_list_of_types(&pointers); class__fprintf(mini_class, cu, fp_classes); fputs(";\n\n", fp_classes); - class__emit_class_state_collector(class, cu, mini_class); + class__emit_class_state_collector(class, mini_class); err = 0; out: return err; @@ -676,7 +670,7 @@ static int function__emit_probes(struct function *func, uint32_t function_id, const char *member) { struct parameter *pos; - const char *name = function__name(func, cu); + const char *name = function__name(func); fprintf(fp_methods, "probe %s%s = kernel.function(\"%s@%s\")%s\n" "{\n" @@ -698,14 +692,13 @@ static int function__emit_probes(struct function *func, uint32_t function_id, continue; if (member != NULL) - fprintf(fp_methods, "\tif ($%s)\n\t", - parameter__name(pos, cu)); + fprintf(fp_methods, "\tif ($%s)\n\t", parameter__name(pos)); fprintf(fp_methods, "\tctracer__method_hook(%d, %d, $%s%s%s, %d);\n", probe_type, function_id, - parameter__name(pos, cu), + parameter__name(pos), member ? "->" : "", member ?: "", class__size(mini_class)); break; @@ -734,7 +727,7 @@ static int cu_emit_probes_iterator(struct cu *cu, void *cookie) list_for_each_entry(pos, &cu->tool_list, tool_node) { uint32_t function_id = (long)pos->priv; - if (methods__add(&probes_emitted, function__name(pos, cu)) != 0) + if (methods__add(&probes_emitted, function__name(pos)) != 0) continue; function__emit_probes(pos, function_id, cu, target_type_id, 0, NULL); /* entry */ function__emit_probes(pos, function_id, cu, target_type_id, 1, NULL); /* exit */ @@ -777,13 +770,13 @@ static int cu_emit_pointer_probes_iterator(struct cu *cu, void *cookie) list_for_each_entry(pos_tag, &cu->tool_list, tool_node) { uint32_t function_id = (long)pos_tag->priv; - if (methods__add(&probes_emitted, function__name(pos_tag, cu)) != 0) + if (methods__add(&probes_emitted, function__name(pos_tag)) != 0) continue; function__emit_probes(pos_tag, function_id, cu, target_type_id, 0, - class_member__name(pos_member, cu)); /* entry */ + class_member__name(pos_member)); /* entry */ function__emit_probes(pos_tag, function_id, cu, target_type_id, 1, - class_member__name(pos_member, cu)); /* exit */ + class_member__name(pos_member)); /* exit */ } return 0; @@ -801,8 +794,7 @@ static int cu_emit_functions_table(struct cu *cu, void *fp) list_for_each_entry(pos, &cu->tool_list, tool_node) if (pos->priv != NULL) { uint32_t function_id = (long)pos->priv; - fprintf(fp, "%d:%s\n", function_id, - function__name(pos, cu)); + fprintf(fp, "%d:%s\n", function_id, function__name(pos)); pos->priv = NULL; } @@ -830,20 +822,13 @@ static int elf__open(const char *filename) goto out_close; } - GElf_Ehdr ehdr; - if (gelf_getehdr(elf, &ehdr) == NULL) { - fprintf(stderr, "%s: cannot get elf header.\n", __func__); - goto out_elf_end; - } - GElf_Shdr shdr; size_t init_index; - Elf_Scn *init = elf_section_by_name(elf, &ehdr, &shdr, ".init.text", - &init_index); + Elf_Scn *init = elf_section_by_name(elf, &shdr, ".init.text", &init_index); if (init == NULL) goto out_elf_end; - struct elf_symtab *symtab = elf_symtab__new(".symtab", elf, &ehdr); + struct elf_symtab *symtab = elf_symtab__new(".symtab", elf); if (symtab == NULL) goto out_elf_end; @@ -920,7 +905,7 @@ static const char *dirname, *glob; static int recursive; static error_t ctracer__options_parser(int key, char *arg, - struct argp_state *state __unused) + struct argp_state *state __maybe_unused) { switch (key) { case 'd': src_dir = arg; break; @@ -955,10 +940,11 @@ int main(int argc, char *argv[]) FILE *fp_functions; int rc = EXIT_FAILURE; - if (dwarves__init(0)) { + if (dwarves__init()) { fputs("ctracer: insufficient memory\n", stderr); goto out; } + dwarves__resolve_cacheline_size(NULL, 0); if (argp_parse(&ctracer__argp, argc, argv, 0, &remaining, NULL) || remaining < argc) { @@ -1028,7 +1014,7 @@ failure: } snprintf(functions_filename, sizeof(functions_filename), - "%s/%s.functions", src_dir, class__name(tag__class(class), cu)); + "%s/%s.functions", src_dir, class__name(tag__class(class))); fp_functions = fopen(functions_filename, "w"); if (fp_functions == NULL) { fprintf(stderr, "ctracer: couldn't create %s\n", @@ -1081,7 +1067,7 @@ failure: class__emit_classes(class, cu); fputc('\n', fp_collector); - class__emit_ostra_converter(class, cu); + class__emit_ostra_converter(class); cu_blacklist = strlist__new(true); if (cu_blacklist != NULL) diff --git a/dtagnames.c b/dtagnames.c index 0ffcbf7..343f055 100644 --- a/dtagnames.c +++ b/dtagnames.c @@ -7,26 +7,18 @@ #include <stdio.h> #include <stdlib.h> -#include <malloc.h> #include "dwarves.h" #include "dutil.h" -static void print_malloc_stats(void) -{ - struct mallinfo m = mallinfo(); - - fprintf(stderr, "size: %u\n", m.uordblks); -} - -static int class__tag_name(struct tag *tag, struct cu *cu __unused, - void *cookie __unused) +static int class__tag_name(struct tag *tag, struct cu *cu __maybe_unused, + void *cookie __maybe_unused) { puts(dwarf_tag_name(tag->tag)); return 0; } -static int cu__dump_class_tag_names(struct cu *cu, void *cookie __unused) +static int cu__dump_class_tag_names(struct cu *cu, void *cookie __maybe_unused) { cu__for_all_tags(cu, class__tag_name, NULL); return 0; @@ -37,15 +29,16 @@ static void cus__dump_class_tag_names(struct cus *cus) cus__for_each_cu(cus, cu__dump_class_tag_names, NULL, NULL); } -int main(int argc __unused, char *argv[]) +int main(int argc __maybe_unused, char *argv[]) { int err, rc = EXIT_FAILURE; struct cus *cus = cus__new(); - if (dwarves__init(0) || cus == NULL) { + if (dwarves__init() || cus == NULL) { fputs("dtagnames: insufficient memory\n", stderr); goto out; } + dwarves__resolve_cacheline_size(NULL, 0); err = cus__load_files(cus, NULL, argv + 1); if (err != 0) { @@ -54,7 +47,6 @@ int main(int argc __unused, char *argv[]) } cus__dump_class_tag_names(cus); - print_malloc_stats(); rc = EXIT_SUCCESS; out: cus__delete(cus); @@ -13,12 +13,15 @@ #include <stdlib.h> #include <string.h> -void *zalloc(const size_t size) +void *zalloc(size_t size) { - void *s = malloc(size); - if (s != NULL) - memset(s, 0, size); - return s; + return calloc(1, size); +} + +void __zfree(void **ptr) +{ + free(*ptr); + *ptr = NULL; } struct str_node *str_node__new(const char *s, bool dupstr) @@ -43,8 +46,11 @@ out_delete: static void str_node__delete(struct str_node *snode, bool dupstr) { + if (snode == NULL) + return; + if (dupstr) - free((void *)snode->s); + zfree(&snode->s); free(snode); } @@ -174,17 +180,22 @@ bool strlist__has_entry(struct strlist *slist, const char *entry) return false; } -Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, - GElf_Shdr *shp, const char *name, size_t *index) +Elf_Scn *elf_section_by_name(Elf *elf, GElf_Shdr *shp, const char *name, size_t *index) { Elf_Scn *sec = NULL; size_t cnt = 1; + size_t str_idx; + + if (elf_getshdrstrndx(elf, &str_idx)) + return NULL; while ((sec = elf_nextscn(elf, sec)) != NULL) { char *str; gelf_getshdr(sec, shp); - str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); + str = elf_strptr(elf, str_idx, shp->sh_name); + if (!str) + return NULL; if (!strcmp(name, str)) { if (index) *index = cnt; @@ -10,6 +10,7 @@ */ #include <stdbool.h> +#include <linux/stddef.h> #include <stddef.h> #include <string.h> #include <elf.h> @@ -20,8 +21,8 @@ #define BITS_PER_LONG __BITS_PER_LONG -#ifndef __unused -#define __unused __attribute__ ((unused)) +#ifndef __maybe_unused +#define __maybe_unused __attribute__((__unused__)) #endif #ifndef __pure @@ -30,6 +31,10 @@ #define roundup(x,y) ((((x) + ((y) - 1)) / (y)) * (y)) +#ifndef DW_TAG_LLVM_annotation +#define DW_TAG_LLVM_annotation 0x6000 +#endif + static inline __attribute__((const)) bool is_power_of_2(unsigned long n) { return (n != 0 && ((n & (n - 1)) == 0)); @@ -321,8 +326,7 @@ static inline bool strstarts(const char *str, const char *prefix) void *zalloc(const size_t size); -Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, - GElf_Shdr *shp, const char *name, size_t *index); +Elf_Scn *elf_section_by_name(Elf *elf, GElf_Shdr *shp, const char *name, size_t *index); Elf_Scn *elf_section_by_idx(Elf *elf, GElf_Shdr *shp, int idx); @@ -336,4 +340,8 @@ static inline int elf_getshdrstrndx(Elf *elf, size_t *dst) char *strlwr(char *s); +void __zfree(void **ptr); + +#define zfree(ptr) __zfree((void **)(ptr)) + #endif /* _DUTIL_H_ */ diff --git a/dwarf_loader.c b/dwarf_loader.c index 4638df7..e30b03c 100644 --- a/dwarf_loader.c +++ b/dwarf_loader.c @@ -12,7 +12,8 @@ #include <fcntl.h> #include <fnmatch.h> #include <libelf.h> -#include <obstack.h> +#include <limits.h> +#include <pthread.h> #include <search.h> #include <stdio.h> #include <stdlib.h> @@ -23,11 +24,8 @@ #include "list.h" #include "dwarves.h" #include "dutil.h" -#include "pahole_strings.h" #include "hash.h" -struct strings *strings; - #ifndef DW_AT_alignment #define DW_AT_alignment 0x88 #endif @@ -41,7 +39,28 @@ struct strings *strings; #define DW_TAG_GNU_call_site_parameter 0x410a #endif -#define hashtags__fn(key) hash_64(key, HASHTAGS__BITS) +#ifndef DW_TAG_call_site +#define DW_TAG_call_site 0x48 +#define DW_TAG_call_site_parameter 0x49 +#endif + +#ifndef DW_FORM_implicit_const +#define DW_FORM_implicit_const 0x21 +#endif + +#ifndef DW_OP_addrx +#define DW_OP_addrx 0xa1 +#endif + +static pthread_mutex_t libdw__lock = PTHREAD_MUTEX_INITIALIZER; + +static uint32_t hashtags__bits = 12; +static uint32_t max_hashtags__bits = 21; + +static uint32_t hashtags__fn(Dwarf_Off key) +{ + return hash_64(key, hashtags__bits); +} bool no_bitfield_type_recode = true; @@ -79,8 +98,8 @@ struct dwarf_tag { }; struct tag *tag; uint32_t small_id; - strings_t decl_file; uint16_t decl_line; + const char *decl_file; }; static dwarf_off_ref dwarf_tag__spec(struct dwarf_tag *dtag) @@ -93,40 +112,77 @@ static void dwarf_tag__set_spec(struct dwarf_tag *dtag, dwarf_off_ref spec) *(dwarf_off_ref *)(dtag + 1) = spec; } -#define HASHTAGS__BITS 15 -#define HASHTAGS__SIZE (1UL << HASHTAGS__BITS) - -#define obstack_chunk_alloc malloc -#define obstack_chunk_free free - -static void *obstack_zalloc(struct obstack *obstack, size_t size) -{ - void *o = obstack_alloc(obstack, size); - - if (o) - memset(o, 0, size); - return o; -} - struct dwarf_cu { - struct hlist_head hash_tags[HASHTAGS__SIZE]; - struct hlist_head hash_types[HASHTAGS__SIZE]; - struct obstack obstack; + struct hlist_head *hash_tags; + struct hlist_head *hash_types; + struct dwarf_tag *last_type_lookup; struct cu *cu; struct dwarf_cu *type_unit; }; -static void dwarf_cu__init(struct dwarf_cu *dcu) +static int dwarf_cu__init(struct dwarf_cu *dcu, struct cu *cu) { + static struct dwarf_tag sentinel_dtag = { .id = ULLONG_MAX, }; + uint64_t hashtags_size = 1UL << hashtags__bits; + + dcu->cu = cu; + + dcu->hash_tags = cu__malloc(cu, sizeof(struct hlist_head) * hashtags_size); + if (!dcu->hash_tags) + return -ENOMEM; + + dcu->hash_types = cu__malloc(cu, sizeof(struct hlist_head) * hashtags_size); + if (!dcu->hash_types) { + cu__free(cu, dcu->hash_tags); + return -ENOMEM; + } + unsigned int i; - for (i = 0; i < HASHTAGS__SIZE; ++i) { + for (i = 0; i < hashtags_size; ++i) { INIT_HLIST_HEAD(&dcu->hash_tags[i]); INIT_HLIST_HEAD(&dcu->hash_types[i]); } - obstack_init(&dcu->obstack); dcu->type_unit = NULL; + // To avoid a per-lookup check against NULL in dwarf_cu__find_type_by_ref() + dcu->last_type_lookup = &sentinel_dtag; + return 0; +} + +static struct dwarf_cu *dwarf_cu__new(struct cu *cu) +{ + struct dwarf_cu *dwarf_cu = cu__zalloc(cu, sizeof(*dwarf_cu)); + + if (dwarf_cu != NULL && dwarf_cu__init(dwarf_cu, cu) != 0) { + cu__free(cu, dwarf_cu); + dwarf_cu = NULL; + } + + return dwarf_cu; +} + +static void dwarf_cu__delete(struct cu *cu) +{ + if (cu == NULL || cu->priv == NULL) + return; + + struct dwarf_cu *dcu = cu->priv; + + // dcu->hash_tags & dcu->hash_types are on cu->obstack + cu__free(cu, dcu); + cu->priv = NULL; +} + +static void __tag__print_type_not_found(struct tag *tag, const char *func) +{ + struct dwarf_tag *dtag = tag->priv; + fprintf(stderr, "%s: couldn't find %#llx type for %#llx (%s)!\n", func, + (unsigned long long)dtag->type.off, (unsigned long long)dtag->id, + dwarf_tag_name(tag->tag)); } +#define tag__print_type_not_found(tag) \ + __tag__print_type_not_found(tag, __func__) + static void hashtags__hash(struct hlist_head *hashtable, struct dwarf_tag *dtag) { @@ -142,7 +198,7 @@ static struct dwarf_tag *hashtags__find(const struct hlist_head *hashtable, struct dwarf_tag *tpos; struct hlist_node *pos; - uint16_t bucket = hashtags__fn(id); + uint32_t bucket = hashtags__fn(id); const struct hlist_head *head = hashtable + bucket; hlist_for_each_entry(tpos, pos, head, hash_node) { @@ -173,7 +229,7 @@ static struct dwarf_tag *dwarf_cu__find_tag_by_ref(const struct dwarf_cu *cu, return hashtags__find(cu->hash_tags, ref->off); } -static struct dwarf_tag *dwarf_cu__find_type_by_ref(const struct dwarf_cu *dcu, +static struct dwarf_tag *dwarf_cu__find_type_by_ref(struct dwarf_cu *dcu, const struct dwarf_off_ref *ref) { if (dcu == NULL) @@ -184,14 +240,21 @@ static struct dwarf_tag *dwarf_cu__find_type_by_ref(const struct dwarf_cu *dcu, return NULL; } } - return hashtags__find(dcu->hash_types, ref->off); -} -extern struct strings *strings; + if (dcu->last_type_lookup->id == ref->off) + return dcu->last_type_lookup; + + struct dwarf_tag *dtag = hashtags__find(dcu->hash_types, ref->off); + + if (dtag) + dcu->last_type_lookup = dtag; + + return dtag; +} static void *memdup(const void *src, size_t len, struct cu *cu) { - void *s = obstack_alloc(&cu->obstack, len); + void *s = cu__malloc(cu, len); if (s != NULL) memcpy(s, src, len); return s; @@ -248,6 +311,7 @@ static uint64_t attr_numeric(Dwarf_Die *die, uint32_t name) return addr; } break; + case DW_FORM_implicit_const: case DW_FORM_data1: case DW_FORM_data2: case DW_FORM_data4: @@ -274,7 +338,12 @@ static uint64_t attr_numeric(Dwarf_Die *die, uint32_t name) return 0; } -static uint64_t dwarf_expr(const uint8_t *expr, uint32_t len __unused) +static uint64_t attr_alignment(Dwarf_Die *die, struct conf_load *conf) +{ + return conf->ignore_alignment_attr ? 0 : attr_numeric(die, DW_AT_alignment); +} + +static uint64_t dwarf_expr(const uint8_t *expr, uint32_t len __maybe_unused) { /* Common case: offset from start of the class */ if (expr[0] == DW_OP_plus_uconst || @@ -290,15 +359,12 @@ static uint64_t dwarf_expr(const uint8_t *expr, uint32_t len __unused) return UINT64_MAX; } -static Dwarf_Off attr_offset(Dwarf_Die *die, const uint32_t name) +static Dwarf_Off __attr_offset(Dwarf_Attribute *attr) { - Dwarf_Attribute attr; Dwarf_Block block; - if (dwarf_attr(die, name, &attr) == NULL) - return 0; - - switch (dwarf_whatform(&attr)) { + switch (dwarf_whatform(attr)) { + case DW_FORM_implicit_const: case DW_FORM_data1: case DW_FORM_data2: case DW_FORM_data4: @@ -306,24 +372,41 @@ static Dwarf_Off attr_offset(Dwarf_Die *die, const uint32_t name) case DW_FORM_sdata: case DW_FORM_udata: { Dwarf_Word value; - if (dwarf_formudata(&attr, &value) == 0) + if (dwarf_formudata(attr, &value) == 0) return value; break; } default: - if (dwarf_formblock(&attr, &block) == 0) + if (dwarf_formblock(attr, &block) == 0) return dwarf_expr(block.data, block.length); } return 0; } -static const char *attr_string(Dwarf_Die *die, uint32_t name) +static Dwarf_Off attr_offset(Dwarf_Die *die, const uint32_t name) { Dwarf_Attribute attr; - if (dwarf_attr(die, name, &attr) != NULL) - return dwarf_formstring(&attr); - return NULL; + + if (dwarf_attr(die, name, &attr) == NULL) + return 0; + + return __attr_offset(&attr); +} + +static const char *attr_string(Dwarf_Die *die, uint32_t name, struct conf_load *conf __maybe_unused) +{ + const char *str = NULL; + Dwarf_Attribute attr; + + if (dwarf_attr(die, name, &attr) != NULL) { + str = dwarf_formstring(&attr); + + if (conf && conf->kabi_prefix && str && strncmp(str, conf->kabi_prefix, conf->kabi_prefix_len) == 0) + return conf->kabi_prefix; + } + + return str; } static struct dwarf_off_ref attr_type(Dwarf_Die *die, uint32_t attr_name) @@ -346,8 +429,19 @@ static int attr_location(Dwarf_Die *die, Dwarf_Op **expr, size_t *exprlen) { Dwarf_Attribute attr; if (dwarf_attr(die, DW_AT_location, &attr) != NULL) { - if (dwarf_getlocation(&attr, expr, exprlen) == 0) + if (dwarf_getlocation(&attr, expr, exprlen) == 0) { + /* DW_OP_addrx needs additional lookup for real addr. */ + if (*exprlen != 0 && expr[0]->atom == DW_OP_addrx) { + Dwarf_Attribute addr_attr; + dwarf_getlocation_attr(&attr, expr[0], &addr_attr); + + Dwarf_Addr address; + dwarf_formaddr (&addr_attr, &address); + + expr[0]->number = address; + } return 0; + } } return 1; @@ -355,13 +449,12 @@ static int attr_location(Dwarf_Die *die, Dwarf_Op **expr, size_t *exprlen) static void *__tag__alloc(struct dwarf_cu *dcu, size_t size, bool spec) { - struct dwarf_tag *dtag = obstack_zalloc(&dcu->obstack, - (sizeof(*dtag) + - (spec ? sizeof(dwarf_off_ref) : 0))); + struct dwarf_tag *dtag = cu__zalloc(dcu->cu, (sizeof(*dtag) + (spec ? sizeof(dwarf_off_ref) : 0))); + if (dtag == NULL) return NULL; - struct tag *tag = obstack_zalloc(&dcu->cu->obstack, size); + struct tag *tag = cu__zalloc(dcu->cu, size); if (tag == NULL) return NULL; @@ -402,19 +495,22 @@ static void tag__init(struct tag *tag, struct cu *cu, Dwarf_Die *die) tag->recursivity_level = 0; if (cu->extra_dbg_info) { + pthread_mutex_lock(&libdw__lock); + int32_t decl_line; const char *decl_file = dwarf_decl_file(die); - static const char *last_decl_file; - static uint32_t last_decl_file_idx; + static const char *last_decl_file, *last_decl_file_ptr; - if (decl_file != last_decl_file) { - last_decl_file_idx = strings__add(strings, decl_file); - last_decl_file = decl_file; + if (decl_file != last_decl_file_ptr) { + last_decl_file = decl_file ? strdup(decl_file) : NULL; + last_decl_file_ptr = decl_file; } - dtag->decl_file = last_decl_file_idx; + dtag->decl_file = last_decl_file; dwarf_decl_line(die, &decl_line); dtag->decl_line = decl_line; + + pthread_mutex_unlock(&libdw__lock); } INIT_LIST_HEAD(&tag->node); @@ -444,19 +540,30 @@ static struct ptr_to_member_type *ptr_to_member_type__new(Dwarf_Die *die, return ptr; } -static struct base_type *base_type__new(Dwarf_Die *die, struct cu *cu) +static uint8_t encoding_to_float_type(uint64_t encoding) +{ + switch (encoding) { + case DW_ATE_complex_float: return BT_FP_CMPLX; + case DW_ATE_float: return BT_FP_SINGLE; + case DW_ATE_imaginary_float: return BT_FP_IMGRY; + default: return 0; + } +} + +static struct base_type *base_type__new(Dwarf_Die *die, struct cu *cu, struct conf_load *conf) { struct base_type *bt = tag__alloc(cu, sizeof(*bt)); if (bt != NULL) { tag__init(&bt->tag, cu, die); - bt->name = strings__add(strings, attr_string(die, DW_AT_name)); + bt->name = attr_string(die, DW_AT_name, conf); bt->bit_size = attr_numeric(die, DW_AT_byte_size) * 8; uint64_t encoding = attr_numeric(die, DW_AT_encoding); bt->is_bool = encoding == DW_ATE_boolean; bt->is_signed = encoding == DW_ATE_signed; bt->is_varargs = false; bt->name_has_encoding = true; + bt->float_type = encoding_to_float_type(encoding); } return bt; @@ -491,32 +598,32 @@ static struct string_type *string_type__new(Dwarf_Die *die, struct cu *cu) } static void namespace__init(struct namespace *namespace, Dwarf_Die *die, - struct cu *cu) + struct cu *cu, struct conf_load *conf) { tag__init(&namespace->tag, cu, die); INIT_LIST_HEAD(&namespace->tags); - namespace->sname = 0; - namespace->name = strings__add(strings, attr_string(die, DW_AT_name)); + INIT_LIST_HEAD(&namespace->annots); + namespace->name = attr_string(die, DW_AT_name, conf); namespace->nr_tags = 0; namespace->shared_tags = 0; } -static struct namespace *namespace__new(Dwarf_Die *die, struct cu *cu) +static struct namespace *namespace__new(Dwarf_Die *die, struct cu *cu, struct conf_load *conf) { struct namespace *namespace = tag__alloc(cu, sizeof(*namespace)); if (namespace != NULL) - namespace__init(namespace, die, cu); + namespace__init(namespace, die, cu, conf); return namespace; } -static void type__init(struct type *type, Dwarf_Die *die, struct cu *cu) +static void type__init(struct type *type, Dwarf_Die *die, struct cu *cu, struct conf_load *conf) { - namespace__init(&type->namespace, die, cu); + namespace__init(&type->namespace, die, cu, conf); __type__init(type); type->size = attr_numeric(die, DW_AT_byte_size); - type->alignment = attr_numeric(die, DW_AT_alignment); + type->alignment = attr_alignment(die, conf); type->declaration = attr_numeric(die, DW_AT_declaration); dwarf_tag__set_spec(type->namespace.tag.priv, attr_type(die, DW_AT_specification)); @@ -527,23 +634,23 @@ static void type__init(struct type *type, Dwarf_Die *die, struct cu *cu) type->nr_static_members = 0; } -static struct type *type__new(Dwarf_Die *die, struct cu *cu) +static struct type *type__new(Dwarf_Die *die, struct cu *cu, struct conf_load *conf) { struct type *type = tag__alloc_with_spec(cu, sizeof(*type)); if (type != NULL) - type__init(type, die, cu); + type__init(type, die, cu, conf); return type; } -static struct enumerator *enumerator__new(Dwarf_Die *die, struct cu *cu) +static struct enumerator *enumerator__new(Dwarf_Die *die, struct cu *cu, struct conf_load *conf) { struct enumerator *enumerator = tag__alloc(cu, sizeof(*enumerator)); if (enumerator != NULL) { tag__init(&enumerator->tag, cu, die); - enumerator->name = strings__add(strings, attr_string(die, DW_AT_name)); + enumerator->name = attr_string(die, DW_AT_name, conf); enumerator->value = attr_numeric(die, DW_AT_const_value); } @@ -560,6 +667,7 @@ static enum vscope dwarf__location(Dwarf_Die *die, uint64_t *addr, struct locati Dwarf_Op *expr = location->expr; switch (expr->atom) { case DW_OP_addr: + case DW_OP_addrx: scope = VSCOPE_GLOBAL; *addr = expr[0].number; break; @@ -592,7 +700,7 @@ const char *variable__scope_str(const struct variable *var) return "unknown"; } -static struct variable *variable__new(Dwarf_Die *die, struct cu *cu) +static struct variable *variable__new(Dwarf_Die *die, struct cu *cu, struct conf_load *conf) { struct variable *var; bool has_specification; @@ -606,12 +714,14 @@ static struct variable *variable__new(Dwarf_Die *die, struct cu *cu) if (var != NULL) { tag__init(&var->ip.tag, cu, die); - var->name = strings__add(strings, attr_string(die, DW_AT_name)); + var->name = attr_string(die, DW_AT_name, conf); /* variable is visible outside of its enclosing cu */ var->external = dwarf_hasattr(die, DW_AT_external); /* non-defining declaration of an object */ var->declaration = dwarf_hasattr(die, DW_AT_declaration); + var->has_specification = has_specification; var->scope = VSCOPE_UNKNOWN; + INIT_LIST_HEAD(&var->annots); var->ip.addr = 0; if (!var->declaration && cu->has_addr_info) var->scope = dwarf__location(die, &var->ip.addr, &var->location); @@ -630,21 +740,25 @@ static int tag__recode_dwarf_bitfield(struct tag *tag, struct cu *cu, uint16_t b type_id_t short_id; struct tag *recoded; /* in all the cases the name is at the same offset */ - strings_t name = tag__namespace(tag)->name; + const char *name = namespace__name(tag__namespace(tag)); switch (tag->tag) { case DW_TAG_typedef: { const struct dwarf_tag *dtag = tag->priv; - struct dwarf_tag *dtype = dwarf_cu__find_type_by_ref(cu->priv, - &dtag->type); + struct dwarf_tag *dtype = dwarf_cu__find_type_by_ref(cu->priv, &dtag->type); + + if (dtype == NULL) { + tag__print_type_not_found(tag); + return -ENOENT; + } + struct tag *type = dtype->tag; id = tag__recode_dwarf_bitfield(type, cu, bit_size); if (id < 0) return id; - struct type *new_typedef = obstack_zalloc(&cu->obstack, - sizeof(*new_typedef)); + struct type *new_typedef = cu__zalloc(cu, sizeof(*new_typedef)); if (new_typedef == NULL) return -ENOMEM; @@ -659,13 +773,19 @@ static int tag__recode_dwarf_bitfield(struct tag *tag, struct cu *cu, uint16_t b case DW_TAG_volatile_type: { const struct dwarf_tag *dtag = tag->priv; struct dwarf_tag *dtype = dwarf_cu__find_type_by_ref(cu->priv, &dtag->type); + + if (dtype == NULL) { + tag__print_type_not_found(tag); + return -ENOENT; + } + struct tag *type = dtype->tag; id = tag__recode_dwarf_bitfield(type, cu, bit_size); - if (id == tag->type) + if (id >= 0 && (uint32_t)id == tag->type) return id; - recoded = obstack_zalloc(&cu->obstack, sizeof(*recoded)); + recoded = cu__zalloc(cu, sizeof(*recoded)); if (recoded == NULL) return -ENOMEM; @@ -680,19 +800,18 @@ static int tag__recode_dwarf_bitfield(struct tag *tag, struct cu *cu, uint16_t b * the dwarf_cu as in dwarf there are no such things * as base_types of less than 8 bits, etc. */ - recoded = cu__find_base_type_by_sname_and_size(cu, name, bit_size, &short_id); + recoded = cu__find_base_type_by_name_and_size(cu, name, bit_size, &short_id); if (recoded != NULL) return short_id; - struct base_type *new_bt = obstack_zalloc(&cu->obstack, - sizeof(*new_bt)); + struct base_type *new_bt = cu__zalloc(cu, sizeof(*new_bt)); if (new_bt == NULL) return -ENOMEM; recoded = (struct tag *)new_bt; recoded->tag = DW_TAG_base_type; recoded->top_level = 1; - new_bt->name = name; + new_bt->name = strdup(name); new_bt->bit_size = bit_size; break; @@ -702,12 +821,12 @@ static int tag__recode_dwarf_bitfield(struct tag *tag, struct cu *cu, uint16_t b * the dwarf_cu as in dwarf there are no such things * as enumeration_types of less than 8 bits, etc. */ - recoded = cu__find_enumeration_by_sname_and_size(cu, name, bit_size, &short_id); + recoded = cu__find_enumeration_by_name_and_size(cu, name, bit_size, &short_id); if (recoded != NULL) return short_id; struct type *alias = tag__type(tag); - struct type *new_enum = obstack_zalloc(&cu->obstack, sizeof(*new_enum)); + struct type *new_enum = cu__zalloc(cu, sizeof(*new_enum)); if (new_enum == NULL) return -ENOMEM; @@ -720,13 +839,13 @@ static int tag__recode_dwarf_bitfield(struct tag *tag, struct cu *cu, uint16_t b */ new_enum->namespace.tags.next = &alias->namespace.tags; new_enum->namespace.shared_tags = 1; - new_enum->namespace.name = name; + new_enum->namespace.name = strdup(name); new_enum->size = bit_size; break; default: fprintf(stderr, "%s: tag=%s, name=%s, bit_size=%d\n", __func__, dwarf_tag_name(tag->tag), - strings__ptr(strings, name), bit_size); + name, bit_size); return -EINVAL; } @@ -734,10 +853,55 @@ static int tag__recode_dwarf_bitfield(struct tag *tag, struct cu *cu, uint16_t b if (cu__add_tag(cu, recoded, &new_id) == 0) return new_id; - obstack_free(&cu->obstack, recoded); + free(recoded); return -ENOMEM; } +static int add_llvm_annotation(Dwarf_Die *die, int component_idx, struct conf_load *conf, + struct list_head *head) +{ + struct llvm_annotation *annot; + const char *name; + + if (conf->skip_encoding_btf_decl_tag) + return 0; + + /* Only handle btf_decl_tag annotation for now. */ + name = attr_string(die, DW_AT_name, conf); + if (strcmp(name, "btf_decl_tag") != 0) + return 0; + + annot = zalloc(sizeof(*annot)); + if (!annot) + return -ENOMEM; + + annot->value = attr_string(die, DW_AT_const_value, conf); + annot->component_idx = component_idx; + list_add_tail(&annot->node, head); + return 0; +} + +static int add_child_llvm_annotations(Dwarf_Die *die, int component_idx, + struct conf_load *conf, struct list_head *head) +{ + Dwarf_Die child; + int ret; + + if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0) + return 0; + + die = &child; + do { + if (dwarf_tag(die) == DW_TAG_LLVM_annotation) { + ret = add_llvm_annotation(die, component_idx, conf, head); + if (ret) + return ret; + } + } while (dwarf_siblingof(die, die) == 0); + + return 0; +} + int class_member__dwarf_recode_bitfield(struct class_member *member, struct cu *cu) { @@ -757,58 +921,76 @@ int class_member__dwarf_recode_bitfield(struct class_member *member, } static struct class_member *class_member__new(Dwarf_Die *die, struct cu *cu, - bool in_union) + bool in_union, struct conf_load *conf) { struct class_member *member = tag__alloc(cu, sizeof(*member)); if (member != NULL) { tag__init(&member->tag, cu, die); - member->name = strings__add(strings, attr_string(die, DW_AT_name)); - member->is_static = !in_union && !dwarf_hasattr(die, DW_AT_data_member_location); - member->const_value = attr_numeric(die, DW_AT_const_value); - member->alignment = attr_numeric(die, DW_AT_alignment); - member->byte_offset = attr_offset(die, DW_AT_data_member_location); - /* - * Bit offset calculated here is valid only for byte-aligned - * fields. For bitfields on little-endian archs we need to - * adjust them taking into account byte size of the field, - * which might not be yet known. So we'll re-calculate bit - * offset later, in class_member__cache_byte_size. - */ - member->bit_offset = member->byte_offset * 8; + member->name = attr_string(die, DW_AT_name, conf); + member->alignment = attr_alignment(die, conf); + + Dwarf_Attribute attr; + + member->has_bit_offset = dwarf_attr(die, DW_AT_data_bit_offset, &attr) != NULL; + + if (member->has_bit_offset) { + member->bit_offset = __attr_offset(&attr); + // byte_offset and bitfield_offset will be recalculated later, when + // we discover the size of this bitfield base type. + } else { + if (dwarf_attr(die, DW_AT_data_member_location, &attr) != NULL) { + member->byte_offset = __attr_offset(&attr); + } else { + member->is_static = !in_union; + } + + /* + * Bit offset calculated here is valid only for byte-aligned + * fields. For bitfields on little-endian archs we need to + * adjust them taking into account byte size of the field, + * which might not be yet known. So we'll re-calculate bit + * offset later, in class_member__cache_byte_size. + */ + member->bit_offset = member->byte_offset * 8; + member->bitfield_offset = attr_numeric(die, DW_AT_bit_offset); + } + /* * If DW_AT_byte_size is not present, byte size will be * determined later in class_member__cache_byte_size using * base integer/enum type */ member->byte_size = attr_numeric(die, DW_AT_byte_size); - member->bitfield_offset = attr_numeric(die, DW_AT_bit_offset); member->bitfield_size = attr_numeric(die, DW_AT_bit_size); member->bit_hole = 0; member->bitfield_end = 0; member->visited = 0; - member->accessibility = attr_numeric(die, DW_AT_accessibility); - member->virtuality = attr_numeric(die, DW_AT_virtuality); + + if (!cu__is_c(cu)) { + member->accessibility = attr_numeric(die, DW_AT_accessibility); + member->const_value = attr_numeric(die, DW_AT_const_value); + member->virtuality = attr_numeric(die, DW_AT_virtuality); + } member->hole = 0; } return member; } -static struct parameter *parameter__new(Dwarf_Die *die, struct cu *cu) +static struct parameter *parameter__new(Dwarf_Die *die, struct cu *cu, struct conf_load *conf) { struct parameter *parm = tag__alloc(cu, sizeof(*parm)); if (parm != NULL) { tag__init(&parm->tag, cu, die); - parm->name = strings__add(strings, attr_string(die, DW_AT_name)); + parm->name = attr_string(die, DW_AT_name, conf); } return parm; } -static struct inline_expansion *inline_expansion__new(Dwarf_Die *die, - struct cu *cu) +static struct inline_expansion *inline_expansion__new(Dwarf_Die *die, struct cu *cu, struct conf_load *conf) { struct inline_expansion *exp = tag__alloc(cu, sizeof(*exp)); @@ -816,8 +998,7 @@ static struct inline_expansion *inline_expansion__new(Dwarf_Die *die, struct dwarf_tag *dtag = exp->ip.tag.priv; tag__init(&exp->ip.tag, cu, die); - dtag->decl_file = - strings__add(strings, attr_string(die, DW_AT_call_file)); + dtag->decl_file = attr_string(die, DW_AT_call_file, conf); dtag->decl_line = attr_numeric(die, DW_AT_call_line); dtag->type = attr_type(die, DW_AT_abstract_origin); exp->ip.addr = 0; @@ -853,13 +1034,13 @@ out: return exp; } -static struct label *label__new(Dwarf_Die *die, struct cu *cu) +static struct label *label__new(Dwarf_Die *die, struct cu *cu, struct conf_load *conf) { struct label *label = tag__alloc(cu, sizeof(*label)); if (label != NULL) { tag__init(&label->ip.tag, cu, die); - label->name = strings__add(strings, attr_string(die, DW_AT_name)); + label->name = attr_string(die, DW_AT_name, conf); if (!cu->has_addr_info || dwarf_lowpc(die, &label->ip.addr)) label->ip.addr = 0; } @@ -867,12 +1048,12 @@ static struct label *label__new(Dwarf_Die *die, struct cu *cu) return label; } -static struct class *class__new(Dwarf_Die *die, struct cu *cu) +static struct class *class__new(Dwarf_Die *die, struct cu *cu, struct conf_load *conf) { struct class *class = tag__alloc_with_spec(cu, sizeof(*class)); if (class != NULL) { - type__init(&class->type, die, cu); + type__init(&class->type, die, cu, conf); INIT_LIST_HEAD(&class->vtable); class->nr_vtable_entries = class->nr_holes = @@ -921,9 +1102,10 @@ static struct lexblock *lexblock__new(Dwarf_Die *die, struct cu *cu) static void ftype__init(struct ftype *ftype, Dwarf_Die *die, struct cu *cu) { +#ifndef NDEBUG const uint16_t tag = dwarf_tag(die); assert(tag == DW_TAG_subprogram || tag == DW_TAG_subroutine_type); - +#endif tag__init(&ftype->tag, cu, die); INIT_LIST_HEAD(&ftype->parms); ftype->nr_parms = 0; @@ -940,15 +1122,15 @@ static struct ftype *ftype__new(Dwarf_Die *die, struct cu *cu) return ftype; } -static struct function *function__new(Dwarf_Die *die, struct cu *cu) +static struct function *function__new(Dwarf_Die *die, struct cu *cu, struct conf_load *conf) { struct function *func = tag__alloc_with_spec(cu, sizeof(*func)); if (func != NULL) { ftype__init(&func->proto, die, cu); lexblock__init(&func->lexblock, cu, die); - func->name = strings__add(strings, attr_string(die, DW_AT_name)); - func->linkage_name = strings__add(strings, attr_string(die, DW_AT_MIPS_linkage_name)); + func->name = attr_string(die, DW_AT_name, conf); + func->linkage_name = attr_string(die, DW_AT_MIPS_linkage_name, conf); func->inlined = attr_numeric(die, DW_AT_inline); func->declaration = dwarf_hasattr(die, DW_AT_declaration); func->external = dwarf_hasattr(die, DW_AT_external); @@ -958,6 +1140,7 @@ static struct function *function__new(Dwarf_Die *die, struct cu *cu) func->accessibility = attr_numeric(die, DW_AT_accessibility); func->virtuality = attr_numeric(die, DW_AT_virtuality); INIT_LIST_HEAD(&func->vtable_node); + INIT_LIST_HEAD(&func->annots); INIT_LIST_HEAD(&func->tool_node); func->vtable_entry = -1; if (dwarf_hasattr(die, DW_AT_vtable_elem_location)) @@ -1005,10 +1188,10 @@ static struct tag unsupported_tag; #define cu__tag_not_handled(die) __cu__tag_not_handled(die, __FUNCTION__) static struct tag *__die__process_tag(Dwarf_Die *die, struct cu *cu, - int toplevel, const char *fn); + int toplevel, const char *fn, struct conf_load *conf); -#define die__process_tag(die, cu, toplevel) \ - __die__process_tag(die, cu, toplevel, __FUNCTION__) +#define die__process_tag(die, cu, toplevel, conf_load) \ + __die__process_tag(die, cu, toplevel, __FUNCTION__, conf_load) static struct tag *die__create_new_tag(Dwarf_Die *die, struct cu *cu) { @@ -1023,6 +1206,89 @@ static struct tag *die__create_new_tag(Dwarf_Die *die, struct cu *cu) return tag; } +static struct btf_type_tag_ptr_type *die__create_new_btf_type_tag_ptr_type(Dwarf_Die *die, struct cu *cu) +{ + struct btf_type_tag_ptr_type *tag; + + tag = tag__alloc_with_spec(cu, sizeof(struct btf_type_tag_ptr_type)); + if (tag == NULL) + return NULL; + + tag__init(&tag->tag, cu, die); + tag->tag.has_btf_type_tag = true; + INIT_LIST_HEAD(&tag->tags); + return tag; +} + +static struct btf_type_tag_type *die__create_new_btf_type_tag_type(Dwarf_Die *die, struct cu *cu, + struct conf_load *conf) +{ + struct btf_type_tag_type *tag; + + tag = tag__alloc_with_spec(cu, sizeof(struct btf_type_tag_type)); + if (tag == NULL) + return NULL; + + tag__init(&tag->tag, cu, die); + tag->value = attr_string(die, DW_AT_const_value, conf); + return tag; +} + +static struct tag *die__create_new_pointer_tag(Dwarf_Die *die, struct cu *cu, + struct conf_load *conf) +{ + struct btf_type_tag_ptr_type *tag = NULL; + struct btf_type_tag_type *annot; + Dwarf_Die *cdie, child; + const char *name; + uint32_t id; + + /* If no child tags or skipping btf_type_tag encoding, just create a new tag + * and return + */ + if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0 || + conf->skip_encoding_btf_type_tag) + return tag__new(die, cu); + + /* Otherwise, check DW_TAG_LLVM_annotation child tags */ + cdie = &child; + do { + if (dwarf_tag(cdie) != DW_TAG_LLVM_annotation) + continue; + + /* Only check btf_type_tag annotations */ + name = attr_string(cdie, DW_AT_name, conf); + if (strcmp(name, "btf_type_tag") != 0) + continue; + + if (tag == NULL) { + /* Create a btf_type_tag_ptr type. */ + tag = die__create_new_btf_type_tag_ptr_type(die, cu); + if (!tag) + return NULL; + } + + /* Create a btf_type_tag type for this annotation. */ + annot = die__create_new_btf_type_tag_type(cdie, cu, conf); + if (annot == NULL) + return NULL; + + if (cu__table_add_tag(cu, &annot->tag, &id) < 0) + return NULL; + + struct dwarf_tag *dtag = annot->tag.priv; + dtag->small_id = id; + cu__hash(cu, &annot->tag); + + /* For a list of DW_TAG_LLVM_annotation like tag1 -> tag2 -> tag3, + * the tag->tags contains tag3 -> tag2 -> tag1. + */ + list_add(&annot->node, &tag->tags); + } while (dwarf_siblingof(cdie, cdie) == 0); + + return tag ? &tag->tag : tag__new(die, cu); +} + static struct tag *die__create_new_ptr_to_member_type(Dwarf_Die *die, struct cu *cu) { @@ -1032,18 +1298,18 @@ static struct tag *die__create_new_ptr_to_member_type(Dwarf_Die *die, } static int die__process_class(Dwarf_Die *die, - struct type *class, struct cu *cu); + struct type *class, struct cu *cu, struct conf_load *conf); -static struct tag *die__create_new_class(Dwarf_Die *die, struct cu *cu) +static struct tag *die__create_new_class(Dwarf_Die *die, struct cu *cu, struct conf_load *conf) { Dwarf_Die child; - struct class *class = class__new(die, cu); + struct class *class = class__new(die, cu, conf); if (class != NULL && dwarf_haschildren(die) != 0 && dwarf_child(die, &child) == 0) { - if (die__process_class(&child, &class->type, cu) != 0) { - class__delete(class, cu); + if (die__process_class(&child, &class->type, cu, conf) != 0) { + class__delete(class); class = NULL; } } @@ -1052,18 +1318,18 @@ static struct tag *die__create_new_class(Dwarf_Die *die, struct cu *cu) } static int die__process_namespace(Dwarf_Die *die, struct namespace *namespace, - struct cu *cu); + struct cu *cu, struct conf_load *conf); -static struct tag *die__create_new_namespace(Dwarf_Die *die, struct cu *cu) +static struct tag *die__create_new_namespace(Dwarf_Die *die, struct cu *cu, struct conf_load *conf) { Dwarf_Die child; - struct namespace *namespace = namespace__new(die, cu); + struct namespace *namespace = namespace__new(die, cu, conf); if (namespace != NULL && dwarf_haschildren(die) != 0 && dwarf_child(die, &child) == 0) { - if (die__process_namespace(&child, namespace, cu) != 0) { - namespace__delete(namespace, cu); + if (die__process_namespace(&child, namespace, cu, conf) != 0) { + namespace__delete(namespace); namespace = NULL; } } @@ -1071,16 +1337,16 @@ static struct tag *die__create_new_namespace(Dwarf_Die *die, struct cu *cu) return namespace ? &namespace->tag : NULL; } -static struct tag *die__create_new_union(Dwarf_Die *die, struct cu *cu) +static struct tag *die__create_new_union(Dwarf_Die *die, struct cu *cu, struct conf_load *conf) { Dwarf_Die child; - struct type *utype = type__new(die, cu); + struct type *utype = type__new(die, cu, conf); if (utype != NULL && dwarf_haschildren(die) != 0 && dwarf_child(die, &child) == 0) { - if (die__process_class(&child, utype, cu) != 0) { - type__delete(utype, cu); + if (die__process_class(&child, utype, cu, conf) != 0) { + type__delete(utype); utype = NULL; } } @@ -1088,9 +1354,9 @@ static struct tag *die__create_new_union(Dwarf_Die *die, struct cu *cu) return utype ? &utype->namespace.tag : NULL; } -static struct tag *die__create_new_base_type(Dwarf_Die *die, struct cu *cu) +static struct tag *die__create_new_base_type(Dwarf_Die *die, struct cu *cu, struct conf_load *conf) { - struct base_type *base = base_type__new(die, cu); + struct base_type *base = base_type__new(die, cu, conf); if (base == NULL) return NULL; @@ -1102,18 +1368,15 @@ static struct tag *die__create_new_base_type(Dwarf_Die *die, struct cu *cu) return &base->tag; } -static struct tag *die__create_new_typedef(Dwarf_Die *die, struct cu *cu) +static struct tag *die__create_new_typedef(Dwarf_Die *die, struct cu *cu, struct conf_load *conf) { - struct type *tdef = type__new(die, cu); + struct type *tdef = type__new(die, cu, conf); if (tdef == NULL) return NULL; - if (dwarf_haschildren(die)) { - struct dwarf_tag *dtag = tdef->namespace.tag.priv; - fprintf(stderr, "%s: DW_TAG_typedef %llx WITH children!\n", - __func__, (unsigned long long)dtag->id); - } + if (add_child_llvm_annotations(die, -1, conf, &tdef->namespace.annots)) + return NULL; return &tdef->namespace.tag; } @@ -1153,7 +1416,7 @@ static struct tag *die__create_new_array(Dwarf_Die *die, struct cu *cu) return &array->tag; out_free: - obstack_free(&cu->obstack, array); + free(array); return NULL; } @@ -1170,16 +1433,21 @@ static struct tag *die__create_new_string_type(Dwarf_Die *die, struct cu *cu) static struct tag *die__create_new_parameter(Dwarf_Die *die, struct ftype *ftype, struct lexblock *lexblock, - struct cu *cu) + struct cu *cu, struct conf_load *conf, + int param_idx) { - struct parameter *parm = parameter__new(die, cu); + struct parameter *parm = parameter__new(die, cu, conf); if (parm == NULL) return NULL; - if (ftype != NULL) + if (ftype != NULL) { ftype__add_parameter(ftype, parm); - else { + if (param_idx >= 0) { + if (add_child_llvm_annotations(die, param_idx, conf, &(tag__function(&ftype->tag)->annots))) + return NULL; + } + } else { /* * DW_TAG_formal_parameters on a non DW_TAG_subprogram nor * DW_TAG_subroutine_type tag happens sometimes, likely due to @@ -1197,9 +1465,9 @@ static struct tag *die__create_new_parameter(Dwarf_Die *die, static struct tag *die__create_new_label(Dwarf_Die *die, struct lexblock *lexblock, - struct cu *cu) + struct cu *cu, struct conf_load *conf) { - struct label *label = label__new(die, cu); + struct label *label = label__new(die, cu, conf); if (label == NULL) return NULL; @@ -1208,15 +1476,18 @@ static struct tag *die__create_new_label(Dwarf_Die *die, return &label->ip.tag; } -static struct tag *die__create_new_variable(Dwarf_Die *die, struct cu *cu) +static struct tag *die__create_new_variable(Dwarf_Die *die, struct cu *cu, struct conf_load *conf) { - struct variable *var = variable__new(die, cu); + struct variable *var = variable__new(die, cu, conf); + + if (var == NULL || add_child_llvm_annotations(die, -1, conf, &var->annots)) + return NULL; - return var ? &var->ip.tag : NULL; + return &var->ip.tag; } static struct tag *die__create_new_subroutine_type(Dwarf_Die *die, - struct cu *cu) + struct cu *cu, struct conf_load *conf) { Dwarf_Die child; struct ftype *ftype = ftype__new(die, cu); @@ -1237,13 +1508,13 @@ static struct tag *die__create_new_subroutine_type(Dwarf_Die *die, tag__print_not_supported(dwarf_tag(die)); continue; case DW_TAG_formal_parameter: - tag = die__create_new_parameter(die, ftype, NULL, cu); + tag = die__create_new_parameter(die, ftype, NULL, cu, conf, -1); break; case DW_TAG_unspecified_parameters: ftype->unspec_parms = 1; continue; default: - tag = die__process_tag(die, cu, 0); + tag = die__process_tag(die, cu, 0, conf); if (tag == NULL) goto out_delete; @@ -1271,16 +1542,16 @@ hash: out: return &ftype->tag; out_delete_tag: - tag__delete(tag, cu); + tag__delete(tag); out_delete: - ftype__delete(ftype, cu); + ftype__delete(ftype); return NULL; } -static struct tag *die__create_new_enumeration(Dwarf_Die *die, struct cu *cu) +static struct tag *die__create_new_enumeration(Dwarf_Die *die, struct cu *cu, struct conf_load *conf) { Dwarf_Die child; - struct type *enumeration = type__new(die, cu); + struct type *enumeration = type__new(die, cu, conf); if (enumeration == NULL) return NULL; @@ -1304,7 +1575,7 @@ static struct tag *die__create_new_enumeration(Dwarf_Die *die, struct cu *cu) cu__tag_not_handled(die); continue; } - enumerator = enumerator__new(die, cu); + enumerator = enumerator__new(die, cu, conf); if (enumerator == NULL) goto out_delete; @@ -1313,14 +1584,15 @@ static struct tag *die__create_new_enumeration(Dwarf_Die *die, struct cu *cu) out: return &enumeration->namespace.tag; out_delete: - enumeration__delete(enumeration, cu); + enumeration__delete(enumeration); return NULL; } static int die__process_class(Dwarf_Die *die, struct type *class, - struct cu *cu) + struct cu *cu, struct conf_load *conf) { const bool is_union = tag__is_union(&class->namespace.tag); + int member_idx = 0; do { switch (dwarf_tag(die)) { @@ -1344,7 +1616,7 @@ static int die__process_class(Dwarf_Die *die, struct type *class, continue; case DW_TAG_inheritance: case DW_TAG_member: { - struct class_member *member = class_member__new(die, cu, is_union); + struct class_member *member = class_member__new(die, cu, is_union, conf); if (member == NULL) return -ENOMEM; @@ -1353,7 +1625,7 @@ static int die__process_class(Dwarf_Die *die, struct type *class, uint32_t id; if (cu__table_add_tag(cu, &member->tag, &id) < 0) { - class_member__delete(member, cu); + class_member__delete(member); return -ENOMEM; } @@ -1363,10 +1635,17 @@ static int die__process_class(Dwarf_Die *die, struct type *class, type__add_member(class, member); cu__hash(cu, &member->tag); + if (add_child_llvm_annotations(die, member_idx, conf, &class->namespace.annots)) + return -ENOMEM; + member_idx++; } continue; + case DW_TAG_LLVM_annotation: + if (add_llvm_annotation(die, -1, conf, &class->namespace.annots)) + return -ENOMEM; + continue; default: { - struct tag *tag = die__process_tag(die, cu, 0); + struct tag *tag = die__process_tag(die, cu, 0, conf); if (tag == NULL) return -ENOMEM; @@ -1379,7 +1658,7 @@ static int die__process_class(Dwarf_Die *die, struct type *class, uint32_t id; if (cu__table_add_tag(cu, tag, &id) < 0) { - tag__delete(tag, cu); + tag__delete(tag); return -ENOMEM; } @@ -1403,11 +1682,11 @@ static int die__process_class(Dwarf_Die *die, struct type *class, } static int die__process_namespace(Dwarf_Die *die, struct namespace *namespace, - struct cu *cu) + struct cu *cu, struct conf_load *conf) { struct tag *tag; do { - tag = die__process_tag(die, cu, 0); + tag = die__process_tag(die, cu, 0, conf); if (tag == NULL) goto out_enomem; @@ -1429,36 +1708,36 @@ static int die__process_namespace(Dwarf_Die *die, struct namespace *namespace, return 0; out_delete_tag: - tag__delete(tag, cu); + tag__delete(tag); out_enomem: return -ENOMEM; } static int die__process_function(Dwarf_Die *die, struct ftype *ftype, - struct lexblock *lexblock, struct cu *cu); + struct lexblock *lexblock, struct cu *cu, struct conf_load *conf); static int die__create_new_lexblock(Dwarf_Die *die, - struct cu *cu, struct lexblock *father) + struct cu *cu, struct lexblock *father, struct conf_load *conf) { struct lexblock *lexblock = lexblock__new(die, cu); if (lexblock != NULL) { - if (die__process_function(die, NULL, lexblock, cu) != 0) + if (die__process_function(die, NULL, lexblock, cu, conf) != 0) goto out_delete; } if (father != NULL) lexblock__add_lexblock(father, lexblock); return 0; out_delete: - lexblock__delete(lexblock, cu); + lexblock__delete(lexblock); return -ENOMEM; } static struct tag *die__create_new_inline_expansion(Dwarf_Die *die, struct lexblock *lexblock, - struct cu *cu); + struct cu *cu, struct conf_load *conf); -static int die__process_inline_expansion(Dwarf_Die *die, struct lexblock *lexblock, struct cu *cu) +static int die__process_inline_expansion(Dwarf_Die *die, struct lexblock *lexblock, struct cu *cu, struct conf_load *conf) { Dwarf_Die child; struct tag *tag; @@ -1471,6 +1750,8 @@ static int die__process_inline_expansion(Dwarf_Die *die, struct lexblock *lexblo uint32_t id; switch (dwarf_tag(die)) { + case DW_TAG_call_site: + case DW_TAG_call_site_parameter: case DW_TAG_GNU_call_site: case DW_TAG_GNU_call_site_parameter: /* @@ -1484,7 +1765,7 @@ static int die__process_inline_expansion(Dwarf_Die *die, struct lexblock *lexblo */ continue; case DW_TAG_lexical_block: - if (die__create_new_lexblock(die, cu, lexblock) != 0) + if (die__create_new_lexblock(die, cu, lexblock, conf) != 0) goto out_enomem; continue; case DW_TAG_formal_parameter: @@ -1501,13 +1782,15 @@ static int die__process_inline_expansion(Dwarf_Die *die, struct lexblock *lexblo */ continue; case DW_TAG_inlined_subroutine: - tag = die__create_new_inline_expansion(die, lexblock, cu); + tag = die__create_new_inline_expansion(die, lexblock, cu, conf); break; case DW_TAG_label: - tag = die__create_new_label(die, lexblock, cu); + if (conf->ignore_labels) + continue; + tag = die__create_new_label(die, lexblock, cu, conf); break; default: - tag = die__process_tag(die, cu, 0); + tag = die__process_tag(die, cu, 0, conf); if (tag == NULL) goto out_enomem; @@ -1534,22 +1817,22 @@ hash: return 0; out_delete_tag: - tag__delete(tag, cu); + tag__delete(tag); out_enomem: return -ENOMEM; } static struct tag *die__create_new_inline_expansion(Dwarf_Die *die, struct lexblock *lexblock, - struct cu *cu) + struct cu *cu, struct conf_load *conf) { - struct inline_expansion *exp = inline_expansion__new(die, cu); + struct inline_expansion *exp = inline_expansion__new(die, cu, conf); if (exp == NULL) return NULL; - if (die__process_inline_expansion(die, lexblock, cu) != 0) { - obstack_free(&cu->obstack, exp); + if (die__process_inline_expansion(die, lexblock, cu, conf) != 0) { + free(exp); return NULL; } @@ -1559,8 +1842,9 @@ static struct tag *die__create_new_inline_expansion(Dwarf_Die *die, } static int die__process_function(Dwarf_Die *die, struct ftype *ftype, - struct lexblock *lexblock, struct cu *cu) + struct lexblock *lexblock, struct cu *cu, struct conf_load *conf) { + int param_idx = 0; Dwarf_Die child; struct tag *tag; @@ -1572,6 +1856,8 @@ static int die__process_function(Dwarf_Die *die, struct ftype *ftype, uint32_t id; switch (dwarf_tag(die)) { + case DW_TAG_call_site: + case DW_TAG_call_site_parameter: case DW_TAG_GNU_call_site: case DW_TAG_GNU_call_site_parameter: /* @@ -1602,10 +1888,10 @@ static int die__process_function(Dwarf_Die *die, struct ftype *ftype, tag__print_not_supported(dwarf_tag(die)); continue; case DW_TAG_formal_parameter: - tag = die__create_new_parameter(die, ftype, lexblock, cu); + tag = die__create_new_parameter(die, ftype, lexblock, cu, conf, param_idx++); break; case DW_TAG_variable: - tag = die__create_new_variable(die, cu); + tag = die__create_new_variable(die, cu, conf); if (tag == NULL) goto out_enomem; lexblock__add_variable(lexblock, tag__variable(tag)); @@ -1615,17 +1901,28 @@ static int die__process_function(Dwarf_Die *die, struct ftype *ftype, ftype->unspec_parms = 1; continue; case DW_TAG_label: - tag = die__create_new_label(die, lexblock, cu); + if (conf->ignore_labels) + continue; + tag = die__create_new_label(die, lexblock, cu, conf); break; case DW_TAG_inlined_subroutine: - tag = die__create_new_inline_expansion(die, lexblock, cu); + if (conf->ignore_inline_expansions) + continue; + tag = die__create_new_inline_expansion(die, lexblock, cu, conf); break; case DW_TAG_lexical_block: - if (die__create_new_lexblock(die, cu, lexblock) != 0) + // lexblocks can contain types that are then referenced from outside. + // Thus we can't ignore them without more surgery, i.e. by adding code + // to just process types inside lexblocks, leave this for later. + if (die__create_new_lexblock(die, cu, lexblock, conf) != 0) + goto out_enomem; + continue; + case DW_TAG_LLVM_annotation: + if (add_llvm_annotation(die, -1, conf, &(tag__function(&ftype->tag)->annots))) goto out_enomem; continue; default: - tag = die__process_tag(die, cu, 0); + tag = die__process_tag(die, cu, 0, conf); if (tag == NULL) goto out_enomem; @@ -1654,19 +1951,18 @@ hash: return 0; out_delete_tag: - tag__delete(tag, cu); + tag__delete(tag); out_enomem: return -ENOMEM; } -static struct tag *die__create_new_function(Dwarf_Die *die, struct cu *cu) +static struct tag *die__create_new_function(Dwarf_Die *die, struct cu *cu, struct conf_load *conf) { - struct function *function = function__new(die, cu); + struct function *function = function__new(die, cu, conf); if (function != NULL && - die__process_function(die, &function->proto, - &function->lexblock, cu) != 0) { - function__delete(function, cu); + die__process_function(die, &function->proto, &function->lexblock, cu, conf) != 0) { + function__delete(function); function = NULL; } @@ -1674,7 +1970,7 @@ static struct tag *die__create_new_function(Dwarf_Die *die, struct cu *cu) } static struct tag *__die__process_tag(Dwarf_Die *die, struct cu *cu, - int top_level, const char *fn) + int top_level, const char *fn, struct conf_load *conf) { struct tag *tag; @@ -1686,37 +1982,38 @@ static struct tag *__die__process_tag(Dwarf_Die *die, struct cu *cu, case DW_TAG_string_type: // FORTRAN stuff, looks like an array tag = die__create_new_string_type(die, cu); break; case DW_TAG_base_type: - tag = die__create_new_base_type(die, cu); break; + tag = die__create_new_base_type(die, cu, conf); break; case DW_TAG_const_type: case DW_TAG_imported_declaration: case DW_TAG_imported_module: - case DW_TAG_pointer_type: case DW_TAG_reference_type: case DW_TAG_restrict_type: case DW_TAG_unspecified_type: case DW_TAG_volatile_type: tag = die__create_new_tag(die, cu); break; + case DW_TAG_pointer_type: + tag = die__create_new_pointer_tag(die, cu, conf); break; case DW_TAG_ptr_to_member_type: tag = die__create_new_ptr_to_member_type(die, cu); break; case DW_TAG_enumeration_type: - tag = die__create_new_enumeration(die, cu); break; + tag = die__create_new_enumeration(die, cu, conf); break; case DW_TAG_namespace: - tag = die__create_new_namespace(die, cu); break; + tag = die__create_new_namespace(die, cu, conf); break; case DW_TAG_class_type: case DW_TAG_interface_type: case DW_TAG_structure_type: - tag = die__create_new_class(die, cu); break; + tag = die__create_new_class(die, cu, conf); break; case DW_TAG_subprogram: - tag = die__create_new_function(die, cu); break; + tag = die__create_new_function(die, cu, conf); break; case DW_TAG_subroutine_type: - tag = die__create_new_subroutine_type(die, cu); break; + tag = die__create_new_subroutine_type(die, cu, conf); break; case DW_TAG_rvalue_reference_type: case DW_TAG_typedef: - tag = die__create_new_typedef(die, cu); break; + tag = die__create_new_typedef(die, cu, conf); break; case DW_TAG_union_type: - tag = die__create_new_union(die, cu); break; + tag = die__create_new_union(die, cu, conf); break; case DW_TAG_variable: - tag = die__create_new_variable(die, cu); break; + tag = die__create_new_variable(die, cu, conf); break; default: __cu__tag_not_handled(die, fn); /* fall thru */ @@ -1734,10 +2031,10 @@ static struct tag *__die__process_tag(Dwarf_Die *die, struct cu *cu, return tag; } -static int die__process_unit(Dwarf_Die *die, struct cu *cu) +static int die__process_unit(Dwarf_Die *die, struct cu *cu, struct conf_load *conf) { do { - struct tag *tag = die__process_tag(die, cu, 1); + struct tag *tag = die__process_tag(die, cu, 1, conf); if (tag == NULL) return -ENOMEM; @@ -1759,17 +2056,6 @@ static int die__process_unit(Dwarf_Die *die, struct cu *cu) return 0; } -static void __tag__print_type_not_found(struct tag *tag, const char *func) -{ - struct dwarf_tag *dtag = tag->priv; - fprintf(stderr, "%s: couldn't find %#llx type for %#llx (%s)!\n", func, - (unsigned long long)dtag->type.off, (unsigned long long)dtag->id, - dwarf_tag_name(tag->tag)); -} - -#define tag__print_type_not_found(tag) \ - __tag__print_type_not_found(tag, __func__) - static void ftype__recode_dwarf_types(struct tag *tag, struct cu *cu); static int namespace__recode_dwarf_types(struct tag *tag, struct cu *cu) @@ -1990,6 +2276,45 @@ static void lexblock__recode_dwarf_types(struct lexblock *tag, struct cu *cu) } } +static void dwarf_cu__recode_btf_type_tag_ptr(struct btf_type_tag_ptr_type *tag, + uint32_t pointee_type) +{ + struct btf_type_tag_type *annot; + struct dwarf_tag *annot_dtag; + struct tag *prev_tag; + + /* Given source like + * int tag1 tag2 tag3 *p; + * the tag->tags contains tag3 -> tag2 -> tag1, the final type chain looks like: + * pointer -> tag3 -> tag2 -> tag1 -> pointee + * + * Basically it means + * - '*' applies to "int tag1 tag2 tag3" + * - tag3 applies to "int tag1 tag2" + * - tag2 applies to "int tag1" + * - tag1 applies to "int" + * + * This also makes final source code (format c) easier as we can do + * emit for "tag3 -> tag2 -> tag1 -> int" + * emit '*' + * + * For 'tag3 -> tag2 -> tag1 -> int": + * emit for "tag2 -> tag1 -> int" + * emit tag3 + * + * Eventually we can get the source code like + * int tag1 tag2 tag3 *p; + * and this matches the user/kernel code. + */ + prev_tag = &tag->tag; + list_for_each_entry(annot, &tag->tags, node) { + annot_dtag = annot->tag.priv; + prev_tag->type = annot_dtag->small_id; + prev_tag = &annot->tag; + } + prev_tag->type = pointee_type; +} + static int tag__recode_dwarf_type(struct tag *tag, struct cu *cu) { struct dwarf_tag *dtag = tag->priv; @@ -2083,19 +2408,26 @@ static int tag__recode_dwarf_type(struct tag *tag, struct cu *cu) goto find_type; case DW_TAG_variable: { struct variable *var = tag__variable(tag); - dwarf_off_ref specification = dwarf_tag__spec(dtag); - if (specification.off) { - dtype = dwarf_cu__find_tag_by_ref(cu->priv, &specification); - if (dtype) - var->spec = tag__variable(dtype->tag); + if (var->has_specification) { + dwarf_off_ref specification = dwarf_tag__spec(dtag); + + if (specification.off) { + dtype = dwarf_cu__find_tag_by_ref(cu->priv, + &specification); + if (dtype) + var->spec = tag__variable(dtype->tag); + } } } } if (dtag->type.off == 0) { - tag->type = 0; /* void */ + if (tag->tag != DW_TAG_pointer_type || !tag->has_btf_type_tag) + tag->type = 0; /* void */ + else + dwarf_cu__recode_btf_type_tag_ptr(tag__btf_type_tag_ptr(tag), 0); return 0; } @@ -2107,7 +2439,39 @@ check_type: return 0; } out: - tag->type = dtype->small_id; + if (tag->tag != DW_TAG_pointer_type || !tag->has_btf_type_tag) + tag->type = dtype->small_id; + else + dwarf_cu__recode_btf_type_tag_ptr(tag__btf_type_tag_ptr(tag), dtype->small_id); + + return 0; +} + +static int cu__resolve_func_ret_types(struct cu *cu) +{ + struct ptr_table *pt = &cu->functions_table; + uint32_t i; + + for (i = 0; i < pt->nr_entries; ++i) { + struct tag *tag = pt->entries[i]; + + if (tag == NULL || tag->type != 0) + continue; + + struct function *fn = tag__function(tag); + if (!fn->abstract_origin) + continue; + + struct dwarf_tag *dtag = tag->priv; + struct dwarf_tag *dfunc; + dfunc = dwarf_cu__find_tag_by_ref(cu->priv, &dtag->abstract_origin); + if (dfunc == NULL) { + tag__print_abstract_origin_not_found(tag); + return -1; + } + + tag->type = dfunc->tag->type; + } return 0; } @@ -2138,8 +2502,7 @@ static const char *dwarf_tag__decl_file(const struct tag *tag, const struct cu *cu) { struct dwarf_tag *dtag = tag->priv; - return cu->extra_dbg_info ? - strings__ptr(strings, dtag->decl_file) : NULL; + return cu->extra_dbg_info ? dtag->decl_file : NULL; } static uint32_t dwarf_tag__decl_line(const struct tag *tag, @@ -2156,19 +2519,24 @@ static unsigned long long dwarf_tag__orig_id(const struct tag *tag, return cu->extra_dbg_info ? dtag->id : 0; } -static const char *dwarf__strings_ptr(const struct cu *cu __unused, - strings_t s) -{ - return s ? strings__ptr(strings, s) : NULL; -} - struct debug_fmt_ops dwarf__ops; -static int die__process(Dwarf_Die *die, struct cu *cu) +static int die__process(Dwarf_Die *die, struct cu *cu, struct conf_load *conf) { Dwarf_Die child; const uint16_t tag = dwarf_tag(die); + if (tag == DW_TAG_skeleton_unit) { + static bool warned; + + if (!warned) { + fprintf(stderr, "WARNING: DW_TAG_skeleton_unit used, please look for a .dwo file and use it instead.\n" + " A future version of pahole will support do this automagically.\n"); + warned = true; + } + return 0; // so that other units can be processed + } + if (tag == DW_TAG_partial_unit) { static bool warned; @@ -2182,15 +2550,15 @@ static int die__process(Dwarf_Die *die, struct cu *cu) } if (tag != DW_TAG_compile_unit && tag != DW_TAG_type_unit) { - fprintf(stderr, "%s: DW_TAG_compile_unit, DW_TAG_type_unit or DW_TAG_partial_unit expected got %s!\n", - __FUNCTION__, dwarf_tag_name(tag)); + fprintf(stderr, "%s: DW_TAG_compile_unit, DW_TAG_type_unit, DW_TAG_partial_unit or DW_TAG_skeleton_unit expected got %s (0x%x)!\n", + __FUNCTION__, dwarf_tag_name(tag), tag); return -EINVAL; } cu->language = attr_numeric(die, DW_AT_language); if (dwarf_child(die, &child) == 0) { - int err = die__process_unit(&child, cu); + int err = die__process_unit(&child, cu, conf); if (err) return err; } @@ -2203,12 +2571,16 @@ static int die__process(Dwarf_Die *die, struct cu *cu) return 0; } -static int die__process_and_recode(Dwarf_Die *die, struct cu *cu) +static int die__process_and_recode(Dwarf_Die *die, struct cu *cu, struct conf_load *conf) { - int ret = die__process(die, cu); + int ret = die__process(die, cu, conf); if (ret != 0) return ret; - return cu__recode_dwarf_types(cu); + ret = cu__recode_dwarf_types(cu); + if (ret != 0) + return ret; + + return cu__resolve_func_ret_types(cu); } static int class_member__cache_byte_size(struct tag *tag, struct cu *cu, @@ -2250,7 +2622,7 @@ static int class_member__cache_byte_size(struct tag *tag, struct cu *cu, member->bit_size = member->byte_size * 8; /* - * XXX: after all the attemps to determine byte size, we might still + * XXX: after all the attempts to determine byte size, we might still * be unsuccessful, because base_type__name_to_size doesn't know about * the base_type name, so one has to add there when such base_type * isn't found. pahole will put zero on the struct output so it should @@ -2261,24 +2633,31 @@ static int class_member__cache_byte_size(struct tag *tag, struct cu *cu, return 0; } - /* - * For little-endian architectures, DWARF data emitted by gcc/clang - * specifies bitfield offset as an offset from the highest-order bit - * of an underlying integral type (e.g., int) to a highest-order bit - * of a bitfield. E.g., for bitfield taking first 5 bits of int-backed - * bitfield, bit offset will be 27 (sizeof(int) - 0 offset - 5 bit - * size), which is very counter-intuitive and isn't a natural - * extension of byte offset, which on little-endian points to - * lowest-order byte. So here we re-adjust bitfield offset to be an - * offset from lowest-order bit of underlying integral type to - * a lowest-order bit of a bitfield. This makes bitfield offset - * a natural extension of byte offset for bitfields and is uniform - * with how big-endian bit offsets work. - */ - if (cu->little_endian) { - member->bitfield_offset = member->bit_size - member->bitfield_offset - member->bitfield_size; + if (!member->has_bit_offset) { + /* + * For little-endian architectures, DWARF data emitted by gcc/clang + * specifies bitfield offset as an offset from the highest-order bit + * of an underlying integral type (e.g., int) to a highest-order bit + * of a bitfield. E.g., for bitfield taking first 5 bits of int-backed + * bitfield, bit offset will be 27 (sizeof(int) - 0 offset - 5 bit + * size), which is very counter-intuitive and isn't a natural + * extension of byte offset, which on little-endian points to + * lowest-order byte. So here we re-adjust bitfield offset to be an + * offset from lowest-order bit of underlying integral type to + * a lowest-order bit of a bitfield. This makes bitfield offset + * a natural extension of byte offset for bitfields and is uniform + * with how big-endian bit offsets work. + */ + if (cu->little_endian) + member->bitfield_offset = member->bit_size - member->bitfield_offset - member->bitfield_size; + + member->bit_offset = member->byte_offset * 8 + member->bitfield_offset; + } else { + // DWARF5 has DW_AT_data_bit_offset, offset in bits from the + // start of the container type (struct, class, etc). + member->byte_offset = member->bit_offset / 8; + member->bitfield_offset = member->bit_offset - member->byte_offset * 8; } - member->bit_offset = member->byte_offset * 8 + member->bitfield_offset; /* make sure bitfield offset is non-negative */ if (member->bitfield_offset < 0) { @@ -2303,10 +2682,8 @@ static int class_member__cache_byte_size(struct tag *tag, struct cu *cu, return 0; } -static int finalize_cu(struct cus *cus, struct cu *cu, struct dwarf_cu *dcu, - struct conf_load *conf) +static int cu__finalize(struct cu *cu, struct conf_load *conf) { - base_type_name_to_size_table__init(strings); cu__for_all_tags(cu, class_member__cache_byte_size, conf); if (conf && conf->steal) { return conf->steal(cu, conf); @@ -2314,11 +2691,9 @@ static int finalize_cu(struct cus *cus, struct cu *cu, struct dwarf_cu *dcu, return LSK__KEEPIT; } -static int finalize_cu_immediately(struct cus *cus, struct cu *cu, - struct dwarf_cu *dcu, - struct conf_load *conf) +static int cus__finalize(struct cus *cus, struct cu *cu, struct conf_load *conf) { - int lsk = finalize_cu(cus, cu, dcu, conf); + int lsk = cu__finalize(cu, conf); switch (lsk) { case LSK__DELETE: cu__delete(cu); @@ -2326,20 +2701,32 @@ static int finalize_cu_immediately(struct cus *cus, struct cu *cu, case LSK__STOP_LOADING: break; case LSK__KEEPIT: - if (!cu->extra_dbg_info) - obstack_free(&dcu->obstack, NULL); cus__add(cus, cu); break; } return lsk; } -static int cus__load_debug_types(struct cus *cus, struct conf_load *conf, - Dwfl_Module *mod, Dwarf *dw, Elf *elf, - const char *filename, - const unsigned char *build_id, - int build_id_len, - struct cu **cup, struct dwarf_cu *dcup) +static int cu__set_common(struct cu *cu, struct conf_load *conf, + Dwfl_Module *mod, Elf *elf) +{ + cu->uses_global_strings = true; + cu->elf = elf; + cu->dwfl = mod; + cu->extra_dbg_info = conf ? conf->extra_dbg_info : 0; + cu->has_addr_info = conf ? conf->get_addr_info : 0; + + GElf_Ehdr ehdr; + if (gelf_getehdr(elf, &ehdr) == NULL) + return DWARF_CB_ABORT; + + cu->little_endian = ehdr.e_ident[EI_DATA] == ELFDATA2LSB; + return 0; +} + +static int __cus__load_debug_types(struct conf_load *conf, Dwfl_Module *mod, Dwarf *dw, Elf *elf, + const char *filename, const unsigned char *build_id, + int build_id_len, struct cu **cup, struct dwarf_cu *dcup) { Dwarf_Off off = 0, noff, type_off; size_t cuhl; @@ -2356,24 +2743,14 @@ static int cus__load_debug_types(struct cus *cus, struct conf_load *conf, struct cu *cu; cu = cu__new("", pointer_size, build_id, - build_id_len, filename); - if (cu == NULL) { + build_id_len, filename, conf->use_obstack); + if (cu == NULL || + cu__set_common(cu, conf, mod, elf) != 0) { return DWARF_CB_ABORT; } - cu->uses_global_strings = true; - cu->elf = elf; - cu->dwfl = mod; - cu->extra_dbg_info = conf ? conf->extra_dbg_info : 0; - cu->has_addr_info = conf ? conf->get_addr_info : 0; - - GElf_Ehdr ehdr; - if (gelf_getehdr(elf, &ehdr) == NULL) { + if (dwarf_cu__init(dcup, cu) != 0) return DWARF_CB_ABORT; - } - cu->little_endian = ehdr.e_ident[EI_DATA] == ELFDATA2LSB; - - dwarf_cu__init(dcup); dcup->cu = cu; /* Funny hack. */ dcup->type_unit = dcup; @@ -2387,7 +2764,7 @@ static int cus__load_debug_types(struct cus *cus, struct conf_load *conf, Dwarf_Die *cu_die = dwarf_offdie_types(dw, off + cuhl, &die_mem); - if (die__process(cu_die, *cup) != 0) + if (die__process(cu_die, *cup, conf) != 0) return DWARF_CB_ABORT; off = noff; @@ -2399,40 +2776,236 @@ static int cus__load_debug_types(struct cus *cus, struct conf_load *conf, return 0; } -static int cus__load_module(struct cus *cus, struct conf_load *conf, - Dwfl_Module *mod, Dwarf *dw, Elf *elf, - const char *filename) +/* Match the define in linux:include/linux/elfnote.h */ +#define LINUX_ELFNOTE_BUILD_LTO 0x101 + +static bool cus__merging_cu(Dwarf *dw, Elf *elf) { + Elf_Scn *section = NULL; + while ((section = elf_nextscn(elf, section)) != 0) { + GElf_Shdr header; + if (!gelf_getshdr(section, &header)) + continue; + + if (header.sh_type != SHT_NOTE) + continue; + + Elf_Data *data = NULL; + while ((data = elf_getdata(section, data)) != 0) { + size_t name_off, desc_off, offset = 0; + GElf_Nhdr hdr; + while ((offset = gelf_getnote(data, offset, &hdr, &name_off, &desc_off)) != 0) { + if (hdr.n_type != LINUX_ELFNOTE_BUILD_LTO) + continue; + + /* owner is Linux */ + if (strcmp((char *)data->d_buf + name_off, "Linux") != 0) + continue; + + return *(int *)(data->d_buf + desc_off) != 0; + } + } + } + Dwarf_Off off = 0, noff; size_t cuhl; - const unsigned char *build_id = NULL; + + while (dwarf_nextcu (dw, off, &noff, &cuhl, NULL, NULL, NULL) == 0) { + Dwarf_Die die_mem; + Dwarf_Die *cu_die = dwarf_offdie(dw, off + cuhl, &die_mem); + + if (cu_die == NULL) + break; + + Dwarf_Off offset = 0; + while (true) { + size_t length; + Dwarf_Abbrev *abbrev = dwarf_getabbrev (cu_die, offset, &length); + if (abbrev == NULL || abbrev == DWARF_END_ABBREV) + break; + + size_t attrcnt; + if (dwarf_getattrcnt (abbrev, &attrcnt) != 0) + return false; + + unsigned int attr_num, attr_form; + Dwarf_Off aboffset; + size_t j; + for (j = 0; j < attrcnt; ++j) { + if (dwarf_getabbrevattr (abbrev, j, &attr_num, &attr_form, + &aboffset)) + return false; + if (attr_form == DW_FORM_ref_addr) + return true; + } + + offset += length; + } + + off = noff; + } + + return false; +} + +struct dwarf_cus { + struct cus *cus; + struct conf_load *conf; + Dwfl_Module *mod; + Dwarf *dw; + Elf *elf; + const char *filename; + Dwarf_Off off; + const unsigned char *build_id; + int build_id_len; + int error; + struct dwarf_cu *type_dcu; +}; + +static int dwarf_cus__create_and_process_cu(struct dwarf_cus *dcus, Dwarf_Die *cu_die, uint8_t pointer_size) +{ + /* + * DW_AT_name in DW_TAG_compile_unit can be NULL, first seen in: + * + * /usr/libexec/gcc/x86_64-redhat-linux/4.3.2/ecj1.debug + */ + const char *name = attr_string(cu_die, DW_AT_name, dcus->conf); + struct cu *cu = cu__new(name ?: "", pointer_size, dcus->build_id, dcus->build_id_len, dcus->filename, dcus->conf->use_obstack); + if (cu == NULL || cu__set_common(cu, dcus->conf, dcus->mod, dcus->elf) != 0) + return DWARF_CB_ABORT; + + struct dwarf_cu *dcu = dwarf_cu__new(cu); + + if (dcu == NULL) + return DWARF_CB_ABORT; + + dcu->type_unit = dcus->type_dcu; + cu->priv = dcu; + cu->dfops = &dwarf__ops; + + if (die__process_and_recode(cu_die, cu, dcus->conf) != 0 || + cus__finalize(dcus->cus, cu, dcus->conf) == LSK__STOP_LOADING) + return DWARF_CB_ABORT; + + return DWARF_CB_OK; +} + +static int dwarf_cus__nextcu(struct dwarf_cus *dcus, Dwarf_Die *die_mem, Dwarf_Die **cu_die, uint8_t *pointer_size, uint8_t *offset_size) +{ + Dwarf_Off noff; + size_t cuhl; + int ret; + + cus__lock(dcus->cus); + + if (dcus->error) { + ret = dcus->error; + goto out_unlock; + } + + ret = dwarf_nextcu(dcus->dw, dcus->off, &noff, &cuhl, NULL, pointer_size, offset_size); + if (ret == 0) { + *cu_die = dwarf_offdie(dcus->dw, dcus->off + cuhl, die_mem); + if (*cu_die != NULL) + dcus->off = noff; + } + +out_unlock: + cus__unlock(dcus->cus); + + return ret; +} + +static void *dwarf_cus__process_cu_thread(void *arg) +{ + struct dwarf_cus *dcus = arg; uint8_t pointer_size, offset_size; + Dwarf_Die die_mem, *cu_die; -#ifdef HAVE_DWFL_MODULE_BUILD_ID - GElf_Addr vaddr; - int build_id_len = dwfl_module_build_id(mod, &build_id, &vaddr); -#else - int build_id_len = 0; -#endif + while (dwarf_cus__nextcu(dcus, &die_mem, &cu_die, &pointer_size, &offset_size) == 0) { + if (cu_die == NULL) + break; - struct cu *type_cu; - struct dwarf_cu type_dcu; - int type_lsk = LSK__KEEPIT; + if (dwarf_cus__create_and_process_cu(dcus, cu_die, pointer_size) == DWARF_CB_ABORT) + goto out_abort; + } - int res = cus__load_debug_types(cus, conf, mod, dw, elf, filename, - build_id, build_id_len, - &type_cu, &type_dcu); - if (res != 0) { - return res; + if (dcus->conf->thread_exit && dcus->conf->thread_exit() != 0) + goto out_abort; + + return (void *)DWARF_CB_OK; +out_abort: + return (void *)DWARF_CB_ABORT; +} + +static int dwarf_cus__threaded_process_cus(struct dwarf_cus *dcus) +{ + pthread_t threads[dcus->conf->nr_jobs]; + int i; + + for (i = 0; i < dcus->conf->nr_jobs; ++i) { + dcus->error = pthread_create(&threads[i], NULL, dwarf_cus__process_cu_thread, dcus); + if (dcus->error) + goto out_join; } - if (type_cu != NULL) { - type_lsk = finalize_cu(cus, type_cu, &type_dcu, conf); - if (type_lsk == LSK__KEEPIT) { - cus__add(cus, type_cu); - } + dcus->error = 0; + +out_join: + while (--i >= 0) { + void *res; + int err = pthread_join(threads[i], &res); + + if (err == 0 && res != NULL) + dcus->error = (long)res; + } + + return dcus->error; +} + +static int __dwarf_cus__process_cus(struct dwarf_cus *dcus) +{ + uint8_t pointer_size, offset_size; + Dwarf_Off noff; + size_t cuhl; + + while (dwarf_nextcu(dcus->dw, dcus->off, &noff, &cuhl, NULL, &pointer_size, &offset_size) == 0) { + Dwarf_Die die_mem; + Dwarf_Die *cu_die = dwarf_offdie(dcus->dw, dcus->off + cuhl, &die_mem); + + if (cu_die == NULL) + break; + + if (dwarf_cus__create_and_process_cu(dcus, cu_die, pointer_size) == DWARF_CB_ABORT) + return DWARF_CB_ABORT; + + dcus->off = noff; } + return 0; +} + +static int dwarf_cus__process_cus(struct dwarf_cus *dcus) +{ + if (dcus->conf->nr_jobs > 1) + return dwarf_cus__threaded_process_cus(dcus); + + return __dwarf_cus__process_cus(dcus); +} + +static int cus__merge_and_process_cu(struct cus *cus, struct conf_load *conf, + Dwfl_Module *mod, Dwarf *dw, Elf *elf, + const char *filename, + const unsigned char *build_id, + int build_id_len, + struct dwarf_cu *type_dcu) +{ + uint8_t pointer_size, offset_size; + struct dwarf_cu *dcu = NULL; + Dwarf_Off off = 0, noff; + struct cu *cu = NULL; + size_t cuhl; + while (dwarf_nextcu(dw, off, &noff, &cuhl, NULL, &pointer_size, &offset_size) == 0) { Dwarf_Die die_mem; @@ -2441,46 +3014,123 @@ static int cus__load_module(struct cus *cus, struct conf_load *conf, if (cu_die == NULL) break; - /* - * DW_AT_name in DW_TAG_compile_unit can be NULL, first - * seen in: - * /usr/libexec/gcc/x86_64-redhat-linux/4.3.2/ecj1.debug - */ - const char *name = attr_string(cu_die, DW_AT_name); - struct cu *cu = cu__new(name ?: "", pointer_size, - build_id, build_id_len, filename); - if (cu == NULL) - return DWARF_CB_ABORT; - cu->uses_global_strings = true; - cu->elf = elf; - cu->dwfl = mod; - cu->extra_dbg_info = conf ? conf->extra_dbg_info : 0; - cu->has_addr_info = conf ? conf->get_addr_info : 0; - - GElf_Ehdr ehdr; - if (gelf_getehdr(elf, &ehdr) == NULL) { - return DWARF_CB_ABORT; - } - cu->little_endian = ehdr.e_ident[EI_DATA] == ELFDATA2LSB; + if (cu == NULL) { + cu = cu__new("", pointer_size, build_id, build_id_len, + filename, conf->use_obstack); + if (cu == NULL || cu__set_common(cu, conf, mod, elf) != 0) + goto out_abort; - struct dwarf_cu dcu; + dcu = zalloc(sizeof(*dcu)); + if (dcu == NULL) + goto out_abort; - dwarf_cu__init(&dcu); - dcu.cu = cu; - dcu.type_unit = type_cu ? &type_dcu : NULL; - cu->priv = &dcu; - cu->dfops = &dwarf__ops; + /* Merged cu tends to need a lot more memory. + * Let us start with max_hashtags__bits and + * go down to find a proper hashtag bit value. + */ + uint32_t default_hbits = hashtags__bits; + for (hashtags__bits = max_hashtags__bits; + hashtags__bits >= default_hbits; + hashtags__bits--) { + if (dwarf_cu__init(dcu, cu) == 0) + break; + } + if (hashtags__bits < default_hbits) + goto out_abort; - if (die__process_and_recode(cu_die, cu) != 0) - return DWARF_CB_ABORT; + dcu->cu = cu; + dcu->type_unit = type_dcu; + cu->priv = dcu; + cu->dfops = &dwarf__ops; + cu->language = attr_numeric(cu_die, DW_AT_language); + } - if (finalize_cu_immediately(cus, cu, &dcu, conf) - == LSK__STOP_LOADING) - return DWARF_CB_ABORT; + Dwarf_Die child; + if (dwarf_child(cu_die, &child) == 0) { + if (die__process_unit(&child, cu, conf) != 0) + goto out_abort; + } off = noff; } + if (cu == NULL) + return 0; + + /* process merged cu */ + if (cu__recode_dwarf_types(cu) != LSK__KEEPIT) + goto out_abort; + + /* + * for lto build, the function return type may not be + * resolved due to the return type of a subprogram is + * encoded in another subprogram through abstract_origin + * tag. Let us visit all subprograms again to resolve this. + */ + if (cu__resolve_func_ret_types(cu) != LSK__KEEPIT) + goto out_abort; + + if (cus__finalize(cus, cu, conf) == LSK__STOP_LOADING) + goto out_abort; + + return 0; + +out_abort: + dwarf_cu__delete(cu); + cu__delete(cu); + return DWARF_CB_ABORT; +} + +static int cus__load_module(struct cus *cus, struct conf_load *conf, + Dwfl_Module *mod, Dwarf *dw, Elf *elf, + const char *filename) +{ + const unsigned char *build_id = NULL; +#ifdef HAVE_DWFL_MODULE_BUILD_ID + GElf_Addr vaddr; + int build_id_len = dwfl_module_build_id(mod, &build_id, &vaddr); +#else + int build_id_len = 0; +#endif + struct cu *type_cu; + struct dwarf_cu type_dcu; + int type_lsk = LSK__KEEPIT; + + int res = __cus__load_debug_types(conf, mod, dw, elf, filename, build_id, build_id_len, &type_cu, &type_dcu); + if (res != 0) { + return res; + } + + if (type_cu != NULL) { + type_lsk = cu__finalize(type_cu, conf); + if (type_lsk == LSK__KEEPIT) { + cus__add(cus, type_cu); + } + } + + if (cus__merging_cu(dw, elf)) { + res = cus__merge_and_process_cu(cus, conf, mod, dw, elf, filename, + build_id, build_id_len, + type_cu ? &type_dcu : NULL); + } else { + struct dwarf_cus dcus = { + .off = 0, + .cus = cus, + .conf = conf, + .mod = mod, + .dw = dw, + .elf = elf, + .filename = filename, + .type_dcu = type_cu ? &type_dcu : NULL, + .build_id = build_id, + .build_id_len = build_id_len, + }; + res = dwarf_cus__process_cus(&dcus); + } + + if (res) + return res; + if (type_lsk == LSK__DELETE) cu__delete(type_cu); @@ -2495,9 +3145,9 @@ struct process_dwflmod_parms { }; static int cus__process_dwflmod(Dwfl_Module *dwflmod, - void **userdata __unused, - const char *name __unused, - Dwarf_Addr base __unused, + void **userdata __maybe_unused, + const char *name __maybe_unused, + Dwarf_Addr base __maybe_unused, void *arg) { struct process_dwflmod_parms *parms = arg; @@ -2535,6 +3185,16 @@ static int cus__process_dwflmod(Dwfl_Module *dwflmod, return err; } +static void dwarf_loader__exit(struct cus *cus) +{ + Dwfl *dwfl = cus__priv(cus); + + if (dwfl) { + dwfl_end(dwfl); + cus__set_priv(cus, NULL); + } +} + static int cus__process_file(struct cus *cus, struct conf_load *conf, int fd, const char *filename) { @@ -2559,6 +3219,9 @@ static int cus__process_file(struct cus *cus, struct conf_load *conf, int fd, Dwfl *dwfl = dwfl_begin(&callbacks); + cus__set_priv(cus, dwfl); + cus__set_loader_exit(cus, dwarf_loader__exit); + if (dwfl_report_offline(dwfl, filename, filename, dwfl_fd) == NULL) return -1; @@ -2573,7 +3236,10 @@ static int cus__process_file(struct cus *cus, struct conf_load *conf, int fd, /* Process the one or more modules gleaned from this file. */ dwfl_getmodules(dwfl, cus__process_dwflmod, &parms, 0); - dwfl_end(dwfl); + + // We can't call dwfl_end(dwfl) here, as we keep pointers to strings + // allocated by libdw that will be freed at dwfl_end(), so leave this for + // cus__delete(). return parms.nr_dwarf_sections_found ? 0 : -1; } @@ -2582,6 +3248,21 @@ static int dwarf__load_file(struct cus *cus, struct conf_load *conf, { int fd, err; + if (conf->max_hashtable_bits != 0) { + if (conf->max_hashtable_bits > 31) + return -E2BIG; + + max_hashtags__bits = conf->max_hashtable_bits; + } + + if (conf->hashtable_bits != 0) { + if (conf->hashtable_bits > max_hashtags__bits) + return -E2BIG; + + hashtags__bits = conf->hashtable_bits; + } else if (hashtags__bits > max_hashtags__bits) + return -EINVAL; + elf_version(EV_CURRENT); fd = open(filename, O_RDONLY); @@ -2595,26 +3276,12 @@ static int dwarf__load_file(struct cus *cus, struct conf_load *conf, return err; } -static int dwarf__init(void) -{ - strings = strings__new(); - return strings != NULL ? 0 : -ENOMEM; -} - -static void dwarf__exit(void) -{ - strings__delete(strings); - strings = NULL; -} - struct debug_fmt_ops dwarf__ops = { .name = "dwarf", - .init = dwarf__init, - .exit = dwarf__exit, .load_file = dwarf__load_file, - .strings__ptr = dwarf__strings_ptr, .tag__decl_file = dwarf_tag__decl_file, .tag__decl_line = dwarf_tag__decl_line, .tag__orig_id = dwarf_tag__orig_id, + .cu__delete = dwarf_cu__delete, .has_alignment_info = true, }; @@ -14,6 +14,8 @@ #include <fcntl.h> #include <fnmatch.h> #include <libelf.h> +#include <limits.h> +#include <pthread.h> #include <search.h> #include <stdio.h> #include <stdarg.h> @@ -27,13 +29,44 @@ #include "list.h" #include "dwarves.h" #include "dutil.h" -#include "pahole_strings.h" -#include <obstack.h> + +#define min(x, y) ((x) < (y) ? (x) : (y)) #define obstack_chunk_alloc malloc #define obstack_chunk_free free -#define min(x, y) ((x) < (y) ? (x) : (y)) +static void *obstack_zalloc(struct obstack *obstack, size_t size) +{ + void *o = obstack_alloc(obstack, size); + + if (o) + memset(o, 0, size); + return o; +} + +void *cu__zalloc(struct cu *cu, size_t size) +{ + if (cu->use_obstack) + return obstack_zalloc(&cu->obstack, size); + + return zalloc(size); +} + +void *cu__malloc(struct cu *cu, size_t size) +{ + if (cu->use_obstack) + return obstack_alloc(&cu->obstack, size); + + return malloc(size); +} + +void cu__free(struct cu *cu, void *ptr) +{ + if (!cu->use_obstack) + free(ptr); + + // When using an obstack we'll free everything in cu__delete() +} int tag__is_base_type(const struct tag *tag, const struct cu *cu) { @@ -70,18 +103,6 @@ bool tag__is_array(const struct tag *tag, const struct cu *cu) return 0; } -const char *cu__string(const struct cu *cu, strings_t s) -{ - if (cu->dfops && cu->dfops->strings__ptr) - return cu->dfops->strings__ptr(cu, s); - return NULL; -} - -static inline const char *s(const struct cu *cu, strings_t i) -{ - return cu__string(cu, i); -} - int __tag__has_type_loop(const struct tag *tag, const struct tag *type, char *bf, size_t len, FILE *fp, const char *fn, int line) @@ -108,43 +129,49 @@ int __tag__has_type_loop(const struct tag *tag, const struct tag *type, return 0; } -static void lexblock__delete_tags(struct tag *tag, struct cu *cu) +static void lexblock__delete_tags(struct tag *tag) { struct lexblock *block = tag__lexblock(tag); struct tag *pos, *n; list_for_each_entry_safe_reverse(pos, n, &block->tags, node) { list_del_init(&pos->node); - tag__delete(pos, cu); + tag__delete(pos); } } -void lexblock__delete(struct lexblock *block, struct cu *cu) +void lexblock__delete(struct lexblock *block) { - lexblock__delete_tags(&block->ip.tag, cu); - obstack_free(&cu->obstack, block); + if (block == NULL) + return; + + lexblock__delete_tags(&block->ip.tag); + free(block); } -void tag__delete(struct tag *tag, struct cu *cu) +void tag__delete(struct tag *tag) { + if (tag == NULL) + return; + assert(list_empty(&tag->node)); switch (tag->tag) { case DW_TAG_union_type: - type__delete(tag__type(tag), cu); break; + type__delete(tag__type(tag)); break; case DW_TAG_class_type: case DW_TAG_structure_type: - class__delete(tag__class(tag), cu); break; + class__delete(tag__class(tag)); break; case DW_TAG_enumeration_type: - enumeration__delete(tag__type(tag), cu); break; + enumeration__delete(tag__type(tag)); break; case DW_TAG_subroutine_type: - ftype__delete(tag__ftype(tag), cu); break; + ftype__delete(tag__ftype(tag)); break; case DW_TAG_subprogram: - function__delete(tag__function(tag), cu); break; + function__delete(tag__function(tag)); break; case DW_TAG_lexical_block: - lexblock__delete(tag__lexblock(tag), cu); break; + lexblock__delete(tag__lexblock(tag)); break; default: - obstack_free(&cu->obstack, tag); + free(tag); } } @@ -181,9 +208,8 @@ size_t __tag__id_not_found_fprintf(FILE *fp, type_id_t id, return fprintf(fp, "<ERROR(%s:%d): %d not found!>\n", fn, line, id); } -static struct base_type_name_to_size { +static struct ase_type_name_to_size { const char *name; - strings_t sname; size_t size; } base_type_name_to_size_table[] = { { .name = "unsigned", .size = 32, }, @@ -224,19 +250,6 @@ static struct base_type_name_to_size { { .name = NULL }, }; -void base_type_name_to_size_table__init(struct strings *strings) -{ - int i = 0; - - while (base_type_name_to_size_table[i].name != NULL) { - if (base_type_name_to_size_table[i].sname == 0) - base_type_name_to_size_table[i].sname = - strings__find(strings, - base_type_name_to_size_table[i].name); - ++i; - } -} - size_t base_type__name_to_size(struct base_type *bt, struct cu *cu) { int i = 0; @@ -244,22 +257,21 @@ size_t base_type__name_to_size(struct base_type *bt, struct cu *cu) const char *name, *orig_name; if (bt->name_has_encoding) - name = s(cu, bt->name); + name = bt->name; else - name = base_type__name(bt, cu, bf, sizeof(bf)); + name = base_type__name(bt, bf, sizeof(bf)); orig_name = name; try_again: while (base_type_name_to_size_table[i].name != NULL) { if (bt->name_has_encoding) { - if (base_type_name_to_size_table[i].sname == bt->name) { + if (strcmp(base_type_name_to_size_table[i].name, bt->name) == 0) { size_t size; found: size = base_type_name_to_size_table[i].size; return size ?: ((size_t)cu->addr_size * 8); } - } else if (strcmp(base_type_name_to_size_table[i].name, - name) == 0) + } else if (strcmp(base_type_name_to_size_table[i].name, name) == 0) goto found; ++i; } @@ -290,38 +302,40 @@ static const char *base_type_fp_type_str[] = { [BT_FP_IMGRY_LDBL] = "imaginary long double", }; -const char *base_type__name(const struct base_type *bt, const struct cu *cu, - char *bf, size_t len) +const char *__base_type__name(const struct base_type *bt) +{ + return bt->name; +} + +const char *base_type__name(const struct base_type *bt, char *bf, size_t len) { if (bt->name_has_encoding) - return s(cu, bt->name); + return __base_type__name(bt); if (bt->float_type) - snprintf(bf, len, "%s %s", - base_type_fp_type_str[bt->float_type], - s(cu, bt->name)); + snprintf(bf, len, "%s %s", base_type_fp_type_str[bt->float_type], bt->name); else - snprintf(bf, len, "%s%s%s", - bt->is_bool ? "bool " : "", - bt->is_varargs ? "... " : "", - s(cu, bt->name)); + snprintf(bf, len, "%s%s%s", bt->is_bool ? "bool " : "", bt->is_varargs ? "... " : "", bt->name); return bf; } -void namespace__delete(struct namespace *space, struct cu *cu) +void namespace__delete(struct namespace *space) { struct tag *pos, *n; + if (space == NULL) + return; + namespace__for_each_tag_safe_reverse(space, pos, n) { list_del_init(&pos->node); /* Look for nested namespaces */ if (tag__has_namespace(pos)) - namespace__delete(tag__namespace(pos), cu); - tag__delete(pos, cu); + namespace__delete(tag__namespace(pos)); + tag__delete(pos); } - tag__delete(&space->tag, cu); + tag__delete(&space->tag); } void __type__init(struct type *type) @@ -412,10 +426,43 @@ static void cu__find_class_holes(struct cu *cu) class__find_holes(pos); } +struct cus { + uint32_t nr_entries; + struct list_head cus; + pthread_mutex_t mutex; + void (*loader_exit)(struct cus *cus); + void *priv; // Used in dwarf_loader__exit() +}; + +void cus__lock(struct cus *cus) +{ + pthread_mutex_lock(&cus->mutex); +} + +void cus__unlock(struct cus *cus) +{ + pthread_mutex_unlock(&cus->mutex); +} + +bool cus__empty(const struct cus *cus) +{ + return list_empty(&cus->cus); +} + +uint32_t cus__nr_entries(const struct cus *cus) +{ + return cus->nr_entries; +} + void cus__add(struct cus *cus, struct cu *cu) { + cus__lock(cus); + cus->nr_entries++; list_add_tail(&cu->node, &cus->cus); + + cus__unlock(cus); + cu__find_class_holes(cu); } @@ -427,8 +474,7 @@ static void ptr_table__init(struct ptr_table *pt) static void ptr_table__exit(struct ptr_table *pt) { - free(pt->entries); - pt->entries = NULL; + zfree(&pt->entries); } static int ptr_table__add(struct ptr_table *pt, void *ptr, uint32_t *idxp) @@ -437,12 +483,16 @@ static int ptr_table__add(struct ptr_table *pt, void *ptr, uint32_t *idxp) const uint32_t rc = pt->nr_entries; if (nr_entries > pt->allocated_entries) { - uint32_t allocated_entries = pt->allocated_entries + 256; + uint32_t allocated_entries = pt->allocated_entries + 2048; void *entries = realloc(pt->entries, sizeof(void *) * allocated_entries); if (entries == NULL) return -ENOMEM; + /* Zero out the new range */ + memset(entries + pt->allocated_entries * sizeof(void *), 0, + (allocated_entries - pt->allocated_entries) * sizeof(void *)); + pt->allocated_entries = allocated_entries; pt->entries = entries; } @@ -458,7 +508,7 @@ static int ptr_table__add_with_id(struct ptr_table *pt, void *ptr, { /* Assume we won't be fed with the same id more than once */ if (id >= pt->allocated_entries) { - uint32_t allocated_entries = roundup(id + 1, 256); + uint32_t allocated_entries = roundup(id + 1, 2048); void *entries = realloc(pt->entries, sizeof(void *) * allocated_entries); if (entries == NULL) @@ -555,21 +605,42 @@ int cu__add_tag_with_id(struct cu *cu, struct tag *tag, uint32_t id) return err; } +int cus__fprintf_ptr_table_stats_csv_header(FILE *fp) +{ + return fprintf(fp, "# cu,tags,allocated_tags,types,allocated_types,functions,allocated_functions\n"); +} + +int cu__fprintf_ptr_table_stats_csv(struct cu *cu, FILE *fp) +{ + int printed = fprintf(fp, "%s,%u,%u,%u,%u,%u,%u\n", cu->name, + cu->tags_table.nr_entries, cu->tags_table.allocated_entries, + cu->types_table.nr_entries, cu->types_table.allocated_entries, + cu->functions_table.nr_entries, cu->functions_table.allocated_entries); + + return printed; +} + struct cu *cu__new(const char *name, uint8_t addr_size, const unsigned char *build_id, int build_id_len, - const char *filename) + const char *filename, bool use_obstack) { struct cu *cu = malloc(sizeof(*cu) + build_id_len); if (cu != NULL) { uint32_t void_id; + cu->use_obstack = use_obstack; + if (cu->use_obstack) + obstack_init(&cu->obstack); + cu->name = strdup(name); - cu->filename = strdup(filename); - if (cu->name == NULL || cu->filename == NULL) + if (cu->name == NULL) goto out_free; - obstack_init(&cu->obstack); + cu->filename = strdup(filename); + if (cu->filename == NULL) + goto out_free_name; + ptr_table__init(&cu->tags_table); ptr_table__init(&cu->types_table); ptr_table__init(&cu->functions_table); @@ -578,7 +649,7 @@ struct cu *cu__new(const char *name, uint8_t addr_size, * so make sure we don't use it */ if (ptr_table__add(&cu->types_table, NULL, &void_id) < 0) - goto out_free_name; + goto out_free_filename; cu->functions = RB_ROOT; @@ -599,28 +670,36 @@ struct cu *cu__new(const char *name, uint8_t addr_size, cu->build_id_len = build_id_len; if (build_id_len > 0) memcpy(cu->build_id, build_id, build_id_len); + cu->priv = NULL; } -out: + return cu; + +out_free_filename: + zfree(&cu->filename); out_free_name: - free(cu->name); - free(cu->filename); + zfree(&cu->name); out_free: free(cu); - cu = NULL; - goto out; + return NULL; } void cu__delete(struct cu *cu) { + if (cu == NULL) + return; + ptr_table__exit(&cu->tags_table); ptr_table__exit(&cu->types_table); ptr_table__exit(&cu->functions_table); if (cu->dfops && cu->dfops->cu__delete) cu->dfops->cu__delete(cu); - obstack_free(&cu->obstack, NULL); - free(cu->filename); - free(cu->name); + + if (cu->use_obstack) + obstack_free(&cu->obstack, NULL); + + zfree(&cu->filename); + zfree(&cu->name); free(cu); } @@ -677,7 +756,7 @@ struct tag *cu__find_base_type_by_name(const struct cu *cu, const struct base_type *bt = tag__base_type(pos); char bf[64]; - const char *bname = base_type__name(bt, cu, bf, sizeof(bf)); + const char *bname = base_type__name(bt, bf, sizeof(bf)); if (!bname || strcmp(bname, name) != 0) continue; @@ -689,23 +768,22 @@ struct tag *cu__find_base_type_by_name(const struct cu *cu, return NULL; } -struct tag *cu__find_base_type_by_sname_and_size(const struct cu *cu, - strings_t sname, - uint16_t bit_size, - type_id_t *idp) +struct tag *cu__find_base_type_by_name_and_size(const struct cu *cu, const char *name, + uint16_t bit_size, type_id_t *idp) { uint32_t id; struct tag *pos; - if (sname == 0) + if (name == NULL) return NULL; cu__for_each_type(cu, id, pos) { if (pos->tag == DW_TAG_base_type) { const struct base_type *bt = tag__base_type(pos); + char bf[64]; if (bt->bit_size == bit_size && - bt->name == sname) { + strcmp(base_type__name(bt, bf, sizeof(bf)), name) == 0) { if (idp != NULL) *idp = id; return pos; @@ -716,15 +794,13 @@ struct tag *cu__find_base_type_by_sname_and_size(const struct cu *cu, return NULL; } -struct tag *cu__find_enumeration_by_sname_and_size(const struct cu *cu, - strings_t sname, - uint16_t bit_size, - type_id_t *idp) +struct tag *cu__find_enumeration_by_name_and_size(const struct cu *cu, const char *name, + uint16_t bit_size, type_id_t *idp) { uint32_t id; struct tag *pos; - if (sname == 0) + if (name == NULL) return NULL; cu__for_each_type(cu, id, pos) { @@ -732,7 +808,7 @@ struct tag *cu__find_enumeration_by_sname_and_size(const struct cu *cu, const struct type *t = tag__type(pos); if (t->size == bit_size && - t->namespace.name == sname) { + strcmp(type__name(t), name) == 0) { if (idp != NULL) *idp = id; return pos; @@ -754,7 +830,7 @@ struct tag *cu__find_enumeration_by_name(const struct cu *cu, const char *name, cu__for_each_type(cu, id, pos) { if (pos->tag == DW_TAG_enumeration_type) { const struct type *type = tag__type(pos); - const char *tname = type__name(type, cu); + const char *tname = type__name(type); if (tname && strcmp(tname, name) == 0) { if (idp != NULL) @@ -767,39 +843,6 @@ struct tag *cu__find_enumeration_by_name(const struct cu *cu, const char *name, return NULL; } -struct tag *cu__find_struct_by_sname(const struct cu *cu, strings_t sname, - const int include_decls, type_id_t *idp) -{ - uint32_t id; - struct tag *pos; - - if (sname == 0) - return NULL; - - cu__for_each_type(cu, id, pos) { - struct type *type; - - if (!tag__is_struct(pos)) - continue; - - type = tag__type(pos); - if (type->namespace.name == sname) { - if (!type->declaration) - goto found; - - if (include_decls) - goto found; - } - } - - return NULL; -found: - if (idp != NULL) - *idp = id; - return pos; - -} - struct tag *cu__find_type_by_name(const struct cu *cu, const char *name, const int include_decls, type_id_t *idp) { if (cu == NULL || name == NULL) @@ -814,7 +857,7 @@ struct tag *cu__find_type_by_name(const struct cu *cu, const char *name, const i continue; type = tag__type(pos); - const char *tname = type__name(type, cu); + const char *tname = type__name(type); if (tname && strcmp(tname, name) == 0) { if (!type->declaration) goto found; @@ -831,21 +874,26 @@ found: return pos; } -struct tag *cus__find_type_by_name(const struct cus *cus, struct cu **cu, const char *name, +struct tag *cus__find_type_by_name(struct cus *cus, struct cu **cu, const char *name, const int include_decls, type_id_t *id) { struct cu *pos; + struct tag *tag = NULL; + + cus__lock(cus); list_for_each_entry(pos, &cus->cus, node) { - struct tag *tag = cu__find_type_by_name(pos, name, include_decls, id); + tag = cu__find_type_by_name(pos, name, include_decls, id); if (tag != NULL) { if (cu != NULL) *cu = pos; - return tag; + break; } } - return NULL; + cus__unlock(cus); + + return tag; } static struct tag *__cu__find_struct_by_name(const struct cu *cu, const char *name, @@ -863,7 +911,7 @@ static struct tag *__cu__find_struct_by_name(const struct cu *cu, const char *na continue; type = tag__type(pos); - const char *tname = type__name(type, cu); + const char *tname = type__name(type); if (tname && strcmp(tname, name) == 0) { if (!type->declaration) goto found; @@ -892,32 +940,36 @@ struct tag *cu__find_struct_or_union_by_name(const struct cu *cu, const char *na return __cu__find_struct_by_name(cu, name, include_decls, true, idp); } -static struct tag *__cus__find_struct_by_name(const struct cus *cus, - struct cu **cu, const char *name, +static struct tag *__cus__find_struct_by_name(struct cus *cus, struct cu **cu, const char *name, const int include_decls, bool unions, type_id_t *id) { + struct tag *tag = NULL; struct cu *pos; + cus__lock(cus); + list_for_each_entry(pos, &cus->cus, node) { struct tag *tag = __cu__find_struct_by_name(pos, name, include_decls, unions, id); if (tag != NULL) { if (cu != NULL) *cu = pos; - return tag; + break; } } - return NULL; + cus__unlock(cus); + + return tag; } -struct tag *cus__find_struct_by_name(const struct cus *cus, struct cu **cu, const char *name, +struct tag *cus__find_struct_by_name(struct cus *cus, struct cu **cu, const char *name, const int include_decls, type_id_t *idp) { return __cus__find_struct_by_name(cus, cu, name, include_decls, false, idp); } -struct tag *cus__find_struct_or_union_by_name(const struct cus *cus, struct cu **cu, const char *name, - const int include_decls, type_id_t *idp) +struct tag *cus__find_struct_or_union_by_name(struct cus *cus, struct cu **cu, const char *name, + const int include_decls, type_id_t *idp) { return __cus__find_struct_by_name(cus, cu, name, include_decls, true, idp); } @@ -947,32 +999,68 @@ struct function *cu__find_function_at_addr(const struct cu *cu, } -struct function *cus__find_function_at_addr(const struct cus *cus, - uint64_t addr, struct cu **cu) +struct function *cus__find_function_at_addr(struct cus *cus, uint64_t addr, struct cu **cu) { + struct function *f = NULL; struct cu *pos; + cus__lock(cus); + list_for_each_entry(pos, &cus->cus, node) { - struct function *f = cu__find_function_at_addr(pos, addr); + f = cu__find_function_at_addr(pos, addr); if (f != NULL) { if (cu != NULL) *cu = pos; - return f; + break; } } - return NULL; + + cus__unlock(cus); + + return f; } -struct cu *cus__find_cu_by_name(const struct cus *cus, const char *name) +static struct cu *__cus__find_cu_by_name(struct cus *cus, const char *name) { struct cu *pos; list_for_each_entry(pos, &cus->cus, node) if (pos->name && strcmp(pos->name, name) == 0) - return pos; + goto out; - return NULL; + pos = NULL; +out: + return pos; +} + +struct cu *cus__find_cu_by_name(struct cus *cus, const char *name) +{ + struct cu *pos; + + cus__lock(cus); + + pos = __cus__find_cu_by_name(cus, name); + + cus__unlock(cus); + + return pos; +} + +struct cu *cus__find_pair(struct cus *cus, const char *name) +{ + struct cu *cu; + + cus__lock(cus); + + if (cus->nr_entries == 1) + cu = list_first_entry(&cus->cus, struct cu, node); + else + cu = __cus__find_cu_by_name(cus, name); + + cus__unlock(cus); + + return cu; } struct tag *cu__find_function_by_name(const struct cu *cu, const char *name) @@ -983,7 +1071,7 @@ struct tag *cu__find_function_by_name(const struct cu *cu, const char *name) uint32_t id; struct function *pos; cu__for_each_function(cu, id, pos) { - const char *fname = function__name(pos, cu); + const char *fname = function__name(pos); if (fname && strcmp(fname, name) == 0) return function__tag(pos); } @@ -1050,11 +1138,9 @@ size_t tag__size(const struct tag *tag, const struct cu *cu) return size; } -const char *variable__name(const struct variable *var, const struct cu *cu) +const char *variable__name(const struct variable *var) { - if (cu->dfops && cu->dfops->variable__name) - return cu->dfops->variable__name(var, cu); - return s(cu, var->name); + return var->name; } const char *variable__type_name(const struct variable *var, @@ -1065,15 +1151,14 @@ const char *variable__type_name(const struct variable *var, return tag != NULL ? tag__name(tag, cu, bf, len, NULL) : NULL; } -void class_member__delete(struct class_member *member, struct cu *cu) +void class_member__delete(struct class_member *member) { - obstack_free(&cu->obstack, member); + free(member); } -static struct class_member *class_member__clone(const struct class_member *from, - struct cu *cu) +static struct class_member *class_member__clone(const struct class_member *from) { - struct class_member *member = obstack_alloc(&cu->obstack, sizeof(*member)); + struct class_member *member = malloc(sizeof(*member)); if (member != NULL) memcpy(member, from, sizeof(*member)); @@ -1081,42 +1166,52 @@ static struct class_member *class_member__clone(const struct class_member *from, return member; } -static void type__delete_class_members(struct type *type, struct cu *cu) +static void type__delete_class_members(struct type *type) { struct class_member *pos, *next; type__for_each_tag_safe_reverse(type, pos, next) { list_del_init(&pos->tag.node); - class_member__delete(pos, cu); + class_member__delete(pos); } } -void class__delete(struct class *class, struct cu *cu) +void class__delete(struct class *class) { - if (class->type.namespace.sname != NULL) - free(class->type.namespace.sname); - type__delete_class_members(&class->type, cu); - obstack_free(&cu->obstack, class); + if (class == NULL) + return; + + type__delete_class_members(&class->type); + free(class); } -void type__delete(struct type *type, struct cu *cu) +void type__delete(struct type *type) { - type__delete_class_members(type, cu); - obstack_free(&cu->obstack, type); + if (type == NULL) + return; + + type__delete_class_members(type); + free(type); } -static void enumerator__delete(struct enumerator *enumerator, struct cu *cu) +static void enumerator__delete(struct enumerator *enumerator) { - obstack_free(&cu->obstack, enumerator); + free(enumerator); } -void enumeration__delete(struct type *type, struct cu *cu) +void enumeration__delete(struct type *type) { struct enumerator *pos, *n; + + if (type == NULL) + return; + type__for_each_enumerator_safe_reverse(type, pos, n) { list_del_init(&pos->tag.node); - enumerator__delete(pos, cu); + enumerator__delete(pos); } + + free(type); } void class__add_vtable_entry(struct class *class, struct function *vtable_entry) @@ -1150,8 +1245,7 @@ struct class_member *type__last_member(struct type *type) return NULL; } -static int type__clone_members(struct type *type, const struct type *from, - struct cu *cu) +static int type__clone_members(struct type *type, const struct type *from) { struct class_member *pos; @@ -1159,7 +1253,7 @@ static int type__clone_members(struct type *type, const struct type *from, INIT_LIST_HEAD(&type->namespace.tags); type__for_each_member(from, pos) { - struct class_member *clone = class_member__clone(pos, cu); + struct class_member *clone = class_member__clone(pos); if (clone == NULL) return -1; @@ -1169,23 +1263,21 @@ static int type__clone_members(struct type *type, const struct type *from, return 0; } -struct class *class__clone(const struct class *from, - const char *new_class_name, struct cu *cu) +struct class *class__clone(const struct class *from, const char *new_class_name) { - struct class *class = obstack_alloc(&cu->obstack, sizeof(*class)); + struct class *class = malloc(sizeof(*class)); if (class != NULL) { memcpy(class, from, sizeof(*class)); if (new_class_name != NULL) { - class->type.namespace.name = 0; - class->type.namespace.sname = strdup(new_class_name); - if (class->type.namespace.sname == NULL) { + class->type.namespace.name = strdup(new_class_name); + if (class->type.namespace.name == NULL) { free(class); return NULL; } } - if (type__clone_members(&class->type, &from->type, cu) != 0) { - class__delete(class, cu); + if (type__clone_members(&class->type, &from->type) != 0) { + class__delete(class); class = NULL; } } @@ -1205,19 +1297,17 @@ void lexblock__add_lexblock(struct lexblock *block, struct lexblock *child) list_add_tail(&child->ip.tag.node, &block->tags); } -const char *function__name(struct function *func, const struct cu *cu) +const char *function__name(struct function *func) { - if (cu->dfops && cu->dfops->function__name) - return cu->dfops->function__name(func, cu); - return s(cu, func->name); + return func->name; } -static void parameter__delete(struct parameter *parm, struct cu *cu) +static void parameter__delete(struct parameter *parm) { - obstack_free(&cu->obstack, parm); + free(parm); } -void ftype__delete(struct ftype *type, struct cu *cu) +void ftype__delete(struct ftype *type) { struct parameter *pos, *n; @@ -1226,15 +1316,18 @@ void ftype__delete(struct ftype *type, struct cu *cu) ftype__for_each_parameter_safe_reverse(type, pos, n) { list_del_init(&pos->tag.node); - parameter__delete(pos, cu); + parameter__delete(pos); } - obstack_free(&cu->obstack, type); + free(type); } -void function__delete(struct function *func, struct cu *cu) +void function__delete(struct function *func) { - lexblock__delete_tags(&func->lexblock.ip.tag, cu); - ftype__delete(&func->proto, cu); + if (func == NULL) + return; + + lexblock__delete_tags(&func->lexblock.ip.tag); + ftype__delete(&func->proto); } int ftype__has_parm_of_type(const struct ftype *ftype, const type_id_t target, @@ -1312,10 +1405,9 @@ void class__find_holes(struct class *class) { const struct type *ctype = &class->type; struct class_member *pos, *last = NULL; - int cur_bitfield_end = ctype->size * 8, cur_bitfield_size = 0; + uint32_t cur_bitfield_end = ctype->size * 8, cur_bitfield_size = 0; int bit_holes = 0, byte_holes = 0; - int bit_start, bit_end; - int last_seen_bit = 0; + uint32_t bit_start, bit_end, last_seen_bit = 0; bool in_bitfield = false; if (!tag__is_struct(class__tag(class))) @@ -1355,7 +1447,7 @@ void class__find_holes(struct class *class) last_seen_bit = bitfield_end; } if (pos->bitfield_size) { - int aligned_start = pos->byte_offset * 8; + uint32_t aligned_start = pos->byte_offset * 8; /* we can have some alignment byte padding left, * but we need to be careful about bitfield spanning * multiple aligned boundaries */ @@ -1423,7 +1515,7 @@ void class__find_holes(struct class *class) static size_t type__natural_alignment(struct type *type, const struct cu *cu); -static size_t tag__natural_alignment(struct tag *tag, const struct cu *cu) +size_t tag__natural_alignment(struct tag *tag, const struct cu *cu) { size_t natural_alignment = 1; @@ -1628,16 +1720,14 @@ int class__has_hole_ge(const struct class *class, const uint16_t size) return 0; } -struct class_member *type__find_member_by_name(const struct type *type, - const struct cu *cu, - const char *name) +struct class_member *type__find_member_by_name(const struct type *type, const char *name) { if (name == NULL) return NULL; struct class_member *pos; type__for_each_data_member(type, pos) { - const char *curr_name = class_member__name(pos, cu); + const char *curr_name = class_member__name(pos); if (curr_name && strcmp(curr_name, name) == 0) return pos; } @@ -1658,7 +1748,7 @@ static int strcommon(const char *a, const char *b) return i; } -void enumeration__calc_prefix(struct type *enumeration, const struct cu *cu) +static void enumeration__calc_prefix(struct type *enumeration) { if (enumeration->member_prefix) return; @@ -1668,7 +1758,7 @@ void enumeration__calc_prefix(struct type *enumeration, const struct cu *cu) struct enumerator *entry; type__for_each_enumerator(enumeration, entry) { - const char *curr_name = enumerator__name(entry, cu); + const char *curr_name = enumerator__name(entry); if (previous_name) { int curr_common_part = strcommon(curr_name, previous_name); @@ -1680,8 +1770,14 @@ void enumeration__calc_prefix(struct type *enumeration, const struct cu *cu) previous_name = curr_name; } - enumeration->member_prefix = strndup(curr_name, common_part); - enumeration->member_prefix_len = common_part == INT32_MAX ? 0 : common_part; + enumeration->member_prefix = NULL; + enumeration->member_prefix_len = 0; + + if (common_part != INT32_MAX) { + enumeration->member_prefix = strndup(curr_name, common_part); + if (enumeration->member_prefix != NULL) + enumeration->member_prefix_len = common_part; + } } void enumerations__calc_prefix(struct list_head *enumerations) @@ -1689,26 +1785,9 @@ void enumerations__calc_prefix(struct list_head *enumerations) struct tag_cu_node *pos; list_for_each_entry(pos, enumerations, node) - enumeration__calc_prefix(tag__type(pos->tc.tag), pos->tc.cu); + enumeration__calc_prefix(tag__type(pos->tc.tag)); } -const char *enumeration__prefix(struct type *enumeration, const struct cu *cu) -{ - if (!enumeration->member_prefix) - enumeration__calc_prefix(enumeration, cu); - - return enumeration->member_prefix; -} - -uint16_t enumeration__prefix_len(struct type *enumeration, const struct cu *cu) -{ - if (!enumeration->member_prefix) - enumeration__calc_prefix(enumeration, cu); - - return enumeration->member_prefix_len; -} - - uint32_t type__nr_members_of_type(const struct type *type, const type_id_t type_id) { struct class_member *pos; @@ -1828,6 +1907,8 @@ void cus__for_each_cu(struct cus *cus, { struct cu *pos; + cus__lock(cus); + list_for_each_entry(pos, &cus->cus, node) { struct cu *cu = pos; if (filter != NULL) { @@ -1838,6 +1919,8 @@ void cus__for_each_cu(struct cus *cus, if (iterator(cu, cookie)) break; } + + cus__unlock(cus); } int cus__load_dir(struct cus *cus, struct conf_load *conf, @@ -1892,17 +1975,15 @@ out: /* * This should really do demand loading of DSOs, STABS anyone? 8-) */ -extern struct debug_fmt_ops dwarf__ops, ctf__ops, btf_elf__ops; +extern struct debug_fmt_ops dwarf__ops, ctf__ops, btf__ops; static struct debug_fmt_ops *debug_fmt_table[] = { &dwarf__ops, - &btf_elf__ops, + &btf__ops, &ctf__ops, NULL, }; -struct debug_fmt_ops *dwarves__active_loader; - static int debugging_formats__loader(const char *name) { int i = 0; @@ -1940,7 +2021,6 @@ int cus__load_file(struct cus *cus, struct conf_load *conf, conf->conf_fprintf->has_alignment_info = debug_fmt_table[loader]->has_alignment_info; err = 0; - dwarves__active_loader = debug_fmt_table[loader]; if (debug_fmt_table[loader]->load_file(cus, conf, filename) == 0) break; @@ -1952,20 +2032,17 @@ int cus__load_file(struct cus *cus, struct conf_load *conf, fp = sep + 1; } free(fpath); - dwarves__active_loader = NULL; return err; } while (debug_fmt_table[i] != NULL) { if (conf && conf->conf_fprintf) conf->conf_fprintf->has_alignment_info = debug_fmt_table[i]->has_alignment_info; - dwarves__active_loader = debug_fmt_table[i]; if (debug_fmt_table[i]->load_file(cus, conf, filename) == 0) return 0; ++i; } - dwarves__active_loader = NULL; return -EINVAL; } @@ -2080,18 +2157,15 @@ static int elf_read_build_id(Elf *elf, void *bf, size_t size) * '.note' (VDSO specific) */ do { - sec = elf_section_by_name(elf, &ehdr, &shdr, - ".note.gnu.build-id", NULL); + sec = elf_section_by_name(elf, &shdr, ".note.gnu.build-id", NULL); if (sec) break; - sec = elf_section_by_name(elf, &ehdr, &shdr, - ".notes", NULL); + sec = elf_section_by_name(elf, &shdr, ".notes", NULL); if (sec) break; - sec = elf_section_by_name(elf, &ehdr, &shdr, - ".note", NULL); + sec = elf_section_by_name(elf, &shdr, ".note", NULL); if (sec) break; @@ -2204,8 +2278,6 @@ static int filename__sprintf_build_id(const char *pathname, char *sbuild_id) return build_id__sprintf(build_id, sizeof(build_id), sbuild_id); } -#define zfree(ptr) ({ free(*ptr); *ptr = NULL; }) - static int vmlinux_path__nr_entries; static char **vmlinux_path; @@ -2289,10 +2361,8 @@ static int cus__load_running_kernel(struct cus *cus, struct conf_load *conf) if (conf && conf->conf_fprintf) conf->conf_fprintf->has_alignment_info = debug_fmt_table[loader]->has_alignment_info; - dwarves__active_loader = debug_fmt_table[loader]; if (debug_fmt_table[loader]->load_file(cus, conf, "/sys/kernel/btf/vmlinux") == 0) return 0; - dwarves__active_loader = NULL; } try_elf: elf_version(EV_CURRENT); @@ -2329,7 +2399,7 @@ int cus__load_files(struct cus *cus, struct conf_load *conf, return i ? 0 : cus__load_running_kernel(cus, conf); } -int cus__fprintf_load_files_err(struct cus *cus, const char *tool, char *argv[], int err, FILE *output) +int cus__fprintf_load_files_err(struct cus *cus __maybe_unused, const char *tool, char *argv[], int err, FILE *output) { /* errno is not properly preserved in some cases, sigh */ return fprintf(output, "%s: %s: %s\n", tool, argv[-err - 1], @@ -2341,8 +2411,11 @@ struct cus *cus__new(void) struct cus *cus = malloc(sizeof(*cus)); if (cus != NULL) { - cus->nr_entries = 0; + cus->nr_entries = 0; + cus->priv = NULL; + cus->loader_exit = NULL; INIT_LIST_HEAD(&cus->cus); + pthread_mutex_init(&cus->mutex, NULL); } return cus; @@ -2355,20 +2428,38 @@ void cus__delete(struct cus *cus) if (cus == NULL) return; + cus__lock(cus); + list_for_each_entry_safe(pos, n, &cus->cus, node) { list_del_init(&pos->node); cu__delete(pos); } + if (cus->loader_exit) + cus->loader_exit(cus); + + cus__unlock(cus); + free(cus); } -void dwarves__fprintf_init(uint16_t user_cacheline_size); +void cus__set_priv(struct cus *cus, void *priv) +{ + cus->priv = priv; +} -int dwarves__init(uint16_t user_cacheline_size) +void *cus__priv(struct cus *cus) { - dwarves__fprintf_init(user_cacheline_size); + return cus->priv; +} +void cus__set_loader_exit(struct cus *cus, void (*loader_exit)(struct cus *cus)) +{ + cus->loader_exit = loader_exit; +} + +int dwarves__init(void) +{ int i = 0; int err = 0; @@ -2402,7 +2493,7 @@ void dwarves__exit(void) struct argp_state; -void dwarves_print_version(FILE *fp, struct argp_state *state __unused) +void dwarves_print_version(FILE *fp, struct argp_state *state __maybe_unused) { fprintf(fp, "v%u.%u\n", DWARVES_MAJOR_VERSION, DWARVES_MINOR_VERSION); } @@ -18,7 +18,6 @@ #include "dutil.h" #include "list.h" #include "rbtree.h" -#include "pahole_strings.h" struct cu; @@ -34,22 +33,42 @@ enum load_steal_kind { */ typedef uint32_t type_id_t; +struct btf; struct conf_fprintf; /** struct conf_load - load configuration + * @thread_exit - called at the end of a thread, 1st user: BTF encoder dedup * @extra_dbg_info - keep original debugging format extra info * (e.g. DWARF's decl_{line,file}, id, etc) * @fixup_silly_bitfields - Fixup silly things such as "int foo:32;" * @get_addr_info - wheter to load DW_AT_location and other addr info + * @nr_jobs - -j argument, number of threads to use + * @ptr_table_stats - print developer oriented ptr_table statistics. + * @skip_missing - skip missing types rather than bailing out. */ struct conf_load { enum load_steal_kind (*steal)(struct cu *cu, struct conf_load *conf); + int (*thread_exit)(void); void *cookie; char *format_path; + int nr_jobs; bool extra_dbg_info; + bool use_obstack; bool fixup_silly_bitfields; bool get_addr_info; + bool ignore_alignment_attr; + bool ignore_inline_expansions; + bool ignore_labels; + bool ptr_table_stats; + bool skip_encoding_btf_decl_tag; + bool skip_missing; + bool skip_encoding_btf_type_tag; + uint8_t hashtable_bits; + uint8_t max_hashtable_bits; + uint16_t kabi_prefix_len; + const char *kabi_prefix; + struct btf *base_btf; struct conf_fprintf *conf_fprintf; }; @@ -82,6 +101,7 @@ struct conf_fprintf { const char *header_type; const char *range; uint32_t skip; + uint16_t cacheline_size; uint8_t indent; uint8_t expand_types:1; uint8_t expand_pointers:1; @@ -107,10 +127,7 @@ struct conf_fprintf { uint8_t strip_inline:1; }; -struct cus { - uint32_t nr_entries; - struct list_head cus; -}; +struct cus; struct cus *cus__new(void); void cus__delete(struct cus *cus); @@ -127,20 +144,30 @@ int cus__load_dir(struct cus *cus, struct conf_load *conf, void cus__add(struct cus *cus, struct cu *cu); void cus__print_error_msg(const char *progname, const struct cus *cus, const char *filename, const int err); -struct cu *cus__find_cu_by_name(const struct cus *cus, const char *name); -struct tag *cus__find_struct_by_name(const struct cus *cus, struct cu **cu, +struct cu *cus__find_pair(struct cus *cus, const char *name); +struct cu *cus__find_cu_by_name(struct cus *cus, const char *name); +struct tag *cus__find_struct_by_name(struct cus *cus, struct cu **cu, const char *name, const int include_decls, type_id_t *id); -struct tag *cus__find_struct_or_union_by_name(const struct cus *cus, struct cu **cu, +struct tag *cus__find_struct_or_union_by_name(struct cus *cus, struct cu **cu, const char *name, const int include_decls, type_id_t *id); struct tag *cu__find_type_by_name(const struct cu *cu, const char *name, const int include_decls, type_id_t *idp); -struct tag *cus__find_type_by_name(const struct cus *cus, struct cu **cu, const char *name, +struct tag *cus__find_type_by_name(struct cus *cus, struct cu **cu, const char *name, const int include_decls, type_id_t *id); -struct function *cus__find_function_at_addr(const struct cus *cus, - uint64_t addr, struct cu **cu); +struct function *cus__find_function_at_addr(struct cus *cus, uint64_t addr, struct cu **cu); void cus__for_each_cu(struct cus *cus, int (*iterator)(struct cu *cu, void *cookie), void *cookie, struct cu *(*filter)(struct cu *cu)); +bool cus__empty(const struct cus *cus); +uint32_t cus__nr_entries(const struct cus *cus); + +void cus__lock(struct cus *cus); +void cus__unlock(struct cus *cus); + +void *cus__priv(struct cus *cus); +void cus__set_priv(struct cus *cus, void *priv); + +void cus__set_loader_exit(struct cus *cus, void (*loader_exit)(struct cus *cus)); struct ptr_table { void **entries; @@ -178,15 +205,9 @@ enum dwarf_languages { /** struct debug_fmt_ops - specific to the underlying debug file format * - * @function__name - will be called by function__name(), giving a chance to - * formats such as CTF to get this from some other place - * than the global strings table. CTF does this by storing - * GElf_Sym->st_name in function->name, and by using - * function->name as an index into the .strtab ELF section. - * @variable__name - will be called by variable__name(), see @function_name * cu__delete - called at cu__delete(), to give a chance to formats such as * CTF to keep the .strstab ELF section available till the cu is - * deleted. See @function__name + * deleted. */ struct debug_fmt_ops { const char *name; @@ -201,13 +222,6 @@ struct debug_fmt_ops { const struct cu *cu); unsigned long long (*tag__orig_id)(const struct tag *tag, const struct cu *cu); - void (*tag__free_orig_info)(struct tag *tag, - struct cu *cu); - const char *(*function__name)(struct function *tag, - const struct cu *cu); - const char *(*variable__name)(const struct variable *var, - const struct cu *cu); - const char *(*strings__ptr)(const struct cu *cu, strings_t s); void (*cu__delete)(struct cu *cu); bool has_alignment_info; }; @@ -225,11 +239,12 @@ struct cu { char *name; char *filename; void *priv; - struct obstack obstack; struct debug_fmt_ops *dfops; Elf *elf; Dwfl_Module *dwfl; + struct obstack obstack; uint32_t cached_symtab_nr_entries; + bool use_obstack; uint8_t addr_size; uint8_t extra_dbg_info:1; uint8_t has_addr_info:1; @@ -249,10 +264,16 @@ struct cu { struct cu *cu__new(const char *name, uint8_t addr_size, const unsigned char *build_id, int build_id_len, - const char *filename); + const char *filename, bool use_obstack); void cu__delete(struct cu *cu); -const char *cu__string(const struct cu *cu, strings_t s); +void *cu__malloc(struct cu *cu, size_t size); +void *cu__zalloc(struct cu *cu, size_t size); +void cu__free(struct cu *cu, void *ptr); + +int cu__fprintf_ptr_table_stats_csv(struct cu *cu, FILE *fp); + +int cus__fprintf_ptr_table_stats_csv_header(FILE *fp); static inline int cu__cache_symtab(struct cu *cu) { @@ -267,6 +288,11 @@ static inline __pure bool cu__is_c_plus_plus(const struct cu *cu) return cu->language == LANG_C_plus_plus; } +static inline __pure bool cu__is_c(const struct cu *cu) +{ + return cu->language == LANG_C; +} + /** * cu__for_each_cached_symtab_entry - iterate thru the cached symtab entries * @cu: struct cu instance @@ -354,20 +380,14 @@ int cu__table_add_tag_with_id(struct cu *cu, struct tag *tag, uint32_t id); int cu__table_nullify_type_entry(struct cu *cu, uint32_t id); struct tag *cu__find_base_type_by_name(const struct cu *cu, const char *name, type_id_t *id); -struct tag *cu__find_base_type_by_sname_and_size(const struct cu *cu, - strings_t name, - uint16_t bit_size, - type_id_t *idp); +struct tag *cu__find_base_type_by_name_and_size(const struct cu *cu, const char* name, + uint16_t bit_size, type_id_t *idp); struct tag *cu__find_enumeration_by_name(const struct cu *cu, const char *name, type_id_t *idp); -struct tag *cu__find_enumeration_by_sname_and_size(const struct cu *cu, - strings_t sname, - uint16_t bit_size, - type_id_t *idp); +struct tag *cu__find_enumeration_by_name_and_size(const struct cu *cu, const char* name, + uint16_t bit_size, type_id_t *idp); struct tag *cu__find_first_typedef_of_type(const struct cu *cu, const type_id_t type); struct tag *cu__find_function_by_name(const struct cu *cu, const char *name); -struct tag *cu__find_struct_by_sname(const struct cu *cu, strings_t sname, - const int include_decls, type_id_t *idp); struct function *cu__find_function_at_addr(const struct cu *cu, uint64_t addr); struct tag *cu__function(const struct cu *cu, const uint32_t id); @@ -394,6 +414,7 @@ struct tag { uint16_t tag; bool visited; bool top_level; + bool has_btf_type_tag; uint16_t recursivity_level; void *priv; }; @@ -404,7 +425,7 @@ struct tag_cu { struct cu *cu; }; -void tag__delete(struct tag *tag, struct cu *cu); +void tag__delete(struct tag *tag); static inline int tag__is_enumeration(const struct tag *tag) { @@ -514,7 +535,8 @@ static inline int tag__is_tag_type(const struct tag *tag) tag->tag == DW_TAG_restrict_type || tag->tag == DW_TAG_subroutine_type || tag->tag == DW_TAG_unspecified_type || - tag->tag == DW_TAG_volatile_type; + tag->tag == DW_TAG_volatile_type || + tag->tag == DW_TAG_LLVM_annotation; } static inline const char *tag__decl_file(const struct tag *tag, @@ -541,12 +563,6 @@ static inline unsigned long long tag__orig_id(const struct tag *tag, return 0; } -static inline void tag__free_orig_info(struct tag *tag, struct cu *cu) -{ - if (cu->dfops && cu->dfops->tag__free_orig_info) - cu->dfops->tag__free_orig_info(tag, cu); -} - size_t tag__fprintf_decl_info(const struct tag *tag, const struct cu *cu, FILE *fp); size_t tag__fprintf(struct tag *tag, const struct cu *cu, @@ -561,7 +577,7 @@ void tag__not_found_die(const char *file, int line, const char *func); __LINE__, __func__); } while (0) size_t tag__size(const struct tag *tag, const struct cu *cu); -size_t tag__nr_cachelines(const struct tag *tag, const struct cu *cu); +size_t tag__nr_cachelines(const struct conf_fprintf *conf, const struct tag *tag, const struct cu *cu); struct tag *tag__follow_typedef(const struct tag *tag, const struct cu *cu); struct tag *tag__strip_typedefs_and_modifiers(const struct tag *tag, const struct cu *cu); @@ -587,19 +603,57 @@ static inline struct ptr_to_member_type * return (struct ptr_to_member_type *)tag; } +struct llvm_annotation { + const char *value; + int16_t component_idx; + struct list_head node; +}; + +/** struct btf_type_tag_type - representing a btf_type_tag annotation + * + * @tag - DW_TAG_LLVM_annotation tag + * @value - btf_type_tag value string + * @node - list_head node + */ +struct btf_type_tag_type { + struct tag tag; + const char *value; + struct list_head node; +}; + +/** The struct btf_type_tag_ptr_type - type containing both pointer type and + * its btf_type_tag annotations + * + * @tag - pointer type tag + * @tags - btf_type_tag annotations for the pointer type + */ +struct btf_type_tag_ptr_type { + struct tag tag; + struct list_head tags; +}; + +static inline struct btf_type_tag_ptr_type *tag__btf_type_tag_ptr(struct tag *tag) +{ + return (struct btf_type_tag_ptr_type *)tag; +} + +static inline struct btf_type_tag_type *tag__btf_type_tag(struct tag *tag) +{ + return (struct btf_type_tag_type *)tag; +} + /** struct namespace - base class for enums, structs, unions, typedefs, etc * - * @sname - for clones, for instance, where we can't always add a new string * @tags - class_member, enumerators, etc * @shared_tags: if this bit is set, don't free the entries in @tags */ struct namespace { struct tag tag; - strings_t name; + const char *name; uint16_t nr_tags; uint8_t shared_tags; - char * sname; struct list_head tags; + struct list_head annots; }; static inline struct namespace *tag__namespace(const struct tag *tag) @@ -607,7 +661,7 @@ static inline struct namespace *tag__namespace(const struct tag *tag) return (struct namespace *)tag; } -void namespace__delete(struct namespace *nspace, struct cu *cu); +void namespace__delete(struct namespace *nspace); /** * namespace__for_each_tag - iterate thru all the tags @@ -647,7 +701,7 @@ static inline struct inline_expansion * struct label { struct ip_tag ip; - strings_t name; + const char *name; }; static inline struct label *tag__label(const struct tag *tag) @@ -655,10 +709,9 @@ static inline struct label *tag__label(const struct tag *tag) return (struct label *)tag; } -static inline const char *label__name(const struct label *label, - const struct cu *cu) +static inline const char *label__name(const struct label *label) { - return cu__string(cu, label->name); + return label->name; } enum vscope { @@ -676,12 +729,14 @@ struct location { struct variable { struct ip_tag ip; - strings_t name; + const char *name; uint8_t external:1; uint8_t declaration:1; + uint8_t has_specification:1; enum vscope scope; struct location location; struct hlist_node tool_hnode; + struct list_head annots; struct variable *spec; }; @@ -693,7 +748,7 @@ static inline struct variable *tag__variable(const struct tag *tag) enum vscope variable__scope(const struct variable *var); const char *variable__scope_str(const struct variable *var); -const char *variable__name(const struct variable *var, const struct cu *cu); +const char *variable__name(const struct variable *var); const char *variable__type_name(const struct variable *var, const struct cu *cu, char *bf, size_t len); @@ -714,7 +769,7 @@ static inline struct lexblock *tag__lexblock(const struct tag *tag) return (struct lexblock *)tag; } -void lexblock__delete(struct lexblock *lexblock, struct cu *cu); +void lexblock__delete(struct lexblock *lexblock); struct function; @@ -729,8 +784,8 @@ size_t lexblock__fprintf(const struct lexblock *lexblock, const struct cu *cu, const struct conf_fprintf *conf, FILE *fp); struct parameter { - struct tag tag; - strings_t name; + struct tag tag; + const char *name; }; static inline struct parameter *tag__parameter(const struct tag *tag) @@ -738,10 +793,9 @@ static inline struct parameter *tag__parameter(const struct tag *tag) return (struct parameter *)tag; } -static inline const char *parameter__name(const struct parameter *parm, - const struct cu *cu) +static inline const char *parameter__name(const struct parameter *parm) { - return cu__string(cu, parm->name); + return parm->name; } /* @@ -759,7 +813,7 @@ static inline struct ftype *tag__ftype(const struct tag *tag) return (struct ftype *)tag; } -void ftype__delete(struct ftype *ftype, struct cu *cu); +void ftype__delete(struct ftype *ftype); /** * ftype__for_each_parameter - iterate thru all the parameters @@ -802,8 +856,8 @@ struct function { struct ftype proto; struct lexblock lexblock; struct rb_node rb_node; - strings_t name; - strings_t linkage_name; + const char *name; + const char *linkage_name; uint32_t cu_total_size_inline_expansions; uint16_t cu_total_nr_inline_expansions; uint8_t inlined:2; @@ -815,6 +869,7 @@ struct function { uint8_t btf:1; int32_t vtable_entry; struct list_head vtable_node; + struct list_head annots; /* fields used by tools */ union { struct list_head tool_node; @@ -833,7 +888,7 @@ static inline struct tag *function__tag(const struct function *func) return (struct tag *)func; } -void function__delete(struct function *func, struct cu *cu); +void function__delete(struct function *func); static __pure inline int tag__is_function(const struct tag *tag) { @@ -848,12 +903,11 @@ static __pure inline int tag__is_function(const struct tag *tag) #define function__for_each_parameter(func, cu, pos) \ ftype__for_each_parameter(func->btf ? tag__ftype(cu__type(cu, func->proto.tag.type)) : &func->proto, pos) -const char *function__name(struct function *func, const struct cu *cu); +const char *function__name(struct function *func); -static inline const char *function__linkage_name(const struct function *func, - const struct cu *cu) +static inline const char *function__linkage_name(const struct function *func) { - return cu__string(cu, func->linkage_name); + return func->linkage_name; } size_t function__fprintf_stats(const struct tag *tag_func, @@ -899,10 +953,11 @@ static inline int function__inlined(const struct function *func) * @accessibility - DW_ACCESS_{public,protected,private} * @virtuality - DW_VIRTUALITY_{none,virtual,pure_virtual} * @hole - If there is a hole before the next one (or the end of the struct) + * @has_bit_offset: Don't recalcule this, it came from the debug info (DWARF5's DW_AT_data_bit_offset) */ struct class_member { struct tag tag; - strings_t name; + const char *name; uint32_t bit_offset; uint32_t bit_size; uint32_t byte_offset; @@ -915,22 +970,22 @@ struct class_member { uint32_t alignment; uint8_t visited:1; uint8_t is_static:1; + uint8_t has_bit_offset:1; uint8_t accessibility:2; uint8_t virtuality:2; uint16_t hole; }; -void class_member__delete(struct class_member *member, struct cu *cu); +void class_member__delete(struct class_member *member); static inline struct class_member *tag__class_member(const struct tag *tag) { return (struct class_member *)tag; } -static inline const char *class_member__name(const struct class_member *member, - const struct cu *cu) +static inline const char *class_member__name(const struct class_member *member) { - return cu__string(cu, member->name); + return member->name; } static __pure inline int tag__is_class_member(const struct tag *tag) @@ -951,7 +1006,8 @@ struct tag_cu_node { /** * struct type - base type for enumerations, structs and unions * - * @nnr_members: number of non static DW_TAG_member entries + * @node: Used in emissions->fwd_decls, i.e. only on the 'dwarves_emit.c' file + * @nr_members: number of non static DW_TAG_member entries * @nr_static_members: number of static DW_TAG_member entries * @nr_tags: number of tags * @alignment: DW_AT_alignement, zero if not present, gcc emits since circa 7.3.1 @@ -988,6 +1044,8 @@ struct type { void __type__init(struct type *type); +size_t tag__natural_alignment(struct tag *tag, const struct cu *cu); + static inline struct class *type__class(const struct type *type) { return (struct class *)type; @@ -998,7 +1056,17 @@ static inline struct tag *type__tag(const struct type *type) return (struct tag *)type; } -void type__delete(struct type *type, struct cu *cu); +void type__delete(struct type *type); + +static inline struct class_member *type__first_member(struct type *type) +{ + return list_first_entry(&type->namespace.tags, struct class_member, tag.node); +} + +static inline struct class_member *class_member__next(struct class_member *member) +{ + return list_entry(member->tag.node.next, struct class_member, tag.node); +} /** * type__for_each_tag - iterate thru all the tags @@ -1093,17 +1161,10 @@ struct class_member * type__find_first_biggest_size_base_type_member(struct type *type, const struct cu *cu); -struct class_member *type__find_member_by_name(const struct type *type, - const struct cu *cu, - const char *name); +struct class_member *type__find_member_by_name(const struct type *type, const char *name); uint32_t type__nr_members_of_type(const struct type *type, const type_id_t oftype); struct class_member *type__last_member(struct type *type); -void enumeration__calc_prefix(struct type *type, const struct cu *cu); -const char *enumeration__prefix(struct type *type, const struct cu *cu); -uint16_t enumeration__prefix_len(struct type *type, const struct cu *cu); -int enumeration__max_entry_name_len(struct type *type, const struct cu *cu); - void enumerations__calc_prefix(struct list_head *enumerations); size_t typedef__fprintf(const struct tag *tag_type, const struct cu *cu, @@ -1139,31 +1200,27 @@ static inline struct tag *class__tag(const struct class *cls) return (struct tag *)cls; } -struct class *class__clone(const struct class *from, - const char *new_class_name, struct cu *cu); -void class__delete(struct class *cls, struct cu *cu); +struct class *class__clone(const struct class *from, const char *new_class_name); +void class__delete(struct class *cls); static inline struct list_head *class__tags(struct class *cls) { return &cls->type.namespace.tags; } -static __pure inline const char *namespace__name(const struct namespace *nspace, - const struct cu *cu) +static __pure inline const char *namespace__name(const struct namespace *nspace) { - return nspace->sname ?: cu__string(cu, nspace->name); + return nspace->name; } -static __pure inline const char *type__name(const struct type *type, - const struct cu *cu) +static __pure inline const char *type__name(const struct type *type) { - return namespace__name(&type->namespace, cu); + return namespace__name(&type->namespace); } -static __pure inline const char *class__name(struct class *cls, - const struct cu *cu) +static __pure inline const char *class__name(struct class *cls) { - return type__name(&cls->type, cu); + return type__name(&cls->type); } static inline int class__is_struct(const struct class *cls) @@ -1184,10 +1241,9 @@ size_t class__fprintf(struct class *cls, const struct cu *cu, FILE *fp); void class__add_vtable_entry(struct class *cls, struct function *vtable_entry); static inline struct class_member * - class__find_member_by_name(const struct class *cls, - const struct cu *cu, const char *name) + class__find_member_by_name(const struct class *cls, const char *name) { - return type__find_member_by_name(&cls->type, cu, name); + return type__find_member_by_name(&cls->type, name); } static inline uint16_t class__nr_members(const struct class *cls) @@ -1253,7 +1309,7 @@ enum base_type_float_type { struct base_type { struct tag tag; - strings_t name; + const char *name; uint16_t bit_size; uint8_t name_has_encoding:1; uint8_t is_signed:1; @@ -1272,10 +1328,10 @@ static inline uint16_t base_type__size(const struct tag *tag) return tag__base_type(tag)->bit_size / 8; } -const char *base_type__name(const struct base_type *btype, const struct cu *cu, - char *bf, size_t len); +const char *__base_type__name(const struct base_type *bt); + +const char *base_type__name(const struct base_type *btype, char *bf, size_t len); -void base_type_name_to_size_table__init(struct strings *strings); size_t base_type__name_to_size(struct base_type *btype, struct cu *cu); struct array_type { @@ -1302,24 +1358,24 @@ static inline struct string_type *tag__string_type(const struct tag *tag) struct enumerator { struct tag tag; - strings_t name; + const char *name; uint32_t value; struct tag_cu type_enum; // To cache the type_enum searches }; -static inline const char *enumerator__name(const struct enumerator *enumerator, - const struct cu *cu) +static inline const char *enumerator__name(const struct enumerator *enumerator) { - return cu__string(cu, enumerator->name); + return enumerator->name; } -void enumeration__delete(struct type *type, struct cu *cu); +void enumeration__delete(struct type *type); void enumeration__add(struct type *type, struct enumerator *enumerator); -size_t enumeration__fprintf(const struct tag *tag_enum, const struct cu *cu, +size_t enumeration__fprintf(const struct tag *tag_enum, const struct conf_fprintf *conf, FILE *fp); -int dwarves__init(uint16_t user_cacheline_size); +int dwarves__init(void); void dwarves__exit(void); +void dwarves__resolve_cacheline_size(const struct conf_load *conf, uint16_t user_cacheline_size); const char *dwarf_tag_name(const uint32_t tag); @@ -1328,7 +1384,7 @@ struct argp_state; void dwarves_print_version(FILE *fp, struct argp_state *state); void dwarves_print_numeric_version(FILE *fp); -extern bool print_numeric_version;; +extern bool print_numeric_version; extern bool no_bitfield_type_recode; diff --git a/dwarves_emit.c b/dwarves_emit.c index 434e339..5bf7946 100644 --- a/dwarves_emit.c +++ b/dwarves_emit.c @@ -37,7 +37,6 @@ static void type_emissions__add_fwd_decl(struct type_emissions *emissions, } struct type *type_emissions__find_definition(const struct type_emissions *emissions, - const struct cu *cu, const char *name) { struct type *pos; @@ -46,15 +45,14 @@ struct type *type_emissions__find_definition(const struct type_emissions *emissi return NULL; list_for_each_entry(pos, &emissions->definitions, node) - if (type__name(pos, cu) != NULL && - strcmp(type__name(pos, cu), name) == 0) + if (type__name(pos) != NULL && + strcmp(type__name(pos), name) == 0) return pos; return NULL; } static struct type *type_emissions__find_fwd_decl(const struct type_emissions *emissions, - const struct cu *cu, const char *name) { struct type *pos; @@ -63,7 +61,7 @@ static struct type *type_emissions__find_fwd_decl(const struct type_emissions *e return NULL; list_for_each_entry(pos, &emissions->fwd_decls, node) { - const char *curr_name = type__name(pos, cu); + const char *curr_name = type__name(pos); if (curr_name && strcmp(curr_name, name) == 0) return pos; @@ -72,7 +70,7 @@ static struct type *type_emissions__find_fwd_decl(const struct type_emissions *e return NULL; } -static int enumeration__emit_definitions(struct tag *tag, struct cu *cu, +static int enumeration__emit_definitions(struct tag *tag, struct type_emissions *emissions, const struct conf_fprintf *conf, FILE *fp) @@ -84,8 +82,7 @@ static int enumeration__emit_definitions(struct tag *tag, struct cu *cu, return 0; /* Ok, lets look at the previous CUs: */ - if (type_emissions__find_definition(emissions, cu, - type__name(etype, cu)) != NULL) { + if (type_emissions__find_definition(emissions, type__name(etype)) != NULL) { /* * Yes, so lets mark it visited on this CU too, * to speed up the lookup. @@ -94,7 +91,7 @@ static int enumeration__emit_definitions(struct tag *tag, struct cu *cu, return 0; } - enumeration__fprintf(tag, cu, conf, fp); + enumeration__fprintf(tag, conf, fp); fputs(";\n", fp); type_emissions__add_definition(emissions, etype); return 1; @@ -114,8 +111,7 @@ static int typedef__emit_definitions(struct tag *tdef, struct cu *cu, return 0; /* Ok, lets look at the previous CUs: */ - if (type_emissions__find_definition(emissions, cu, - type__name(def, cu)) != NULL) { + if (type_emissions__find_definition(emissions, type__name(def)) != NULL) { /* * Yes, so lets mark it visited on this CU too, * to speed up the lookup. @@ -155,25 +151,23 @@ static int typedef__emit_definitions(struct tag *tdef, struct cu *cu, .suffix = NULL, }; - if (type__name(ctype, cu) == NULL) { + if (type__name(ctype) == NULL) { fputs("typedef ", fp); - conf.suffix = type__name(def, cu); - enumeration__emit_definitions(type, cu, emissions, - &conf, fp); + conf.suffix = type__name(def); + enumeration__emit_definitions(type, emissions, &conf, fp); goto out; } else - enumeration__emit_definitions(type, cu, emissions, - &conf, fp); + enumeration__emit_definitions(type, emissions, &conf, fp); } break; case DW_TAG_structure_type: case DW_TAG_union_type: { struct type *ctype = tag__type(type); - if (type__name(ctype, cu) == NULL) { + if (type__name(ctype) == NULL) { if (type__emit_definitions(type, cu, emissions, fp)) type__emit(type, cu, "typedef", - type__name(def, cu), fp); + type__name(def), fp); goto out; } else if (type__emit_definitions(type, cu, emissions, fp)) type__emit(type, cu, NULL, NULL, fp); @@ -197,19 +191,18 @@ out: return 1; } -int type__emit_fwd_decl(struct type *ctype, const struct cu *cu, - struct type_emissions *emissions, FILE *fp) +static int type__emit_fwd_decl(struct type *ctype, struct type_emissions *emissions, FILE *fp) { /* Have we already emitted this in this CU? */ if (ctype->fwd_decl_emitted) return 0; - const char *name = type__name(ctype, cu); + const char *name = type__name(ctype); if (name == NULL) return 0; /* Ok, lets look at the previous CUs: */ - if (type_emissions__find_fwd_decl(emissions, cu, name) != NULL) { + if (type_emissions__find_fwd_decl(emissions, name) != NULL) { /* * Yes, so lets mark it visited on this CU too, * to speed up the lookup. @@ -220,7 +213,7 @@ int type__emit_fwd_decl(struct type *ctype, const struct cu *cu, fprintf(fp, "%s %s;\n", tag__is_union(&ctype->namespace.tag) ? "union" : "struct", - type__name(ctype, cu)); + type__name(ctype)); type_emissions__add_fwd_decl(emissions, ctype); return 1; } @@ -249,12 +242,11 @@ next_indirection: case DW_TAG_typedef: return typedef__emit_definitions(type, cu, emissions, fp); case DW_TAG_enumeration_type: - if (type__name(tag__type(type), cu) != NULL) { + if (type__name(tag__type(type)) != NULL) { struct conf_fprintf conf = { .suffix = NULL, }; - return enumeration__emit_definitions(type, cu, emissions, - &conf, fp); + return enumeration__emit_definitions(type, emissions, &conf, fp); } break; case DW_TAG_structure_type: @@ -264,11 +256,10 @@ next_indirection: * Struct defined inline, no name, need to have its * members types emitted. */ - if (type__name(tag__type(type), cu) == NULL) + if (type__name(tag__type(type)) == NULL) type__emit_definitions(type, cu, emissions, fp); - return type__emit_fwd_decl(tag__type(type), cu, - emissions, fp); + return type__emit_fwd_decl(tag__type(type), emissions, fp); } if (type__emit_definitions(type, cu, emissions, fp)) type__emit(type, cu, NULL, NULL, fp); @@ -308,8 +299,7 @@ int type__emit_definitions(struct tag *tag, struct cu *cu, return 0; /* Ok, lets look at the previous CUs: */ - if (type_emissions__find_definition(emissions, cu, - type__name(ctype, cu)) != NULL) { + if (type_emissions__find_definition(emissions, type__name(ctype)) != NULL) { ctype->definition_emitted = 1; return 0; } @@ -333,7 +323,7 @@ void type__emit(struct tag *tag, struct cu *cu, { struct type *ctype = tag__type(tag); - if (type__name(ctype, cu) != NULL || + if (type__name(ctype) != NULL || suffix != NULL || prefix != NULL) { struct conf_fprintf conf = { .prefix = prefix, diff --git a/dwarves_emit.h b/dwarves_emit.h index 5d8c7ab..be02acd 100644 --- a/dwarves_emit.h +++ b/dwarves_emit.h @@ -27,12 +27,9 @@ int ftype__emit_definitions(struct ftype *ftype, struct cu *cu, struct type_emissions *emissions, FILE *fp); int type__emit_definitions(struct tag *tag, struct cu *cu, struct type_emissions *emissions, FILE *fp); -int type__emit_fwd_decl(struct type *ctype, const struct cu *cu, - struct type_emissions *emissions, FILE *fp); void type__emit(struct tag *tag_type, struct cu *cu, const char *prefix, const char *suffix, FILE *fp); struct type *type_emissions__find_definition(const struct type_emissions *temissions, - const struct cu *cu, const char *name); #endif /* _DWARVES_EMIT_H_ */ diff --git a/dwarves_fprintf.c b/dwarves_fprintf.c index c96a6fb..c5921d7 100644 --- a/dwarves_fprintf.c +++ b/dwarves_fprintf.c @@ -10,10 +10,14 @@ #include <dwarf.h> #include <errno.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include <unistd.h> #include <inttypes.h> #include <elfutils/version.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> #include "config.h" #include "dwarves.h" @@ -80,6 +84,15 @@ static const char *dwarf_tag_names[] = { [DW_TAG_type_unit] = "type_unit", [DW_TAG_rvalue_reference_type] = "rvalue_reference_type", #endif +#if _ELFUTILS_PREREQ(0, 170) + [DW_TAG_coarray_type] = "coarray_type", + [DW_TAG_generic_subrange] = "generic_subrange", + [DW_TAG_dynamic_type] = "dynamic_type", + [DW_TAG_call_site] = "call_site", + [DW_TAG_call_site_parameter] = "call_site_parameter", + [DW_TAG_skeleton_unit] = "skeleton_unit", + [DW_TAG_immutable_type] = "immutable_type", +#endif }; static const char *dwarf_gnu_tag_names[] = { @@ -103,11 +116,15 @@ static const char *dwarf_gnu_tag_names[] = { const char *dwarf_tag_name(const uint32_t tag) { if (tag >= DW_TAG_array_type && tag <= +#if _ELFUTILS_PREREQ(0, 170) + DW_TAG_immutable_type +#else #ifdef STB_GNU_UNIQUE DW_TAG_rvalue_reference_type #else DW_TAG_shared_type #endif +#endif ) return dwarf_tag_names[tag]; else if (tag >= DW_TAG_MIPS_loop && tag <= @@ -123,7 +140,7 @@ const char *dwarf_tag_name(const uint32_t tag) return "INVALID"; } -static const struct conf_fprintf conf_fprintf__defaults = { +static struct conf_fprintf conf_fprintf__defaults = { .name_spacing = 23, .type_spacing = 26, .emit_stats = 1, @@ -131,11 +148,10 @@ static const struct conf_fprintf conf_fprintf__defaults = { const char tabs[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; -static size_t cacheline_size; -size_t tag__nr_cachelines(const struct tag *tag, const struct cu *cu) +size_t tag__nr_cachelines(const struct conf_fprintf *conf, const struct tag *tag, const struct cu *cu) { - return (tag__size(tag, cu) + cacheline_size - 1) / cacheline_size; + return (tag__size(tag, cu) + conf->cacheline_size - 1) / conf->cacheline_size; } static const char *tag__accessibility(const struct tag *tag) @@ -248,8 +264,7 @@ static size_t array_type__fprintf(const struct tag *tag, return printed; } -static size_t string_type__fprintf(const struct tag *tag, - const struct cu *cu, const char *name, +static size_t string_type__fprintf(const struct tag *tag, const char *name, const struct conf_fprintf *conf, FILE *fp) { @@ -274,21 +289,19 @@ size_t typedef__fprintf(const struct tag *tag, const struct cu *cu, * to avoid all these checks? */ if (tag->type == 0) - return fprintf(fp, "typedef void %s", type__name(type, cu)); + return fprintf(fp, "typedef void %s", type__name(type)); tag_type = cu__type(cu, tag->type); if (tag_type == NULL) { printed = fprintf(fp, "typedef "); printed += tag__id_not_found_fprintf(fp, tag->type); - return printed + fprintf(fp, " %s", type__name(type, cu)); + return printed + fprintf(fp, " %s", type__name(type)); } switch (tag_type->tag) { case DW_TAG_array_type: printed = fprintf(fp, "typedef "); - return printed + array_type__fprintf(tag_type, cu, - type__name(type, cu), - pconf, fp); + return printed + array_type__fprintf(tag_type, cu, type__name(type), pconf, fp); case DW_TAG_pointer_type: if (tag_type->type == 0) /* void pointer */ break; @@ -296,8 +309,7 @@ size_t typedef__fprintf(const struct tag *tag, const struct cu *cu, if (ptr_type == NULL) { printed = fprintf(fp, "typedef "); printed += tag__id_not_found_fprintf(fp, tag_type->type); - return printed + fprintf(fp, " *%s", - type__name(type, cu)); + return printed + fprintf(fp, " *%s", type__name(type)); } if (ptr_type->tag != DW_TAG_subroutine_type) break; @@ -306,40 +318,35 @@ size_t typedef__fprintf(const struct tag *tag, const struct cu *cu, /* Fall thru */ case DW_TAG_subroutine_type: printed = fprintf(fp, "typedef "); - return printed + ftype__fprintf(tag__ftype(tag_type), cu, - type__name(type, cu), - 0, is_pointer, 0, - true, pconf, fp); + return printed + ftype__fprintf(tag__ftype(tag_type), cu, type__name(type), + 0, is_pointer, 0, true, pconf, fp); case DW_TAG_class_type: case DW_TAG_structure_type: { struct type *ctype = tag__type(tag_type); - if (type__name(ctype, cu) != NULL) - return fprintf(fp, "typedef struct %s %s", - type__name(ctype, cu), - type__name(type, cu)); + if (type__name(ctype) != NULL) + return fprintf(fp, "typedef struct %s %s", type__name(ctype), type__name(type)); struct conf_fprintf tconf = *pconf; - tconf.suffix = type__name(type, cu); + tconf.suffix = type__name(type); return fprintf(fp, "typedef ") + __class__fprintf(tag__class(tag_type), cu, &tconf, fp); } case DW_TAG_enumeration_type: { struct type *ctype = tag__type(tag_type); - if (type__name(ctype, cu) != NULL) - return fprintf(fp, "typedef enum %s %s", type__name(ctype, cu), type__name(type, cu)); + if (type__name(ctype) != NULL) + return fprintf(fp, "typedef enum %s %s", type__name(ctype), type__name(type)); struct conf_fprintf tconf = *pconf; - tconf.suffix = type__name(type, cu); - return fprintf(fp, "typedef ") + enumeration__fprintf(tag_type, cu, &tconf, fp); + tconf.suffix = type__name(type); + return fprintf(fp, "typedef ") + enumeration__fprintf(tag_type, &tconf, fp); } } return fprintf(fp, "typedef %s %s", - tag__name(tag_type, cu, bf, sizeof(bf), pconf), - type__name(type, cu)); + tag__name(tag_type, cu, bf, sizeof(bf), pconf), type__name(type)); } static size_t imported_declaration__fprintf(const struct tag *tag, @@ -365,12 +372,12 @@ static size_t imported_module__fprintf(const struct tag *tag, const char *name = "<IMPORTED MODULE ERROR!>"; if (tag__is_namespace(module)) - name = namespace__name(tag__namespace(module), cu); + name = namespace__name(tag__namespace(module)); return fprintf(fp, "using namespace %s", name); } -int enumeration__max_entry_name_len(struct type *type, const struct cu *cu) +static int enumeration__max_entry_name_len(struct type *type) { if (type->max_tag_name_len) goto out; @@ -378,7 +385,7 @@ int enumeration__max_entry_name_len(struct type *type, const struct cu *cu) struct enumerator *pos; type__for_each_enumerator(type, pos) { - int len = strlen(enumerator__name(pos, cu)); + int len = strlen(enumerator__name(pos)); if (type->max_tag_name_len < len) type->max_tag_name_len = len; @@ -387,23 +394,23 @@ out: return type->max_tag_name_len; } -size_t enumeration__fprintf(const struct tag *tag, const struct cu *cu, - const struct conf_fprintf *conf, FILE *fp) +size_t enumeration__fprintf(const struct tag *tag, const struct conf_fprintf *conf, FILE *fp) { struct type *type = tag__type(tag); struct enumerator *pos; - int max_entry_name_len = enumeration__max_entry_name_len(type, cu); - size_t printed = fprintf(fp, "enum%s%s {\n", - type__name(type, cu) ? " " : "", - type__name(type, cu) ?: ""); + int max_entry_name_len = enumeration__max_entry_name_len(type); + size_t printed = fprintf(fp, "enum%s%s {\n", type__name(type) ? " " : "", type__name(type) ?: ""); int indent = conf->indent; if (indent >= (int)sizeof(tabs)) indent = sizeof(tabs) - 1; - type__for_each_enumerator(type, pos) - printed += fprintf(fp, "%.*s\t%-*s = %u,\n", indent, tabs, - max_entry_name_len, enumerator__name(pos, cu), pos->value); + type__for_each_enumerator(type, pos) { + printed += fprintf(fp, "%.*s\t%-*s = ", indent, tabs, + max_entry_name_len, enumerator__name(pos)); + printed += fprintf(fp, conf->hex_fmt ? "%#x" : "%u", pos->value); + printed += fprintf(fp, ",\n"); + } printed += fprintf(fp, "%.*s}", indent, tabs); @@ -494,14 +501,13 @@ static const char *__tag__name(const struct tag *tag, const struct cu *cu, char bf2[64]; if (bt->name) - name = base_type__name(tag__base_type(tag), cu, - bf2, sizeof(bf2)); + name = base_type__name(tag__base_type(tag), bf2, sizeof(bf2)); strncpy(bf, name, len); } break; case DW_TAG_subprogram: - strncpy(bf, function__name(tag__function(tag), cu), len); + strncpy(bf, function__name(tag__function(tag)), len); break; case DW_TAG_pointer_type: return tag__ptr_name(tag, cu, bf, len, "*"); @@ -513,8 +519,7 @@ static const char *__tag__name(const struct tag *tag, const struct cu *cu, type = cu__type(cu, id); if (type != NULL) - snprintf(suffix, sizeof(suffix), "%s::*", - class__name(tag__class(type), cu)); + snprintf(suffix, sizeof(suffix), "%s::*", class__name(tag__class(type))); else { size_t l = tag__id_not_found_snprintf(suffix, sizeof(suffix), @@ -564,14 +569,14 @@ static const char *__tag__name(const struct tag *tag, const struct cu *cu, } break; case DW_TAG_member: - snprintf(bf, len, "%s", class_member__name(tag__class_member(tag), cu)); + snprintf(bf, len, "%s", class_member__name(tag__class_member(tag))); break; case DW_TAG_variable: - snprintf(bf, len, "%s", variable__name(tag__variable(tag), cu)); + snprintf(bf, len, "%s", variable__name(tag__variable(tag))); break; default: snprintf(bf, len, "%s%s", tag__prefix(cu, tag->tag, pconf), - type__name(tag__type(tag), cu) ?: ""); + type__name(tag__type(tag)) ?: ""); break; } @@ -618,7 +623,7 @@ static size_t type__fprintf_stats(struct type *type, const struct cu *cu, { size_t printed = fprintf(fp, "\n%.*s/* size: %d, cachelines: %zd, members: %u", conf->indent, tabs, type->size, - tag__nr_cachelines(type__tag(type), cu), type->nr_members); + tag__nr_cachelines(conf, type__tag(type), cu), type->nr_members); if (type->nr_static_members != 0) printed += fprintf(fp, ", static members: %u */\n", type->nr_static_members); @@ -697,11 +702,9 @@ static size_t type__fprintf(struct tag *type, const struct cu *cu, ctype = tag__type(type); if (typedef_expanded) - printed += fprintf(fp, " -> %s", - type__name(ctype, cu)); + printed += fprintf(fp, " -> %s", type__name(ctype)); else { - printed += fprintf(fp, "/* typedef %s", - type__name(ctype, cu)); + printed += fprintf(fp, "/* typedef %s", type__name(ctype)); typedef_expanded = 1; } type_type = cu__type(cu, type->type); @@ -746,7 +749,7 @@ next_type: break; } if ((tag__is_struct(ptype) || tag__is_union(ptype) || - tag__is_enumeration(ptype)) && type__name(tag__type(ptype), cu) == NULL) { + tag__is_enumeration(ptype)) && type__name(tag__type(ptype)) == NULL) { if (name == namebfptr) goto out_type_not_found; snprintf(namebfptr, sizeof(namebfptr), "* %.*s", (int)sizeof(namebfptr) - 3, name); @@ -785,18 +788,18 @@ print_default: printed += array_type__fprintf(type, cu, name, &tconf, fp); break; case DW_TAG_string_type: - printed += string_type__fprintf(type, cu, name, &tconf, fp); + printed += string_type__fprintf(type, name, &tconf, fp); break; case DW_TAG_class_type: case DW_TAG_structure_type: ctype = tag__type(type); - if (type__name(ctype, cu) != NULL && !expand_types) { + if (type__name(ctype) != NULL && !expand_types) { printed += fprintf(fp, "%s %-*s %s", (type->tag == DW_TAG_class_type && !tconf.classes_as_structs) ? "class" : "struct", tconf.type_spacing - 7, - type__name(ctype, cu), name); + type__name(ctype), name ?: ""); } else { struct class *cclass = tag__class(type); @@ -810,10 +813,8 @@ print_default: case DW_TAG_union_type: ctype = tag__type(type); - if (type__name(ctype, cu) != NULL && !expand_types) { - printed += fprintf(fp, "union %-*s %s", - tconf.type_spacing - 6, - type__name(ctype, cu), name); + if (type__name(ctype) != NULL && !expand_types) { + printed += fprintf(fp, "union %-*s %s", tconf.type_spacing - 6, type__name(ctype), name ?: ""); } else { tconf.type_spacing -= 8; printed += union__fprintf(ctype, cu, &tconf, fp); @@ -822,12 +823,10 @@ print_default: case DW_TAG_enumeration_type: ctype = tag__type(type); - if (type__name(ctype, cu) != NULL) - printed += fprintf(fp, "enum %-*s %s", - tconf.type_spacing - 5, - type__name(ctype, cu), name); + if (type__name(ctype) != NULL) + printed += fprintf(fp, "enum %-*s %s", tconf.type_spacing - 5, type__name(ctype), name ?: ""); else - printed += enumeration__fprintf(type, cu, &tconf, fp); + printed += enumeration__fprintf(type, &tconf, fp); break; } out: @@ -850,10 +849,11 @@ static size_t class_member__fprintf(struct class_member *member, bool union_memb struct conf_fprintf *conf, FILE *fp) { const int size = member->byte_size; + int member_alignment_printed = 0; struct conf_fprintf sconf = *conf; uint32_t offset = member->byte_offset; size_t printed = 0, printed_cacheline = 0; - const char *cm_name = class_member__name(member, cu), + const char *cm_name = class_member__name(member), *name = cm_name; if (!sconf.rel_offset) { @@ -876,7 +876,21 @@ static size_t class_member__fprintf(struct class_member *member, bool union_memb if (member->is_static) printed += fprintf(fp, "static "); - printed += type__fprintf(type, cu, name, &sconf, fp); + /* For struct-like constructs, the name of the member cannot be + * conflated with the name of its type, otherwise __attribute__ are + * printed in the wrong order. + */ + if (tag__is_union(type) || tag__is_struct(type) || + tag__is_enumeration(type)) { + printed += type__fprintf(type, cu, NULL, &sconf, fp); + if (name) { + if (!type__name(tag__type(type))) + printed += fprintf(fp, " "); + printed += fprintf(fp, "%s", name); + } + } else { + printed += type__fprintf(type, cu, name, &sconf, fp); + } if (member->is_static) { if (member->const_value != 0) @@ -885,8 +899,10 @@ static size_t class_member__fprintf(struct class_member *member, bool union_memb printed += fprintf(fp, ":%u", member->bitfield_size); } - if (!sconf.suppress_aligned_attribute && member->alignment != 0) - printed += fprintf(fp, " __attribute__((__aligned__(%u)))", member->alignment); + if (!sconf.suppress_aligned_attribute && member->alignment != 0) { + member_alignment_printed = fprintf(fp, " __attribute__((__aligned__(%u)))", member->alignment); + printed += member_alignment_printed; + } fputc(';', fp); ++printed; @@ -894,10 +910,10 @@ static size_t class_member__fprintf(struct class_member *member, bool union_memb if ((tag__is_union(type) || tag__is_struct(type) || tag__is_enumeration(type)) && /* Look if is a type defined inline */ - type__name(tag__type(type), cu) == NULL) { + type__name(tag__type(type)) == NULL) { if (!sconf.suppress_offset_comment) { /* Check if this is a anonymous union */ - int slen = cm_name ? (int)strlen(cm_name) : -1; + int slen = member_alignment_printed + (cm_name ? (int)strlen(cm_name) : -1); int size_spacing = 5; if (tag__is_struct(type) && tag__class(type)->is_packed && !conf->suppress_packed) { @@ -905,12 +921,6 @@ static size_t class_member__fprintf(struct class_member *member, bool union_memb slen += packed_len; } - if (tag__type(type)->alignment != 0 && !conf->suppress_aligned_attribute) { - char bftmp[64]; - int aligned_len = snprintf(bftmp, sizeof(bftmp), " __attribute__((__aligned__(%u)))", tag__type(type)->alignment); - slen += aligned_len; - } - printed += fprintf(fp, sconf.hex_fmt ? "%*s/* %#5x" : "%*s/* %5u", @@ -995,8 +1005,8 @@ static size_t union__fprintf(struct type *type, const struct cu *cu, if (conf->prefix != NULL) printed += fprintf(fp, "%s ", conf->prefix); - printed += fprintf(fp, "union%s%s {\n", type__name(type, cu) ? " " : "", - type__name(type, cu) ?: ""); + printed += fprintf(fp, "union%s%s {\n", type__name(type) ? " " : "", + type__name(type) ?: ""); uconf = *conf; uconf.indent = indent + 1; @@ -1081,7 +1091,7 @@ size_t ftype__fprintf_parms(const struct ftype *ftype, indent, tabs); } else first_parm = 0; - name = conf->no_parm_names ? NULL : parameter__name(pos, cu); + name = conf->no_parm_names ? NULL : parameter__name(pos); type = cu__type(cu, pos->tag.type); if (type == NULL) { snprintf(sbf, sizeof(sbf), @@ -1155,7 +1165,7 @@ static size_t function__tag_fprintf(const struct tag *tag, const struct cu *cu, break; } printed = fprintf(fp, "%.*s", indent, tabs); - name = function__name(alias, cu); + name = function__name(alias); n = fprintf(fp, "%s", name); size_t namelen = 0; if (name != NULL) @@ -1167,7 +1177,7 @@ static size_t function__tag_fprintf(const struct tag *tag, const struct cu *cu, exp->size, (unsigned long long)exp->ip.addr); #if 0 n = fprintf(fp, "%s(); /* size=%zd, low_pc=%#llx */", - function__name(alias, cu), exp->size, + function__name(alias), exp->size, (unsigned long long)exp->ip.addr); #endif c = 69; @@ -1178,7 +1188,7 @@ static size_t function__tag_fprintf(const struct tag *tag, const struct cu *cu, printed = fprintf(fp, "%.*s", indent, tabs); n = fprintf(fp, "%s %s; /* scope: %s */", variable__type_name(vtag, cu, bf, sizeof(bf)), - variable__name(vtag, cu), + variable__name(vtag), variable__scope_str(vtag)); c += n; printed += n; @@ -1188,7 +1198,7 @@ static size_t function__tag_fprintf(const struct tag *tag, const struct cu *cu, printed = fprintf(fp, "%.*s", indent, tabs); fputc('\n', fp); ++printed; - c = fprintf(fp, "%s:", label__name(label, cu)); + c = fprintf(fp, "%s:", label__name(label)); printed += c; } break; @@ -1227,7 +1237,7 @@ size_t lexblock__fprintf(const struct lexblock *block, const struct cu *cu, (unsigned long long)block->ip.addr); else printed += fprintf(fp, " /* %s+%#llx */", - function__name(function, cu), + function__name(function), (unsigned long long)offset); } printed += fprintf(fp, "\n"); @@ -1272,7 +1282,7 @@ static size_t function__fprintf(const struct tag *tag, const struct cu *cu, func->virtuality == DW_VIRTUALITY_pure_virtual) printed += fprintf(fp, "virtual "); - printed += ftype__fprintf(ftype, cu, function__name(func, cu), + printed += ftype__fprintf(ftype, cu, function__name(func), inlined, 0, 0, false, conf, fp); if (func->virtuality == DW_VIRTUALITY_pure_virtual) @@ -1306,11 +1316,11 @@ static size_t class__fprintf_cacheline_boundary(struct conf_fprintf *conf, FILE *fp) { int indent = conf->indent; - uint32_t cacheline = offset / cacheline_size; + uint32_t cacheline = offset / conf->cacheline_size; size_t printed = 0; if (cacheline > *conf->cachelinep) { - const uint32_t cacheline_pos = offset % cacheline_size; + const uint32_t cacheline_pos = offset % conf->cacheline_size; const uint32_t cacheline_in_bytes = offset - cacheline_pos; if (cacheline_pos == 0) @@ -1330,8 +1340,7 @@ static size_t class__fprintf_cacheline_boundary(struct conf_fprintf *conf, return printed; } -static size_t class__vtable_fprintf(struct class *class, const struct cu *cu, - const struct conf_fprintf *conf, FILE *fp) +static size_t class__vtable_fprintf(struct class *class, const struct conf_fprintf *conf, FILE *fp) { struct function *pos; size_t printed = 0; @@ -1345,8 +1354,8 @@ static size_t class__vtable_fprintf(struct class *class, const struct cu *cu, list_for_each_entry(pos, &class->vtable, vtable_node) { printed += fprintf(fp, "%.*s [%d] = %s(%s), \n", conf->indent, tabs, pos->vtable_entry, - function__name(pos, cu), - function__linkage_name(pos, cu)); + function__name(pos), + function__linkage_name(pos)); } printed += fprintf(fp, "%.*s} */", conf->indent, tabs); @@ -1381,8 +1390,8 @@ static size_t __class__fprintf(struct class *class, const struct cu *cu, t == DW_TAG_structure_type) ? "struct" : t == DW_TAG_class_type ? "class" : "interface"), - type__name(type, cu) ? " " : "", - type__name(type, cu) ?: ""); + type__name(type) ? " " : "", + type__name(type) ?: ""); int indent = cconf.indent; if (indent >= (int)sizeof(tabs)) @@ -1421,7 +1430,7 @@ static size_t __class__fprintf(struct class *class, const struct cu *cu, struct tag *pos_type = cu__type(cu, tag_pos->type); if (pos_type != NULL) printed += fprintf(fp, " %s", - type__name(tag__type(pos_type), cu)); + type__name(tag__type(pos_type))); else printed += tag__id_not_found_fprintf(fp, tag_pos->type); } @@ -1702,7 +1711,7 @@ static size_t __class__fprintf(struct class *class, const struct cu *cu, } if (!cconf.show_only_data_members) - class__vtable_fprintf(class, cu, &cconf, fp); + class__vtable_fprintf(class, &cconf, fp); if (!cconf.emit_stats) goto out; @@ -1753,7 +1762,7 @@ static size_t __class__fprintf(struct class *class, const struct cu *cu, } printed += fprintf(fp, " */\n"); } - cacheline = (cconf.base_offset + type->size) % cacheline_size; + cacheline = (cconf.base_offset + type->size) % conf->cacheline_size; if (cacheline != 0) printed += fprintf(fp, "%.*s/* last cacheline: %u bytes */\n", cconf.indent, tabs, @@ -1764,7 +1773,7 @@ static size_t __class__fprintf(struct class *class, const struct cu *cu, printed += fprintf(fp, "%.*s/* first biggest size base type member: %s %u %zd */\n", cconf.indent, tabs, - class_member__name(m, cu), m->byte_offset, + class_member__name(m), m->byte_offset, m->byte_size); } @@ -1806,7 +1815,7 @@ static size_t variable__fprintf(const struct tag *tag, const struct cu *cu, const struct conf_fprintf *conf, FILE *fp) { const struct variable *var = tag__variable(tag); - const char *name = variable__name(var, cu); + const char *name = variable__name(var); size_t printed = 0; if (name != NULL) { @@ -1827,8 +1836,7 @@ static size_t namespace__fprintf(const struct tag *tag, const struct cu *cu, { struct namespace *space = tag__namespace(tag); struct conf_fprintf cconf = *conf; - size_t printed = fprintf(fp, "namespace %s {\n", - namespace__name(space, cu)); + size_t printed = fprintf(fp, "namespace %s {\n", namespace__name(space)); struct tag *pos; ++cconf.indent; @@ -1887,7 +1895,7 @@ size_t tag__fprintf(struct tag *tag, const struct cu *cu, printed += array_type__fprintf(tag, cu, "array", pconf, fp); break; case DW_TAG_enumeration_type: - printed += enumeration__fprintf(tag, cu, pconf, fp); + printed += enumeration__fprintf(tag, pconf, fp); break; case DW_TAG_typedef: printed += typedef__fprintf(tag, cu, pconf, fp); @@ -1933,8 +1941,7 @@ size_t tag__fprintf(struct tag *tag, const struct cu *cu, const struct function *func = tag__function(tag); if (func->linkage_name) - printed += fprintf(fp, " /* linkage=%s */", - function__linkage_name(func, cu)); + printed += fprintf(fp, " /* linkage=%s */", function__linkage_name(func)); } if (pconf->expand_types) @@ -1946,22 +1953,58 @@ size_t tag__fprintf(struct tag *tag, const struct cu *cu, void cus__print_error_msg(const char *progname, const struct cus *cus, const char *filename, const int err) { - if (err == -EINVAL || (cus != NULL && list_empty(&cus->cus))) + if (err == -EINVAL || (cus != NULL && cus__empty(cus))) fprintf(stderr, "%s: couldn't load debugging info from %s\n", progname, filename); else - fprintf(stderr, "%s: %s\n", progname, strerror(err)); + fprintf(stderr, "%s: %s\n", progname, strerror(-err)); } -void dwarves__fprintf_init(uint16_t user_cacheline_size) +#ifndef _SC_LEVEL1_DCACHE_LINESIZE +int filename__read_int(const char *filename, int *value) { + char line[64]; + int fd = open(filename, O_RDONLY), err = -1; + + if (fd < 0) + return -1; + + if (read(fd, line, sizeof(line)) > 0) { + *value = atoi(line); + err = 0; + } + + close(fd); + return err; +} +#endif + +static long cacheline__size(void) +{ +#ifdef _SC_LEVEL1_DCACHE_LINESIZE + return sysconf(_SC_LEVEL1_DCACHE_LINESIZE); +#else + int value; + return filename__read_int("/sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size", &value) == 0 ? value : -1; +#endif +} + +void dwarves__resolve_cacheline_size(const struct conf_load *conf, uint16_t user_cacheline_size) +{ + uint16_t size; + if (user_cacheline_size == 0) { - long sys_cacheline_size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE); + long sys_cacheline_size = cacheline__size(); if (sys_cacheline_size > 0) - cacheline_size = sys_cacheline_size; + size = sys_cacheline_size; else - cacheline_size = 64; /* Fall back to a sane value */ + size = 64; /* Fall back to a sane value */ } else - cacheline_size = user_cacheline_size; + size = user_cacheline_size; + + if (conf) + conf->conf_fprintf->cacheline_size = size; + + conf_fprintf__defaults.cacheline_size = size; } diff --git a/dwarves_reorganize.c b/dwarves_reorganize.c index bae5b6e..79b159b 100644 --- a/dwarves_reorganize.c +++ b/dwarves_reorganize.c @@ -225,7 +225,7 @@ static bool class__move_member(struct class *class, struct class_member *dest, if (verbose) fprintf(fp, " bitfield('%s' ... ", - class_member__name(from, cu)); + class_member__name(from)); class__for_each_member_safe_from(class, from, pos, tmp) { /* * Have we reached the end of the bitfield? @@ -241,10 +241,10 @@ static bool class__move_member(struct class *class, struct class_member *dest, list_splice(&from_list, &dest->tag.node); if (verbose) fprintf(fp, "'%s')", - class_member__name(tail_from, cu)); + class_member__name(tail_from)); } else { if (verbose) - fprintf(fp, " '%s'", class_member__name(from, cu)); + fprintf(fp, " '%s'", class_member__name(from)); /* * Remove 'from' from the list */ @@ -260,8 +260,8 @@ static bool class__move_member(struct class *class, struct class_member *dest, if (verbose) fprintf(fp, " from after '%s' to after '%s' */\n", - class_member__name(from_prev, cu), - class_member__name(dest, cu)); + class_member__name(from_prev), + class_member__name(dest)); if (from_padding) { /* @@ -279,7 +279,7 @@ static bool class__move_member(struct class *class, struct class_member *dest, if (verbose) fprintf(fp, "/* adding %zd bytes from %s to " "the padding */\n", - from_size, class_member__name(from, cu)); + from_size, class_member__name(from)); } } else if (from_was_last) { class->type.size -= from_size + class->padding; @@ -358,9 +358,9 @@ static void class__move_bit_member(struct class *class, const struct cu *cu, if (verbose) fprintf(fp, "/* Moving '%s:%u' from after '%s' to " "after '%s:%u' */\n", - class_member__name(from, cu), from->bitfield_size, - class_member__name(from_prev, cu), - class_member__name(dest, cu), dest->bitfield_size); + class_member__name(from), from->bitfield_size, + class_member__name(from_prev), + class_member__name(dest), dest->bitfield_size); /* * Remove 'from' from the list */ @@ -512,12 +512,12 @@ static int class__demote_bitfields(struct class *class, const struct cu *cu, char old_bf[64], new_bf[64]; fprintf(fp, "/* Demoting bitfield ('%s' ... '%s') " "from '%s' to '%s' */\n", - class_member__name(bitfield_head, cu), - class_member__name(member, cu), + class_member__name(bitfield_head), + class_member__name(member), base_type__name(tag__base_type(old_type_tag), - cu, old_bf, sizeof(old_bf)), + old_bf, sizeof(old_bf)), base_type__name(tag__base_type(new_type_tag), - cu, new_bf, sizeof(new_bf))); + new_bf, sizeof(new_bf))); } class__demote_bitfield_members(class, @@ -557,11 +557,11 @@ static int class__demote_bitfields(struct class *class, const struct cu *cu, char old_bf[64], new_bf[64]; fprintf(fp, "/* Demoting bitfield ('%s') " "from '%s' to '%s' */\n", - class_member__name(member, cu), + class_member__name(member), base_type__name(tag__base_type(old_type_tag), - cu, old_bf, sizeof(old_bf)), + old_bf, sizeof(old_bf)), base_type__name(tag__base_type(new_type_tag), - cu, new_bf, sizeof(new_bf))); + new_bf, sizeof(new_bf))); } class__demote_bitfield_members(class, member, member, diff --git a/elf_symtab.c b/elf_symtab.c index 741990e..3a31d64 100644 --- a/elf_symtab.c +++ b/elf_symtab.c @@ -15,13 +15,15 @@ #define HASHSYMS__BITS 8 #define HASHSYMS__SIZE (1UL << HASHSYMS__BITS) -struct elf_symtab *elf_symtab__new(const char *name, Elf *elf, GElf_Ehdr *ehdr) +struct elf_symtab *elf_symtab__new(const char *name, Elf *elf) { + size_t symtab_index; + if (name == NULL) name = ".symtab"; GElf_Shdr shdr; - Elf_Scn *sec = elf_section_by_name(elf, ehdr, &shdr, name, NULL); + Elf_Scn *sec = elf_section_by_name(elf, &shdr, name, &symtab_index); if (sec == NULL) return NULL; @@ -29,7 +31,7 @@ struct elf_symtab *elf_symtab__new(const char *name, Elf *elf, GElf_Ehdr *ehdr) if (gelf_getshdr(sec, &shdr) == NULL) return NULL; - struct elf_symtab *symtab = malloc(sizeof(*symtab)); + struct elf_symtab *symtab = zalloc(sizeof(*symtab)); if (symtab == NULL) return NULL; @@ -41,6 +43,12 @@ struct elf_symtab *elf_symtab__new(const char *name, Elf *elf, GElf_Ehdr *ehdr) if (symtab->syms == NULL) goto out_free_name; + /* + * This returns extended section index table's + * section index, if it exists. + */ + int symtab_xindex = elf_scnshndx(sec); + sec = elf_getscn(elf, shdr.sh_link); if (sec == NULL) goto out_free_name; @@ -49,11 +57,40 @@ struct elf_symtab *elf_symtab__new(const char *name, Elf *elf, GElf_Ehdr *ehdr) if (symtab->symstrs == NULL) goto out_free_name; + /* + * The .symtab section has optional extended section index + * table, load its data so it can be used to resolve symbol's + * section index. + **/ + if (symtab_xindex > 0) { + GElf_Shdr shdr_xindex; + Elf_Scn *sec_xindex; + + sec_xindex = elf_getscn(elf, symtab_xindex); + if (sec_xindex == NULL) + goto out_free_name; + + if (gelf_getshdr(sec_xindex, &shdr_xindex) == NULL) + goto out_free_name; + + /* Extra check to verify it's correct type */ + if (shdr_xindex.sh_type != SHT_SYMTAB_SHNDX) + goto out_free_name; + + /* Extra check to verify it belongs to the .symtab */ + if (symtab_index != shdr_xindex.sh_link) + goto out_free_name; + + symtab->syms_sec_idx_table = elf_getdata(elf_getscn(elf, symtab_xindex), NULL); + if (symtab->syms_sec_idx_table == NULL) + goto out_free_name; + } + symtab->nr_syms = shdr.sh_size / shdr.sh_entsize; return symtab; out_free_name: - free(symtab->name); + zfree(&symtab->name); out_delete: free(symtab); return NULL; @@ -63,6 +100,6 @@ void elf_symtab__delete(struct elf_symtab *symtab) { if (symtab == NULL) return; - free(symtab->name); + zfree(&symtab->name); free(symtab); } diff --git a/elf_symtab.h b/elf_symtab.h index 359add6..5d4cb52 100644 --- a/elf_symtab.h +++ b/elf_symtab.h @@ -16,10 +16,12 @@ struct elf_symtab { uint32_t nr_syms; Elf_Data *syms; Elf_Data *symstrs; + /* Data of SHT_SYMTAB_SHNDX section. */ + Elf_Data *syms_sec_idx_table; char *name; }; -struct elf_symtab *elf_symtab__new(const char *name, Elf *elf, GElf_Ehdr *ehdr); +struct elf_symtab *elf_symtab__new(const char *name, Elf *elf); void elf_symtab__delete(struct elf_symtab *symtab); static inline uint32_t elf_symtab__nr_symbols(const struct elf_symtab *symtab) @@ -77,6 +79,19 @@ static inline bool elf_sym__is_local_object(const GElf_Sym *sym) sym->st_shndx != SHN_UNDEF; } +static inline bool +elf_sym__get(Elf_Data *syms, Elf_Data *syms_sec_idx_table, + int id, GElf_Sym *sym, Elf32_Word *sym_sec_idx) +{ + if (!gelf_getsymshndx(syms, syms_sec_idx_table, id, sym, sym_sec_idx)) + return false; + + if (sym->st_shndx != SHN_XINDEX) + *sym_sec_idx = sym->st_shndx; + + return true; +} + /** * elf_symtab__for_each_symbol - iterate thru all the symbols * @@ -89,4 +104,18 @@ static inline bool elf_sym__is_local_object(const GElf_Sym *sym) index < symtab->nr_syms; \ index++, gelf_getsym(symtab->syms, index, &sym)) +/** + * elf_symtab__for_each_symbol_index - iterate through all the symbols, + * that takes extended symbols indexes into account + * + * @symtab: struct elf_symtab instance to iterate + * @index: uint32_t index + * @sym: GElf_Sym iterator + * @sym_sec_idx: symbol's index + */ +#define elf_symtab__for_each_symbol_index(symtab, id, sym, sym_sec_idx) \ + for (id = 0; id < symtab->nr_syms; id++) \ + if (elf_sym__get(symtab->syms, symtab->syms_sec_idx_table, \ + id, &sym, &sym_sec_idx)) + #endif /* _ELF_SYMTAB_H_ */ diff --git a/elfcreator.c b/elfcreator.c index 3bbee84..8e775c5 100644 --- a/elfcreator.c +++ b/elfcreator.c @@ -108,7 +108,7 @@ static void update_dyn_cache(ElfCreator *ctor) ctor->dyndata = elf_getdata(ctor->dynscn, NULL); } -void elfcreator_copy_scn(ElfCreator *ctor, Elf *src, Elf_Scn *scn) +void elfcreator_copy_scn(ElfCreator *ctor, Elf_Scn *scn) { Elf_Scn *newscn; Elf_Data *indata, *outdata; diff --git a/elfcreator.h b/elfcreator.h index a071de5..c2c8fe4 100644 --- a/elfcreator.h +++ b/elfcreator.h @@ -12,7 +12,7 @@ typedef struct elf_creator ElfCreator; extern ElfCreator *elfcreator_begin(char *path, Elf *elf); -extern void elfcreator_copy_scn(ElfCreator *ctor, Elf *src, Elf_Scn *scn); +extern void elfcreator_copy_scn(ElfCreator *ctor, Elf_Scn *scn); extern void elfcreator_end(ElfCreator *ctor); #endif /* ELFCREATOR_H */ @@ -41,7 +41,10 @@ struct gobuffer *gobuffer__new(void) void __gobuffer__delete(struct gobuffer *gb) { - free(gb->entries); + if (gb == NULL) + return; + + zfree(&gb->entries); } void gobuffer__delete(struct gobuffer *gb) @@ -16,55 +16,9 @@ #include <stdint.h> -/* 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */ -#define GOLDEN_RATIO_PRIME_32 0x9e370001UL -/* 2^63 + 2^61 - 2^57 + 2^54 - 2^51 - 2^18 + 1 */ -#define GOLDEN_RATIO_PRIME_64 0x9e37fffffffc0001UL - -#if __WORDSIZE == 32 -#define GOLDEN_RATIO_PRIME GOLDEN_RATIO_PRIME_32 -#define hash_long(val, bits) hash_32(val, bits) -#elif __WORDSIZE == 64 -#define hash_long(val, bits) hash_64(val, bits) -#define GOLDEN_RATIO_PRIME GOLDEN_RATIO_PRIME_64 -#else -#error Wordsize not 32 or 64 -#endif - static inline uint64_t hash_64(const uint64_t val, const unsigned int bits) { - uint64_t hash = val; - - /* Sigh, gcc can't optimise this alone like it does for 32 bits. */ - uint64_t n = hash; - n <<= 18; - hash -= n; - n <<= 33; - hash -= n; - n <<= 3; - hash += n; - n <<= 3; - hash -= n; - n <<= 4; - hash += n; - n <<= 2; - hash += n; - - /* High bits are more random, so use them. */ - return hash >> (64 - bits); -} - -static inline uint32_t hash_32(uint32_t val, unsigned int bits) -{ - /* On some cpus multiply is faster, on others gcc will do shifts */ - uint32_t hash = val * GOLDEN_RATIO_PRIME_32; - - /* High bits are more random, so use them. */ - return hash >> (32 - bits); + return (val * 11400714819323198485LLU) >> (64 - bits); } -static inline unsigned long hash_ptr(void *ptr, unsigned int bits) -{ - return hash_long((unsigned long)ptr, bits); -} #endif /* _LINUX_HASH_H */ diff --git a/lib/include/bpf b/lib/include/bpf new file mode 120000 index 0000000..4c41b71 --- /dev/null +++ b/lib/include/bpf @@ -0,0 +1 @@ +../bpf/src
\ No newline at end of file diff --git a/libbtf.c b/libbtf.c deleted file mode 100644 index 3709087..0000000 --- a/libbtf.c +++ /dev/null @@ -1,826 +0,0 @@ -/* - SPDX-License-Identifier: GPL-2.0-only - - Copyright (C) 2019 Facebook - */ - -#include <fcntl.h> -#include <gelf.h> -#include <limits.h> -#include <malloc.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> -#include <stdarg.h> - -#include "libbtf.h" -#include "lib/bpf/include/uapi/linux/btf.h" -#include "lib/bpf/include/linux/err.h" -#include "lib/bpf/src/btf.h" -#include "lib/bpf/src/libbpf.h" -#include "dutil.h" -#include "gobuffer.h" -#include "dwarves.h" -#include "elf_symtab.h" - -struct btf *base_btf; -uint8_t btf_elf__verbose; -uint8_t btf_elf__force; - -static int btf_var_secinfo_cmp(const void *a, const void *b) -{ - const struct btf_var_secinfo *av = a; - const struct btf_var_secinfo *bv = b; - - return av->offset - bv->offset; -} - -static int libbpf_log(enum libbpf_print_level level, const char *format, va_list args) -{ - return vfprintf(stderr, format, args); -} - -int btf_elf__load(struct btf_elf *btfe) -{ - int err; - - libbpf_set_print(libbpf_log); - - /* free initial empty BTF */ - btf__free(btfe->btf); - if (btfe->raw_btf) - btfe->btf = btf__parse_raw_split(btfe->filename, btfe->base_btf); - else - btfe->btf = btf__parse_elf_split(btfe->filename, btfe->base_btf); - - err = libbpf_get_error(btfe->btf); - if (err) - return err; - - return 0; -} - -struct btf_elf *btf_elf__new(const char *filename, Elf *elf, struct btf *base_btf) -{ - struct btf_elf *btfe = zalloc(sizeof(*btfe)); - GElf_Shdr shdr; - Elf_Scn *sec; - - if (!btfe) - return NULL; - - btfe->in_fd = -1; - btfe->filename = strdup(filename); - if (btfe->filename == NULL) - goto errout; - - btfe->base_btf = base_btf; - btfe->btf = btf__new_empty_split(base_btf); - if (libbpf_get_error(btfe->btf)) { - fprintf(stderr, "%s: failed to create empty BTF.\n", __func__); - goto errout; - } - - if (strstarts(filename, "/sys/kernel/btf/")) { -try_as_raw_btf: - btfe->raw_btf = true; - btfe->wordsize = sizeof(long); - btfe->is_big_endian = BYTE_ORDER == BIG_ENDIAN; - btf__set_endianness(btfe->btf, - btfe->is_big_endian ? BTF_BIG_ENDIAN : BTF_LITTLE_ENDIAN); - return btfe; - } - - if (elf != NULL) { - btfe->elf = elf; - } else { - btfe->in_fd = open(filename, O_RDONLY); - if (btfe->in_fd < 0) - goto errout; - - if (elf_version(EV_CURRENT) == EV_NONE) { - fprintf(stderr, "%s: cannot set libelf version.\n", - __func__); - goto errout; - } - - btfe->elf = elf_begin(btfe->in_fd, ELF_C_READ_MMAP, NULL); - if (!btfe->elf) { - fprintf(stderr, "%s: cannot read %s ELF file.\n", - __func__, filename); - goto errout; - } - } - - if (gelf_getehdr(btfe->elf, &btfe->ehdr) == NULL) { - struct btf_header hdr; - if (lseek(btfe->in_fd, 0, SEEK_SET) == 0 && - read(btfe->in_fd, &hdr, sizeof(hdr)) == sizeof(hdr) && - hdr.magic == BTF_MAGIC) { - close(btfe->in_fd); - elf_end(btfe->elf); - btfe->in_fd = -1; - goto try_as_raw_btf; - } - if (btf_elf__verbose) - fprintf(stderr, "%s: cannot get elf header.\n", __func__); - goto errout; - } - - switch (btfe->ehdr.e_ident[EI_DATA]) { - case ELFDATA2LSB: - btfe->is_big_endian = false; - btf__set_endianness(btfe->btf, BTF_LITTLE_ENDIAN); - break; - case ELFDATA2MSB: - btfe->is_big_endian = true; - btf__set_endianness(btfe->btf, BTF_BIG_ENDIAN); - break; - default: - fprintf(stderr, "%s: unknown elf endianness.\n", __func__); - goto errout; - } - - switch (btfe->ehdr.e_ident[EI_CLASS]) { - case ELFCLASS32: btfe->wordsize = 4; break; - case ELFCLASS64: btfe->wordsize = 8; break; - default: btfe->wordsize = 0; break; - } - - btfe->symtab = elf_symtab__new(NULL, btfe->elf, &btfe->ehdr); - if (!btfe->symtab) { - if (btf_elf__verbose) - printf("%s: '%s' doesn't have symtab.\n", __func__, - btfe->filename); - return btfe; - } - - /* find percpu section's shndx */ - sec = elf_section_by_name(btfe->elf, &btfe->ehdr, &shdr, PERCPU_SECTION, - NULL); - if (!sec) { - if (btf_elf__verbose) - printf("%s: '%s' doesn't have '%s' section\n", __func__, - btfe->filename, PERCPU_SECTION); - return btfe; - } - btfe->percpu_shndx = elf_ndxscn(sec); - btfe->percpu_base_addr = shdr.sh_addr; - btfe->percpu_sec_sz = shdr.sh_size; - - return btfe; - -errout: - btf_elf__delete(btfe); - return NULL; -} - -void btf_elf__delete(struct btf_elf *btfe) -{ - if (!btfe) - return; - - if (btfe->in_fd != -1) { - close(btfe->in_fd); - if (btfe->elf) - elf_end(btfe->elf); - } - - elf_symtab__delete(btfe->symtab); - __gobuffer__delete(&btfe->percpu_secinfo); - btf__free(btfe->btf); - free(btfe->filename); - free(btfe); -} - -const char *btf_elf__string(struct btf_elf *btfe, uint32_t ref) -{ - const char *s = btf__str_by_offset(btfe->btf, ref); - - return s && s[0] == '\0' ? NULL : s; -} - -#define BITS_PER_BYTE 8 -#define BITS_PER_BYTE_MASK (BITS_PER_BYTE - 1) -#define BITS_PER_BYTE_MASKED(bits) ((bits) & BITS_PER_BYTE_MASK) -#define BITS_ROUNDDOWN_BYTES(bits) ((bits) >> 3) -#define BITS_ROUNDUP_BYTES(bits) (BITS_ROUNDDOWN_BYTES(bits) + !!BITS_PER_BYTE_MASKED(bits)) - -static const char * const btf_kind_str[NR_BTF_KINDS] = { - [BTF_KIND_UNKN] = "UNKNOWN", - [BTF_KIND_INT] = "INT", - [BTF_KIND_PTR] = "PTR", - [BTF_KIND_ARRAY] = "ARRAY", - [BTF_KIND_STRUCT] = "STRUCT", - [BTF_KIND_UNION] = "UNION", - [BTF_KIND_ENUM] = "ENUM", - [BTF_KIND_FWD] = "FWD", - [BTF_KIND_TYPEDEF] = "TYPEDEF", - [BTF_KIND_VOLATILE] = "VOLATILE", - [BTF_KIND_CONST] = "CONST", - [BTF_KIND_RESTRICT] = "RESTRICT", - [BTF_KIND_FUNC] = "FUNC", - [BTF_KIND_FUNC_PROTO] = "FUNC_PROTO", - [BTF_KIND_VAR] = "VAR", - [BTF_KIND_DATASEC] = "DATASEC", -}; - -static const char *btf_elf__printable_name(const struct btf_elf *btfe, uint32_t offset) -{ - if (!offset) - return "(anon)"; - else - return btf__str_by_offset(btfe->btf, offset); -} - -static const char * btf_elf__int_encoding_str(uint8_t encoding) -{ - if (encoding == 0) - return "(none)"; - else if (encoding == BTF_INT_SIGNED) - return "SIGNED"; - else if (encoding == BTF_INT_CHAR) - return "CHAR"; - else if (encoding == BTF_INT_BOOL) - return "BOOL"; - else - return "UNKN"; -} - - -__attribute ((format (printf, 5, 6))) -static void btf_elf__log_err(const struct btf_elf *btfe, int kind, const char *name, - bool output_cr, const char *fmt, ...) -{ - fprintf(stderr, "[%u] %s %s", btf__get_nr_types(btfe->btf) + 1, - btf_kind_str[kind], name ?: "(anon)"); - - if (fmt && *fmt) { - va_list ap; - - fprintf(stderr, " "); - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - } - - if (output_cr) - fprintf(stderr, "\n"); -} - -__attribute ((format (printf, 5, 6))) -static void btf_elf__log_type(const struct btf_elf *btfe, const struct btf_type *t, - bool err, bool output_cr, const char *fmt, ...) -{ - uint8_t kind; - FILE *out; - - if (!btf_elf__verbose && !err) - return; - - kind = BTF_INFO_KIND(t->info); - out = err ? stderr : stdout; - - fprintf(out, "[%u] %s %s", - btf__get_nr_types(btfe->btf), btf_kind_str[kind], - btf_elf__printable_name(btfe, t->name_off)); - - if (fmt && *fmt) { - va_list ap; - - fprintf(out, " "); - va_start(ap, fmt); - vfprintf(out, fmt, ap); - va_end(ap); - } - - if (output_cr) - fprintf(out, "\n"); -} - -__attribute ((format (printf, 5, 6))) -static void btf_log_member(const struct btf_elf *btfe, - const struct btf_type *t, - const struct btf_member *member, - bool err, const char *fmt, ...) -{ - FILE *out; - - if (!btf_elf__verbose && !err) - return; - - out = err ? stderr : stdout; - - if (btf_kflag(t)) - fprintf(out, "\t%s type_id=%u bitfield_size=%u bits_offset=%u", - btf_elf__printable_name(btfe, member->name_off), - member->type, - BTF_MEMBER_BITFIELD_SIZE(member->offset), - BTF_MEMBER_BIT_OFFSET(member->offset)); - else - fprintf(out, "\t%s type_id=%u bits_offset=%u", - btf_elf__printable_name(btfe, member->name_off), - member->type, - member->offset); - - if (fmt && *fmt) { - va_list ap; - - fprintf(out, " "); - va_start(ap, fmt); - vfprintf(out, fmt, ap); - va_end(ap); - } - - fprintf(out, "\n"); -} - -__attribute ((format (printf, 6, 7))) -static void btf_log_func_param(const struct btf_elf *btfe, - const char *name, uint32_t type, - bool err, bool is_last_param, - const char *fmt, ...) -{ - FILE *out; - - if (!btf_elf__verbose && !err) - return; - - out = err ? stderr : stdout; - - if (is_last_param && !type) - fprintf(out, "vararg)\n"); - else - fprintf(out, "%u %s%s", type, name, is_last_param ? ")\n" : ", "); - - if (fmt && *fmt) { - va_list ap; - - fprintf(out, " "); - va_start(ap, fmt); - vfprintf(out, fmt, ap); - va_end(ap); - } -} - -int32_t btf_elf__add_base_type(struct btf_elf *btfe, const struct base_type *bt, - const char *name) -{ - struct btf *btf = btfe->btf; - const struct btf_type *t; - uint8_t encoding = 0; - int32_t id; - - if (bt->is_signed) { - encoding = BTF_INT_SIGNED; - } else if (bt->is_bool) { - encoding = BTF_INT_BOOL; - } else if (bt->float_type) { - fprintf(stderr, "float_type is not supported\n"); - return -1; - } - - id = btf__add_int(btf, name, BITS_ROUNDUP_BYTES(bt->bit_size), encoding); - if (id < 0) { - btf_elf__log_err(btfe, BTF_KIND_INT, name, true, "Error emitting BTF type"); - } else { - t = btf__type_by_id(btf, id); - btf_elf__log_type(btfe, t, false, true, - "size=%u nr_bits=%u encoding=%s%s", - t->size, bt->bit_size, - btf_elf__int_encoding_str(encoding), - id < 0 ? " Error in emitting BTF" : "" ); - } - - return id; -} - -int32_t btf_elf__add_ref_type(struct btf_elf *btfe, uint16_t kind, uint32_t type, - const char *name, bool kind_flag) -{ - struct btf *btf = btfe->btf; - const struct btf_type *t; - int32_t id; - - switch (kind) { - case BTF_KIND_PTR: - id = btf__add_ptr(btf, type); - break; - case BTF_KIND_VOLATILE: - id = btf__add_volatile(btf, type); - break; - case BTF_KIND_CONST: - id = btf__add_const(btf, type); - break; - case BTF_KIND_RESTRICT: - id = btf__add_restrict(btf, type); - break; - case BTF_KIND_TYPEDEF: - id = btf__add_typedef(btf, name, type); - break; - case BTF_KIND_FWD: - id = btf__add_fwd(btf, name, kind_flag); - break; - case BTF_KIND_FUNC: - id = btf__add_func(btf, name, BTF_FUNC_STATIC, type); - break; - default: - btf_elf__log_err(btfe, kind, name, true, "Unexpected kind for reference"); - return -1; - } - - if (id > 0) { - t = btf__type_by_id(btf, id); - if (kind == BTF_KIND_FWD) - btf_elf__log_type(btfe, t, false, true, "%s", kind_flag ? "union" : "struct"); - else - btf_elf__log_type(btfe, t, false, true, "type_id=%u", t->type); - } else { - btf_elf__log_err(btfe, kind, name, true, "Error emitting BTF type"); - } - return id; -} - -int32_t btf_elf__add_array(struct btf_elf *btfe, uint32_t type, uint32_t index_type, uint32_t nelems) -{ - struct btf *btf = btfe->btf; - const struct btf_type *t; - const struct btf_array *array; - int32_t id; - - id = btf__add_array(btf, index_type, type, nelems); - if (id > 0) { - t = btf__type_by_id(btf, id); - array = btf_array(t); - btf_elf__log_type(btfe, t, false, true, - "type_id=%u index_type_id=%u nr_elems=%u", - array->type, array->index_type, array->nelems); - } else { - btf_elf__log_err(btfe, BTF_KIND_ARRAY, NULL, true, - "type_id=%u index_type_id=%u nr_elems=%u Error emitting BTF type", - type, index_type, nelems); - } - return id; -} - -int btf_elf__add_member(struct btf_elf *btfe, const char *name, uint32_t type, - uint32_t bitfield_size, uint32_t offset) -{ - struct btf *btf = btfe->btf; - const struct btf_type *t; - const struct btf_member *m; - int err; - - err = btf__add_field(btf, name, type, offset, bitfield_size); - t = btf__type_by_id(btf, btf__get_nr_types(btf)); - if (err) { - fprintf(stderr, "[%u] %s %s's field '%s' offset=%u bit_size=%u type=%u Error emitting field\n", - btf__get_nr_types(btf), btf_kind_str[btf_kind(t)], - btf_elf__printable_name(btfe, t->name_off), - name, offset, bitfield_size, type); - } else { - m = &btf_members(t)[btf_vlen(t) - 1]; - btf_log_member(btfe, t, m, false, NULL); - } - return err; -} - -int32_t btf_elf__add_struct(struct btf_elf *btfe, uint8_t kind, const char *name, uint32_t size) -{ - struct btf *btf = btfe->btf; - const struct btf_type *t; - int32_t id; - - switch (kind) { - case BTF_KIND_STRUCT: - id = btf__add_struct(btf, name, size); - break; - case BTF_KIND_UNION: - id = btf__add_union(btf, name, size); - break; - default: - btf_elf__log_err(btfe, kind, name, true, "Unexpected kind of struct"); - return -1; - } - - if (id < 0) { - btf_elf__log_err(btfe, kind, name, true, "Error emitting BTF type"); - } else { - t = btf__type_by_id(btf, id); - btf_elf__log_type(btfe, t, false, true, "size=%u", t->size); - } - - return id; -} - -int32_t btf_elf__add_enum(struct btf_elf *btfe, const char *name, uint32_t bit_size) -{ - struct btf *btf = btfe->btf; - const struct btf_type *t; - int32_t id, size; - - size = BITS_ROUNDUP_BYTES(bit_size); - id = btf__add_enum(btf, name, size); - if (id > 0) { - t = btf__type_by_id(btf, id); - btf_elf__log_type(btfe, t, false, true, "size=%u", t->size); - } else { - btf_elf__log_err(btfe, BTF_KIND_ENUM, name, true, - "size=%u Error emitting BTF type", size); - } - return id; -} - -int btf_elf__add_enum_val(struct btf_elf *btfe, const char *name, int32_t value) -{ - struct btf *btf = btfe->btf; - int err; - - err = btf__add_enum_value(btf, name, value); - if (!err) { - if (btf_elf__verbose) - printf("\t%s val=%d\n", name, value); - } else { - fprintf(stderr, "\t%s val=%d Error emitting BTF enum value\n", - name, value); - } - return err; -} - -static int32_t btf_elf__add_func_proto_param(struct btf_elf *btfe, const char *name, - uint32_t type, bool is_last_param) -{ - int err; - - err = btf__add_func_param(btfe->btf, name, type); - if (!err) { - btf_log_func_param(btfe, name, type, false, is_last_param, NULL); - return 0; - } else { - btf_log_func_param(btfe, name, type, true, is_last_param, - "Error adding func param"); - return -1; - } -} - -extern struct debug_fmt_ops *dwarves__active_loader; - -int32_t btf_elf__add_func_proto(struct btf_elf *btfe, struct cu *cu, struct ftype *ftype, uint32_t type_id_off) -{ - struct btf *btf = btfe->btf; - const struct btf_type *t; - struct parameter *param; - uint16_t nr_params, param_idx; - int32_t id, type_id; - - /* add btf_type for func_proto */ - nr_params = ftype->nr_parms + (ftype->unspec_parms ? 1 : 0); - type_id = ftype->tag.type == 0 ? 0 : type_id_off + ftype->tag.type; - - id = btf__add_func_proto(btf, type_id); - if (id > 0) { - t = btf__type_by_id(btf, id); - btf_elf__log_type(btfe, t, false, false, "return=%u args=(%s", - t->type, !nr_params ? "void)\n" : ""); - } else { - btf_elf__log_err(btfe, BTF_KIND_FUNC_PROTO, NULL, true, - "return=%u vlen=%u Error emitting BTF type", - type_id, nr_params); - return id; - } - - /* add parameters */ - param_idx = 0; - ftype__for_each_parameter(ftype, param) { - const char *name = dwarves__active_loader->strings__ptr(cu, param->name); - - type_id = param->tag.type == 0 ? 0 : type_id_off + param->tag.type; - ++param_idx; - if (btf_elf__add_func_proto_param(btfe, name, type_id, param_idx == nr_params)) - return -1; - } - - ++param_idx; - if (ftype->unspec_parms) - if (btf_elf__add_func_proto_param(btfe, NULL, 0, param_idx == nr_params)) - return -1; - - return id; -} - -int32_t btf_elf__add_var_type(struct btf_elf *btfe, uint32_t type, const char *name, - uint32_t linkage) -{ - struct btf *btf = btfe->btf; - const struct btf_type *t; - int32_t id; - - id = btf__add_var(btf, name, linkage, type); - if (id > 0) { - t = btf__type_by_id(btf, id); - btf_elf__log_type(btfe, t, false, true, "type=%u linkage=%u", - t->type, btf_var(t)->linkage); - } else { - btf_elf__log_err(btfe, BTF_KIND_VAR, name, true, - "type=%u linkage=%u Error emitting BTF type", - type, linkage); - } - return id; -} - -int32_t btf_elf__add_var_secinfo(struct gobuffer *buf, uint32_t type, - uint32_t offset, uint32_t size) -{ - struct btf_var_secinfo si = { - .type = type, - .offset = offset, - .size = size, - }; - return gobuffer__add(buf, &si, sizeof(si)); -} - -int32_t btf_elf__add_datasec_type(struct btf_elf *btfe, const char *section_name, - struct gobuffer *var_secinfo_buf) -{ - struct btf *btf = btfe->btf; - size_t sz = gobuffer__size(var_secinfo_buf); - uint16_t nr_var_secinfo = sz / sizeof(struct btf_var_secinfo); - struct btf_var_secinfo *last_vsi, *vsi; - const struct btf_type *t; - uint32_t datasec_sz; - int32_t err, id, i; - - qsort(var_secinfo_buf->entries, nr_var_secinfo, - sizeof(struct btf_var_secinfo), btf_var_secinfo_cmp); - - last_vsi = (struct btf_var_secinfo *)var_secinfo_buf->entries + nr_var_secinfo - 1; - datasec_sz = last_vsi->offset + last_vsi->size; - - id = btf__add_datasec(btf, section_name, datasec_sz); - if (id < 0) { - btf_elf__log_err(btfe, BTF_KIND_DATASEC, section_name, true, - "size=%u vlen=%u Error emitting BTF type", - datasec_sz, nr_var_secinfo); - } else { - t = btf__type_by_id(btf, id); - btf_elf__log_type(btfe, t, false, true, "size=%u vlen=%u", - t->size, nr_var_secinfo); - } - - for (i = 0; i < nr_var_secinfo; i++) { - vsi = (struct btf_var_secinfo *)var_secinfo_buf->entries + i; - err = btf__add_datasec_var_info(btf, vsi->type, vsi->offset, vsi->size); - if (!err) { - if (btf_elf__verbose) - printf("\ttype=%u offset=%u size=%u\n", - vsi->type, vsi->offset, vsi->size); - } else { - fprintf(stderr, "\ttype=%u offset=%u size=%u Error emitting BTF datasec var info\n", - vsi->type, vsi->offset, vsi->size); - return -1; - } - } - - return id; -} - -static int btf_elf__write(const char *filename, struct btf *btf) -{ - GElf_Shdr shdr_mem, *shdr; - GElf_Ehdr ehdr_mem, *ehdr; - Elf_Data *btf_data = NULL; - Elf_Scn *scn = NULL; - Elf *elf = NULL; - const void *raw_btf_data; - uint32_t raw_btf_size; - int fd, err = -1; - size_t strndx; - - fd = open(filename, O_RDWR); - if (fd < 0) { - fprintf(stderr, "Cannot open %s\n", filename); - return -1; - } - - if (elf_version(EV_CURRENT) == EV_NONE) { - fprintf(stderr, "Cannot set libelf version.\n"); - goto out; - } - - elf = elf_begin(fd, ELF_C_RDWR, NULL); - if (elf == NULL) { - fprintf(stderr, "Cannot update ELF file.\n"); - goto out; - } - - elf_flagelf(elf, ELF_C_SET, ELF_F_DIRTY); - - ehdr = gelf_getehdr(elf, &ehdr_mem); - if (ehdr == NULL) { - fprintf(stderr, "%s: elf_getehdr failed.\n", __func__); - goto out; - } - - switch (ehdr_mem.e_ident[EI_DATA]) { - case ELFDATA2LSB: - btf__set_endianness(btf, BTF_LITTLE_ENDIAN); - break; - case ELFDATA2MSB: - btf__set_endianness(btf, BTF_BIG_ENDIAN); - break; - default: - fprintf(stderr, "%s: unknown elf endianness.\n", __func__); - goto out; - } - - /* - * First we look if there was already a .BTF section to overwrite. - */ - - elf_getshdrstrndx(elf, &strndx); - while ((scn = elf_nextscn(elf, scn)) != NULL) { - shdr = gelf_getshdr(scn, &shdr_mem); - if (shdr == NULL) - continue; - char *secname = elf_strptr(elf, strndx, shdr->sh_name); - if (strcmp(secname, ".BTF") == 0) { - btf_data = elf_getdata(scn, btf_data); - break; - } - } - - raw_btf_data = btf__get_raw_data(btf, &raw_btf_size); - - if (btf_data) { - /* Exisiting .BTF section found */ - btf_data->d_buf = (void *)raw_btf_data; - btf_data->d_size = raw_btf_size; - elf_flagdata(btf_data, ELF_C_SET, ELF_F_DIRTY); - - if (elf_update(elf, ELF_C_NULL) >= 0 && - elf_update(elf, ELF_C_WRITE) >= 0) - err = 0; - } else { - const char *llvm_objcopy; - char tmp_fn[PATH_MAX]; - char cmd[PATH_MAX * 2]; - - llvm_objcopy = getenv("LLVM_OBJCOPY"); - if (!llvm_objcopy) - llvm_objcopy = "llvm-objcopy"; - - /* Use objcopy to add a .BTF section */ - snprintf(tmp_fn, sizeof(tmp_fn), "%s.btf", filename); - close(fd); - fd = creat(tmp_fn, S_IRUSR | S_IWUSR); - if (fd == -1) { - fprintf(stderr, "%s: open(%s) failed!\n", __func__, - tmp_fn); - goto out; - } - - if (write(fd, raw_btf_data, raw_btf_size) != raw_btf_size) { - fprintf(stderr, "%s: write of %d bytes to '%s' failed: %d!\n", - __func__, raw_btf_size, tmp_fn, errno); - goto out; - } - - snprintf(cmd, sizeof(cmd), "%s --add-section .BTF=%s %s", - llvm_objcopy, tmp_fn, filename); - if (system(cmd)) { - fprintf(stderr, "%s: failed to add .BTF section to '%s': %d!\n", - __func__, tmp_fn, errno); - goto out; - } - - err = 0; - unlink(tmp_fn); - } - -out: - if (fd != -1) - close(fd); - if (elf) - elf_end(elf); - return err; -} - -int btf_elf__encode(struct btf_elf *btfe, uint8_t flags) -{ - struct btf *btf = btfe->btf; - - /* Empty file, nothing to do, so... done! */ - if (btf__get_nr_types(btf) == 0) - return 0; - - if (btf__dedup(btf, NULL, NULL)) { - fprintf(stderr, "%s: btf__dedup failed!\n", __func__); - return -1; - } - - return btf_elf__write(btfe->filename, btf); -} diff --git a/libbtf.h b/libbtf.h deleted file mode 100644 index 191f586..0000000 --- a/libbtf.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - SPDX-License-Identifier: GPL-2.0-only - - Copyright (C) 2019 Facebook - */ - -#ifndef _LIBBTF_H -#define _LIBBTF_H - -#include "gobuffer.h" - -#include <stdbool.h> -#include <stdint.h> -#include "lib/bpf/src/btf.h" - -struct btf_elf { - void *priv; - Elf *elf; - GElf_Ehdr ehdr; - struct elf_symtab *symtab; - struct gobuffer percpu_secinfo; - char *filename; - int in_fd; - uint8_t wordsize; - bool is_big_endian; - bool raw_btf; // "/sys/kernel/btf/vmlinux" - uint32_t percpu_shndx; - uint64_t percpu_base_addr; - uint64_t percpu_sec_sz; - struct btf *btf; - struct btf *base_btf; -}; - -extern struct btf *base_btf; -extern uint8_t btf_elf__verbose; -extern uint8_t btf_elf__force; -#define btf_elf__verbose_log(fmt, ...) { if (btf_elf__verbose) printf(fmt, __VA_ARGS__); } - -#define PERCPU_SECTION ".data..percpu" - -struct cu; -struct base_type; -struct ftype; - -struct btf_elf *btf_elf__new(const char *filename, Elf *elf, struct btf *base_btf); -void btf_elf__delete(struct btf_elf *btf); - -int32_t btf_elf__add_base_type(struct btf_elf *btf, const struct base_type *bt, - const char *name); -int32_t btf_elf__add_ref_type(struct btf_elf *btf, uint16_t kind, uint32_t type, - const char *name, bool kind_flag); -int btf_elf__add_member(struct btf_elf *btf, const char *name, uint32_t type, - uint32_t bitfield_size, uint32_t bit_offset); -int32_t btf_elf__add_struct(struct btf_elf *btf, uint8_t kind, const char *name, uint32_t size); -int32_t btf_elf__add_array(struct btf_elf *btf, uint32_t type, uint32_t index_type, - uint32_t nelems); -int32_t btf_elf__add_enum(struct btf_elf *btf, const char *name, uint32_t size); -int btf_elf__add_enum_val(struct btf_elf *btf, const char *name, int32_t value); -int32_t btf_elf__add_func_proto(struct btf_elf *btf, struct cu *cu, struct ftype *ftype, - uint32_t type_id_off); -int32_t btf_elf__add_var_type(struct btf_elf *btfe, uint32_t type, const char *name, - uint32_t linkage); -int32_t btf_elf__add_var_secinfo(struct gobuffer *buf, uint32_t type, - uint32_t offset, uint32_t size); -int32_t btf_elf__add_datasec_type(struct btf_elf *btfe, const char *section_name, - struct gobuffer *var_secinfo_buf); -int btf_elf__encode(struct btf_elf *btf, uint8_t flags); - -const char *btf_elf__string(struct btf_elf *btf, uint32_t ref); -int btf_elf__load(struct btf_elf *btf); - -#endif /* _LIBBTF_H */ @@ -19,7 +19,6 @@ #include "ctf.h" #include "dutil.h" #include "gobuffer.h" -#include "pahole_strings.h" bool ctf__ignore_symtab_function(const GElf_Sym *sym, const char *sym_name) { @@ -133,8 +132,7 @@ int ctf__load(struct ctf *ctf) { int err = -ENOTSUP; GElf_Shdr shdr; - Elf_Scn *sec = elf_section_by_name(ctf->elf, &ctf->ehdr, - &shdr, ".SUNW_ctf", NULL); + Elf_Scn *sec = elf_section_by_name(ctf->elf, &shdr, ".SUNW_ctf", NULL); if (sec == NULL) return -ESRCH; @@ -229,7 +227,7 @@ out_close: if (elf == NULL) close(ctf->in_fd); out_delete_filename: - free(ctf->filename); + zfree(&ctf->filename); out_delete: free(ctf); return NULL; @@ -246,8 +244,8 @@ void ctf__delete(struct ctf *ctf) __gobuffer__delete(&ctf->types); __gobuffer__delete(&ctf->funcs); elf_symtab__delete(ctf->symtab); - free(ctf->filename); - free(ctf->buf); + zfree(&ctf->filename); + zfree(&ctf->buf); free(ctf); } } @@ -284,7 +282,7 @@ size_t ctf__get_size(struct ctf *ctf) int ctf__load_symtab(struct ctf *ctf) { - ctf->symtab = elf_symtab__new(".symtab", ctf->elf, &ctf->ehdr); + ctf->symtab = elf_symtab__new(".symtab", ctf->elf); return ctf->symtab == NULL ? -1 : 0; } @@ -505,6 +503,7 @@ int ctf__add_object(struct ctf *ctf, uint16_t type) sizeof(type)) >= 0 ? 0 : -ENOMEM; } +#if 0 static const void *ctf__compress(void *orig_buf, unsigned int *size) { z_stream z = { @@ -770,3 +769,4 @@ out_close: close(fd); return err; } +#endif @@ -290,6 +290,14 @@ static inline void list_splice_init(struct list_head *list, list_entry((ptr)->next, type, member) /** + * list_next_entry - get the next element in list + * @pos: the type * to cursor + * @member: the name of the list_head within the struct. + */ +#define list_next_entry(pos, member) \ + list_entry((pos)->member.next, typeof(*(pos)), member) + +/** * list_for_each - iterate over a list * @pos: the &struct list_head to use as a loop cursor. * @head: the head for your list. diff --git a/man-pages/pahole.1 b/man-pages/pahole.1 index 352bb5e..c1ec63e 100644 --- a/man-pages/pahole.1 +++ b/man-pages/pahole.1 @@ -21,7 +21,7 @@ It also uses these structure layouts to pretty print data feed to its standard input, e.g.: .PP .nf -$ pahole --header elf64_hdr < /lib/modules/5.8.0-rc6+/build/vmlinux +$ pahole --header elf64_hdr --prettify /lib/modules/5.8.0-rc6+/build/vmlinux { .e_ident = { 127, 69, 76, 70, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, .e_type = 2, @@ -101,6 +101,15 @@ or file URLs (e.g.: file://class_list.txt) Set cacheline size to SIZE bytes. .TP +.B \-\-sort +Sort the output by type name, maybe this will grow to allow sorting by other +criteria. + +This is mostly needed so that pretty printing from BTF and DWARF can be +comparable when using using multiple threads to load DWARF data, when the order +that the types in the compile units is processed is not deterministic. + +.TP .B \-\-count=COUNT Pretty print the first COUNT records from input. @@ -120,6 +129,13 @@ includes "ctf" and "dwarf". The default format path used is equivalent to "-F dwarf,ctf". .TP +.B \-\-hashbits=BITS +Allows specifying the number of bits for the debugging format loader to use. +The only one affected so far is the "dwarf" one, its default now is 15, the +maximum for it is now 21 bits. Tweak it to see if it improves performance as +the kernel evolves and more types and functions have to be loaded. + +.TP .B \-\-hex Print offsets and sizes in hexadecimal. @@ -182,6 +198,19 @@ the debugging information. Do not encode VARs in BTF. .TP +.B \-\-skip_encoding_btf_decl_tag +Do not encode decl tags in BTF. + +.TP +.B \-\-skip_encoding_btf_type_tag +Do not encode type tags in BTF. + +.TP +.B \-j, \-\-jobs=N +Run N jobs in parallel. Defaults to number of online processors + 10% (like +the 'ninja' build system) if no argument is specified. + +.TP .B \-J, \-\-btf_encode Encode BTF information from DWARF, used in the Linux kernel build process when CONFIG_DEBUG_INFO_BTF=y is present, introduced in Linux v5.2. Used to implement @@ -190,6 +219,10 @@ features such as BPF CO-RE (Compile Once - Run Everywhere). See \fIhttps://nakryiko.com/posts/bpf-portability-and-co-re/\fR. .TP +.B \-\-btf_encode_detached=FILENAME +Same thing as -J/--btf_encode, but storing the raw BTF info into a separate file. + +.TP .B \-\-btf_encode_force Ignore those symbols found invalid when encoding BTF. @@ -200,12 +233,30 @@ This may be inferred when asking for a /sys/kernel/btf/MODULE, when it will be a to "/sys/kernel/btf/vmlinux". .TP +.B \-\-btf_gen_floats +Allow producing BTF_KIND_FLOAT entries in systems where the vmlinux DWARF +information has float types. + +.TP +.B \-\-btf_gen_all +Allow using all the BTF features supported by pahole. + +.TP .B \-l, \-\-show_first_biggest_size_base_type_member Show first biggest size base_type member. .TP .B \-m, \-\-nr_methods -Show number of methods. +Show number of methods of all classes, i.e. the number of functions have arguments that +are pointers to a given class. + +To get the number of methods for an specific class, please use: + + $ pahole --nr_methods | grep -w sock + sock 1005 + $ + +In the above example it used the BTF information in /sys/kernel/btf/vmlinux. .TP .B \-M, \-\-show_only_data_members @@ -230,6 +281,10 @@ Show only structs that has holes that can be packed if members are reorganized, for instance when using the \fB\-\-reorganize\fR option. .TP +.B \-P, \-\-with_flexible_array +Show only structs that have a flexible array. + +.TP .B \-q, \-\-quiet Be quieter. @@ -284,6 +339,12 @@ Converts silly bitfields such as "int foo:32" to plain "int foo". be verbose .TP +.B \-\-ptr_table_stats +Print statistics about ptr_table data structures, used to hold all the types, +tags and functions data structures, for development tuning of such tables, tuned +for a typical 2021 vmlinux file. + +.TP .B \-w, \-\-word_size=WORD_SIZE Change the arch word size to WORD_SIZE. @@ -327,6 +388,10 @@ Show a traditional string version, i.e.: "v1.18". Show a numeric only version, suitable for use in Makefiles and scripts where one wants to know what if the installed version has some feature, i.e.: 118 instead of "v1.18". +.TP +.B \-\-kabi_prefix=STRING +When the prefix of the string is STRING, treat the string as STRING. + .SH NOTES To enable the generation of debugging information in the Linux kernel build @@ -537,6 +602,11 @@ $ pahole rcu_state | grep raw_spinlock_t -B1 -A5 }; $ .fi + +.SH PRETTY PRINTING +.P +pahole can also use the data structure types to pretty print raw data specified via --prettify. +To consume raw data from the standard input, just use '--prettify -' .P It can also pretty print raw data from stdin according to the type specified: .PP @@ -554,7 +624,7 @@ $ $ ls -la versions -rw-rw-r--. 1 acme acme 7616 Jun 25 11:33 versions $ -$ pahole --count 3 -C modversion_info drivers/scsi/sg.ko < versions +$ pahole --count 3 -C modversion_info drivers/scsi/sg.ko --prettify versions { .crc = 0x8dabd84, .name = "module_layout", @@ -568,7 +638,7 @@ $ pahole --count 3 -C modversion_info drivers/scsi/sg.ko < versions .name = "param_ops_int", }, $ -$ pahole --skip 1 --count 2 -C modversion_info drivers/scsi/sg.ko < versions +$ pahole --skip 1 --count 2 -C modversion_info drivers/scsi/sg.ko --prettify - < versions { .crc = 0x45e4617b, .name = "no_llseek", @@ -580,7 +650,7 @@ $ pahole --skip 1 --count 2 -C modversion_info drivers/scsi/sg.ko < versions $ This is equivalent to: -$ pahole --seek_bytes 64 --count 1 -C modversion_info drivers/scsi/sg.ko < versions +$ pahole --seek_bytes 64 --count 1 -C modversion_info drivers/scsi/sg.ko --prettify versions { .crc = 0x45e4617b, .name = "no_llseek", @@ -589,11 +659,6 @@ $ .fi .P -.SH PRETTY PRINTING -.P -pahole can also use the data structure types to pretty print raw data coming -from its standard input. - .TP .B \-C, \-\-class_name=CLASS_NAME Pretty print according to this class. Arguments may be passed to it to affect how @@ -636,7 +701,7 @@ $ Now we can use this to show the first record from offset zero: .PP .nf -$ pahole -C elf64_hdr --count 1 < /lib/modules/5.8.0-rc3+/build/vmlinux +$ pahole -C elf64_hdr --count 1 --prettify /lib/modules/5.8.0-rc3+/build/vmlinux { .e_ident = { 127, 69, 76, 70, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, .e_type = 2, @@ -659,7 +724,7 @@ $ This is equivalent to: .PP .nf -$ pahole --header elf64_hdr < /lib/modules/5.8.0-rc3+/build/vmlinux +$ pahole --header elf64_hdr --prettify /lib/modules/5.8.0-rc3+/build/vmlinux .fi .P The --header option also allows reference in other command line options to fields in the header. @@ -667,7 +732,7 @@ This is useful when one wants to show multiple records in a file and the range w are located is specified in header fields, such as for perf.data files: .PP .nf -$ pahole --hex ~/bin/perf --header perf_file_header < perf.data +$ pahole --hex ~/bin/perf --header perf_file_header --prettify perf.data { .magic = 0x32454c4946524550, .size = 0x68, @@ -692,7 +757,7 @@ $ So to display the cgroups records in the perf_file_header.data section we can use: .PP .nf -$ pahole ~/bin/perf --header=perf_file_header --seek_bytes '$header.data.offset' --size_bytes='$header.data.size' -C 'perf_event_header(sizeof,type,type_enum=perf_event_type,filter=type==PERF_RECORD_CGROUP)' < perf.data +$ pahole ~/bin/perf --header=perf_file_header --seek_bytes '$header.data.offset' --size_bytes='$header.data.size' -C 'perf_event_header(sizeof,type,type_enum=perf_event_type,filter=type==PERF_RECORD_CGROUP)' --prettify perf.data { .header = { .type = PERF_RECORD_CGROUP, @@ -736,7 +801,7 @@ $ pahole ~/bin/perf --header=perf_file_header --seek_bytes '$header.data.offset' .size = 88, }, .id = 13, - .path = "/machine.slice/machine-qemu\x2d1\x2drhel6.sandy.scope", + .path = "/machine.slice/machine-qemu\\x2d1\\x2drhel6.sandy.scope", }, $ .fi @@ -744,12 +809,12 @@ $ For the common case of the header having a member that has the 'offset' and 'size' members, it is possible to use this more compact form: .PP .nf -$ pahole ~/bin/perf --header=perf_file_header --range=data -C 'perf_event_header(sizeof,type,type_enum=perf_event_type,filter=type==PERF_RECORD_CGROUP)' < perf.data +$ pahole ~/bin/perf --header=perf_file_header --range=data -C 'perf_event_header(sizeof,type,type_enum=perf_event_type,filter=type==PERF_RECORD_CGROUP)' --prettify perf.data .fi .P This uses ~/bin/perf to get the type definitions, the defines 'struct perf_file_header' as the header, then seeks '$header.data.offset' bytes from the start of the file, and considers '$header.data.size' bytes -worth of such records. The filter expression may omit a common prefix, in this case it could additonally be +worth of such records. The filter expression may omit a common prefix, in this case it could additionally be equivalently written as both 'filter=type==CGROUP' or the 'filter=' can also be omitted, getting as compact as 'type==CGROUP': .P @@ -818,7 +883,7 @@ If we remove that type_enum=perf_event_type, we will lose the conversion of 'str more descriptive 'struct perf_record_cgroup', and also the beautification of the header.type field: .PP .nf -$ pahole ~/bin/perf --header=perf_file_header --seek_bytes '$header.data.offset' --size_bytes='$header.data.size' -C 'perf_event_header(sizeof,type,filter=type==19)' < perf.data +$ pahole ~/bin/perf --header=perf_file_header --seek_bytes '$header.data.offset' --size_bytes='$header.data.size' -C 'perf_event_header(sizeof,type,filter=type==19)' --prettify perf.data { .type = 19, .misc = 0, @@ -850,7 +915,7 @@ $ Some of the records are not found in 'type_enum=perf_event_type' so some of the records don't get converted to a type that fully shows its contents. For perf we know that those are in another enumeration, 'enum perf_user_event_type', so, for these cases, we can create a 'virtual enum', i.e. the sum of two enums and then get all those entries decoded and properly casted, first few records with just 'enum perf_event_type': .PP .nf -$ pahole ~/bin/perf --header=perf_file_header --seek_bytes '$header.data.offset' --size_bytes='$header.data.size' -C 'perf_event_header(sizeof,type,type_enum=perf_event_type)' --count 4 < perf.data +$ pahole ~/bin/perf --header=perf_file_header --seek_bytes '$header.data.offset' --size_bytes='$header.data.size' -C 'perf_event_header(sizeof,type,type_enum=perf_event_type)' --count 4 --prettify perf.data { .type = 79, .misc = 0, @@ -881,7 +946,7 @@ $ Now with both enumerations, i.e. with 'type_enum=perf_event_type+perf_user_event_type': .PP .nf -$ pahole ~/bin/perf --header=perf_file_header --seek_bytes '$header.data.offset' --size_bytes='$header.data.size' -C 'perf_event_header(sizeof,type,type_enum=perf_event_type+perf_user_event_type)' --count 5 < perf.data +$ pahole ~/bin/perf --header=perf_file_header --seek_bytes '$header.data.offset' --size_bytes='$header.data.size' -C 'perf_event_header(sizeof,type,type_enum=perf_event_type+perf_user_event_type)' --count 5 --prettify perf.data { .header = { .type = PERF_RECORD_TIME_CONV, @@ -940,7 +1005,7 @@ data range with the following command: .PP .nf pahole ~/bin/perf --header=perf_file_header \ - -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' < perf.data + -C 'perf_file_attr(range=attrs),perf_event_header(range=data,sizeof,type,type_enum=perf_event_type+perf_user_event_type)' --prettify perf.data .fi .SH SEE ALSO @@ -10,28 +10,39 @@ #include <assert.h> #include <stdio.h> #include <dwarf.h> +#include <elfutils/version.h> #include <inttypes.h> +#include <limits.h> +#include <pthread.h> #include <search.h> #include <stdarg.h> #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <bpf/btf.h> +#include "bpf/libbpf.h" #include "dwarves_reorganize.h" #include "dwarves.h" #include "dutil.h" -#include "ctf_encoder.h" +//#include "ctf_encoder.h" FIXME: disabled, probably its better to move to Oracle's libctf #include "btf_encoder.h" -#include "libbtf.h" -#include "lib/bpf/src/libbpf.h" +static struct btf_encoder *btf_encoder; +static char *detached_btf_filename; static bool btf_encode; +static bool btf_gen_floats; static bool ctf_encode; +static bool sort_output; +static bool need_resort; static bool first_obj_only; static bool skip_encoding_btf_vars; static bool btf_encode_force; static const char *base_btf_file; +static const char *prettify_input_filename; +static FILE *prettify_input; + static uint8_t class__include_anonymous; static uint8_t class__include_nested_anonymous; static uint8_t word_size, original_word_size; @@ -52,6 +63,7 @@ static uint16_t nr_holes; static uint16_t nr_bit_holes; static uint16_t hole_size_ge; static uint8_t show_packable; +static bool show_with_flexible_array; static uint8_t global_verbose; static uint8_t recursive; static size_t cacheline_size; @@ -79,23 +91,23 @@ static struct conf_load conf_load = { struct structure { struct list_head node; struct rb_node rb_node; - char *name; + struct class *class; + struct cu *cu; + uint32_t id; uint32_t nr_files; uint32_t nr_methods; }; -static struct structure *structure__new(const char *name) +static struct structure *structure__new(struct class *class, struct cu *cu, uint32_t id) { struct structure *st = malloc(sizeof(*st)); if (st != NULL) { - st->name = strdup(name); - if (st->name == NULL) { - free(st); - return NULL; - } st->nr_files = 1; st->nr_methods = 0; + st->class = class; + st->cu = cu; + st->id = id; } return st; @@ -103,28 +115,209 @@ static struct structure *structure__new(const char *name) static void structure__delete(struct structure *st) { - free(st->name); + if (st == NULL) + return; + free(st); } static struct rb_root structures__tree = RB_ROOT; static LIST_HEAD(structures__list); +static pthread_mutex_t structures_lock = PTHREAD_MUTEX_INITIALIZER; + +static int type__compare_members_types(struct type *a, struct cu *cu_a, struct type *b, struct cu *cu_b) +{ + int ret = strcmp(type__name(a), type__name(b)); + + if (ret) + return ret; + + // a->nr_members should be equal to b->nr_members at this point + + if (a->nr_members == 0) + return 0; + + struct class_member *ma, *mb = type__first_member(b); + + type__for_each_member(a, ma) { + struct tag *type_ma = cu__type(cu_a, ma->tag.type), + *type_mb = cu__type(cu_b, mb->tag.type); + + if (type_ma && !type_mb && class_member__name(mb) == NULL) { + /* + * FIXME This is happening with a vmlinux built with + * clang and thin-LTO, and since this is not + * multithreadeded, we can get the previous behaviour + * by considering just the first occurence, the one with + * all the class member names and proper types, and since + * the name, size, number of members is the same, consider them equal + * and use the complete type (the first one found). + * With this btfdiff works for both non-thin-LTO and thin-LTO vmlinux files + */ + return 0; + } + + if (!type_ma || !type_mb) // shuldn't happen + return type_ma ? 1 : -1; // best effort + + const char *name_a = class_member__name(ma), + *name_b = class_member__name(mb); + + if (name_a && name_b) { + ret = strcmp(name_a, name_b); + if (ret) + return ret; + } + + ret = (int)ma->bit_offset - (int)mb->bit_offset; + if (ret) + return ret; + + ret = (int)ma->bitfield_size - (int)mb->bitfield_size; + if (ret) + return ret; + + char bf_a[1024], bf_b[1024]; + + ret = strcmp(tag__name(type_ma, cu_a, bf_a, sizeof(bf_a), NULL), + tag__name(type_mb, cu_b, bf_b, sizeof(bf_b), NULL)); + if (ret) + return ret; + + mb = class_member__next(mb); + } + + return 0; +} + +static int type__compare_members(struct type *a, struct type *b) +{ + int ret; + + // a->nr_members should be equal to b->nr_members at this point + + if (a->nr_members == 0) + return 0; + + struct class_member *ma, *mb = type__first_member(b); + + // Don't look at the types, as we may be referring to a CU being loaded + // in another thread and since we're not locking the ptr_table's, we + // may race When printing all the types using --sort we'll do an extra + // check that takes into account the types, since at that time all the + // ptr_tables/cus are quiescent. + + type__for_each_member(a, ma) { + const char *name_a = class_member__name(ma), + *name_b = class_member__name(mb); + + if (name_a && name_b) { + ret = strcmp(name_a, name_b); + if (ret) + return ret; + } + + ret = (int)ma->bit_offset - (int)mb->bit_offset; + if (ret) + return ret; + + ret = (int)ma->bitfield_size - (int)mb->bitfield_size; + if (ret) + return ret; + + mb = class_member__next(mb); + } + + /* + Since we didn't check the types, we may end with at least this btfdiff output: -static struct structure *structures__add(struct class *class, - const struct cu *cu, - bool *existing_entry) ++++ /tmp/btfdiff.btf.b5DJu4 2021-08-18 12:06:27.773932193 -0300 +@@ -31035,7 +31035,7 @@ struct elf_note_info { + struct memelfnote auxv; / * 56 24 * / + / * --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- * / + struct memelfnote files; / * 80 24 * / +- compat_siginfo_t csigdata; / * 104 128 * / ++ siginfo_t csigdata; / * 104 128 * / + + So if we're printing everything, consider the types as different and + at the end with type__compare_members_types() when using --sort, + we'll need as well to resort, to avoid things like: + +@@ -47965,8 +47965,8 @@ struct instance_attribute { + + / * XXX last struct has 6 bytes of padding * / + +- ssize_t (*show)(struct edac_device_instance *, char *); / * 16 8 * / +- ssize_t (*store)(struct edac_device_instance *, const char *, size_t); / * 24 8 * / ++ ssize_t (*show)(struct edac_pci_ctl_info *, char *); / * 16 8 * / ++ ssize_t (*store)(struct edac_pci_ctl_info *, const char *, size_t); / * 24 8 * / + + / * size: 32, cachelines: 1, members: 3 * / + / * paddings: 1, sum paddings: 6 * / +@@ -47977,8 +47977,8 @@ struct instance_attribute { + + / * XXX last struct has 6 bytes of padding * / + +- ssize_t (*show)(struct edac_pci_ctl_info *, char *); / * 16 8 * / +- ssize_t (*store)(struct edac_pci_ctl_info *, const char *, size_t); / * 24 8 * / ++ ssize_t (*show)(struct edac_device_instance *, char *); / * 16 8 * / ++ ssize_t (*store)(struct edac_device_instance *, const char *, size_t); / * 24 8 * / + + / * size: 32, cachelines: 1, members: 3 * / + / * paddings: 1, sum paddings: 6 * / + + I.e. the difference is in the arguments to those show/store function + pointers, but since we didn't took the types into account when first + sorting, we need to resort. + + So the first sort weeds out duplicates when loading from multiple + CUs, i.e. DWARF, the second will make sure both BTF and DWARF are + sorted taking into account types and then btfdiff finally will be + happy and we can continue to depend on it for regression tests for + the BTF and DWARF encoder and loader + + */ + + if (sort_output) { + need_resort = true; + return 1; + } + + return 0; +} + +static int type__compare(struct type *a, struct cu *cu_a, struct type *b, struct cu *cu_b) +{ + int ret = strcmp(type__name(a), type__name(b)); + + if (ret) + goto found; + + ret = (int)a->size - (int)b->size; + if (ret) + goto found; + + ret = (int)a->nr_members - (int)b->nr_members; + if (ret) + goto found; + + ret = type__compare_members(a, b); +found: + return ret; +} + +static struct structure *__structures__add(struct class *class, struct cu *cu, uint32_t id, bool *existing_entry) { struct rb_node **p = &structures__tree.rb_node; struct rb_node *parent = NULL; struct structure *str; - const char *new_class_name = class__name(class, cu); while (*p != NULL) { int rc; parent = *p; str = rb_entry(parent, struct structure, rb_node); - rc = strcmp(str->name, new_class_name); + rc = type__compare(&str->class->type, str->cu, &class->type, cu); if (rc > 0) p = &(*p)->rb_left; @@ -136,7 +329,7 @@ static struct structure *structures__add(struct class *class, } } - str = structure__new(new_class_name); + str = structure__new(class, cu, id); if (str == NULL) return NULL; @@ -150,7 +343,18 @@ static struct structure *structures__add(struct class *class, return str; } -void structures__delete(void) +static struct structure *structures__add(struct class *class, struct cu *cu, uint32_t id, bool *existing_entry) +{ + struct structure *str; + + pthread_mutex_lock(&structures_lock); + str = __structures__add(class, cu, id, existing_entry); + pthread_mutex_unlock(&structures_lock); + + return str; +} + +static void __structures__delete(void) { struct rb_node *next = rb_first(&structures__tree); @@ -162,48 +366,50 @@ void structures__delete(void) } } +void structures__delete(void) +{ + pthread_mutex_lock(&structures_lock); + __structures__delete(); + pthread_mutex_unlock(&structures_lock); +} + static void nr_definitions_formatter(struct structure *st) { - printf("%s%c%u\n", st->name, separator, st->nr_files); + printf("%s%c%u\n", class__name(st->class), separator, st->nr_files); } -static void nr_members_formatter(struct class *class, - struct cu *cu, uint32_t id __unused) +static void nr_members_formatter(struct class *class, struct cu *cu __maybe_unused, uint32_t id __maybe_unused) { - printf("%s%c%u\n", class__name(class, cu), separator, - class__nr_members(class)); + printf("%s%c%u\n", class__name(class), separator, class__nr_members(class)); } static void nr_methods_formatter(struct structure *st) { - printf("%s%c%u\n", st->name, separator, st->nr_methods); + printf("%s%c%u\n", class__name(st->class), separator, st->nr_methods); } -static void size_formatter(struct class *class, - struct cu *cu, uint32_t id __unused) +static void size_formatter(struct class *class, struct cu *cu __maybe_unused, uint32_t id __maybe_unused) { - printf("%s%c%d%c%u\n", class__name(class, cu), separator, + printf("%s%c%d%c%u\n", class__name(class), separator, class__size(class), separator, tag__is_union(class__tag(class)) ? 0 : class->nr_holes); } -static void class_name_len_formatter(struct class *class, struct cu *cu, - uint32_t id __unused) +static void class_name_len_formatter(struct class *class, struct cu *cu __maybe_unused, uint32_t id __maybe_unused) { - const char *name = class__name(class, cu); + const char *name = class__name(class); printf("%s%c%zd\n", name, separator, strlen(name)); } -static void class_name_formatter(struct class *class, - struct cu *cu, uint32_t id __unused) +static void class_name_formatter(struct class *class, struct cu *cu __maybe_unused, uint32_t id __maybe_unused) { - puts(class__name(class, cu)); + puts(class__name(class)); } static void class_formatter(struct class *class, struct cu *cu, uint32_t id) { struct tag *typedef_alias = NULL; struct tag *tag = class__tag(class); - const char *name = class__name(class, cu); + const char *name = class__name(class); if (name == NULL) { /* @@ -227,7 +433,7 @@ static void class_formatter(struct class *class, struct cu *cu, uint32_t id) struct type *tdef = tag__type(typedef_alias); conf.prefix = "typedef"; - conf.suffix = type__name(tdef, cu); + conf.suffix = type__name(tdef); } else conf.prefix = conf.suffix = NULL; @@ -242,7 +448,7 @@ static void print_packable_info(struct class *c, struct cu *cu, uint32_t id) const size_t orig_size = class__size(c); const size_t new_size = class__size(c->priv); const size_t savings = orig_size - new_size; - const char *name = class__name(c, cu); + const char *name = class__name(c); /* Anonymous struct? Try finding a typedef */ if (name == NULL) { @@ -250,7 +456,7 @@ static void print_packable_info(struct class *c, struct cu *cu, uint32_t id) cu__find_first_typedef_of_type(cu, id); if (tdef != NULL) - name = class__name(tag__class(tdef), cu); + name = class__name(tag__class(tdef)); } if (name != NULL) printf("%s%c%zd%c%zd%c%zd\n", @@ -309,7 +515,7 @@ static void print_classes(struct cu *cu) * and I'm sleepy, will leave for later... */ if (pos->type.namespace.name != 0) { - str = structures__add(pos, cu, &existing_entry); + str = structures__add(pos, cu, id, &existing_entry); if (str == NULL) { fprintf(stderr, "pahole: insufficient memory for " "processing %s, skipping it...\n", cu->name); @@ -325,11 +531,73 @@ static void print_classes(struct cu *cu) if (show_packable && !global_verbose) print_packable_info(pos, cu, id); + else if (sort_output && formatter == class_formatter) + continue; // we'll print it at the end, in order, out of structures__tree else if (formatter != NULL) formatter(pos, cu, id); } } +static void __print_ordered_classes(struct rb_root *root) +{ + struct rb_node *next = rb_first(root); + + while (next) { + struct structure *st = rb_entry(next, struct structure, rb_node); + + class_formatter(st->class, st->cu, st->id); + + next = rb_next(&st->rb_node); + } + +} + +static void resort_add(struct rb_root *resorted, struct structure *str) +{ + struct rb_node **p = &resorted->rb_node; + struct rb_node *parent = NULL; + struct structure *node; + + while (*p != NULL) { + int rc; + + parent = *p; + node = rb_entry(parent, struct structure, rb_node); + rc = type__compare_members_types(&node->class->type, node->cu, &str->class->type, str->cu); + + if (rc > 0) + p = &(*p)->rb_left; + else if (rc < 0) + p = &(*p)->rb_right; + else + return; // Duplicate, ignore it + } + + rb_link_node(&str->rb_node, parent, p); + rb_insert_color(&str->rb_node, resorted); +} + +static void resort_classes(struct rb_root *resorted, struct list_head *head) +{ + struct structure *str; + + list_for_each_entry(str, head, node) + resort_add(resorted, str); +} + +static void print_ordered_classes(void) +{ + if (!need_resort) { + __print_ordered_classes(&structures__tree); + } else { + struct rb_root resorted = RB_ROOT; + + resort_classes(&resorted, &structures__list); + __print_ordered_classes(&resorted); + } +} + + static struct cu *cu__filter(struct cu *cu) { if (cu__exclude_prefix != NULL && @@ -348,7 +616,7 @@ static int class__packable(struct class *class, struct cu *cu) if (class->nr_holes == 0 && class->nr_bit_holes == 0) return 0; - clone = class__clone(class, NULL, cu); + clone = class__clone(class, NULL); if (clone == NULL) return 0; class__reorganize(clone, cu, 0, stdout); @@ -356,13 +624,33 @@ static int class__packable(struct class *class, struct cu *cu) class->priv = clone; return 1; } - /* FIXME: we need to free in the right order, - * cu->obstack is being corrupted... - class__delete(clone, cu); - */ + class__delete(clone); return 0; } +static bool class__has_flexible_array(struct class *class, struct cu *cu) +{ + struct class_member *member = type__last_member(&class->type); + + if (member == NULL) + return false; + + struct tag *type = cu__type(cu, member->tag.type); + + if (type->tag != DW_TAG_array_type) + return false; + + struct array_type *array = tag__array_type(type); + + if (array->dimensions > 1) + return false; + + if (array->nr_entries == NULL || array->nr_entries[0] == 0) + return true; + + return false; +} + static struct class *class__filter(struct class *class, struct cu *cu, uint32_t tag_id) { @@ -388,7 +676,7 @@ static struct class *class__filter(struct class *class, struct cu *cu, return NULL; } - name = class__name(class, cu); + name = class__name(class); if (class__is_declaration(class)) return NULL; @@ -403,7 +691,7 @@ static struct class *class__filter(struct class *class, struct cu *cu, if (tdef != NULL) { struct class *c = tag__class(tdef); - name = class__name(c, cu); + name = class__name(c); } } if (name != NULL && strncmp(class__exclude_prefix, name, @@ -418,7 +706,7 @@ static struct class *class__filter(struct class *class, struct cu *cu, if (tdef != NULL) { struct class *c = tag__class(tdef); - name = class__name(c, cu); + name = class__name(c); } } if (name != NULL && strncmp(class__include_prefix, name, @@ -457,6 +745,9 @@ static struct class *class__filter(struct class *class, struct cu *cu, if (show_packable && !class__packable(class, cu)) return NULL; + if (show_with_flexible_array && !class__has_flexible_array(class, cu)) + return NULL; + return class; } @@ -509,13 +800,12 @@ static void class__resize_LP(struct tag *tag, struct cu *cu) case DW_TAG_base_type: { struct base_type *bt = tag__base_type(type); char bf[64]; - const char *name = base_type__name(bt, cu, bf, - sizeof(bf)); + const char *name = base_type__name(bt, bf, sizeof(bf)); if (strcmp(name, "long int") != 0 && strcmp(name, "long unsigned int") != 0) break; - /* fallthru */ } + /* fallthru */ case DW_TAG_pointer_type: diff = word_size_diff; break; @@ -616,7 +906,7 @@ static void tag__fixup_word_size(struct tag *tag, struct cu *cu) if (!bt->name) return; char bf[64]; - const char *name = base_type__name(bt, cu, bf, sizeof(bf)); + const char *name = base_type__name(bt, bf, sizeof(bf)); if (strcmp(name, "long int") == 0 || strcmp(name, "long unsigned int") == 0) @@ -674,7 +964,7 @@ static void cu__account_nr_methods(struct cu *cu) continue; bool existing_entry; - str = structures__add(class, cu, &existing_entry); + str = structures__add(class, cu, id, &existing_entry); if (str == NULL) { fprintf(stderr, "pahole: insufficient memory " "for processing %s, skipping it...\n", @@ -723,7 +1013,7 @@ static void print_structs_with_pointer_to(struct cu *cu, uint32_t type) if (!looked) { bool existing_entry; - str = structures__add(pos, cu, &existing_entry); + str = structures__add(pos, cu, id, &existing_entry); if (str == NULL) { fprintf(stderr, "pahole: insufficient memory for " "processing %s, skipping it...\n", @@ -737,8 +1027,8 @@ static void print_structs_with_pointer_to(struct cu *cu, uint32_t type) break; looked = true; } - printf("%s: %s\n", str->name, - class_member__name(pos_member, cu)); + printf("%s: %s\n", class__name(str->class), + class_member__name(pos_member)); } } } @@ -750,8 +1040,8 @@ static int type__print_containers(struct type *type, struct cu *cu, uint32_t con return 0; if (ident == 0) { - bool existing_entry; - struct structure *str = structures__add(type__class(type), cu, &existing_entry); + bool existing_entry; // FIXME: This should really just search, no need to try to add it. + struct structure *str = structures__add(type__class(type), cu, 0, &existing_entry); if (str == NULL) { fprintf(stderr, "pahole: insufficient memory for " "processing %s, skipping it...\n", @@ -765,7 +1055,7 @@ static int type__print_containers(struct type *type, struct cu *cu, uint32_t con return 0; } - printf("%.*s%s", ident * 2, tab, type__name(type, cu)); + printf("%.*s%s", ident * 2, tab, type__name(type)); if (global_verbose) printf(": %u", n); putchar('\n'); @@ -825,6 +1115,18 @@ ARGP_PROGRAM_VERSION_HOOK_DEF = dwarves_print_version; #define ARGP_just_packed_structs 319 #define ARGP_numeric_version 320 #define ARGP_btf_base 321 +#define ARGP_btf_gen_floats 322 +#define ARGP_btf_gen_all 323 +#define ARGP_with_flexible_array 324 +#define ARGP_kabi_prefix 325 +#define ARGP_btf_encode_detached 326 +#define ARGP_prettify_input_filename 327 +#define ARGP_sort_output 328 +#define ARGP_hashbits 329 +#define ARGP_devel_stats 330 +#define ARGP_skip_encoding_btf_decl_tag 331 +#define ARGP_skip_missing 332 +#define ARGP_skip_encoding_btf_type_tag 333 static const struct argp_option pahole__options[] = { { @@ -923,6 +1225,11 @@ static const struct argp_option pahole__options[] = { .doc = "show only structs that has holes that can be packed", }, { + .name = "with_flexible_array", + .key = ARGP_with_flexible_array, + .doc = "show only structs with a flexible array", + }, + { .name = "expand_types", .key = 'E', .doc = "expand class members", @@ -1051,7 +1358,7 @@ static const struct argp_option pahole__options[] = { { .name = "ctf_encode", .key = 'Z', - .doc = "Encode as CTF", + .doc = "Encode as CTF: DISABLED, consider helping porting to libctf", }, { .name = "flat_arrays", @@ -1105,11 +1412,30 @@ static const struct argp_option pahole__options[] = { .doc = "Path to the base BTF file", }, { + .name = "kabi_prefix", + .key = ARGP_kabi_prefix, + .arg = "STRING", + .doc = "When the prefix of the string is STRING, treat the string as STRING.", + }, + { + .name = "jobs", + .key = 'j', + .arg = "NR_JOBS", + .flags = OPTION_ARG_OPTIONAL, // Use sysconf(_SC_NPROCESSORS_ONLN) * 1.1 by default + .doc = "run N jobs in parallel [default to number of online processors + 10%]", + }, + { .name = "btf_encode", .key = 'J', .doc = "Encode as BTF", }, { + .name = "btf_encode_detached", + .key = ARGP_btf_encode_detached, + .arg = "FILENAME", + .doc = "Encode as BTF in a detached file", + }, + { .name = "skip_encoding_btf_vars", .key = ARGP_skip_encoding_btf_vars, .doc = "Do not encode VARs in BTF." @@ -1120,6 +1446,16 @@ static const struct argp_option pahole__options[] = { .doc = "Ignore those symbols found invalid when encoding BTF." }, { + .name = "btf_gen_floats", + .key = ARGP_btf_gen_floats, + .doc = "Allow producing BTF_KIND_FLOAT entries." + }, + { + .name = "btf_gen_all", + .key = ARGP_btf_gen_all, + .doc = "Allow using all the BTF features supported by pahole." + }, + { .name = "structs", .key = ARGP_just_structs, .doc = "Show just structs", @@ -1140,6 +1476,43 @@ static const struct argp_option pahole__options[] = { .doc = "Print a numeric version, i.e. 119 instead of v1.19" }, { + .name = "sort", + .key = ARGP_sort_output, + .doc = "Sort types by name", + }, + { + .name = "prettify", + .key = ARGP_prettify_input_filename, + .arg = "PATH", + .doc = "Path to the raw data to pretty print", + }, + { + .name = "hashbits", + .key = ARGP_hashbits, + .arg = "BITS", + .doc = "Number of bits for the hash table key", + }, + { + .name = "ptr_table_stats", + .key = ARGP_devel_stats, + .doc = "Print internal data structures stats", + }, + { + .name = "skip_encoding_btf_decl_tag", + .key = ARGP_skip_encoding_btf_decl_tag, + .doc = "Do not encode TAGs in BTF." + }, + { + .name = "skip_missing", + .key = ARGP_skip_missing, + .doc = "skip missing types passed to -C rather than stop", + }, + { + .name = "skip_encoding_btf_type_tag", + .key = ARGP_skip_encoding_btf_type_tag, + .doc = "Do not encode TAGs in BTF." + }, + { .name = NULL, } }; @@ -1170,8 +1543,24 @@ static error_t pahole__options_parser(int key, char *arg, conf_load.extra_dbg_info = 1; break; case 'i': find_containers = 1; class_name = arg; break; + case 'j': +#if _ELFUTILS_PREREQ(0, 178) + conf_load.nr_jobs = arg ? atoi(arg) : + sysconf(_SC_NPROCESSORS_ONLN) * 1.1; +#else + fputs("pahole: Multithreading requires elfutils >= 0.178. Continuing with a single thread...\n", stderr); +#endif + break; + case ARGP_btf_encode_detached: + detached_btf_filename = arg; // fallthru case 'J': btf_encode = 1; conf_load.get_addr_info = true; + conf_load.ignore_alignment_attr = true; + // XXX for now, test this more thoroughly + // We may have some references from formal parameters, etc, (abstract_origin) + // conf_load.ignore_inline_expansions = true; + conf_load.ignore_labels = true; + conf_load.use_obstack = true; no_bitfield_type_recode = true; break; case 'l': conf.show_first_biggest_size_base_type_member = 1; break; case 'M': conf.show_only_data_members = 1; break; @@ -1208,7 +1597,7 @@ static error_t pahole__options_parser(int key, char *arg, if (!global_verbose) formatter = class_name_formatter; break; - case 'Z': ctf_encode = 1; break; + // case 'Z': ctf_encode = 1; break; // FIXME: Disabled case ARGP_flat_arrays: conf.flat_arrays = 1; break; case ARGP_suppress_aligned_attribute: conf.suppress_aligned_attribute = 1; break; @@ -1252,8 +1641,31 @@ static error_t pahole__options_parser(int key, char *arg, btf_encode_force = true; break; case ARGP_btf_base: base_btf_file = arg; break; + case ARGP_kabi_prefix: + conf_load.kabi_prefix = arg; + conf_load.kabi_prefix_len = strlen(arg); break; case ARGP_numeric_version: print_numeric_version = true; break; + case ARGP_btf_gen_floats: + btf_gen_floats = true; break; + case ARGP_btf_gen_all: + btf_gen_floats = true; break; + case ARGP_with_flexible_array: + show_with_flexible_array = true; break; + case ARGP_prettify_input_filename: + prettify_input_filename = arg; break; + case ARGP_sort_output: + sort_output = true; break; + case ARGP_hashbits: + conf_load.hashtable_bits = atoi(arg); break; + case ARGP_devel_stats: + conf_load.ptr_table_stats = true; break; + case ARGP_skip_encoding_btf_decl_tag: + conf_load.skip_encoding_btf_decl_tag = true; break; + case ARGP_skip_missing: + conf_load.skip_missing = true; break; + case ARGP_skip_encoding_btf_type_tag: + conf_load.skip_encoding_btf_type_tag = true; break; default: return ARGP_ERR_UNKNOWN; } @@ -1273,7 +1685,7 @@ static void do_reorg(struct tag *class, struct cu *cu) int savings; const uint8_t reorg_verbose = show_reorg_steps ? 2 : global_verbose; - struct class *clone = class__clone(tag__class(class), NULL, cu); + struct class *clone = class__clone(tag__class(class), NULL); if (clone == NULL) { fprintf(stderr, "pahole: out of memory!\n"); exit(EXIT_FAILURE); @@ -1288,8 +1700,8 @@ static void do_reorg(struct tag *class, struct cu *cu) tag__fprintf(class__tag(clone), cu, &conf, stdout); if (savings != 0) { const size_t cacheline_savings = - (tag__nr_cachelines(class, cu) - - tag__nr_cachelines(class__tag(clone), cu)); + (tag__nr_cachelines(&conf, class, cu) - + tag__nr_cachelines(&conf, class__tag(clone), cu)); printf(" /* saved %d byte%s", savings, savings != 1 ? "s" : ""); @@ -1302,13 +1714,10 @@ static void do_reorg(struct tag *class, struct cu *cu) } else putchar('\n'); - /* FIXME: we need to free in the right order, - * cu->obstack is being corrupted... - class__delete(clone, cu); - */ + class__delete(clone); } -static int tag__fprintf_hexdump_value(struct tag *type, struct cu *cu, void *instance, int _sizeof, FILE *fp) +static int instance__fprintf_hexdump_value(void *instance, int _sizeof, FILE *fp) { uint8_t *contents = instance; int i, printed = 0; @@ -1378,13 +1787,13 @@ static int class_member__fprintf_bitfield_value(struct class_member *member, voi return fprintf(fp, format, class_member__bitfield_value(member, instance)); } -static const char *enumeration__lookup_value(struct type *enumeration, struct cu *cu, uint64_t value) +static const char *enumeration__lookup_value(struct type *enumeration, uint64_t value) { struct enumerator *entry; type__for_each_enumerator(enumeration, entry) { if (entry->value == value) - return enumerator__name(entry, cu); + return enumerator__name(entry); } return NULL; @@ -1395,7 +1804,7 @@ static const char *enumerations__lookup_value(struct list_head *enumerations, ui struct tag_cu_node *pos; list_for_each_entry(pos, enumerations, node) { - const char *s = enumeration__lookup_value(tag__type(pos->tc.tag), pos->tc.cu, value); + const char *s = enumeration__lookup_value(tag__type(pos->tc.tag), value); if (s) return s; } @@ -1403,7 +1812,7 @@ static const char *enumerations__lookup_value(struct list_head *enumerations, ui return NULL; } -static struct enumerator *enumeration__lookup_entry_from_value(struct type *enumeration, struct cu *cu, uint64_t value) +static struct enumerator *enumeration__lookup_entry_from_value(struct type *enumeration, uint64_t value) { struct enumerator *entry; @@ -1415,14 +1824,13 @@ static struct enumerator *enumeration__lookup_entry_from_value(struct type *enum return NULL; } -static struct enumerator *enumerations__lookup_entry_from_value(struct list_head *enumerations, struct cu **cup, uint64_t value) +static struct enumerator *enumerations__lookup_entry_from_value(struct list_head *enumerations, uint64_t value) { struct tag_cu_node *pos; list_for_each_entry(pos, enumerations, node) { - struct enumerator *enumerator = enumeration__lookup_entry_from_value(tag__type(pos->tc.tag), pos->tc.cu, value); + struct enumerator *enumerator = enumeration__lookup_entry_from_value(tag__type(pos->tc.tag), value); if (enumerator) { - *cup = pos->tc.cu; return enumerator; } } @@ -1430,12 +1838,12 @@ static struct enumerator *enumerations__lookup_entry_from_value(struct list_head return NULL; } -static int64_t enumeration__lookup_enumerator(struct type *enumeration, struct cu *cu, const char *enumerator) +static int64_t enumeration__lookup_enumerator(struct type *enumeration, const char *enumerator) { struct enumerator *entry; type__for_each_enumerator(enumeration, entry) { - const char *entry_name = enumerator__name(entry, cu); + const char *entry_name = enumerator__name(entry); if (!strcmp(entry_name, enumerator)) return entry->value; @@ -1453,7 +1861,7 @@ static int64_t enumerations__lookup_enumerator(struct list_head *enumerations, c struct tag_cu_node *pos; list_for_each_entry(pos, enumerations, node) { - int64_t value = enumeration__lookup_enumerator(tag__type(pos->tc.tag), pos->tc.cu, enumerator); + int64_t value = enumeration__lookup_enumerator(tag__type(pos->tc.tag), enumerator); if (value != -1) return value; } @@ -1486,7 +1894,7 @@ static int array__fprintf_base_type_value(struct tag *tag, struct cu *cu, void * if (array->dimensions != 1) { // Support multi dimensional arrays later - return tag__fprintf_hexdump_value(tag, cu, instance, _sizeof, fp); + return instance__fprintf_hexdump_value(instance, _sizeof, fp); } if (tag__is_typedef(array_type)) @@ -1523,7 +1931,7 @@ static int array__fprintf_value(struct tag *tag, struct cu *cu, void *instance, if (tag__is_base_type(array_type, cu)) return array__fprintf_base_type_value(tag, cu, instance, _sizeof, fp); - return tag__fprintf_hexdump_value(tag, cu, instance, _sizeof, fp); + return instance__fprintf_hexdump_value(instance, _sizeof, fp); } static int __class__fprintf_value(struct tag *tag, struct cu *cu, void *instance, int _sizeof, int indent, bool brackets, FILE *fp) @@ -1538,7 +1946,7 @@ static int __class__fprintf_value(struct tag *tag, struct cu *cu, void *instance type__for_each_member(type, member) { void *member_contents = instance + member->byte_offset; struct tag *member_type = cu__type(cu, member->tag.type); - const char *name = class_member__name(member, cu); + const char *name = class_member__name(member); if (name) printed += fprintf(fp, "\n%.*s\t.%s = ", indent, tabs, name); @@ -1563,7 +1971,7 @@ static int __class__fprintf_value(struct tag *tag, struct cu *cu, void *instance if (!name) continue; } else { - printed += tag__fprintf_hexdump_value(member_type, cu, member_contents, member->byte_size, fp); + printed += instance__fprintf_hexdump_value(member_contents, member->byte_size, fp); } fputc(',', fp); @@ -1585,7 +1993,7 @@ static int tag__fprintf_value(struct tag *type, struct cu *cu, void *instance, i if (tag__is_struct(type)) return class__fprintf_value(type, cu, instance, _sizeof, 0, fp); - return tag__fprintf_hexdump_value(type, cu, instance, _sizeof, fp); + return instance__fprintf_hexdump_value(instance, _sizeof, fp); } static int pipe_seek(FILE *fp, off_t offset) @@ -1596,7 +2004,7 @@ static int pipe_seek(FILE *fp, off_t offset) if (chunk > offset) chunk = offset; - while (fread(bf, chunk, 1, stdin) == 1) { + while (fread(bf, chunk, 1, fp) == 1) { offset -= chunk; if (offset == 0) return 0; @@ -1607,7 +2015,7 @@ static int pipe_seek(FILE *fp, off_t offset) return offset == 0 ? 0 : -1; } -static uint64_t tag__real_sizeof(struct tag *tag, struct cu *cu, int _sizeof, void *instance) +static uint64_t tag__real_sizeof(struct tag *tag, int _sizeof, void *instance) { if (tag__is_struct(tag)) { struct type *type = tag__type(tag); @@ -1658,8 +2066,7 @@ static struct tag *tag__real_type(struct tag *tag, struct cu **cup, void *instan if (!list_empty(&type->type_enum) && type->type_member) { struct class_member *member = type->type_member; uint64_t value = base_type__value(instance + member->byte_offset, member->byte_size); - struct cu *cu_enumerator; - struct enumerator *enumerator = enumerations__lookup_entry_from_value(&type->type_enum, &cu_enumerator, value); + struct enumerator *enumerator = enumerations__lookup_entry_from_value(&type->type_enum, value); char name[1024]; if (!enumerator) @@ -1670,7 +2077,7 @@ static struct tag *tag__real_type(struct tag *tag, struct cu **cup, void *instan return enumerator->type_enum.tag; } - snprintf(name, sizeof(name), "%s", enumerator__name(enumerator, cu_enumerator)); + snprintf(name, sizeof(name), "%s", enumerator__name(enumerator)); strlwr(name); struct tag *real_type = cu__find_type_by_name(*cup, name, false, NULL); @@ -1723,7 +2130,7 @@ static void type_instance__delete(struct type_instance *instance) static int64_t type_instance__int_value(struct type_instance *instance, const char *member_name_orig) { struct cu *cu = instance->cu; - struct class_member *member = type__find_member_by_name(instance->type, cu, member_name_orig); + struct class_member *member = type__find_member_by_name(instance->type, member_name_orig); int byte_offset = 0; if (!member) { @@ -1744,7 +2151,7 @@ static int64_t type_instance__int_value(struct type_instance *instance, const ch *sep = 0; while (1) { - member = type__find_member_by_name(type, cu, member_name); + member = type__find_member_by_name(type, member_name); if (!member) { out_free_member_name: free(member_name_alloc); @@ -1761,7 +2168,7 @@ out_free_member_name: } - member = type__find_member_by_name(type, cu, member_name); + member = type__find_member_by_name(type, member_name); free(member_name_alloc); if (member == NULL) return -1; @@ -1784,7 +2191,7 @@ static int64_t type__instance_read_once(struct type_instance *instance, FILE *fp instance->read_already = true; - return fread(instance->instance, instance->type->size, 1, stdin) != 1 ? -1 : instance->type->size; + return fread(instance->instance, instance->type->size, 1, fp) != 1 ? -1 : (int64_t)instance->type->size; } /* @@ -1813,7 +2220,7 @@ struct prototype { }; -static int prototype__stdio_fprintf_value(struct prototype *prototype, struct type_instance *header, FILE *fp) +static int prototype__stdio_fprintf_value(struct prototype *prototype, struct type_instance *header, FILE *input, FILE *output) { struct tag *type = prototype->class; struct cu *cu = prototype->cu; @@ -1827,9 +2234,9 @@ static int prototype__stdio_fprintf_value(struct prototype *prototype, struct ty if (instance == NULL) return -ENOMEM; - if (type__instance_read_once(header, stdin) < 0) { + if (type__instance_read_once(header, input) < 0) { int err = --errno; - fprintf(stderr, "pahole: --header (%s) type not be read\n", conf.header_type); + fprintf(stderr, "pahole: --header (%s) type couldn't be read\n", conf.header_type); return err; } @@ -1865,17 +2272,18 @@ static int prototype__stdio_fprintf_value(struct prototype *prototype, struct ty free(member_name); - off_t total_read_bytes = ftell(stdin); + off_t total_read_bytes = ftell(input); - // Since we're reading stdin, we need to account for what we already read + // Since we're reading input, we need to account for what we already read + // FIXME: we now have a FILE pointer that _may_ be stdin, but not necessarily if (seek_bytes < total_read_bytes) { - fprintf(stderr, "pahole: can't go back in stdin, already read %" PRIu64 " bytes, can't go to position %#" PRIx64 "\n", + fprintf(stderr, "pahole: can't go back in input, already read %" PRIu64 " bytes, can't go to position %#" PRIx64 "\n", total_read_bytes, seek_bytes); return -ENOMEM; } if (global_verbose) { - fprintf(fp, "pahole: range.seek_bytes evaluated from range=%s is %#" PRIx64 " \n", + fprintf(output, "pahole: range.seek_bytes evaluated from range=%s is %#" PRIx64 " \n", range, seek_bytes); } @@ -1897,13 +2305,13 @@ static int prototype__stdio_fprintf_value(struct prototype *prototype, struct ty size_bytes = value; if (global_verbose) { - fprintf(fp, "pahole: range.size_bytes evaluated from range=%s is %#" PRIx64 " \n", + fprintf(output, "pahole: range.size_bytes evaluated from range=%s is %#" PRIx64 " \n", range, size_bytes); } free(member_name); - if (pipe_seek(stdin, seek_bytes) < 0) { + if (pipe_seek(input, seek_bytes) < 0) { int err = --errno; fprintf(stderr, "Couldn't --seek_bytes %s (%" PRIu64 "\n", conf.seek_bytes, seek_bytes); return err; @@ -1947,11 +2355,11 @@ static int prototype__stdio_fprintf_value(struct prototype *prototype, struct ty if (header) { - // Since we're reading stdin, we need to account for already read header: - seek_bytes -= ftell(stdin); + // Since we're reading input, we need to account for already read header: + seek_bytes -= ftell(input); } - if (pipe_seek(stdin, seek_bytes) < 0) { + if (pipe_seek(input, seek_bytes) < 0) { int err = --errno; fprintf(stderr, "Couldn't --seek_bytes %s (%" PRIu64 "\n", conf.seek_bytes, seek_bytes); return err; @@ -1986,11 +2394,11 @@ static int prototype__stdio_fprintf_value(struct prototype *prototype, struct ty do_read: { uint64_t read_bytes = 0; - off_t record_offset = ftell(stdin); + off_t record_offset = ftell(input); - while (fread(instance, _sizeof, 1, stdin) == 1) { + while (fread(instance, _sizeof, 1, input) == 1) { // Read it from each record/instance - int real_sizeof = tag__real_sizeof(type, cu, _sizeof, instance); + int real_sizeof = tag__real_sizeof(type, _sizeof, instance); if (real_sizeof > _sizeof) { if (real_sizeof > max_sizeof) { @@ -2003,7 +2411,7 @@ do_read: instance = new_instance; max_sizeof = real_sizeof; } - if (fread(instance + _sizeof, real_sizeof - _sizeof, 1, stdin) != 1) { + if (fread(instance + _sizeof, real_sizeof - _sizeof, 1, input) != 1) { fprintf(stderr, "Couldn't read record: %d bytes\n", real_sizeof); printed = -1; goto out; @@ -2070,14 +2478,14 @@ do_read: real_type = type; if (global_verbose) { - printed += fprintf(fp, "// type=%s, offset=%#" PRIx64 ", sizeof=%d", type__name(tag__type(type), cu), record_offset, _sizeof); + printed += fprintf(output, "// type=%s, offset=%#" PRIx64 ", sizeof=%d", type__name(tag__type(type)), record_offset, _sizeof); if (real_sizeof != _sizeof) - printed += fprintf(fp, ", real_sizeof=%d\n", real_sizeof); + printed += fprintf(output, ", real_sizeof=%d\n", real_sizeof); else - printed += fprintf(fp, "\n"); + printed += fprintf(output, "\n"); } - printed += tag__fprintf_value(real_type, real_type_cu, instance, real_sizeof, fp); - printed += fprintf(fp, ",\n"); + printed += tag__fprintf_value(real_type, real_type_cu, instance, real_sizeof, output); + printed += fprintf(output, ",\n"); if (conf.count && ++count == conf.count) break; @@ -2085,7 +2493,7 @@ next_record: if (read_bytes >= size_bytes) break; - record_offset = ftell(stdin); + record_offset = ftell(input); } } out: @@ -2093,7 +2501,7 @@ out: return printed; } -static int class_member_filter__parse(struct class_member_filter *filter, struct type *type, struct cu *cu, char *sfilter) +static int class_member_filter__parse(struct class_member_filter *filter, struct type *type, char *sfilter) { const char *member_name = sfilter; char *sep = strstr(sfilter, "=="); @@ -2116,11 +2524,11 @@ static int class_member_filter__parse(struct class_member_filter *filter, struct char before = s[1]; s[1] = '\0'; - filter->left = type__find_member_by_name(type, cu, member_name); + filter->left = type__find_member_by_name(type, member_name); if (!filter->left) { if (global_verbose) - fprintf(stderr, "The '%s' member wasn't found in '%s'\n", member_name, type__name(type, cu)); + fprintf(stderr, "The '%s' member wasn't found in '%s'\n", member_name, type__name(type)); s[1] = before; return -1; } @@ -2130,7 +2538,7 @@ static int class_member_filter__parse(struct class_member_filter *filter, struct while (isspace(*value)) if (*++value == '\0') { if (global_verbose) - fprintf(stderr, "The '%s' member was asked without a value to filter '%s'\n", member_name, type__name(type, cu)); + fprintf(stderr, "The '%s' member was asked without a value to filter '%s'\n", member_name, type__name(type)); return -1; // no value } @@ -2155,7 +2563,7 @@ static int class_member_filter__parse(struct class_member_filter *filter, struct if (enumerator_value < 0) { if (global_verbose) fprintf(stderr, "Couldn't resolve right operand ('%s') in '%s' with the specified 'type=%s' and type_enum' \n", - value, sfilter, class_member__name(type->type_member, cu)); + value, sfilter, class_member__name(type->type_member)); return -1; } @@ -2164,11 +2572,11 @@ static int class_member_filter__parse(struct class_member_filter *filter, struct return 0; } -static struct class_member_filter *class_member_filter__new(struct type *type, struct cu *cu, char *sfilter) +static struct class_member_filter *class_member_filter__new(struct type *type, char *sfilter) { struct class_member_filter *filter = malloc(sizeof(*filter)); - if (filter && class_member_filter__parse(filter, type, cu, sfilter)) { + if (filter && class_member_filter__parse(filter, type, sfilter)) { free(filter); filter = NULL; } @@ -2391,32 +2799,64 @@ out: static struct type_instance *header; static enum load_steal_kind pahole_stealer(struct cu *cu, - struct conf_load *conf_load __unused) + struct conf_load *conf_load) { int ret = LSK__DELETE; if (!cu__filter(cu)) goto filter_it; + if (conf_load->ptr_table_stats) { + static bool first = true; + + if (first) { + cus__fprintf_ptr_table_stats_csv_header(stderr); + first = false; + } + cu__fprintf_ptr_table_stats_csv(cu, stderr); + } + if (btf_encode) { - if (cu__encode_btf(cu, global_verbose, btf_encode_force, - skip_encoding_btf_vars)) { + static pthread_mutex_t btf_lock = PTHREAD_MUTEX_INITIALIZER; + + pthread_mutex_lock(&btf_lock); + /* + * FIXME: + * + * This should be really done at main(), but since in the current codebase only at this + * point we'll have cu->elf setup... + */ + if (!btf_encoder) { + btf_encoder = btf_encoder__new(cu, detached_btf_filename, conf_load->base_btf, skip_encoding_btf_vars, + btf_encode_force, btf_gen_floats, global_verbose); + if (btf_encoder == NULL) { + ret = LSK__STOP_LOADING; + goto out_btf; + } + } + + if (btf_encoder__encode_cu(btf_encoder, cu)) { fprintf(stderr, "Encountered error while encoding BTF.\n"); exit(1); } - return LSK__DELETE; + ret = LSK__DELETE; +out_btf: + pthread_mutex_unlock(&btf_lock); + return ret; } - +#if 0 if (ctf_encode) { cu__encode_ctf(cu, global_verbose); /* * We still have to get the type signature code merged to eliminate * dups, reference another CTF file, etc, so for now just encode the * first cu that is let thru by cu__filter. + * + * FIXME: Disabled, should use Oracle's libctf */ goto dump_and_stop; } - +#endif if (class_name == NULL) { if (stats_formatter == nr_methods_formatter) { cu__account_nr_methods(cu); @@ -2427,6 +2867,10 @@ static enum load_steal_kind pahole_stealer(struct cu *cu, cu_fixup_word_size_iterator(cu); print_classes(cu); + + if (sort_output && formatter == class_formatter) + ret = LSK__KEEPIT; + goto dump_it; } @@ -2451,8 +2895,13 @@ static enum load_steal_kind pahole_stealer(struct cu *cu, static type_id_t class_id; struct tag *class = cu__find_type_by_name(cu, prototype->name, include_decls, &class_id); - if (class == NULL) - return ret; // couldn't find that class name in this CU, continue to the next one. + // couldn't find that class name in this CU, continue to the next one. + if (class == NULL) { + if (conf_load->skip_missing) + continue; + else + return ret; + } if (prototype->nr_args != 0 && !tag__is_struct(class)) { fprintf(stderr, "pahole: attributes are only supported with 'class' and 'struct' types\n"); @@ -2462,7 +2911,7 @@ static enum load_steal_kind pahole_stealer(struct cu *cu, struct type *type = tag__type(class); if (prototype->size) { - type->sizeof_member = type__find_member_by_name(type, cu, prototype->size); + type->sizeof_member = type__find_member_by_name(type, prototype->size); if (type->sizeof_member == NULL) { fprintf(stderr, "pahole: the sizeof member '%s' wasn't found in the '%s' type\n", prototype->size, prototype->name); @@ -2471,7 +2920,7 @@ static enum load_steal_kind pahole_stealer(struct cu *cu, } if (prototype->type) { - type->type_member = type__find_member_by_name(type, cu, prototype->type); + type->type_member = type__find_member_by_name(type, prototype->type); if (type->type_member == NULL) { fprintf(stderr, "pahole: the type member '%s' wasn't found in the '%s' type\n", prototype->type, prototype->name); @@ -2484,7 +2933,7 @@ static enum load_steal_kind pahole_stealer(struct cu *cu, } if (prototype->filter) { - type->filter = class_member_filter__new(type, cu, prototype->filter); + type->filter = class_member_filter__new(type, prototype->filter); if (type->filter == NULL) { fprintf(stderr, "pahole: invalid filter '%s' for '%s'\n", prototype->filter, prototype->name); @@ -2498,7 +2947,7 @@ static enum load_steal_kind pahole_stealer(struct cu *cu, class_id = 0; } - if (!isatty(0)) { + if (prettify_input) { prototype->class = class; prototype->cu = cu; continue; @@ -2534,10 +2983,13 @@ static enum load_steal_kind pahole_stealer(struct cu *cu, } } - // If we got here with pretty printing is because we have everything solved except for type_enum + // If we got here with pretty printing is because we have everything solved except for type_enum or --header + + if (prettify_input) { + // Check if we need to continue loading CUs to get those type_enum= and --header resolved + if (header == NULL && conf.header_type) + return LSK__KEEPIT; - if (!isatty(0)) { - // Check if we need to continue loading CUs to get those type_enum= resolved list_for_each_entry(prototype, &class_names, node) { if (prototype->type_enum && !prototype->type_enum_resolved) return LSK__KEEPIT; @@ -2546,7 +2998,7 @@ static enum load_steal_kind pahole_stealer(struct cu *cu, // All set, pretty print it! list_for_each_entry_safe(prototype, n, &class_names, node) { list_del_init(&prototype->node); - if (prototype__stdio_fprintf_value(prototype, header, stdout) < 0) + if (prototype__stdio_fprintf_value(prototype, header, prettify_input, stdout) < 0) break; } @@ -2583,6 +3035,9 @@ static void prototypes__delete(struct list_head *prototypes) { struct prototype *prototype, *n; + if (prototypes == NULL) + return; + list_for_each_entry_safe(prototype, n, prototypes, node) { list_del_init(&prototype->node); prototype__delete(prototype); @@ -2605,7 +3060,7 @@ static int prototypes__load(struct list_head *prototypes, const char *filename) if (len == 0) continue; entry[len - 1] = '\0'; - if (prototypes__add(&class_names, entry)) + if (prototypes__add(prototypes, entry)) goto out; } @@ -2689,29 +3144,51 @@ int main(int argc, char *argv[]) { int err, remaining, rc = EXIT_FAILURE; - if (!isatty(0)) - conf.hex_fmt = 0; - if (argp_parse(&pahole__argp, argc, argv, 0, &remaining, NULL)) { argp_help(&pahole__argp, stderr, ARGP_HELP_SEE, argv[0]); goto out; } + if (class_name != NULL && stats_formatter == nr_methods_formatter) { + fputs("pahole: -m/nr_methods doesn't work with --class/-C, it shows all classes and the number of its methods\n", stderr); + return rc; + } + if (print_numeric_version) { dwarves_print_numeric_version(stdout); return 0; } - if (dwarves__init(cacheline_size)) { + if (conf_load.hashtable_bits > 31) { + fprintf(stderr, "Invalid --hashbits value (%d) should be less than 32\n", conf_load.hashtable_bits); + goto out; + } + + if (dwarves__init()) { fputs("pahole: insufficient memory\n", stderr); goto out; } + dwarves__resolve_cacheline_size(&conf_load, cacheline_size); + + if (prettify_input_filename) { + if (strcmp(prettify_input_filename, "-") == 0) { + prettify_input = stdin; + } else { + prettify_input = fopen(prettify_input_filename, "r"); + if (prettify_input == NULL) { + fprintf(stderr, "Failed to read input '%s': %s\n", + prettify_input_filename, strerror(errno)); + goto out_dwarves_exit; + } + } + } + if (base_btf_file) { - base_btf = btf__parse(base_btf_file, NULL); - if (libbpf_get_error(base_btf)) { + conf_load.base_btf = btf__parse(base_btf_file, NULL); + if (libbpf_get_error(conf_load.base_btf)) { fprintf(stderr, "Failed to parse base BTF '%s': %ld\n", - base_btf_file, libbpf_get_error(base_btf)); + base_btf_file, libbpf_get_error(conf_load.base_btf)); goto out; } if (!btf_encode && !ctf_encode) { @@ -2731,7 +3208,7 @@ int main(int argc, char *argv[]) conf_load.steal = pahole_stealer; // Make 'pahole --header type < file' a shorter form of 'pahole -C type --count 1 < file' - if (conf.header_type && !class_name && !isatty(0)) { + if (conf.header_type && !class_name && prettify_input) { conf.count = 1; class_name = conf.header_type; conf.header_type = 0; // so that we don't read it and then try to read the -C type @@ -2748,10 +3225,10 @@ try_sole_arg_as_class_names: strstarts(filename, "/sys/kernel/btf/") && strstr(filename, "/vmlinux") == NULL) { base_btf_file = "/sys/kernel/btf/vmlinux"; - base_btf = btf__parse(base_btf_file, NULL); - if (libbpf_get_error(base_btf)) { + conf_load.base_btf = btf__parse(base_btf_file, NULL); + if (libbpf_get_error(conf_load.base_btf)) { fprintf(stderr, "Failed to parse base BTF '%s': %ld\n", - base_btf_file, libbpf_get_error(base_btf)); + base_btf_file, libbpf_get_error(conf_load.base_btf)); goto out; } } @@ -2773,6 +3250,11 @@ try_sole_arg_as_class_names: goto out_cus_delete; } + if (sort_output && formatter == class_formatter) { + print_ordered_classes(); + goto out_ok; + } + if (!list_empty(&class_names)) { struct prototype *prototype; @@ -2811,23 +3293,29 @@ try_sole_arg_as_class_names: header = NULL; if (btf_encode) { - err = btf_encoder__encode(); + err = btf_encoder__encode(btf_encoder); if (err) { fputs("Failed to encode BTF\n", stderr); goto out_cus_delete; } } - +out_ok: if (stats_formatter != NULL) print_stats(); + rc = EXIT_SUCCESS; out_cus_delete: #ifdef DEBUG_CHECK_LEAKS cus__delete(cus); structures__delete(); - btf__free(base_btf); + btf__free(conf_load.base_btf); + conf_load.base_btf = NULL; #endif out_dwarves_exit: + if (prettify_input && prettify_input != stdin) { + fclose(prettify_input); + prettify_input = NULL; + } #ifdef DEBUG_CHECK_LEAKS dwarves__exit(); #endif diff --git a/pahole_strings.h b/pahole_strings.h deleted file mode 100644 index 522fbf2..0000000 --- a/pahole_strings.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef _STRINGS_H_ -#define _STRINGS_H_ 1 -/* - SPDX-License-Identifier: GPL-2.0-only - - Copyright (C) 2008 Arnaldo Carvalho de Melo <acme@redhat.com> -*/ - -#include "lib/bpf/src/btf.h" - -typedef unsigned int strings_t; - -struct strings { - struct btf *btf; -}; - -struct strings *strings__new(void); - -void strings__delete(struct strings *strings); - -strings_t strings__add(struct strings *strings, const char *str); -strings_t strings__find(struct strings *strings, const char *str); -strings_t strings__size(const struct strings *strings); -int strings__copy(const struct strings *strings, void *dst); - -static inline const char *strings__ptr(const struct strings *strings, strings_t s) -{ - return btf__str_by_offset(strings->btf, s); -} - -#endif /* _STRINGS_H_ */ @@ -25,8 +25,7 @@ static void emit_tag(struct tag *tag, uint32_t tag_id, struct cu *cu) if (tag->tag == DW_TAG_base_type) { char bf[64]; - const char *name = base_type__name(tag__base_type(tag), cu, - bf, sizeof(bf)); + const char *name = base_type__name(tag__base_type(tag), bf, sizeof(bf)); if (name == NULL) printf("anonymous base_type\n"); @@ -73,7 +72,7 @@ static int cu__emit_tags(struct cu *cu) } static enum load_steal_kind pdwtags_stealer(struct cu *cu, - struct conf_load *conf_load __unused) + struct conf_load *conf_load __maybe_unused) { cu__emit_tags(cu); return LSK__DELETE; @@ -104,7 +103,7 @@ static const struct argp_option pdwtags__options[] = { } }; -static error_t pdwtags__options_parser(int key, char *arg __unused, +static error_t pdwtags__options_parser(int key, char *arg __maybe_unused, struct argp_state *state) { switch (key) { @@ -132,11 +131,13 @@ int main(int argc, char *argv[]) int remaining, rc = EXIT_FAILURE, err; struct cus *cus = cus__new(); - if (dwarves__init(0) || cus == NULL) { + if (dwarves__init() || cus == NULL) { fputs("pwdtags: insufficient memory\n", stderr); goto out; } + dwarves__resolve_cacheline_size(&pdwtags_conf_load, 0); + if (argp_parse(&pdwtags__argp, argc, argv, 0, &remaining, NULL) || remaining == argc) { argp_help(&pdwtags__argp, stderr, ARGP_HELP_SEE, argv[0]); @@ -80,8 +80,7 @@ static struct fn_stats *fn_stats__find(const char *name) struct fn_stats *pos; list_for_each_entry(pos, &fn_stats__list, node) - if (strcmp(function__name(tag__function(pos->tag), pos->cu), - name) == 0) + if (strcmp(function__name(tag__function(pos->tag)), name) == 0) return pos; return NULL; } @@ -107,7 +106,7 @@ static void fn_stats_inline_exps_fmtr(const struct fn_stats *stats) { struct function *fn = tag__function(stats->tag); if (fn->lexblock.nr_inline_expansions > 0) - printf("%s: %u %d\n", function__name(fn, stats->cu), + printf("%s: %u %d\n", function__name(fn), fn->lexblock.nr_inline_expansions, fn->lexblock.size_inline_expansions); } @@ -116,29 +115,26 @@ static void fn_stats_labels_fmtr(const struct fn_stats *stats) { struct function *fn = tag__function(stats->tag); if (fn->lexblock.nr_labels > 0) - printf("%s: %u\n", function__name(fn, stats->cu), - fn->lexblock.nr_labels); + printf("%s: %u\n", function__name(fn), fn->lexblock.nr_labels); } static void fn_stats_variables_fmtr(const struct fn_stats *stats) { struct function *fn = tag__function(stats->tag); if (fn->lexblock.nr_variables > 0) - printf("%s: %u\n", function__name(fn, stats->cu), - fn->lexblock.nr_variables); + printf("%s: %u\n", function__name(fn), fn->lexblock.nr_variables); } static void fn_stats_nr_parms_fmtr(const struct fn_stats *stats) { struct function *fn = tag__function(stats->tag); - printf("%s: %u\n", function__name(fn, stats->cu), - fn->proto.nr_parms); + printf("%s: %u\n", function__name(fn), fn->proto.nr_parms); } static void fn_stats_name_len_fmtr(const struct fn_stats *stats) { struct function *fn = tag__function(stats->tag); - const char *name = function__name(fn, stats->cu); + const char *name = function__name(fn); printf("%s: %zd\n", name, strlen(name)); } @@ -148,7 +144,7 @@ static void fn_stats_size_fmtr(const struct fn_stats *stats) const size_t size = function__size(fn); if (size != 0) - printf("%s: %zd\n", function__name(fn, stats->cu), size); + printf("%s: %zd\n", function__name(fn), size); } static void fn_stats_fmtr(const struct fn_stats *stats) @@ -164,7 +160,7 @@ static void fn_stats_fmtr(const struct fn_stats *stats) putchar('\n'); } else { struct function *fn = tag__function(stats->tag); - puts(function__name(fn, stats->cu)); + puts(function__name(fn)); } } @@ -180,7 +176,7 @@ static void fn_stats_inline_stats_fmtr(const struct fn_stats *stats) { if (stats->nr_expansions > 1) printf("%-31.31s %6u %7u %6u %6u\n", - function__name(tag__function(stats->tag), stats->cu), + function__name(tag__function(stats->tag)), stats->size_expansions, stats->nr_expansions, stats->size_expansions / stats->nr_expansions, stats->nr_files); @@ -195,17 +191,14 @@ static void print_total_inline_stats(void) static void fn_stats__dupmsg(struct function *func, const struct cu *func_cu, - struct function *dup __unused, + struct function *dup __maybe_unused, const struct cu *dup_cu, char *hdr, const char *fmt, ...) { va_list args; if (!*hdr) - printf("function: %s\nfirst: %s\ncurrent: %s\n", - function__name(func, func_cu), - func_cu->name, - dup_cu->name); + printf("function: %s\nfirst: %s\ncurrent: %s\n", function__name(func), func_cu->name, dup_cu->name); va_start(args, fmt); vprintf(fmt, args); @@ -253,7 +246,7 @@ static bool function__filter(struct function *function, struct cu *cu) if (!function->name) return true; - name = function__name(function, cu); + name = function__name(function); if (show_externals && !function->external) return true; @@ -282,7 +275,7 @@ static bool function__filter(struct function *function, struct cu *cu) return false; } -static int cu_unique_iterator(struct cu *cu, void *cookie __unused) +static int cu_unique_iterator(struct cu *cu, void *cookie __maybe_unused) { cu__account_inline_expansions(cu); @@ -314,7 +307,7 @@ static int cu_class_iterator(struct cu *cu, void *cookie) if (verbose) tag__fprintf(function__tag(pos), cu, &conf, stdout); else - fputs(function__name(pos, cu), stdout); + fputs(function__name(pos), stdout); putchar('\n'); } @@ -385,11 +378,11 @@ static void function__show(struct function *func, struct cu *cu) if (tag__is_pointer(type)) fprintf(stdout, "\n\treturn (void *)0;"); else if (tag__is_struct(type)) - fprintf(stdout, "\n\treturn *(struct %s *)1;", class__name(tag__class(type), cu)); + fprintf(stdout, "\n\treturn *(struct %s *)1;", class__name(tag__class(type))); else if (tag__is_union(type)) - fprintf(stdout, "\n\treturn *(union %s *)1;", type__name(tag__type(type), cu)); + fprintf(stdout, "\n\treturn *(union %s *)1;", type__name(tag__type(type))); else if (tag__is_typedef(type)) - fprintf(stdout, "\n\treturn *(%s *)1;", type__name(tag__type(type), cu)); + fprintf(stdout, "\n\treturn *(%s *)1;", type__name(tag__type(type))); else fprintf(stdout, "\n\treturn 0;"); } @@ -406,7 +399,7 @@ static int cu_function_iterator(struct cu *cu, void *cookie) uint32_t id; cu__for_each_function(cu, id, function) { - if (cookie && strcmp(function__name(function, cu), cookie) != 0) + if (cookie && strcmp(function__name(function), cookie) != 0) continue; function__show(function, cu); if (!expand_types) @@ -439,7 +432,7 @@ int elf_symtab__show(char *filename) goto out_elf_end; } - struct elf_symtab *symtab = elf_symtab__new(symtab_name, elf, &ehdr); + struct elf_symtab *symtab = elf_symtab__new(symtab_name, elf); if (symtab == NULL) goto out_elf_end; @@ -496,7 +489,7 @@ int elf_symtabs__show(char *filenames[]) return EXIT_SUCCESS; } -static enum load_steal_kind pfunct_stealer(struct cu *cu, struct conf_load *conf_load __unused) +static enum load_steal_kind pfunct_stealer(struct cu *cu, struct conf_load *conf_load __maybe_unused) { if (function_name) { @@ -728,11 +721,13 @@ int main(int argc, char *argv[]) if (symtab_name != NULL) return elf_symtabs__show(argv + remaining); - if (dwarves__init(0)) { + if (dwarves__init()) { fputs("pfunct: insufficient memory\n", stderr); goto out; } + dwarves__resolve_cacheline_size(&conf_load, 0); + struct cus *cus = cus__new(); if (cus == NULL) { fputs("pfunct: insufficient memory\n", stderr); @@ -56,7 +56,7 @@ static struct extvar *extvar__new(const struct variable *var, gvar->next = NULL; gvar->var = var; gvar->cu = cu; - gvar->name = variable__name(var, cu); + gvar->name = variable__name(var); } return gvar; @@ -71,7 +71,7 @@ static struct extfun *extfun__new(struct function *fun, gfun->next = NULL; gfun->fun = fun; gfun->cu = cu; - gfun->name = function__name(fun, cu); + gfun->name = function__name(fun); } return gfun; @@ -124,7 +124,7 @@ static void extfun__add(struct function *fun, const struct cu *cu) } } -static int cu_extvar_iterator(struct cu *cu, void *cookie __unused) +static int cu_extvar_iterator(struct cu *cu, void *cookie __maybe_unused) { struct tag *pos; uint32_t id; @@ -137,7 +137,7 @@ static int cu_extvar_iterator(struct cu *cu, void *cookie __unused) return 0; } -static int cu_extfun_iterator(struct cu *cu, void *cookie __unused) +static int cu_extfun_iterator(struct cu *cu, void *cookie __maybe_unused) { struct function *pos; uint32_t id; @@ -169,7 +169,7 @@ static inline struct tag *extfun__tag(const struct extfun *gfun) } static void declaration_action__walk(const void *nodep, const VISIT which, - const int depth __unused) + const int depth __maybe_unused) { uint32_t count = 0; struct tag *tag; @@ -202,7 +202,7 @@ static void declaration_action__walk(const void *nodep, const VISIT which, } static void function_action__walk(const void *nodep, const VISIT which, - const int depth __unused) + const int depth __maybe_unused) { struct tag *tag; const struct extfun *gfun = NULL; @@ -268,7 +268,7 @@ static const struct argp_option pglobal__options[] = { static int walk_var, walk_fun; -static error_t pglobal__options_parser(int key, char *arg __unused, +static error_t pglobal__options_parser(int key, char *arg __maybe_unused, struct argp_state *state) { switch (key) { @@ -303,11 +303,13 @@ int main(int argc, char *argv[]) goto out; } - if (dwarves__init(0)) { + if (dwarves__init()) { fputs("pglobal: insufficient memory\n", stderr); goto out; } + dwarves__resolve_cacheline_size(&conf_load, 0); + struct cus *cus = cus__new(); if (cus == NULL) { fputs("pglobal: insufficient memory\n", stderr); @@ -106,7 +106,7 @@ static void refcnt_function(struct function *function, const struct cu *cu) refcnt_lexblock(&function->lexblock, cu); } -static int cu_refcnt_iterator(struct cu *cu, void *cookie __unused) +static int cu_refcnt_iterator(struct cu *cu, void *cookie __maybe_unused) { struct function *pos; uint32_t id; @@ -117,7 +117,7 @@ static int cu_refcnt_iterator(struct cu *cu, void *cookie __unused) } static int lost_iterator(struct tag *tag, struct cu *cu, - void *cookie __unused) + void *cookie __maybe_unused) { if (!tag->visited && tag__decl_file(tag, cu)) { tag__fprintf(tag, cu, NULL, stdout); @@ -131,16 +131,18 @@ static int cu_lost_iterator(struct cu *cu, void *cookie) return cu__for_all_tags(cu, lost_iterator, cookie); } -int main(int argc __unused, char *argv[]) +int main(int argc __maybe_unused, char *argv[]) { int err; struct cus *cus = cus__new(); - if (dwarves__init(0) || cus == NULL) { + if (dwarves__init() || cus == NULL) { fputs("prefcnt: insufficient memory\n", stderr); return EXIT_FAILURE; } + dwarves__resolve_cacheline_size(NULL, 0); + err = cus__load_files(cus, NULL, argv + 1); if (err != 0) { cus__fprintf_load_files_err(cus, "prefcnt", argv + 1, err, stderr); diff --git a/rpm/SPECS/dwarves.spec b/rpm/SPECS/dwarves.spec index 693a9da..0b4846e 100644 --- a/rpm/SPECS/dwarves.spec +++ b/rpm/SPECS/dwarves.spec @@ -2,7 +2,7 @@ %define libver 1 Name: dwarves -Version: 1.19 +Version: 1.23 Release: 1%{?dist} License: GPLv2 Summary: Debugging Information Manipulation Tools (pahole & friends) @@ -10,7 +10,7 @@ URL: http://acmel.wordpress.com Source: http://fedorapeople.org/~acme/dwarves/%{name}-%{version}.tar.xz Requires: %{libname}%{libver} = %{version}-%{release} BuildRequires: gcc -BuildRequires: cmake +BuildRequires: cmake >= 2.8.12 BuildRequires: zlib-devel BuildRequires: elfutils-devel >= 0.130 @@ -79,7 +79,7 @@ rm -Rf %{buildroot} %files %doc README.ctracer %doc README.btf -%doc changes-v1.19 +%doc changes-v1.23 %doc NEWS %{_bindir}/btfdiff %{_bindir}/codiff @@ -114,7 +114,6 @@ rm -Rf %{buildroot} %doc MANIFEST README %{_includedir}/dwarves/btf_encoder.h %{_includedir}/dwarves/config.h -%{_includedir}/dwarves/ctf_encoder.h %{_includedir}/dwarves/ctf.h %{_includedir}/dwarves/dutil.h %{_includedir}/dwarves/dwarves.h @@ -124,16 +123,84 @@ rm -Rf %{buildroot} %{_includedir}/dwarves/elf_symtab.h %{_includedir}/dwarves/gobuffer.h %{_includedir}/dwarves/hash.h -%{_includedir}/dwarves/libbtf.h %{_includedir}/dwarves/libctf.h %{_includedir}/dwarves/list.h %{_includedir}/dwarves/rbtree.h -%{_includedir}/dwarves/pahole_strings.h %{_libdir}/%{libname}.so %{_libdir}/%{libname}_emit.so %{_libdir}/%{libname}_reorganize.so %changelog +* Wed Dec 8 2021 Arnaldo Carvalho de Melo <acme@redhat.com> - 1.23-1 +- New release: v1.23 +- Process DW_TAG_LLVM_annotation tags. +- Initial support for DW_TAG_skeleton_unit. +- Encode BTF_KIND_TYPE_TAG and BTF_KIND_DECL_TAG +- Fix handling of percpu symbols on s390. +- Use cacheline size to infer struct member alignment from BTF. +- Add --skip_missing to not stop when not finding one of -C arguments. +- Fix __attribute__((__aligned__(N)) printing alignment for struct members. +- Fix nested __attribute__(__aligned__(N)) struct printing order. + +* Mon Aug 23 2021 Arnaldo Carvalho de Melo <acme@redhat.com> - 1.22-1 +- New release: v1.22 +- Introduce -j/--jobs option to specify the number of threads to use. +- Multithreaded DWARF loading, requires elfutils >= 0.178. +- Preparatory work for multithreaded BTF encoding, the focus for 1.23. +- Allow encoding BTF to a separate file. +- Show all different types with the same name, not just the first one found. +- Stop assuming that reading from stdin means pretty, add --prettify. +- Improve type resolution for the --header command line option. +- Do not consider the ftrace filter when encoding BTF for kernel functions. +- Lock calls to non-thread safe elfutils' dwarf_decl_file() and dwarf_decl_line(). +- Change hash table size to one that performs better with current typical vmlinux files. +- Allow tweaking the hash table size from the command line. +- Add --kabi_prefix to avoid deduplication woes when using _RH_KABI_REPLACE(). +- Add --with_flexible_array to show just types with flexible arrays. +- Support btfdiff with a detached BTF file. +- Introduce sorted type output (--sort). +- Disable incomplete CTF encoder. + +* Fri Apr 9 2021 Arnaldo Carvalho de Melo <acme@redhat.com> - 1.21-1 +- New release: v1.21 +- DWARF loader: +- Handle DWARF5 DW_OP_addrx properly +- Handle subprogram ret type with abstract_origin properly +- Check .notes section for LTO build info +- Check .debug_abbrev for cross-CU references +- Permit merging all DWARF CU's for clang LTO built binary +- Factor out common code to initialize a cu +- Permit a flexible HASHTAGS__BITS +- Use a better hashing function, from libbpf +- btf_encoder: +- Add --btf_gen_all flag +- Match ftrace addresses within ELF functions +- Funnel ELF error reporting through a macro +- Sanitize non-regular int base type +- Add support for the floating-point types +- Pretty printer: +- Honour conf_fprintf.hex when printing enumerations + +* Tue Feb 2 2021 Arnaldo Carvalho de Melo <acme@redhat.com> - 1.20-1 +- New release: v1.20 +- btf_encoder: +- Improve ELF error reporting using elf_errmsg(elf_errno()) +- Improve objcopy error handling. +- Fix handling of 'restrict' qualifier, that was being treated as a 'const'. +- Support SHN_XINDEX in st_shndx symbol indexes +- Cope with functions without a name +- Fix BTF variable generation for kernel modules +- Fix address size to match what is in the ELF file being processed. +- Use kernel module ftrace addresses when finding which functions to encode. +- libbpf: +- Allow use of packaged version. +- dwarf_loader: +- Support DW_AT_data_bit_offset +- DW_FORM_implicit_const in attr_numeric() and attr_offset() +- Support DW_TAG_GNU_call_site, standardized rename of DW_TAG_GNU_call_site. +- build: +- Fix compilation on 32-bit architectures. + * Fri Nov 20 2020 Arnaldo Carvalho de Melo <acme@redhat.com> - 1.19-1 - New release: 1.19 - Split BTF @@ -275,7 +342,7 @@ rm -Rf %{buildroot} * Sat Nov 20 2010 Arnaldo Carvalho de Melo <acme@redhat.com> - 1.9-1 - New release -* Tue Feb 08 2010 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.8-2 +* Mon Feb 08 2010 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.8-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild * Fri Dec 4 2009 Arnaldo Carvalho de Melo <acme@redhat.com> - 1.8-1 @@ -426,7 +493,7 @@ rm -Rf %{buildroot} - Fix emission of arrays of structs, unions, etc - use sysconf for the default cacheline size -* Wed Jan 18 2007 Arnaldo Carvalho de Melo <acme@ghostprotocols.net> +* Thu Jan 18 2007 Arnaldo Carvalho de Melo <acme@ghostprotocols.net> - fab0db03ea9046893ca110bb2b7d71b764f61033 - pdwtags added @@ -118,7 +118,7 @@ err: if (!should_copy_scn(elf, shdr, sections) && !copy_all_sections) continue; - elfcreator_copy_scn(ctor, elf, scn); + elfcreator_copy_scn(ctor, scn); } elfcreator_end(ctor); return 0; diff --git a/strings.c b/strings.c deleted file mode 100644 index d37f49d..0000000 --- a/strings.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - SPDX-License-Identifier: GPL-2.0-only - - Copyright (C) 2008 Arnaldo Carvalho de Melo <acme@redhat.com> -*/ - -#include "pahole_strings.h" -#include "gobuffer.h" - -#include <search.h> -#include <stdint.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <zlib.h> - -#include "dutil.h" -#include "lib/bpf/src/libbpf.h" - -struct strings *strings__new(void) -{ - struct strings *strs = malloc(sizeof(*strs)); - - if (!strs) - return NULL; - - strs->btf = btf__new_empty(); - if (libbpf_get_error(strs->btf)) { - free(strs); - return NULL; - } - - return strs; -} - -void strings__delete(struct strings *strs) -{ - if (strs == NULL) - return; - btf__free(strs->btf); - free(strs); -} - -strings_t strings__add(struct strings *strs, const char *str) -{ - strings_t index; - - if (str == NULL) - return 0; - - index = btf__add_str(strs->btf, str); - if (index < 0) - return 0; - - return index; -} - -strings_t strings__find(struct strings *strs, const char *str) -{ - return btf__find_str(strs->btf, str); -} - -/* a horrible and inefficient hack to get string section size out of BTF */ -strings_t strings__size(const struct strings *strs) -{ - const struct btf_header *p; - uint32_t sz; - - p = btf__get_raw_data(strs->btf, &sz); - if (!p) - return -1; - - return p->str_len; -} - -/* similarly horrible hack to copy out string section out of BTF */ -int strings__copy(const struct strings *strs, void *dst) -{ - const struct btf_header *p; - uint32_t sz; - - p = btf__get_raw_data(strs->btf, &sz); - if (!p) - return -1; - - memcpy(dst, (void *)p + p->str_off, p->str_len); - return 0; -} @@ -18,10 +18,10 @@ static const char *prefix = "sys_"; static size_t prefix_len = 4; -static bool filter(struct function *f, struct cu *cu) +static bool filter(struct function *f) { if (f->proto.nr_parms != 0) { - const char *name = function__name(f, cu); + const char *name = function__name(f); if (strlen(name) > prefix_len && memcmp(name, prefix, prefix_len) == 0) @@ -30,8 +30,7 @@ static bool filter(struct function *f, struct cu *cu) return true; } -static void zero_extend(const int regparm, const struct base_type *bt, - struct cu *cu, const char *parm) +static void zero_extend(const int regparm, const struct base_type *bt, const char *parm) { const char *instr = "INVALID"; @@ -51,14 +50,14 @@ static void zero_extend(const int regparm, const struct base_type *bt, printf("\t%s\t$a%d, $a%d, 0" "\t/* zero extend $a%d(%s %s) from %d to 64-bit */\n", instr, regparm, regparm, regparm, - base_type__name(bt, cu, bf, sizeof(bf)), + base_type__name(bt, bf, sizeof(bf)), parm, bt->bit_size); } static void emit_wrapper(struct function *f, struct cu *cu) { struct parameter *parm; - const char *name = function__name(f, cu); + const char *name = function__name(f); int regparm = 0, needs_wrapper = 0; function__for_each_parameter(f, cu, parm) { @@ -71,14 +70,12 @@ static void emit_wrapper(struct function *f, struct cu *cu) char bf[64]; if (bt->bit_size < 64 && - strncmp(base_type__name(bt, cu, bf, sizeof(bf)), - "unsigned", 8) == 0) { + strncmp(base_type__name(bt, bf, sizeof(bf)), "unsigned", 8) == 0) { if (!needs_wrapper) { printf("wrap_%s:\n", name); needs_wrapper = 1; } - zero_extend(regparm, bt, cu, - parameter__name(parm, cu)); + zero_extend(regparm, bt, parameter__name(parm)); } } ++regparm; @@ -88,13 +85,13 @@ static void emit_wrapper(struct function *f, struct cu *cu) printf("\tj\t%s\n\n", name); } -static int cu__emit_wrapper(struct cu *cu, void *cookie __unused) +static int cu__emit_wrapper(struct cu *cu, void *cookie __maybe_unused) { struct function *pos; uint32_t id; cu__for_each_function(cu, id, pos) - if (!filter(pos, cu)) + if (!filter(pos)) emit_wrapper(pos, cu); return 0; } |