Squashed 'third_party/elfutils/' content from commit 555e15e
Change-Id: I61cde98949e47e5c8c09c33260de17f30921be79
git-subtree-dir: third_party/elfutils
git-subtree-split: 555e15ebe8bf1eb33d00747173cfc80cc65648a4
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
new file mode 100644
index 0000000..453f1d3
--- /dev/null
+++ b/libdwfl/ChangeLog
@@ -0,0 +1,2903 @@
+2018-02-09 Joshua Watt <JPEWhacker@gmail.com>
+
+ * dwfl_report_elf.c (__libdwfl_elf_address_range): Use FALLTHROUGH
+ macro instead of comment.
+ * frame_unwind.c (expr_eval): Likewise.
+
+2017-11-20 Mark Wielaard <mark@klomp.org>
+
+ * link_map.c (do_check64): Take a char * and calculate type and val
+ offsets before reading, possibly unaligned, values.
+ (do_check32): Likewise.
+ (check64): Remove define.
+ (check32): Likewise.
+ (auxv_format_probe): Call do_check32 and do_check64 directly with
+ a, possibly unaligned, auxv entry pointer.
+ (dwfl_link_map_report): Redefine AUXV_SCAN to not dereference a
+ possibly unaligned auxv entry pointer.
+
+2017-10-16 Mark Wielaard <mark@klomp.org>
+
+ * argp-std.c (parse_opt): For -k call argp_failure not failure to
+ keep dwfl around.
+
+2017-07-26 Yunlian Jiang <yunlian@google.com>
+
+ * argp-std.c (failure): Move to file scope.
+ (fail): Likewise.
+
+2017-04-20 Ulf Hermann <ulf.hermann@qt.io>
+ Mark Wielaard <mark@klomp.org>
+
+ * derelocate.c (compare_secrefs): Compare by end address and then by
+ section number if addresses are equal.
+
+2017-04-20 Ulf Hermann <ulf.hermann@qt.io>
+ Mark Wielaard <mark@klomp.org>
+
+ * linux-kernel-modules.c: Always return NULL from kernel_release() on
+ non-linux systems and set errno to ENOTSUP.
+
+2017-04-20 Ulf Hermann <ulf.hermann@qt.io>
+
+ * libdwflP.h: Don't include config.h.
+ * argp-std.c: Include config.h.
+ * cu.c: Likewise.
+ * derelocate.c: Likewise.
+ * dwfl_addrdie.c: Likewise.
+ * dwfl_addrdwarf.c: Likewise.
+ * dwfl_addrmodule.c: Likewise.
+ * dwfl_begin.c: Likewise.
+ * dwfl_build_id_find_debuginfo.c: Likewise.
+ * dwfl_build_id_find_elf.c: Likewise.
+ * dwfl_cumodule.c: Likewise.
+ * dwfl_dwarf_line.c: Likewise.
+ * dwfl_end.c: Likewise.
+ * dwfl_frame.c: Likewise.
+ * dwfl_frame_regs.c: Likewise.
+ * dwfl_getdwarf.c: Likewise.
+ * dwfl_getmodules.c: Likewise.
+ * dwfl_getsrc.c: Likewise.
+ * dwfl_getsrclines.c: Likewise.
+ * dwfl_line_comp_dir.c: Likewise.
+ * dwfl_linecu.c: Likewise.
+ * dwfl_lineinfo.c: Likewise.
+ * dwfl_linemodule.c: Likewise.
+ * dwfl_module.c: Likewise.
+ * dwfl_module_addrdie.c: Likewise.
+ * dwfl_module_addrname.c: Likewise.
+ * dwfl_module_addrsym.c: Likewise.
+ * dwfl_module_build_id.c: Likewise.
+ * dwfl_module_dwarf_cfi.c: Likewise.
+ * dwfl_module_eh_cfi.c: Likewise.
+ * dwfl_module_getdarf.c: Likewise.
+ * dwfl_module_getelf.c: Likewise.
+ * dwfl_module_getsrc.c: Likewise.
+ * dwfl_module_getsrc_file.c: Likewise.
+ * dwfl_module_getsym.c: Likewise.
+ * dwfl_module_info.c: Likewise.
+ * dwfl_module_nextcu.c: Likewise.
+ * dwfl_module_register_names.c: Likewise.
+ * dwfl_module_report_build_id.c: Likewise.
+ * dwfl_module_return_value_location.c: Likewise.
+ * dwfl_nextcu.c: Likewise.
+ * dwfl_onesrcline.c: Likewise.
+ * dwfl_report_elf.c: Likewise.
+ * dwfl_validate_address.c: Likewise.
+ * dwfl_version.c: Likewise.
+ * find-debuginfo.c: Likewise.
+ * gzip.c: Likewise.
+ * image-header.c: Likewise.
+ * lines.c: Likewise.
+ * linux-core-attach.c: Likewise.
+ * linux-pid-attach.c: Likewise.
+ * offline.c: Likewise.
+ * open.c: Likewise.
+ * relocate.c: Likewise.
+ * segment.c: Likewise.
+
+2017-04-20 Ulf Hermann <ulf.hermann@qt.io>
+
+ * libdwfl.h: Use __const_attribute__.
+
+2017-04-20 Ulf Hermann <ulf.hermann@qt.io>
+
+ * elf-from-memory.c: Explicitly cast phnum to size_t.
+
+2017-04-20 Ulf Hermann <ulf.hermann@qt.io>
+
+ * dwfl_module_getdwarf.c: Check shnum for 0 before subtracting from
+ it.
+
+2017-04-20 Ulf Hermann <ulf.hermann@qt.io>
+
+ * dwfl_frame.c: Drop unused sys/ptrace.h include.
+ * frame_unwind.c: Likewise.
+ * linux-pid-attach.c: Include sys/ptrace.h and sys/syscall.h only on
+ linux.
+
+2017-04-20 Ulf Hermann <ulf.hermann@qt.io>
+
+ * linux-kernel-modules.c: Include sys/types.h before fts.h
+
+2017-03-24 Mark Wielaard <mark@klomp.org>
+
+ * linux-core-attach.c (core_next_thread): If n_namesz == 0 then
+ the note name data is the empty string.
+ (dwfl_core_file_attach): Likewise.
+
+2017-02-15 Ulf Hermann <ulf.hermann@qt.io>
+
+ * linux-kernel-modules.c: Include system.h.
+
+2017-02-15 Ulf Hermann <ulf.hermann@qt.io>
+
+ * linux-kernel-modules.c: Use sysconf(_SC_PAGESIZE) to get page size.
+ * linux-proc-maps.c: Likewise.
+
+2016-12-29 Luiz Angelo Daros de Luca <luizluca@gmail.com>
+
+ * dwfl_build_id_find_elf.c: Include system.h.
+ * dwfl_module_getdwarf.c: Likewise.
+ * libdwfl_crc32_file.c: Don't define LIB_SYSTEM_H.
+
+2016-11-23 Mark Wielaard <mjw@redhat.com>
+
+ * linux-kernel-modules.c: Only include fts.h early if BAD_FTS is
+ defined.
+
+2016-10-11 Akihiko Odaki <akihiko.odaki.4i@stu.hosei.ac.jp>
+
+ * core-file.c: Remove sys/param.h.
+ * dwfl_segment_report_module.c: Likewise. Add system.h include.
+ (MAX): Remove definition.
+ * frame_unwind.c: Add system.h include.
+ (MAX): Remove definition.
+ * linux-core-attach.c (MIN): Remove definition.
+ * linux-pid-attach.c (MAX): Likewise.
+
+2016-08-12 Mark Wielaard <mjw@redhat.com>
+
+ * link_map.c (dwfl_link_map_report): Fix assert, set in.d_size.
+
+2016-04-14 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_module_getsrc_file.c (dwfl_module_getsrc_file): Free match
+ on invalid DWARF if we allocated it.
+
+2016-04-14 Mark Wielaard <mjw@redhat.com>
+
+ * linux-proc-maps.c (proc_maps_report): Free last_file on bad file
+ mapping.
+
+2016-03-01 Steven Chamberlain <steven@pyro.eu.org>
+
+ * linux-pid-attach.c: Removed unused pid_thread_callbacks,
+ pid_thread_detach, pid_detach, pid_set_initial_registers,
+ pid_memory_read, pid_getthread, pid_next_thread in #ifndef
+ __linux__ code.
+
+2016-02-22 Ravi Bangoria <ravi.bangoria@linux.vnet.ibm.com>
+ Mark Wielaard <mjw@redhat.com>
+
+ * find-debuginfo.c (find_debuginfo_in_path): Remember whether
+ debuglink_file is NULL. Try file basename (without .debug) ofr
+ absolute and relative path if debug_file was NULL.
+ * linux-kernel-modules.c (try_kernel_name): If try_debug is true call
+ dwfl_standard_find_debuginfo with NULL debuglink_file, otherwise with
+ basename of fname.
+
+2016-02-13 Mark Wielaard <mjw@redhat.com>
+
+ * linux-proc-maps.c (proc_maps_report): Free last_file when ENOEXEC.
+
+2016-02-13 Mark Wielaard <mjw@redhat.com>
+
+ * frame_unwind.c (new_unwound): Check and return unwound.
+ (handle_cfi): Check new_unwound was able to allocate new memory
+ before use. Return DWFL_E_NOMEM otherwise.
+
+2016-02-11 Mark Wielaard <mjw@redhat.com>
+
+ * relocate.c (relocate_section): Check result of all gelf_get* calls.
+ (__libdwfl_relocate): Likewise.
+ (__libdwfl_relocate_section): Likewise.
+
+2016-02-11 Mark Wielaard <mjw@redhat.com>
+
+ * relocate.c (relocate_section): Check result of gelf_update_* calls.
+
+2016-01-08 Mark Wielaard <mjw@redhat.com>
+
+ * libdwfl_a_SOURCES: Unconditionally add gzip.c.
+ * linux-kernel-modules.c (vmlinux_suffixes): We always have at least
+ .gz support.
+ (try_kernel_name): Likewise.
+ (check_suffix): Likewise.
+ * open.c (decompress): Likewise.
+
+2015-12-18 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_module_getdwarf.c (find_symtab): Uncompress symstr, xndx, sym
+ sections and aux_str, aux_xndx and aux_sym sections if necessary.
+ * relocate.c (relocate_getsym): Uncompress symtab and symtab_shndx
+ if necessary.
+ (resolve_symbol): Uncompress strtab section if necessary.
+ (relocate_section): Uncompress the section the relocations apply to
+ if necessary.
+
+2015-11-18 Chih-Hung Hsieh <chh@google.com>
+
+ * linux-proc-maps.c (proc_maps_report): Move nested function
+ 'report' to file scope.
+
+2015-11-18 Chih-Hung Hsieh <chh@google.com>
+
+ * core-file.c (elf_begin_rand): Move nested function 'fail' to file
+ scope.
+ * core-file.c (dwfl_elf_phdr_memory_callback): Move nested functions
+ 'update_end' and 'more' to file scope.
+
+2015-11-17 Chih-Hung Hsieh <chh@google.com>
+
+ * link_map.c (auxv_format_probe): Move nested functions
+ check64 and check32 to file scope.
+
+2015-12-08 Jose E. Marchesi <jose.marchesi@oracle.com>
+
+ * dwfl_frame.c (state_fetch_pc): Add a backend-defined offset to
+ the value of the return address register as defined by the CFI
+ abi.
+ * frame_unwind.c (handle_cfi): Likewise.
+
+2015-12-01 Mark Wielaard <mjw@redhat.com>
+
+ * link_map.c (dwfl_link_map_report): Track whether in.d_buf comes
+ from exec or memory_callback, free as appropriate.
+
+2015-12-01 Mark Wielaard <mjw@redhat.com>
+
+ * libdwflP.h (struct Dwfl_User_Core): New.
+ (struct DWfl): Replace executable_for_core with user_core.
+ * argp-std.c (parse_opt): Store core and fd in Dwfl user_core.
+ * core-file.c (dwfl_core_file_report): Check and store
+ executable_for_core in Dwfl user_core.
+ * dwfl_build_id_find_elf.c (dwfl_build_id_find_elf): Check and use
+ executable_for_core in Dwfl user_core.
+ * dwfl_end.c (dwfl_end): Release resources held in Dwfl user_core.
+ * link-map.c (report_r_debug): Check executable_for_core in Dwfl
+ user_core.
+ (dwfl_link_map_report): Likewise.
+
+2015-11-16 Chih-Hung Hsieh <chh@google.com>
+
+ * dwfl_module_getdwarf.c (find_prelink_address_sync): Move nested
+ function 'consider_shdr' to file scope.
+ * dwfl_module_getdwarf.c (find_dynsym): Move nested function
+ 'translate_offs' to file scope.
+
+2015-11-16 Chih-Hung Hsieh <chh@google.com>
+
+ * dwfl_module_addrsym.c (__libdwfl_addrsym): Move nested functions
+ 'same_section', 'binding_value', 'try_sym_value', and 'search_table'
+ to file scope.
+
+2015-11-19 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_module.c (__libdwfl_module_free): Remove Dwfl_Module Ebl from
+ eh_cfi and dwarf_cfi cache if necessary before calling dwarf_end and
+ dwarf_cfi_end.
+
+2015-11-13 Chih-Hung Hsieh <chh@google.com>
+
+ * gzip.c (unzip): Move nested functions to file scope.
+
+2015-10-21 Chih-Hung Hsieh <chh@google.com>
+ Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_module_getsrc_file.c (dwfl_module_getsrc_file): Move nested
+ functions 'dwarf_line_file', 'dwfl_line', and 'dwfl_line_file' to
+ file scope. Rename dwarf_line_file to dwfl_dwarf_line_file. Don't
+ use INTUSE.
+
+2015-10-21 Chih-Hung Hsieh <chh@google.com>
+
+ * frame_unwind.c (expr_eval): Move nested function 'push' and 'pop'
+ to file scope. Pass used local variables in struct eval_stack.
+
+2015-10-21 Chih-Hung Hsieh <chh@google.com>
+
+ * dwfl_module.c (dwfl_report_module): Move nested function 'use' to
+ file scope.
+
+2015-10-09 Josh Stone <jistone@redhat.com>
+
+ * core-file.c (elf_begin_rand): Replace loff_t with off_t.
+ * open.c (decompress): Replace off64_t with off_t.
+ * gzip.c (unzip): Likewise.
+ * image-header.c (__libdw_image_header): Likewise.
+ * libdwflP.h: Likewise in function declarations.
+ * argp-std.c (parse_opt): Replace open64 with open.
+ * dwfl_build_id_find_elf.c (__libdwfl_open_mod_by_build_id,
+ dwfl_build_id_find_elf): Likewise.
+ * dwfl_module_getdwarf.c (open_elf_file): Likewise.
+ * dwfl_report_elf.c (dwfl_report_elf): Likewise.
+ * dwfl_segment_report_module.c (dwfl_segment_report_module): Likewise.
+ * link_map.c (report_r_debug): Likewise.
+ * offline.c (dwfl_report_offline): Likewise.
+ * linux-proc-maps.c (grovel_auxv, get_pid_class,
+ dwfl_linux_proc_find_elf): Likewise.
+ (read_proc_memory): Replace off64_t with off_t.
+ * find-debuginfo.c (find_debuginfo_in_path): Replace stat64 and
+ fstat64 with stat and fstat.
+ (try_open): Likewise, and replace open64 with open.
+ * linux-kernel-modules.c: Manually define open and fopen to open64 and
+ fopen64 when needed, since the early fts.h include breaks that.
+ (try_kernel_name): Replace open64 with open.
+ (check_notes): Likewise.
+
+2015-10-09 Jose E. Marchesi <jose.marchesi@oracle.com>
+
+ * linux-proc-maps.c (read_proc_memory): Use seek+read instead of
+ pread, as the later doesn't accept negative offsets.
+
+2015-10-07 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_module_getdwarf.c (MAX): Removed.
+ (find_prelink_address_sync): Allocate exact amount of bytes for
+ phdrs and shdrs.
+ * dwfl_segment_report_module.c (dwfl_segment_report_module):
+ Likewise for phdrs.
+ * elf-from-memory.c (MAX): Removed.
+ (elf_from_remote_memory): Allocate exact amount of bytes for phdrs.
+
+2015-10-05 Chih-Hung Hsieh <chh@google.com>
+
+ * dwfl_module_getdwarf.c (find_prelink_address_sync): Do not use
+ union of variable length arrays.
+ * dwfl_segment_report_module.c (dwfl_segment_report_module): Likewise.
+ * elf-from-memory.c (elf_from_remote_memory): Likewise.
+ * link_map.c (auxv_format_probe): Likewise.
+ * link_map.c (report_r_debug): Likewise.
+ * link_map.c (dwfl_link_map_report): Likewise.
+
+2015-09-18 Chih-Hung Hsieh <chh@google.com>
+
+ * relocate.c (relocate_section): Move nested function "relocate"
+ to file scope, pass all used local variables as constants.
+ Move nested "check_badreltype" to file scope, pass addresses of
+ updated used local variables.
+ * linux-kernel-modules.c (intuit_kernel_bounds): Move nested function
+ "read_address" to file scope, pass all updated local variables in
+ a state structure.
+ * linux-kernel-modules.c (dwfl_linux_kernel_find_elf): Move nested
+ function "subst_name" to file scope, pass all used local variables
+ as constants.
+ * linux-kernel-modules.c (dwfl_linux_kernel_report_kernel): Replace
+ simple nested function "report" with a macro. Work around gcc static
+ analysis error -Werror=maybe-uninitialized.
+
+2015-09-22 Mark Wielaard <mjw@redhat.com>
+
+ * core-file.c: Remove old-style function definitions.
+ * dwfl_error.c: Likewise.
+ * dwfl_module_dwarf_cfi.c: Likewise.
+ * dwfl_module_eh_cfi.c: Likewise.
+ * dwfl_module_register_names.c: Likewise.
+ * dwfl_module_return_value_location.c: Likewise.
+ * dwfl_version.c: Likewise.
+
+2015-09-09 Chih-Hung Hsieh <chh@google.com>
+ Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_frame.c (dwfl_attach_state): Remove redundant NULL tests
+ on parameters declared with __nonnull_attribute__.
+ * libdwfl.h (dwfl_module_getelf): Don't mark first argument as
+ nonnull.
+
+2015-09-08 Mark Wielaard <mjw@redhat.com>
+
+ * libdwflP.h (struct __libdwfl_pid_arg): Add elf and elf_fd.
+ * linux-pid-attach.c (pid_detach): Call elf_end and close.
+ (dwfl_linux_proc_attach): Open and save /proc/PID/exe.
+
+2015-09-04 Chih-Hung Hsieh <chh@google.com
+
+ * frame_unwind.c (expr_eval): Use llabs instead of abs.
+
+2015-08-14 Dodji Seketeli <dodji@seketeli.org>
+
+ * find-debuginfo.c (find_debuginfo_in_path): Try to locate the
+ debug info file named debuglink_file under
+ mod->dwfl->callbacks->debuginfo_path, by looking at
+ the set of sub-trees under mod->dwfl->callbacks->debuginfo_path
+ which is common to the set of non-absolute parent trees of
+ file_name.
+
+2015-06-18 Mark Wielaard <mjw@redhat.com>
+
+ * find-debuginfo.c (try_open): Free fname on all failure paths.
+
+2015-06-18 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_module_getdwarf.c (find_symtab): Check shdr is not NULL and
+ sh_entsize is not zero.
+
+2015-06-06 Mark Wielaard <mjw@redhat.com>
+
+ * find-debuginfo.c (find_debuginfo_in_path): Always free localpath,
+ localname and file_dirname.
+
+2015-06-06 Mark Wielaard <mjw@redhat.com>
+
+ * derelocate.c (cache_sections): Free sortrefs.
+
+2015-06-05 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_segment_report_module.c (dwfl_segment_report_module):
+ If the note_file exists, but the build_id doesn't match discard
+ the file and continue reporting.
+
+2015-06-01 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_build_id_find_elf.c (__libdwfl_open_by_build_id): Copy path
+ pointer before passing to strsep.
+
+2015-05-30 Mark Wielaard <mjw@redhat.com>
+
+ * link_map.c (check32): Use read_4ubyte_unaligned_noncvt to read
+ type and value.
+ (read_addrs): Use read_(4|8)ubyte_unaligned_noncvt or to read
+ adresses.
+
+2015-05-30 Mark Wielaard <mjw@redhat.com>
+
+ * find-debuginfo.c (dwfl_standard_find_debuginfo): Check file_name is
+ not NULL before calling canonicalize_file_name.
+
+2015-05-24 Mark Wielaard <mjw@redhat.com>
+
+ * derelocate.c (check_module): Check mod is not NULL.
+
+2015-05-22 Mark Wielaard <mjw@redhat.com>
+
+ * link_map.c (dwfl_link_map_report): Allocate phdrs and dyn with
+ malloc instead of stack allocating. Call free when done with data.
+
+2015-05-22 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_segment_report_module.c (dwfl_segment_report_module):
+ Allocate phdrs with malloc, not on stack. free in finish.
+ Allocate dyn with malloc, not on stack, free after use.
+
+2015-05-22 Mark Wielaard <mjw@redhat.com>
+
+ * find-debuginfo.c (find_debuginfo_in_path): malloc or strdup,
+ instead of alloca or strdupa, local strings of unknown size.
+ Call free before return.
+
+2015-05-22 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_build_id_find_elf.c (__libdwfl_open_by_build_id): Return
+ error when id_len too small or too large. strdup, not strdupa,
+ and free path when done.
+
+2015-05-19 Mark Wielaard <mjw@redhat.com>
+
+ * elf-from-memory.c (elf_from_remote_memory): Don't allocate all
+ phdrs on the stack. Allocate with malloc and free when done.
+
+2015-05-19 Mark Wielaard <mjw@redhat.com>
+
+ * linux-kernel-modules.c (dwfl_linux_kernel_find_elf): malloc and
+ free alternate_name.
+
+2015-05-19 Mark Wielaard <mjw@redhat.com>
+
+ * linux-kernel-modules.c (dwfl_linux_kernel_report_offline): Don't
+ stack allocate name. Only change chars up to suffix.
+
+2015-05-18 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_module_getdwarf.c (find_prelink_address_sync): Allocate
+ phdrs and shdrs unions with malloc, not alloca. Free after use.
+
+2015-05-18 Mark Wielaard <mjw@redhat.com>
+
+ * derelocate.c (cache_sections): Allocate temporary newrefs and
+ sortrefs with malloc, not alloca. Always free them on return.
+
+2015-05-07 Mark Wielaard <mjw@redhat.com>
+
+ * cu.c (intern_cu): Check for EOF and check cuoff points to a real
+ Dwarf_Die before interning. Explicitly check EOF is expected.
+
+2015-05-05 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_lineinfo.c (dwfl_lineinfo): Check info->file is valid.
+
+2015-05-06 Roland McGrath <roland@hack.frob.com>
+
+ * dwfl_error.c (struct msgtable): Break type definition out of
+ the 'msgtable' initializer.
+ (msgtable): Make it a union of struct msgtable and a char array.
+ (msgstr): Use the full-table char array rather than the msg_0 entry.
+
+2015-04-23 Max Filippov <jcmvbkbc@gmail.com>
+
+ * core-file.c (_compat_without_executable_dwfl_core_file_report):
+ Guard with SYMBOL_VERSIONING.
+ * dwfl_module_build_id.c (_compat_vaddr_at_end_dwfl_module_build_id):
+ Likewise.
+ * dwfl_report_elf.c (_compat_without_add_p_vaddr_dwfl_report_elf):
+ Likewise.
+
+2015-04-02 Mark Wielaard <mjw@redhat.com>
+
+ * segment.c (insert): Check correct number of lookup_elts.
+
+2015-03-31 Mark Wielaard <mjw@redhat.com>
+
+ * core-file.c (core_file_read_eagerly): Special case small images.
+
+2015-01-26 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_module_getdwarf.c (find_symtab): Explicitly clear symdata,
+ syments and first_global on elferr before calling find_dynsym.
+
+2014-12-27 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_module_getsrc.c (dwfl_module_getsrc): Never match a line that
+ has end_sequence set.
+
+2015-01-04 Mark Wielaard <mjw@redhat.com>
+
+ * cu.c (intern_cu): Store result and return directly when finding
+ EOF marker.
+ (__libdwfl_nextcu): Check *nextp as returned by intern_cu isn't -1.
+
+2014-12-19 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_module_getdwarf.c (find_symtab): Always try find_dynsym last.
+
+2014-12-19 Mark Wielaard <mjw@redhat.com>
+
+ * elf-from-memory.c (handle_segment): Remove palign sanity check.
+
+2014-12-18 Mark Wielaard <mjw@redhat.com>
+
+ * relocate.c (resolve_symbol): Make sure symstrdata->d_buf != NULL.
+
+2014-12-13 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_module_getdwarf.c (find_dynsym): elf_getdata_rawchunk takes
+ a size_t, make sure it doesn't overflow.
+
+2014-12-13 Mark Wielaard <mjw@redhat.com>
+
+ * cu.c (cudie_offset): Make sure Dwarf_Off difference doesn't
+ wrap around before returning as int.
+
+2014-12-11 Josh Stone <jistone@redhat.com>
+
+ * dwfl_module_getsrc.c (dwfl_module_getsrc): Return the *last* line
+ record <= addr, rather than returning immediately on matches.
+
+2014-12-09 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_segment_report_module.c (handle_file_note): Check count doesn't
+ overflow.
+
+2014-12-07 Mark Wielaard <mjw@redhat.com>
+
+ * relocate.c (relocate_section): Sanity check section overlap against
+ actually used ehsize, shentsize and phentsize.
+
+2014-12-07 Mark Wielaard <mjw@redhat.com>
+
+ * offline.c (dwfl_offline_section_address): Assert shndx is not zero.
+ * relocate.c (__libdwfl_relocate_value): Don't relocate against
+ section zero.
+
+2014-11-29 Mark Wielaard <mjw@redhat.com>
+
+ * relocate.c (relocate_section): Check relocation section and target
+ section data don't overlap any of the ELF headers.
+ (relocate): Check for offset + size overflow.
+
+2014-11-22 Mark Wielaard <mjw@redhat.com>
+
+ * link_map.c (consider_executable): Use elf_getphdrnum.
+ (dwfl_link_map_report): Likewise.
+
+2014-11-18 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_module_getdwarf.c (find_symtab): Sanity check the data buffer,
+ number of symbols and first_global before use.
+
+2014-11-14 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_module_getdwarf.c (load_symtab): Don't use tables which have
+ a zero sh_entsize.
+
+2014-11-10 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_module_getdwarf.c (find_dynsym): New inner function
+ translate_offs that takes an adjust argument. Try finding
+ the symbol table with and without adjusting to main_bias.
+
+2014-09-26 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ Support NT_FILE for locating files.
+ * core-file.c (dwfl_core_file_report): New variables note_file and
+ note_file_size, set them and pass them to dwfl_segment_report_module.
+ * dwfl_segment_report_module.c: Include common.h and fcntl.h.
+ (buf_has_data, buf_read_ulong, handle_file_note): New functions.
+ (invalid_elf): New function from code of dwfl_segment_report_module.
+ (dwfl_segment_report_module): Add parameters note_file and
+ note_file_size. New variables elf and fd, clean them up in finish.
+ Move some code to invalid_elf. Call handle_file_note, if it found
+ a name verify the file by invalid_elf. Protect elf and fd against
+ cleanup by finish if we found the file for new Dwfl_Module.
+ * libdwflP.h (dwfl_segment_report_module): Add parameters note_file and
+ note_file_size.
+
+2014-09-23 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_segment_report_module.c (dwfl_segment_report_module):
+ Extract ei_class, ei_data and e_type early and use the result.
+
+2014-09-18 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * dwfl_build_id_find_elf.c (dwfl_build_id_find_elf): Use IS_EXECUTABLE.
+ * dwfl_segment_report_module.c (dwfl_segment_report_module): Set
+ IS_EXECUTABLE.
+ * libdwflP.h (struct Dwfl_Module): New field is_executable.
+
+2014-08-28 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * dwfl_module_getdwarf.c (find_offsets): Add parameter main_bias, use
+ it.
+ (find_dynsym): Pass the new parameter main_bias.
+
+2014-08-14 Mark Wielaard <mjw@redhat.com>
+
+ * linux-kernel-modules.c (check-suffix): Also TRY .ko.xz.
+
+2014-07-24 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ Fix report_r_debug for prelinked libraries.
+ * link_map.c (report_r_debug): Comment out variable l_addr.
+ Use instead new variable base recalculated from l_ld.
+
+2014-06-24 Kurt Roeckx <kurt@roeckx.be>
+
+ * linux-pid-attach.c: Make it build on non linux hosts.
+
+2014-06-17 Mark Wielaard <mjw@redhat.com>
+
+ * frame_unwind.c (handle_cfi): Use ebl_func_addr_mask.
+ * dwfl_module_getsym.c (__libdwfl_getsym): Likewise.
+
+2014-06-15 Mark Wielaard <mjw@redhat.com>
+
+ * linux-core-attach.c (core_memory_read): Use libdw/memory-access.h
+ macros read_4ubyte_unaligned_noncvt and read_8ubyte_unaligned_noncvt
+ to read possibly unaligned data.
+ (core_next_thread): Likewise.
+ (core_set_initial_registers): Likewise.
+ (dwfl_core_file_attach): Likewise.
+
+2014-06-11 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_frame.c (__libdwfl_process_free): Reset dwfl->attacherr.
+ (dwfl_attach_state): Set dwfl->attacherr.
+ (dwfl_pid): Check and return dwfl->attacherr if set.
+ (dwfl_getthreads): Likewise.
+ (getthread): Likewise.
+ * libdwflP.h: Add DWFL_E_NO_CORE_FILE.
+ (struct Dwfl): Add attacherr field.
+ * linux-core-attach.c (dwfl_core_file_attach): Set dwfl->attacherr.
+ Don't assert if ELF file is not ET_CORE, just return error.
+ * linux-pid-attach.c (dwfl_linux_proc_attach): Set dwfl->attacherr.
+
+2014-06-10 Mark Wielaard <mjw@redhat.com>
+
+ * argp-std.c (parse_opt): Ignore errors from dwfl_core_file_attach
+ or dwfl_linux_proc_attach.
+
+2014-05-15 Mark Wielaard <mjw@redhat.com>
+
+ * linux-proc-maps.c (grovel_auxv): Close fd on error.
+
+2014-05-02 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_module_getdwarf: Remove ENABLE_DWZ ifdefs so find_debug_altlink
+ is always called.
+
+2014-05-01 Mark Wielaard <mjw@redhat.com>
+
+ * libdwflP.h (struct Dwfl_Module): Add alt, alt_fd and alt_elf fields.
+ (__libdwfl_open_mod_by_build_id): Renamed __libdwfl_open_by_build_id.
+ (__libdwfl_open_by_build_id): New declaration that takes an explicit
+ build-id.
+ * dwfl_build_id_find_debuginfo.c (dwfl_build_id_find_debuginfo): If
+ we already have the Dwarf then look for the alt dwz multi file by
+ build-id.
+ * dwfl_build_id_find_elf.c (__libdwfl_open_by_build_id): Add the
+ build-id we are looking for as argument.
+ (__libdwfl_open_mod_by_build_id): New function, calls
+ __libdwfl_open_by_build_id.
+ (dwfl_build_id_find_elf): Call __libdwfl_open_mod_by_build_id.
+ * dwfl_module.c (__libdwfl_module_free): Release alt, alt_elf and
+ close alt_fd if necessary.
+ * dwfl_module_getdwarf.c (__check_build_id): Removed.
+ (try_debugaltlink): Removed.
+ (open_debugaltlink): Removed.
+ (open_elf_file): First half of open_elf that just opens the elf
+ file but doesn't setup the load address.
+ (open_elf): Call open_elf_file.
+ (find_debug_altlink): New function.
+ (load_dw): Remove loading of dwz multifile.
+ (find_dw): Call find_debug_altlink.
+ * find-debuginfo.c (validate): Handle alt debug case using
+ dwelf_dwarf_gnu_debugaltlink and mod->alt_elf.
+ (find_debuginfo_in_path): Handle alt debug files possibly in .dwz
+ subdirs.
+ * linux-kernel-modules.c (try_kernel_name): Use fakemod.debug.name
+ to store name to find by dwfl_standard_find_debuginfo instead of
+ allocating an extra variable on stack.
+
+2014-04-30 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_module_build_id.c (__libdwfl_find_elf_build_id): Moved to
+ dwelf_elf_gnu_build_id.c.
+ (__libdwfl_find_build_id): Add assert to make sure mod is never NULL.
+ * dwfl_segment_report_module.c (dwfl_segment_report_module): Call
+ dwelf_elf_gnu_build_id directly instead of __libdwfl_find_build_id.
+ * dwfl_module_getdwarf.c (__check_build_id): Implement using
+ dwelf_elf_gnu_build_id.
+
+2014-04-15 Florian Weimer <fweimer@redhat.com>
+
+ * dwfl_module_getdwarf.c (__check_build_id): Moved from libdw.
+ (try_debugaltlink): Likewise.
+ (open_debugaltlink): Likewise.
+ (load_dw): Locate alternate debug information using
+ dwelf_dwarf_gnu_debugaltlink and call open_debugaltlink.
+
+2014-04-11 Mark Wielaard <mjw@redhat.com>
+
+ * Makefile.am (AM_CPPFLAGS): Add libdwelf.
+ * libdwflP.h: Include libdwelfP.h.
+ * dwfl_module_getdwarf.c (find_debuglink): Moved to libdwelf.
+ (find_debuginfo): Use dwelf_elf_gnu_debuglink.
+
+2014-04-22 Mark Wielaard <mjw@redhat.com>
+
+ * frame_unwind.c (__libdwfl_frame_reg_get): Use uint64_t when
+ checking bits.
+ (__libdwfl_frame_reg_set): Likewise.
+
+2014-04-22 Kurt Roeckx <kurt@roeckx.be>
+
+ * linux-pid-attach.c: Make linux only.
+
+2014-03-14 Mark Wielaard <mjw@redhat.com>
+
+ * Makefile.am: Remove !MUDFLAP and MUDFLAP conditions.
+ Remove libelf and libdw definitions when MUDFLAP is defined.
+ * argp-std.c (__libdwfl_argp_mudflap_options): Removed.
+
+2014-03-03 Mark Wielaard <mjw@redhat.com>
+
+ * elf-from-memory.c (elf_from_remote_memory): Keep track of
+ segments_end_mem. Pass memsz to first handle_segment pass. Only
+ extend contents_size and use shdrs if only file bits are in
+ segment.
+
+2014-03-11 Josh Stone <jistone@redhat.com>
+
+ * dwfl_module_getdwarf.c (open_elf): Only explicitly set
+ mod->e_type when processing the main ELF file.
+
+2014-03-04 Mark Wielaard <mjw@redhat.com>
+
+ * libdwflP.h (struct __libdwfl_pid_arg): Moved here and renamed from
+ linux-pid-attach.c (struct pid_arg).
+ (__libdwfl_get_pid_arg): New internal function declaration.
+ (__libdwfl_ptrace_attach): Likewise.
+ (__libdwfl_ptrace_detach): Likewise.
+ * dwfl_frame.c (dwfl_attach_state): Add "(deleted)" files to the
+ special exception modules that cannot be checked at this point.
+ * linux-pid-attach.c (struct pid_arg): Moved to libdwflP.h
+ (ptrace_attach): Renamed to...
+ (__libdwfl_ptrace_attach): New internal function.
+ (__libdwfl_ptrace_detach): Likewise. Extracted from ...
+ (pid_thread_detach): Call __libdwfl_ptrace_detach now.
+ (__libdwfl_get_pid_arg): New internal function.
+ * linux-proc-maps.c (dwfl_linux_proc_find_elf): Check if special
+ module name contains "(deleted)" and dwfl_pid gives an attached
+ pid. If pid is set and try to (re)use ptrace attach state of
+ process before reading memory.
+
+2014-03-03 Mark Wielaard <mjw@redhat.com>
+
+ * elf-from-memory.c (elf_from_remote_memory): Take pagesize as
+ argument. Free buffer when detecting bad elf. Check PT_LOAD
+ alignment requirements on first handle_segment pass. Calculate
+ loadbase, start and end of segment using pagesize, not p_align.
+ * linux-proc-maps.c (dwfl_linux_proc_find_elf): Provide pagesize
+ to elf_from_remote_memory.
+
+2014-02-26 Mark Wielaard <mjw@redhat.com>
+
+ * linux-proc-maps.c (proc_maps_report): Don't assert on bad input.
+
+2014-02-26 Mark Wielaard <mjw@redhat.com>
+
+ * elf-from-memory.c (elf_from_remote_memory): Check against p64
+ p_type in case ELFCLASS64, not against p32 p_type.
+
+2014-01-17 Petr Machata <pmachata@redhat.com>
+
+ * relocate.c (relocate_section): Use gelf_fsize instead of relying
+ on shdr->sh_entsize.
+
+2014-01-05 Mark Wielaard <mjw@redhat.com>
+
+ * frame_unwind.c (handle_cfi): Only skip resetting return register
+ if the regno is not the actual CIE return address register.
+
+2014-01-02 Mark Wielaard <mjw@redhat.com>
+
+ * linux-pid-attach.c (dwfl_linux_proc_attach): Use strtol, not atoi.
+
+2013-12-30 Mark Wielaard <mjw@redhat.com>
+
+ * argp-std.c (parse_opt): Call dwfl_linux_proc_attach and
+ dwfl_core_file_attach explicitly.
+ * core-file.c (dwfl_core_file_report): Don't call
+ __libdwfl_attach_state_for_core implicitly.
+ * dwfl_begin.c (dwfl_begin): Remove setting of process_attach_error.
+ * dwfl_frame.c (dwfl_pid): Set errno to DWFL_E_NO_ATTACH_STATE, not
+ process_attach_error.
+ (dwfl_getthreads): Likewise.
+ (getthread): Likewise.
+ * libdwfl.h (dwfl_core_file_report): Update documentation.
+ (dwfl_linux_proc_report): Likewise.
+ (dwfl_core_file_attach): New function declaration.
+ (dwfl_linux_proc_attach): Likewise.
+ * libdwflP.h (struct Dwfl): Remove process_attach_error.
+ (__libdwfl_attach_state_for_pid): Removed declaration.
+ (__libdwfl_attach_state_for_core): Likewise.
+ (dwfl_core_file_attach): New internal declaration.
+ (dwfl_linux_proc_attach): Likewise.
+ (attach_state_for_core): Renamed to...
+ (dwfl_core_file_attach): ...this. Change return type.
+ (__libdwfl_attach_state_for_core): Removed.
+ * linux-pid-attach.c (struct pid_arg): Add assume_ptrace_stopped.
+ (pid_set_initial_registers): Check assume_ptrace_stopped before
+ calling ptrace.
+ (pid_thread_detach): Likewise.
+ (__libdwfl_attach_state_for_pid): Renamed to...
+ (dwfl_linux_proc_attach): ...this. Adjust return type.
+ * linux-proc-maps.c (dwfl_linux_proc_report): Don't call
+ __libdwfl_attach_state_for_pid implicitly.
+
+2013-12-28 Mark Wielaard <mjw@redhat.com>
+
+ * linux-proc-maps.c (dwfl_linux_proc_find_elf): Don't return special
+ character device files, only regular files.
+
+2013-12-24 Mark Wielaard <mjw@redhat.com>
+
+ * linux-core-attach.c (core_next_thread): Check whether thread_argp
+ is NULL. Reset core_arg->thread_note_offset and malloc a thread_arg
+ in that case. Free thread_arg if there are no more threads.
+
+2013-12-23 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_segment_report_module.c (dwfl_segment_report_module): Free
+ build_id before returning early.
+
+2013-12-23 Mark Wielaard <mjw@redhat.com>
+
+ * linux-pid-attach.c (__libdwfl_attach_state_for_pid): Report actual
+ pid (thread group leader) to dwfl_attach_state.
+
+2013-12-21 Mark Wielaard <mjw@redhat.com>
+
+ * frame_unwind.c (handle_cfi): Track whether the return register
+ has been set and only allow it to be set once.
+
+2013-12-20 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_frame.c (one_arg): New struct.
+ (get_one_thread_cb): New function.
+ (dwfl_getthread): Likewise.
+ (one_thread): New struct.
+ (get_one_thread_frames_cb): New function.
+ (dwfl_getthread_frames): Likewise.
+ * libdwfl.h (Dwfl_Thread_Callbacks): Add get_thread function.
+ (dwfl_getthread_frames): Likewise.
+ * libdwflP.h (dwfl_getthread_frames): New internal function declaration.
+ * linux-core-attach.c (core_thread_callbacks): Initialize get_thread
+ to NULL.
+ * linux-pid-attach.c (pid_getthread): New function.
+ (pid_thread_callbacks): Initialize get_thread to pid_getthread.
+
+2013-12-20 Mark Wielaard <mjw@redhat.com>
+
+ * linux-kernel-modules.c (report_kernel_archive): Correct nested
+ asprintf result check for debug.a.
+
+2013-12-18 Mark Wielaard <mjw@redhat.com>
+
+ * derelocate.c (__libdwfl_find_section_ndx): New internal function.
+ * dwfl_module_addrname.c (dwfl_module_addrname): Use
+ dwfl_module_addrinfo.
+ * dwfl_module_addrsym.c (dwfl_module_addrsym_elf): Replace with...
+ (__libdwfl_addrsym): ...this. Use __libdwfl_getsym, use value
+ for comparisons, not st_value. Fill in off. Search for both value
+ and the (adjusted) sym.st_value when different.
+ (dwfl_module_addrsym): Implement using __libdwfl_addrsym.
+ (dwfl_module_addrinfo): New function.
+ * dwfl_module_getsym.c (dwfl_module_getsym_elf): Replace with...
+ (__libdwfl_getsym): ...this. Use ebl_resolve_sym_value if requested
+ and possible. Adjust sym->st_value only when requested. Fill in addr
+ if available.
+ (dwfl_module_getsym_info): New function.
+ (dwfl_module_getsym): Use __libdwfl_getsym.
+ * libdwfl.h (dwfl_module_getsym_elf): Removed.
+ (dwfl_module_getsym_info): New function declaration.
+ (dwfl_module_addrinfo): Likewise.
+ (dwfl_module_addrsym): Add documentation describing differences
+ with addrinfo variants.
+ (dwfl_module_addrsym_elf): Removed.
+ * libdwflP.h (__libdwfl_getsym): New internal function declaration.
+ (__libdwfl_addrsym): Likewise.
+ (__libdwfl_find_section_ndx): Likewise.
+ (dwfl_module_addrinfo): New internal declaration.
+ (dwfl_module_getsym_info): Likewise.
+ (dwfl_module_addrsym_elf): Removed.
+ (dwfl_module_getsym_elf): Likewise.
+
+2013-12-18 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * argp-std.c (offline_find_elf): Remove.
+ (offline_callbacks): Use dwfl_build_id_find_elf instead.
+ * dwfl_build_id_find_elf.c (dwfl_build_id_find_elf): Move here the code
+ removed above.
+
+2013-12-18 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ unwinder: s390 and s390x
+ * dwfl_frame_pc.c (dwfl_frame_pc): Call ebl_normalize_pc.
+ * frame_unwind.c (new_unwound): New function from ...
+ (handle_cfi): ... here. Call it.
+ (setfunc, getfunc, readfunc): New functions.
+ (__libdwfl_frame_unwind): Call ebl_unwind with those functions.
+ * linux-core-attach.c (core_set_initial_registers): Always iterate
+ through the Ebl_Register_Location loop. Call
+ dwfl_thread_state_register_pc there.
+
+2013-12-17 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * frame_unwind.c (handle_cfi): Call ebl_dwarf_to_regno for RA.
+
+2013-12-17 Mark Wielaard <mjw@redhat.com>
+
+ * linux-pid-attach.c (pid_next_thread): Call rewinddir on first
+ traversal.
+
+2013-12-16 Mark Wielaard <mjw@redhat.com>
+
+ * libdwfl.h (dwfl_module_getsymtab_first_global): New function
+ definition.
+ * dwfl_module_getdwarf.c (dwfl_module_getsymtab_first_global): New
+ function.
+ * libdwflP.h (dwfl_module_getsymtab_first_global): New internal
+ function definition.
+ * dwfl_module_addrsym.c (dwfl_module_addrsym_elf): Use new function.
+
+2013-12-14 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_module.c (__libdwfl_module_free): Free mod->reloc_info if
+ allocated. Call dwarf_cfi_end on mod->eh_cfi if necessary.
+ * frame_unwind.c (handle_cfi): Free frame result from
+ dwarf_cfi_addrframe when done.
+
+2013-12-15 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ unwinder: ppc and ppc64
+ * frame_unwind.c (__libdwfl_frame_reg_get, __libdwfl_frame_reg_set):
+ Call ebl_dwarf_to_regno.
+ * linux-core-attach.c (core_set_initial_registers): Implement
+ pc_register support.
+ * linux-pid-attach.c (pid_thread_state_registers_cb): Implement
+ FIRSTREG -1.
+
+2013-11-30 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ Introduce process_attach_error.
+ * dwfl_begin.c (dwfl_begin): Initialize process_attach_error.
+ * dwfl_frame.c (dwfl_pid, dwfl_getthreads): Use PROCESS_ATTACH_ERROR if
+ PROCESS is NULL.
+ * libdwflP.h (struct Dwfl): New field process_attach_error.
+ * linux-core-attach.c (__libdwfl_attach_state_for_core): Rename to ...
+ (attach_state_for_core): ... here, make it static, change return type,
+ no longer use __libdwfl_seterrno.
+ (__libdwfl_attach_state_for_core): New wrapper for it.
+
+2013-11-27 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_module_addrsym.c (dwfl_module_addrsym): Rename to and call...
+ (dwfl_module_addrsym_elf): this. Add elfp and biasp arguments,
+ keep track of symelf, addr_symelf, closest_elf and sizeless_elf
+ instead of tracking dwfl_files.
+ * dwfl_module_getsym.c (__libdwfl_module_getsym): Renamed to...
+ (dwfl_module_getsym_elf): ...this. Remove dwfl_file argument, add
+ new elfp and biasp arguments. Track elf instead of file.
+ (dwfl_module_getsym): Call dwfl_module_getsym_elf.
+ dwfl_module_info.c (dwfl_module_info): Pass elf to
+ dwfl_adjusted_st_value.
+ * libdwfl.h (dwfl_module_getsym): Document limitations of shndx.
+ (dwfl_module_getsym_elf): New function declaration.
+ (dwfl_module_addrsym_elf): Likewise.
+ * libdwflP.h (dwfl_module_addrsym_elf): INTDECL.
+ (dwfl_module_getsym_elf): Likewise.
+ (dwfl_adjusted_st_value): Take and check elf not dwfl_file.
+ (dwfl_deadjust_st_value): Likewise.
+ (__libdwfl_module_getsym): Removed.
+ * relocate.c (resolve_symbol): Pass elf to dwfl_adjusted_st_value.
+
+2013-11-21 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ Fix non-build-id core files on build-id system.
+ * link_map.c (report_r_debug): Remove valid clearing if build-id cannot
+ be read from memory.
+
+2013-11-21 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * dwfl_segment_report_module.c (dwfl_segment_report_module): New
+ variable close_elf. Call __libdwfl_find_elf_build_id and compare the
+ content, if possible.
+
+2013-11-21 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ link_map: Use proper bias, not l_addr.
+ * core-file.c (dynamic_vaddr_get): Rename to ...
+ (__libdwfl_dynamic_vaddr_get): ... here, make it global,
+ internal_function.
+ (dwfl_core_file_report): Update name in the caller.
+ * libdwflP.h (__libdwfl_dynamic_vaddr_get): New declaration.
+ * link_map.c (report_r_debug): New variable elf_dynamic_vaddr. Call
+ __libdwfl_dynamic_vaddr_get for it. Remove L_ADDR FIXME comment.
+ Use ELF_DYNAMIC_VADDR instead of L_ADDR.
+
+2013-11-19 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ Compatibility with older kernels such as RHEL-6.
+ * linux-pid-attach.c (struct pid_arg): New field tid_was_stopped.
+ (ptrace_attach): New parameter tid_was_stoppedp. Set it.
+ (pid_set_initial_registers): Pass tid_was_stopped.
+ (pid_thread_detach): Use tid_was_stopped.
+
+2013-11-18 Josh Stone <jistone@redhat.com>
+
+ * dwfl_module_getdwarf.c (find_aux_address_sync): New function.
+ (find_aux_sym): Use it.
+
+2013-11-14 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ Code cleanup: Remove const in prototype
+ * dwfl_frame_regs.c (dwfl_thread_state_registers): Remove const from
+ firstreg.
+ * libdwfl.h (dwfl_thread_state_registers): Likewise.
+ * linux-pid-attach.c (pid_thread_state_registers_cb): Likewise.
+
+2013-11-14 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ Fix dwfl_attach_state machine->elf.
+ * dwfl_frame.c (dwfl_attach_state): Change parameter machine to elf.
+ Call ebl_openbackend instead of ebl_openbackend_machine.
+ * libdwfl.h (dwfl_attach_state): Change parameter machine to elf.
+ Update the function description.
+ * linux-core-attach.c (__libdwfl_attach_state_for_core): Pass CORE to
+ dwfl_attach_state.
+ * linux-pid-attach.c (__libdwfl_attach_state_for_pid): Pass NULL to
+ dwfl_attach_state.
+
+2013-11-06 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ Provide __libdwfl_module_getsym to get dwfl_file *.
+ * dwfl_module_addrsym.c (dwfl_module_addrsym) (i_to_symfile): Remove.
+ (dwfl_module_addrsym) (search_table): New variable file. Use
+ __libdwfl_module_getsym. Use file.
+ * dwfl_module_getsym.c (dwfl_module_getsym): Rename to ...
+ (__libdwfl_module_getsym): ... here. Add parameter filep. Set it.
+ (dwfl_module_getsym): New wrapper.
+ * libdwflP.h (__libdwfl_module_getsym): New declaration.
+
+2013-11-13 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ Fix dwfl_module_addrsym for minidebuginfo.
+ * dwfl_module_addrsym.c (dwfl_module_addrsym): New variable
+ addr_symfile.
+ (dwfl_module_addrsym) (same_section): Use it.
+ (dwfl_module_addrsym) (i_to_symfile): New function.
+ (dwfl_module_addrsym) (search_table): Use it.
+
+2013-11-07 Jan Kratochvil <jan.kratochvil@redhat.com>
+ Mark Wielaard <mjw@redhat.com>
+
+ * Makefile.am (libdwfl_a_SOURCES): Add dwfl_frame.c, frame_unwind.c,
+ dwfl_frame_pc.c, linux-pid-attach.c, linux-core-attach.c and
+ dwfl_frame_regs.c.
+ * core-file.c (dwfl_core_file_report): Call
+ __libdwfl_attach_state_for_core.
+ * dwfl_end.c (dwfl_end): Call __libdwfl_process_free.
+ * dwfl_frame.c: New file.
+ * frame_unwind.c: New file.
+ * dwfl_frame_pc.c: New file.
+ * linux-pid-attach.c: New file.
+ * linux-core-attach.c: New file.
+ * dwfl_frame_regs.c: New file.
+ * libdwfl.h (Dwfl_Thread, Dwfl_Frame): New typedefs.
+ (dwfl_core_file_report, dwfl_linux_proc_report): Extend comments.
+ (Dwfl_Thread_Callbacks): New definition.
+ (struct ebl, dwfl_attach_state, dwfl_pid, dwfl_thread_dwfl)
+ (dwfl_thread_tid, dwfl_frame_thread, dwfl_thread_state_registers)
+ (dwfl_thread_state_register_pc, dwfl_getthreads, dwfl_thread_getframes)
+ (dwfl_frame_pc): New declarations.
+ * libdwflP.h (Dwfl_Process): New typedef.
+ (LIBEBL_BAD, CORE_MISSING, INVALID_REGISTER, PROCESS_MEMORY_READ)
+ (PROCESS_NO_ARCH, PARSE_PROC, INVALID_DWARF, UNSUPPORTED_DWARF)
+ (NEXT_THREAD_FAIL, ATTACH_STATE_CONFLICT, NO_ATTACH_STATE, NO_UNWIND)
+ (INVALID_ARGUMENT): New DWFL_ERROR entries.
+ (struct Dwfl): New entry process.
+ (struct Dwfl_Process, struct Dwfl_Thread, struct Dwfl_Frame)
+ (__libdwfl_frame_reg_get, __libdwfl_frame_reg_set)
+ (__libdwfl_process_free, __libdwfl_frame_unwind)
+ (__libdwfl_attach_state_for_pid, __libdwfl_attach_state_for_core)
+ (__libdwfl_segment_start, __libdwfl_segment_end): New declarations.
+ (dwfl_attach_state, dwfl_pid, dwfl_thread_dwfl, dwfl_thread_tid)
+ (dwfl_frame_thread, dwfl_thread_state_registers)
+ (dwfl_thread_state_register_pc, dwfl_getthreads, dwfl_thread_getframes)
+ (dwfl_frame_pc): New INTDECL entries.
+ * linux-proc-maps.c (dwfl_linux_proc_report): Call
+ __libdwfl_attach_state_for_pid.
+ * segment.c (segment_start): Rename to ...
+ (__libdwfl_segment_start): ... here and make it internal_function.
+ (segment_end): Rename to ...
+ (__libdwfl_segment_end): ... here and make it internal_function.
+ (reify_segments, dwfl_report_segment): Rename them at the callers.
+
+2013-11-07 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * core-file.c (dwfl_core_file_report): Remove the use of MAX.
+
+2013-11-07 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * core-file.c (dwfl_core_file_report): Replaced variable sniffed by
+ retval. Fix one forgotten LISTED increase.
+
+2013-11-07 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ Fix core files for re-prelink-ed files.
+ * core-file.c (dynamic_vaddr_get): New function.
+ (dwfl_core_file_report): New variable file_dynamic_vaddr. Call
+ dynamic_vaddr_get instead of using L_ADDR.
+ * libdwflP.h (struct r_debug_info_module): Remove field l_addr.
+ * link_map.c (report_r_debug): Do not initialize l_addr.
+
+2013-11-07 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ Code cleanup.
+ * core-file.c (dwfl_core_file_report): Reindent block of code by
+ continue keyword.
+
+2013-10-30 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * argp-std.c (parse_opt): Use executable parameter of
+ dwfl_core_file_report.
+ * core-file.c (dwfl_core_file_report): Add parameter executable. Set
+ it to DWFL. Add NEW_VERSION for it.
+ (_compat_without_executable_dwfl_core_file_report): New. Twice.
+ * libdwfl.h (dwfl_core_file_report): Add parameter executable, update
+ the function comment.
+
+2013-10-15 Mark Wielaard <mjw@redhat.com>
+
+ * linux-proc-maps.c (proc_maps_report): Ignore non-absolute file
+ mappings.
+ (dwfl_linux_proc_find_elf): Don't abort, just return failure.
+
+2013-09-12 Mark Wielaard <mjw@redhat.com>
+
+ * cu.c (intern_cu): If dwarf_offdie fails free cu.
+
+2013-09-12 Mark Wielaard <mjw@redhat.com>
+
+ * linux-proc-maps.c (proc_maps_report): Don't fclose FILE in
+ bad_report.
+
+2013-09-12 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_module_getdwarf.c (find_symtab): Call elf_getdata with
+ aux_xndxscn, not xndxscn, for aux_symxndxdata.
+
+2013-08-25 Mark Wielaard <mjw@redhat.com>
+
+ * linux-kernel-modules.c (report_kernel): Pass add_p_vaddr as true
+ to dwfl_report_elf.
+
+2013-07-25 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * dwfl_segment_report_module.c (dwfl_segment_report_module): Check for
+ conflicts all the modules, not just the first one. Compare L_LD if it
+ is equal, not if it is in a module address range.
+
+2013-07-23 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * libdwflP.h (__libdwfl_elf_address_range): Add internal_function.
+
+2013-07-23 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * core-file.c (clear_r_debug_info): Close also ELF and FD.
+ (dwfl_core_file_report): Call __libdwfl_report_elf for
+ R_DEBUG_INFO.MODULE.
+ * dwfl_report_elf.c (__libdwfl_elf_address_range): New function from
+ code of ...
+ (__libdwfl_report_elf): ... this function. Call it.
+ * dwfl_segment_report_module.c: Include unistd.h.
+ (dwfl_segment_report_module): Use basename for MODULE->NAME.
+ Clear MODULE if it has no build-id and we have segment with build-id.
+ Ignore this segment only if MODULE still contains valid ELF.
+ * libdwflP.h (__libdwfl_elf_address_range): New declaration.
+ (struct r_debug_info_module): New fields fd, elf, l_addr, start, end
+ and disk_file_has_build_id.
+ (dwfl_link_map_report): Extend the comment.
+ * link_map.c (report_r_debug): Extend the comment. Always fill in new
+ r_debug_info_module. Initialize also the new r_debug_info_module
+ fields. Remove one FIXME comment. Call __libdwfl_elf_address_range
+ instead of __libdwfl_report_elf when R_DEBUG_INFO is not NULL.
+
+2013-07-19 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * libdwflP.h (__libdwfl_find_elf_build_id): Add internal_function.
+
+2013-07-02 Mark Wielaard <mjw@redhat.com>
+
+ * relocate.c (__libdwfl_relocate_value): Remove mod->e_type assert.
+
+2013-06-05 Mark Wielaard <mjw@redhat.com>
+
+ * link_map.c (report_r_debug): Always call release_buffer after
+ memory_callback succeeded reading build_id.
+
+2013-05-30 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * argp-std.c (parse_opt) <ARGP_KEY_SUCCESS> <opt->core> <opt->e>: Set
+ executable_for_core before calling dwfl_core_file_report.
+ * core-file.c (clear_r_debug_info): New function.
+ (dwfl_core_file_report): Move raw segments reporting lower. New
+ variable r_debug_info, pass it to dwfl_segment_report_module. Call
+ clear_r_debug_info in the end. Return sum of LISTED and SNIFFED.
+ * dwfl_module_build_id.c (check_notes): Move into
+ __libdwfl_find_elf_build_id.
+ (__libdwfl_find_build_id): Rename to ...
+ (__libdwfl_find_elf_build_id): ... here. Add parameters build_id_bits,
+ build_id_elfaddr and build_id_len. Verify MOD vs. ELF.
+ (__libdwfl_find_elf_build_id) (check_notes): Remove parameters mod and
+ set, rename data_vaddr to data_elfaddr. Do not call found_build_id.
+ (__libdwfl_find_elf_build_id): Update the check_notes caller, do not
+ adjust its data_elfaddr parameter.
+ (__libdwfl_find_build_id): New wrapper of __libdwfl_find_elf_build_id.
+ * dwfl_segment_report_module.c (dwfl_segment_report_module): New
+ parameter r_debug_info. New variable name_is_final. Adjust addresses
+ according to R_DEBUG_INFO->MODULE. Check conflicts against DWFL.
+ Do not overwrite NAME by SONAME if NAME_IS_FINAL.
+ * libdwflP.h (__libdwfl_find_elf_build_id): New declaration.
+ (struct r_debug_info_module, struct r_debug_info): New definitions.
+ (dwfl_segment_report_module, dwfl_link_map_report): Add parameter
+ r_debug_info.
+ * link_map.c: Include fcntl.h.
+ (report_r_debug): Add parameter r_debug_info, describe it in the
+ function comment. Delete dwfl_addrmodule call and its dependent code.
+ Verify build-id before calling dwfl_report_elf, also supply
+ executable_for_core to it. Store r_debug_info->module info when
+ appropriate.
+ (dwfl_link_map_report): Add parameter r_debug_info. New variable
+ in_ok. Try to read IN from EXECUTABLE_FOR_CORE. Update report_r_debug
+ caller parameters.
+
+2013-04-30 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * dwfl_report_elf.c (__libdwfl_report_elf): Add parameter add_p_vaddr.
+ Set it to true for ET_EXEC and ET_CORE. Provide alternative
+ setup of START and BIAS if !ADD_P_VADDR. Set END from BIAS, not BASE.
+ (dwfl_report_elf): Add parameter add_p_vaddr. Pass it down. Add
+ NEW_VERSION.
+ (_compat_without_add_p_vaddr_dwfl_report_elf) <SHARED>: New, with
+ COMPAT_VERSION.
+ * libdwfl.h (dwfl_report_elf): Add parameter add_p_vaddr. Describe it.
+ * libdwflP.h (__libdwfl_report_elf): Add parameter add_p_vaddr.
+ * link_map.c (report_r_debug): Use true add_p_vaddr for dwfl_report_elf.
+ * linux-kernel-modules.c (report_kernel): Use false add_p_vaddr for
+ dwfl_report_elf.
+ * offline.c (process_elf): Use true add_p_vaddr for dwfl_report_elf.
+
+2013-04-27 Mark Wielaard <mjw@redhat.com>
+
+ * link_map.c: #include system.h.
+
+2013-04-26 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * link_map.c (BE32, BE64, LE32, LE64): Delete the definitions, move
+ them to lib/system.h.
+
+2013-04-24 Mark Wielaard <mjw@redhat.com>
+
+ * Makefile.am: Use AM_CPPFLAGS instead of INCLUDES.
+
+2013-03-20 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * dwfl_report_elf.c (__libdwfl_report_elf): Remove BASE aligning.
+
+2013-03-12 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_getsrclines.c (dwfl_getsrclines): Return 0 on success.
+
+2013-02-22 Mark Wielaard <mjw@redhat.com>
+
+ * open.c (__libdw_gunzip,__libdw_bunzip2,__libdw_unlzma): Define
+ as DWFL_E_BADELF when not used.
+
+2013-02-10 Mark Wielaard <mjw@redhat.com>
+
+ * argp-std.c (parse_opt): Use opt->core and opt->e explicitly in
+ failure messages When handling ARGP_KEY_SUCCESS because arg will
+ not have been set.
+
+2013-01-30 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * linux-proc-maps.c: Include system.h.
+ (PROCEXEFMT, get_pid_class): New.
+ (grovel_auxv): Detect 32-bit vs. 64-bit auxv, possibly call
+ get_pid_class.
+
+2013-01-23 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_module_getdwarf.c (find_aux_sym): Don't substract one
+ from aux_syments by default.
+ (find_symtab): Also succeed when only aux_symdata is found.
+ When no symtab is found always try to load auxiliary table.
+ (dwfl_module_getsymtab): Substract one from result when both
+ tables have symbols.
+ * dwfl_module_getsym.c (dwfl_module_getsym): Only skip auxiliary
+ zero entry when both tables have symbols.
+ * dwfl_module_addrsym.c (dwfl_module_addrsym): Only substract
+ one from first_global when both tables have symbols.
+
+2013-01-16 Mark Wielaard <mjw@redhat.com>
+
+ * libdwflP.h (struct Dwfl_Module): Add aux_sym, aux_symdata,
+ aux_syments, aux_symstrdata, aux_symxndxdata and aux_first_global.
+ (dwfl_adjusted_aux_sym_addr): New function.
+ (dwfl_deadjust_aux_sym_addr): Likewise.
+ (dwfl_adjusted_st_value): Take and check symfile argument.
+ (dwfl_deadjust_st_value): Likewise.
+ * dwfl_module_getdwarf.c (find_prelink_address_sync): Take and
+ use dwfl_file as argument to set address_sync.
+ (find_debuginfo): Call find_prelink_address_sync with debug file.
+ (find_aux_sym): New function.
+ (find_symtab): Use find_aux_sym if all we have is the dynsym table
+ and fill in aux DwflModule fields.
+ (dwfl_module_getsymtab): Return syments plus aux_syments.
+ (load_symtab): Always set first_global.
+ * dwfl_module_addrsym.c (dwfl_module_addrsym): Check symfile
+ when using same_section. Calculate first_global based on both
+ mod->first_global and mod->aux_first_global.
+ * dwfl_module.c (__libdwfl_module_free): Free aux_sym.
+ * dwfl_module_getsym.c (dwfl_module_getsym): Use auxsym table
+ to retrieve symbol and name if necessary, making sure all locals
+ from any table come before any globals.
+ * dwfl_module_info.c (dwfl_module_info): Call dwfl_adjusted_st_value
+ with symfile.
+ * relocate.c (resolve_symbol): Likewise.
+
+2013-01-07 Roland McGrath <roland@hack.frob.com>
+
+ * link_map.c (auxv_format_probe): Handle unaligned 64-bit data, but
+ still assume the data is at least 32-bit aligned anyway.
+ (dwfl_link_map_report): Handle unaligned auxv data.
+
+2012-12-11 Mark Wielaard <mjw@redhat.com>
+
+ * linux-kernel-modules.c (report_kernel): Only free fname if
+ find_kernel_elf succeeds and allocates it.
+ (report_kernel_archive): Fix brackets around unlikely expression.
+
+2012-11-29 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * argp-std.c: Update Copyright year.
+ (offline_find_elf): New function.
+ (offline_callbacks): Use it for find_elf.
+ (struct parse_opt): New.
+ (parse_opt): New key ARGP_KEY_INIT. In other make hook struct
+ parse_opt pointer from former Dwfl pointer. Delay 'e and OPT_COREFILE
+ processing till ARGP_KEY_SUCCESS. Initialize state->input already from
+ ARGP_KEY_SUCCESS. Modify the cleanup in ARGP_KEY_ERROR. Make the
+ final state->input initialization optional.
+ * dwfl_end.c: Update Copyright year.
+ (dwfl_end): Free executable_for_core.
+ * libdwflP.h: Update Copyright year.
+ (struct Dwfl): New field executable_for_core.
+
+2012-11-20 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * dwfl_report_elf.c (__libdwfl_report_elf): Simplify START and BIAS
+ calculation.
+
+2012-10-17 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * dwfl_module_getdwarf.c (mod_verify_build_id): New function with code
+ from ...
+ (__libdwfl_getelf): ... here. Call it.
+
+2012-10-17 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * libdwfl.h (dwfl_module_getelf): Add __nonnull_attribute__.
+
+2012-10-10 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * dwfl_segment_report_module.c (dwfl_segment_report_module):
+ Initialize mod->MAIN_BIAS.
+
+2012-10-10 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * dwfl_module_addrsym.c (dwfl_module_addrsym): New function
+ binding_value. Use it for both zero and non-zero size symbols
+ comparisons.
+
+2012-10-01 Mark Wielaard <mjw@redhat.com>
+
+ * cu.c (cudie_offset): Don't use type_sig8, it might not be
+ initialized and these are always real CUs, never TUs.
+
+2012-10-01 Mark Wielaard <mjw@redhat.com>
+
+ * derelocate.c (find_section): Check next section exists before
+ accessing it.
+
+2012-08-01 Petr Machata <pmachata@redhat.com>
+
+ * offline.c (process_archive_member): Ignore entry "/SYM64/".
+
+2012-03-28 Roland McGrath <roland@hack.frob.com>
+
+ * dwfl_segment_report_module.c
+ (dwfl_segment_report_module: read_portion): Don't use existing buffer
+ when FILESZ is zero (string mode) and available portion doesn't hold
+ a terminated string.
+
+2011-12-02 Roland McGrath <roland@hack.frob.com>
+
+ * elf-from-memory.c (elf_from_remote_memory): Fix ELFCLASS64 case
+ to use elf64_xlatetom and PHDRS.p64.
+ Reported by Serge Pavlov <serge.pavlov.at.gnu@gmail.com>.
+
+2011-11-31 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_module_addrsym.c (dwfl_module_addrsym): First search all
+ global symbols. Then only when that doesn't provide a match search
+ all local symbols too.
+ * dwfl_module_getdwarf.c (load_symtab): Take first_global int arg
+ and fill it in.
+ (find_symtab): Initialize mod->first_global and pass it to load_symtab.
+ * libdwfl/libdwflP.h (Dwfl_Module): Add first_global field.
+
+2011-11-31 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_module_addrsym.c (dwfl_module_addrsym): Only update
+ sizeless_sym if needed and closer to desired addr.
+
+2011-10-20 Mark Wielaard <mjw@redhat.com>
+
+ * derelocate.c (cache_sections): Intern mod->reloc_info check.
+ (dwfl_module_relocations): Don't check mod->reloc_info.
+ (dwfl_module_relocation_info): Likewise.
+ (find_section): Likewise.
+
+2011-07-09 Roland McGrath <roland@hack.frob.com>
+
+ * image-header.c (LE32): Macro removed (now in lib/system.h).
+
+2011-04-11 Mark Wielaard <mjw@redhat.com>
+
+ * linux-kernel-modules.c (vmlinux_suffixes): Guard definition
+ by check for zlib, bzlib or lzma defines to check it isn't empty.
+ (try_kernel_name): Use same guard for use of vmlinux_suffixes.
+
+2011-03-08 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_getdwarf.c (open_elf): Clear errno before CBFAIL.
+ Reported by Kurt Roeckx <kurt@roeckx.be>.
+
+2011-02-11 Roland McGrath <roland@redhat.com>
+
+ * linux-kernel-modules.c (try_kernel_name): Try .gz, .bz2, .xz
+ suffixes if corresponding decompression support is enabled.
+
+2011-02-01 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_getdwarf.c (find_prelink_address_sync): Use the
+ section-end address as the synchronization point, rather than sh_addr.
+
+ * dwfl_module_getdwarf.c (find_prelink_address_sync): Discover
+ PT_INTERP p_vaddr separately from main phdrs and undo phdrs.
+
+ * dwfl_module_getdwarf.c (find_prelink_address_sync): Fix pasto in
+ last change, so we recognize PT_INTERP in ELFCLASS64 correctly.
+
+2011-01-11 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_getdwarf.c (open_elf): Remove section-based
+ address_sync fixup from here.
+ (find_prelink_address_sync): New function.
+ (find_debuginfo): Call it.
+ * libdwflP.h (DWFL_ERRORS): Add BAD_PRELINK error.
+
+2011-01-04 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_getdwarf.c (open_elf): Enhance address_sync calculation
+ logic to consider section addresses, the better to survive all the
+ possible prelink machinations.
+ * libdwflP.h (struct dwfl_file): Comment change.
+
+2010-11-30 Roland McGrath <roland@redhat.com>
+
+ * derelocate.c (dwfl_module_relocations): Remove over-eager assert.
+
+2010-11-12 Roland McGrath <roland@redhat.com>
+
+ * libdwflP.h (struct Dwfl_Module): New member main_bias.
+ (dwfl_adjusted_address, dwfl_deadjust_address): Use it.
+ * dwfl_module_getdwarf.c (__libdwfl_getelf): Initialize it.
+
+ * libdwflP.h (dwfl_deadjust_address): New function.
+ (dwfl_deadjust_dwarf_addr, dwfl_deadjust_st_value): New functions.
+ * cu.c (addrarange): Use dwfl_deadjust_dwarf_addr.
+ * dwfl_module_addrsym.c: Use dwfl_deadjust_st_value.
+
+2010-11-11 Roland McGrath <roland@redhat.com>
+
+ * libdwflP.h (struct dwfl_file): Remove bias member.
+ Add vaddr and address_sync members instead.
+ (dwfl_adjusted_address): Calculate using vaddr.
+ (dwfl_adjusted_dwarf_addr): Calculate using address_sync and call that.
+ (dwfl_adjusted_st_value): Use one of those calls.
+ * dwfl_module_getdwarf.c (open_elf): Initialize vaddr and address_sync.
+ * dwfl_segment_report_module.c (dwfl_segment_report_module): Likewise.
+ * derelocate.c (dwfl_module_relocations): Update ET_EXEC assertions.
+ * link_map.c (consider_executable): Adjust only MOD->low_addr for
+ detected PIE bias change.
+
+ * libdwflP.h (dwfl_adjusted_dwarf_addr): New function.
+ * dwfl_module_info.c: Use it.
+ * cu.c (addrarange): Likewise.
+ * dwfl_dwarf_line.c: Likewise.
+ * dwfl_module_dwarf_cfi.c: Likewise.
+ * dwfl_lineinfo.c: Likewise.
+ * dwfl_nextcu.c: Likewise.
+ * dwfl_module_getdwarf.c (dwfl_module_getdwarf): Likewise.
+
+ * libdwflP.h (dwfl_adjusted_st_value): New function.
+ * relocate.c (resolve_symbol): Use it.
+ * dwfl_module_getsym.c: Likewise.
+ * dwfl_module_addrsym.c: Likewise.
+ * dwfl_module_info.c: Likewise.
+
+ * libdwflP.h (dwfl_adjusted_address): New function.
+ * dwfl_module_build_id.c (__libdwfl_find_build_id): Use it.
+ * relocate.c (__libdwfl_relocate_value): Likewise.
+ * derelocate.c (cache_sections): Likewise.
+ (dwfl_module_address_section): Likewise.
+ * dwfl_module_getelf.c: Likewise.
+ * dwfl_module_eh_cfi.c: Likewise.
+ * link_map.c (consider_executable): Likewise.
+
+2010-08-24 Roland McGrath <roland@redhat.com>
+
+ * dwfl_dwarf_line.c: New file.
+ * Makefile.am (libdwfl_a_SOURCES): Add it.
+
+2010-08-18 Roland McGrath <roland@redhat.com>
+
+ * link_map.c (report_r_debug): Use found name if we have no name,
+ even if we already have an Elf handle.
+
+2010-06-30 Roland McGrath <roland@redhat.com>
+
+ * linux-kernel-modules.c (dwfl_linux_kernel_find_elf): Don't be
+ confused by -1 return from dwfl_build_id_find_elf after it opened
+ the Elf handle.
+ * find-debuginfo.c (dwfl_standard_find_debuginfo): Likewise for
+ dwfl_build_id_find_debuginfo.
+
+2010-06-16 Roland McGrath <roland@redhat.com>
+
+ * cu.c (cudie_offset): Use DIE_OFFSET_FROM_CU_OFFSET macro.
+
+2010-06-14 Roland McGrath <roland@redhat.com>
+
+ * find-debuginfo.c (try_open): Take new arg MAIN_STAT. Compare
+ candidate file to that st_dev/st_ino and pretend it didn't exist
+ if they match.
+ (find_debuginfo_in_path): Update caller, pass main file's info.
+
+2010-05-20 Roland McGrath <roland@redhat.com>
+
+ * linux-proc-maps.c (find_sysinfo_ehdr): Renamed to ...
+ (grovel_auxv): ... this. Take DWFL argument.
+ (dwfl_linux_proc_report): Update caller.
+
+ * dwfl_module_getdwarf.c (open_elf): Calculate alignment for bias
+ based on dwfl->segment_align or manifest alignment of MOD->low_addr.
+
+2010-05-19 Roland McGrath <roland@redhat.com>
+
+ * linux-kernel-modules.c (intuit_kernel_bounds): Rewritten.
+
+2010-05-06 Roland McGrath <roland@redhat.com>
+
+ * segment.c (insert): Clear inserted elements of DWFL->lookup_module.
+
+ * libdwflP.h (DWFL_ERRORS): Add WRONG_ID_ELF.
+ * dwfl_build_id_find_elf.c: Set MOD->main.valid when there is a build
+ ID but we didn't find a file.
+ * dwfl_module_getdwarf.c (__libdwfl_getelf): When that's set, check
+ and refuse any fallback file-by-name if it lacks the matching ID.
+
+ * dwfl_error.c (dwfl_errno): Add INTDEF.
+ * libdwflP.h: Add INTDECL.
+
+ * dwfl_module_getdwarf.c (open_elf): Do elf_end and clear FILE->elf in
+ failure cases.
+
+2010-05-04 Roland McGrath <roland@redhat.com>
+
+ * dwfl_segment_report_module.c: Use "[pie]" rather than "[dso]" for an
+ ET_DYN that has a DT_DEBUG.
+
+ * dwfl_segment_report_module.c: Fix jump-start of NDX-finding loop.
+
+ * segment.c (insert): Fix moving of values following insertion.
+ (reify_segments): Fix up MOD->segment backpointer indices after
+ later insertions in the main loop invalidate them.
+
+ * link_map.c (dwfl_link_map_report): Detect bias of embedded phdrs and
+ apply it to PT_DYNAMIC p_vaddr so we handle a PIE correctly.
+
+ * core-file.c (dwfl_core_file_report): Return any nonzero count of
+ modules reported, even if link_map grovelling failed and only sniffing
+ found anything.
+
+2010-04-26 Roland McGrath <roland@redhat.com>
+
+ * relocate.c (relocate_section): Treat R_*_NONE reloc as no reloc.
+ Works around probably-wrong ld -r behavior for case of a DWARF address
+ constant that refers to a discarded SHF_ALLOC section.
+
+2010-04-14 Roland McGrath <roland@redhat.com>
+
+ * link_map.c (report_r_debug): Limit iterations on the l_next chain to
+ an upper bound on sane possible number of elements.
+
+2010-03-11 Roland McGrath <roland@redhat.com>
+
+ * link_map.c (auxv_format_probe): Fix scanning loop, so we really scan
+ the second half for 32-bit matches.
+
+2010-03-10 Roland McGrath <roland@redhat.com>
+
+ * core-file.c (dwfl_core_file_report): Punt EHDR argument.
+ * argp-std.c (parse_opt): Update caller.
+ * libdwfl.h: Declare dwfl_core_file_report.
+ * libdwflP.h: Don't.
+
+2010-02-17 Roland McGrath <roland@redhat.com>
+
+ * dwfl_segment_report_module.c (addr_segndx): Take new flag argument.
+ If set, find the first index not below ADDR.
+ (dwfl_segment_report_module): Update callers.
+ Pass true when calculating return value.
+
+2010-02-15 Roland McGrath <roland@redhat.com>
+
+ * Makefile.am: Use config/eu.am for common stuff.
+
+ * find-debuginfo.c (find_debuginfo_in_path): Fix uninitialized
+ variable in failure path.
+
+2010-02-02 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_module_dwarf_cfi.c (dwfl_module_dwarf_cfi): Always set bias.
+ * dwfl_module_eh_cfi.c (dwfl_module_eh_cfi): Likewise
+
+2010-01-07 Roland McGrath <roland@redhat.com>
+
+ * core-file.c (dwfl_core_file_report): Use elf_getphdrnum.
+ * dwfl_module_build_id.c (__libdwfl_find_build_id): Likewise.
+ * dwfl_module_getdwarf.c (open_elf, find_dynsym): Likewise.
+ * dwfl_report_elf.c (__libdwfl_report_elf): Likewise.
+
+2010-01-06 Roland McGrath <roland@redhat.com>
+
+ * relocate.c (relocate_getsym): For SHN_COMMON, zero st_value.
+ (relocate_section): Let unresolved SHN_COMMON symbol stay 0.
+
+2009-11-16 Roland McGrath <roland@redhat.com>
+
+ * relocate.c (relocate_section): Skip SHT_NOBITS or empty target scn.
+
+2009-11-12 Petr Machata <pmachata@redhat.com>
+
+ * core-file.c (dwfl_elf_phdr_memory_callback): Only load ahead if
+ the chunk is both offset-contiguous and vaddr-contiguous.
+
+2009-11-05 Roland McGrath <roland@redhat.com>
+
+ * link_map.c (report_r_debug): Skip entries with l_ld==0.
+ Use dwfl_addrmodule for l_ld lookup, don't bail on lookup failure.
+
+2009-09-04 Roland McGrath <roland@redhat.com>
+
+ * image-header.c (__libdw_image_header): Fix tranposed comparison.
+
+2009-08-27 Roland McGrath <roland@redhat.com>
+
+ * image-header.c: New file.
+ * Makefile.am (libdwfl_a_SOURCES): Add it.
+ * libdwflP.h: Declare __libdw_image_header.
+ * open.c (decompress): Don't consume ELF on failure.
+ (what_kind): New function, broken out of ...
+ (__libdw_open_file): ... here. Call it.
+ If it fails, try __libdw_image_header and then try what_kind again.
+
+ * gzip.c (unzip): Reuse *WHOLE as first INPUT_BUFFER,
+ leave it behind for next decompressor.
+ * open.c (decompress): Free BUFFER on failure.
+
+2009-08-26 Roland McGrath <roland@redhat.com>
+
+ * gzip.c (find_zImage_payload): New function, broken out of ...
+ (mapped_zImage): ... here. Call it.
+ (find_zImage_payload) [LZMA]: Match LZMA-compressed kernels with
+ stupid method of just trying the decoder.
+
+ * open.c [USE_LZMA]: Try __libdw_unlzma.
+ * libdwflP.h: Declare it.
+ (DWFL_ERRORS): Add DWFL_E_LZMA.
+ * gzip.c [LZMA]: Implement liblzma version for XZ file format.
+ * lzma.c: New file.
+ * Makefile.am [LZMA] (libdwfl_a_SOURCES): Add it.
+
+ * gzip.c (mapped_zImage): Limit scan to 32kb.
+ Make this unconditional, support bzip2 kernel images too.
+ (unzip): Use direct inflate method for non-mmap case too.
+ Only zlib uses the stream method.
+
+2009-08-09 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_build_id.c: Use new macros for versioned definitions.
+
+2009-07-08 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_dwarf_cfi.c: New file.
+ * dwfl_module_eh_cfi.c: New file.
+ * Makefile.am (libdwfl_a_SOURCES): Add them.
+ * libdwflP.h (struct Dwfl_Module): New members `dwarf_cfi', `eh_cfi.
+ Add INTDECL for dwfl_module_eh_cfi, dwfl_module_dwarf_cfi.
+
+2009-07-08 Roland McGrath <roland@redhat.com>
+
+ * libdwflP.h (struct Dwfl_Module): Reorder members to pack better.
+
+2009-06-18 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_report_elf.c (__libdwfl_report_elf): Return NULL on overlap.
+
+2009-06-13 Ulrich Drepper <drepper@redhat.com>
+
+ * derelocate.c: Don't use deprecated libelf functions.
+ * dwfl_module_getdwarf.c: Likewise.
+ * relocate.c: Likewise.
+
+2009-04-23 Ulrich Drepper <drepper@redhat.com>
+
+ * dwfl_module_build_id.c: Define versioned symbols only if SHARED is
+ defined. Otherwise just define the latest version.
+
+2009-04-22 Roland McGrath <roland@redhat.com>
+
+ * relocate.c (resolve_symbol): Apply correct bias to st_value found in
+ a non-ET_REL module.
+
+ * dwfl_module_build_id.c (__libdwfl_find_build_id): Fix last change to
+ adjust properly for non-ET_REL.
+
+2009-04-21 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_getsym.c: Apply non-ET_REL bias only if SHF_ALLOC.
+
+ * relocate.c (__libdwfl_relocate_value): Assert that MOD is ET_REL.
+ * derelocate.c (cache_sections): Call __libdwfl_relocate_value only
+ for ET_REL.
+ * dwfl_module_build_id.c (__libdwfl_find_build_id): Likewise.
+
+2009-04-20 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_getdwarf.c (__libdwfl_getelf): Add internal_function.
+
+2009-04-19 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_getdwarf.c (find_file): Renamed to ...
+ (__libdwfl_getelf): ... this. Make it global.
+ (find_symtab, find_dw): Update callers.
+ (dwfl_module_getelf): Functions moved ...
+ * dwfl_module_getelf.c: ... here, new file.
+ * Makefile.am (libdwfl_a_SOURCES): Add it.
+ * libdwflP.h: Declare __libdwfl_getelf.
+
+2009-04-14 Roland McGrath <roland@redhat.com>
+
+ * dwfl_segment_report_module.c: Handle DT_STRTAB value being either
+ absolute (already adjusted in place) or needing load bias adjustment.
+
+ * core-file.c (dwfl_elf_phdr_memory_callback): Fix return value for
+ gelf_getphdr failure. Fix file size limit checks.
+
+ * dwfl_segment_report_module.c: Fix underflow in DYNSTRSZ check.
+
+2009-04-08 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_getsym.c: Don't adjust for bias again after
+ __libdwfl_relocate_value.
+
+ * relocate.c (__libdwfl_relocate_value): Don't adjust a value from
+ a non-SHF_ALLOC section.
+ (relocate_getsym): Test st_shndx for SHN_* values, not *SHNDX.
+ * dwfl_module_getsym.c (dwfl_module_getsym): Likewise.
+
+2009-03-09 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_build_id.c (__libdwfl_find_build_id): Move SHSTRNDX
+ variable to outer scope, so we cache it for the loop.
+
+ * relocate.c (__libdwfl_relocate_value): Add MOD->main.bias to sh_addr.
+
+2009-02-12 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_build_id.c (__libdwfl_find_build_id): Use
+ __libdwfl_relocate_value to find correct sh_addr value.
+
+2009-02-10 Roland McGrath <roland@redhat.com>
+
+ * dwfl_report_elf.c (__libdwfl_report_elf): Take new arg SANITY.
+ If false, don't fail for NO_PHDR.
+ (dwfl_report_elf): Update caller.
+ * libdwflP.h: Update decl.
+ * offline.c (process_elf): Call it with false, so we don't refuse
+ dubiously-formed objects here.
+
+ * link_map.c (consider_executable): Don't assert dwfl_addrsegment
+ finds our module. We shouldn't crash when we confuse some guesses.
+
+2009-02-10 Ulrich Drepper <drepper@redhat.com>
+
+ * open.c (decompress): Avoid crash with empty input file.
+
+2009-01-27 Roland McGrath <roland@redhat.com>
+
+ * dwfl_report_elf.c (__libdwfl_report_elf): Ignore trailing PT_LOAD
+ with zero vaddr and memsz.
+
+2009-01-22 Roland McGrath <roland@redhat.com>
+
+ * open.c (decompress): Move BUFFER, SIZE decls outside #if.
+
+ * dwfl_segment_report_module.c (addr_segndx): Remove bogus adjustments
+ after address-matching loop.
+
+ * segment.c (lookup): Fix fencepost in checking for HINT match.
+
+2009-01-14 Roland McGrath <roland@redhat.com>
+
+ * gzip.c [!BZLIB] (mapped_zImage): New function.
+ (unzip) [!BZLIB]: Grok Linux kernel zImage format.
+
+2009-01-10 Ulrich Drepper <drepper@redhat.com>
+
+ * dwfl_error.c: Always use __thread. Remove all !USE_TLS code.
+
+2009-01-08 Roland McGrath <roland@redhat.com>
+
+ * linux-kernel-modules.c (dwfl_linux_kernel_report_offline):
+ Skip subdirectory named "source".
+ (dwfl_linux_kernel_find_elf): Likewise.
+
+2009-01-06 Roland McGrath <roland@redhat.com>
+
+ * linux-kernel-modules.c (check_suffix): New function.
+ Match ".ko", ".ko.gz", and ".ko.bz2" suffixes.
+ (dwfl_linux_kernel_report_offline): Use it.
+ (dwfl_linux_kernel_find_elf): Likewise.
+
+2009-01-05 Roland McGrath <roland@redhat.com>
+
+ * argp-std.c (parse_opt): Use __libdw_open_file for core file.
+ * dwfl_build_id_find_debuginfo.c: Use it to open the file.
+ * dwfl_build_id_find_elf.c: Likewise.
+ * dwfl_module_getdwarf.c (open_elf): Likewise.
+ * dwfl_report_elf.c: Likewise.
+ * find-debuginfo.c (validate): Likewise.
+ * offline.c (__libdwfl_report_offline): Likewise.
+
+ * libdwflP.h: Declare __libdw_open_file.
+ * open.c: New file.
+ * Makefile.am (libdwfl_a_SOURCES): Add it.
+
+ * gzip.c: New file.
+ * Makefile.am [ZLIB] (libdwfl_a_SOURCES): Add it.
+ * bzip2.c: New file.
+ * Makefile.am [BZLIB] (libdwfl_a_SOURCES): Add it.
+ * libdwflP.h: Declare __libdw_gunzip, __libdw_bunzip2.
+
+2008-12-16 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_build_id.c (dwfl_module_build_id): Define with alias and
+ symver magic to bind to ELFUTILS_0.138.
+ (_BUG_COMPAT_dwfl_module_build_id): New function, bug compatible
+ wrapper for ELFUTILS_0.130 version set.
+
+2008-12-18 Roland McGrath <roland@redhat.com>
+
+ * derelocate.c (dwfl_module_relocate_address): Fix last fix: ET_DYN
+ addresses are taken as relative to MOD->low_addr.
+
+2008-12-15 Roland McGrath <roland@redhat.com>
+
+ * derelocate.c (dwfl_module_relocate_address): Apply main.bias, not
+ debug.bias.
+
+2008-12-11 Roland McGrath <roland@redhat.com>
+
+ * offline.c (process_archive): Don't call elf_end and close if
+ returning NULL. Check first elf_begin call and set error code
+ specially for empty archive.
+ Fixes RHBZ#465878.
+
+2008-12-02 Roland McGrath <roland@redhat.com>
+
+ * dwfl_getmodules.c (dwfl_getmodules): Typo fix in last change.
+
+2008-11-26 Roland McGrath <roland@redhat.com>
+
+ * dwfl_getmodules.c (dwfl_getmodules): Encode iteration style in
+ return value, and interpret encoded OFFSET argument.
+
+2008-10-07 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_build_id.c (check_notes): Fix typo in vaddr calculation.
+
+2008-09-29 Roland McGrath <roland@redhat.com>
+
+ * segment.c (insert): Must realloc DWFL->lookup_module here too.
+ (dwfl_report_segment): Clear DWFL->lookup_module before insert calls.
+
+2008-08-28 Roland McGrath <roland@redhat.com>
+
+ * segment.c (reify_segments): Fix last change.
+
+2008-08-27 Roland McGrath <roland@redhat.com>
+
+ * linux-proc-maps.c (read_proc_memory): Return 0 for EINVAL or EPERM
+ failure from pread64.
+
+2008-08-26 Roland McGrath <roland@redhat.com>
+
+ * segment.c (reify_segments): Insert a trailing segment for a module
+ end that is above the highest current segment.
+
+2008-08-25 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_getdwarf.c (open_elf): Extract elf_errno () for
+ coded return value, not plain DWFL_E_LIBELF. Return DWFL_E_BADELF
+ if FILE->elf is not ELF_K_ELF.
+
+ * dwfl_segment_report_module.c: Add a cast.
+
+2008-08-21 Denys Vlasenko <dvlasenk@redhat.com>
+
+ * dwfl_module_addrsym.c (dwfl_module_addrsym): Improve logic
+ which decides which symbol is "closest" to a given address.
+
+2008-08-15 Roland McGrath <roland@redhat.com>
+
+ * argp-std.c (offline_callbacks): Use dwfl_build_id_find_elf.
+ (options, parse_opt): Handle --core.
+
+ * core-file.c: New file.
+ * Makefile.am (libdwfl_a_SOURCES): Add it.
+ * libdwflP.h (dwfl_core_file_report): Declare it.
+
+ * link_map.c: New file.
+ * Makefile.am (libdwfl_a_SOURCES): Add it.
+ * libdwflP.h (dwfl_link_map_report): Declare it.
+
+ * libdwflP.h (MIN, MAX): New macros.
+ (Dwfl_Memory_Callback): New typedef.
+ (Dwfl_Module_Callback): New typedef.
+ (dwfl_segment_report_module): Declare it.
+ * dwfl_segment_report_module.c: New file.
+ * Makefile.am (libdwfl_a_SOURCES): Add it.
+
+ * derelocate.c (dwfl_module_address_section): Add INTDEF.
+ * libdwflP.h: Add INTDECL.
+
+ * segment.c: New file.
+ * Makefile.am (libdwfl_a_SOURCES): Add it.
+ * libdwfl.h: Declare dwfl_addrsegment, dwfl_report_segment.
+ * libdwflP.h (struct Dwfl): New members lookup_elts, lookup_alloc,
+ lookup_addr, lookup_module, lookup_segndx, replace removed members
+ modules, nmodules.
+ (struct Dwfl_Module): New member segment.
+ * dwfl_end.c (dwfl_end): Free the new ones. Iterate via modulelist
+ to each free module.
+ * dwfl_module.c (dwfl_report_begin_add): Do nothing.
+ (dwfl_report_begin): Don't call it. Truncate the segment table instead.
+ (dwfl_report_module): Don't touch DWFL->nmodules.
+ (dwfl_report_end): Don't touch DWFL->modules and DWFL->nmodules.
+ (compare_modules): Function removed.
+ * dwfl_getmodules.c: Rewritten.
+ Add INTDEF.
+ * libdwflP.h: Add INTDECLs.
+ * dwfl_getdwarf.c: Rewritten to call dwfl_getmodules.
+ * dwfl_addrmodule.c: Rewritten to just call dwfl_addrsegment.
+
+2008-08-03 Roland McGrath <roland@redhat.com>
+
+ * linux-kernel-modules.c: Include <fts.h> before <config.h>.
+
+2008-07-17 Roland McGrath <roland@redhat.com>
+
+ * dwfl_build_id_find_elf.c (__libdwfl_open_by_build_id): Set errno to
+ zero if the failure was only ENOENT.
+
+2008-06-03 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_addrsym.c (dwfl_module_addrsym): Exclude undefined
+ symbols.
+
+2008-05-22 Petr Machata <pmachata@redhat.com>
+
+ * dwfl_module_getdwarf.c (open_elf): Bias of ET_EXEC files is always 0.
+
+2008-05-06 Roland McGrath <roland@frob.com>
+
+ * linux-kernel-modules.c (dwfl_linux_kernel_report_offline): Use
+ FTS_LOGICAL here too.
+ (dwfl_linux_kernel_find_elf): Likewise.
+
+2008-04-29 Roland McGrath <roland@redhat.com>
+
+ * find-debuginfo.c (dwfl_standard_find_debuginfo): Try path search
+ based on canonicalize_file_name if it differs from the supplied name.
+
+ * linux-kernel-modules.c (check_module_notes): Use FTS_LOGICAL so
+ we accept symlinks.
+
+2008-04-27 Roland McGrath <roland@redhat.com>
+
+ * linux-kernel-modules.c (report_kernel): Fix crash when
+ dwfl_report_elf fails.
+
+2008-04-05 Roland McGrath <roland@redhat.com>
+
+ * linux-proc-maps.c (proc_maps_report): Don't leak LAST_FILE.
+
+ * dwfl_module_getdwarf.c (find_file): Always free build_id_bits.
+ Clear it after freeing.
+ * dwfl_module_report_build_id.c (dwfl_module_report_build_id): Likewise.
+
+2008-03-26 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_getdwarf.c (load_symtab): Don't return success for
+ SHT_DYNSYM, just set *SYMSCN like the comment says.
+
+ * dwfl_end.c (dwfl_end): Iterate on modulelist chain, not modules array.
+
+ * argp-std.c (parse_opt): On failure, call dwfl_end before argp_failure.
+
+2008-03-19 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_getsrc.c: Adjust address for module bias before search.
+
+2008-03-01 Roland McGrath <roland@redhat.com>
+
+ * libdwflP.h (__libdwfl_seterrno): Remove parameter name from
+ prototype to avoid older compiler's complaint about reuse of the name.
+ (__libdwfl_canon_error): Likewise.
+
+2008-02-19 Roland McGrath <roland@redhat.com>
+
+ * relocate.c (relocate_section): Check for an unhandled relocation
+ type before resolving a reloc's symbol. Lift DWFL_E_BADRELTYPE ->
+ DWFL_E_UNKNOWN_MACHINE check out of loops.
+
+ * dwfl_module_getdwarf.c (load_dw): Skip relocation if
+ DEBUGFILE->relocated is already set.
+
+2008-01-26 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_getdwarf.c (open_elf): Open FILE->name if it's non-null.
+
+ * dwfl_build_id_find_elf.c (__libdwfl_open_by_build_id): Don't clear
+ incoming *FILE_NAME at the start.
+
+2008-01-08 Roland McGrath <roland@redhat.com>
+
+ * Makefile.am (euinclude): Variable removed.
+ (pkginclude_HEADERS): Set this instead of euinclude_HEADERS.
+
+2007-10-23 Roland McGrath <roland@redhat.com>
+
+ * linux-kernel-modules.c (report_kernel_archive): Reorder the kernel
+ module to appear first.
+
+2007-10-20 Roland McGrath <roland@redhat.com>
+
+ * offline.c (process_archive_member): Take FD argument, pass it down
+ to process_file. Return Elf_Cmd, not bool.
+ Call elf_next here, always before elf_end.
+ (process_archive): Update caller. Don't close FD here unless there
+ are no member refs.
+
+ * dwfl_module.c (free_file): Close fd only when elf_end returns zero.
+
+ * libdwflP.h (struct dwfl_file): New bool member `relocated'.
+ * dwfl_module_getdwarf.c (dwfl_module_getelf): For ET_REL, apply
+ partial relocation to one or both files.
+ (dwfl_module_getdwarf): For ET_REL, make sure extra sections'
+ relocations have been applied to the debug file if dwfl_module_getelf
+ has been used before.
+
+ * relocate.c (resolve_symbol): New function.
+ (relocate_section): Call it.
+
+ * relocate.c (relocate_getsym): Handle null MOD->symfile.
+ (relocate_section): Take new bool arg, PARTIAL. If true,
+ no error for BADRELTYPE/RELUNDEF, instead just skip them
+ and leave only those skipped relocs behind the reloc section.
+ (__libdwfl_relocate_section): Take new arg, pass it down.
+ (__libdwfl_relocate): Take new bool arg, DEBUG. If false,
+ do partial relocation on all sections.
+ * dwfl_module_getdwarf.c (load_dw): Update caller.
+ * libdwflP.h: Update decls.
+ * derelocate.c (dwfl_module_address_section): Pass new argument
+ to __libdwfl_relocate_section, true.
+
+ * derelocate.c (cache_sections): Don't cache reloc sections when
+ section_address callback is null.
+
+2007-10-19 Roland McGrath <roland@redhat.com>
+
+ * relocate.c (relocate_section): Fix fencepost error in r_offset check.
+
+ * derelocate.c (struct dwfl_relocation): Add member `relocs'.
+ (struct secref): Likewise.
+ (cache_sections): Cache the relocation section referring to each
+ section we cache, if any.
+ (dwfl_module_address_section): Use __libdwfl_relocate_section as
+ necessary.
+
+ * relocate.c (struct reloc_symtab_cache): New type.
+ (relocate_getsym): Use it instead of four arguments.
+ (__libdwfl_relocate): Update caller.
+ (relocate_section): New function, broken out of ...
+ (__libdwfl_relocate): ... here.
+ (__libdwfl_relocate_section): New function.
+ * libdwflP.h: Declare it.
+
+2007-10-17 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_getsym.c (dwfl_module_getsym): Apply MOD->symfile->bias
+ to relocated st_value.
+
+ * dwfl_report_elf.c (__libdwfl_report_elf): Align initial BASE for
+ ET_REL to 0x100.
+
+2007-10-16 Roland McGrath <roland@redhat.com>
+
+ * dwfl_report_elf.c (__libdwfl_report_elf): Readjust BASE when a later
+ section has larger alignment requirements not met by the original BASE,
+ rather than padding more between sections.
+
+ * dwfl_report_elf.c (__libdwfl_report_elf): Fix bias calculation.
+
+ * dwfl_module_build_id.c (__libdwfl_find_build_id): Apply module bias
+ to sh_addr value.
+
+ * dwfl_report_elf.c (__libdwfl_report_elf): Don't be confused by BASE
+ at zero in ET_REL case. Adjust BASE to necessary alignment.
+
+ * dwfl_module_build_id.c (check_notes): Take -1, not 0, as stub value
+ for DATA_VADDR.
+ (__libdwfl_find_build_id): Update caller.
+
+ * relocate.c (__libdwfl_relocate_value): Don't use sh_offset.
+ * dwfl_report_elf.c (__libdwfl_report_elf): Likewise.
+ * offline.c (dwfl_offline_section_address): Bail early if there is
+ separate debug file.
+
+ * relocate.c (__libdwfl_relocate): Don't return DWFL_E_NO_DWARF.
+
+2007-10-09 Roland McGrath <roland@redhat.com>
+
+ * dwfl_report_elf.c (__libdwfl_report_elf): Clear SHDR->sh_offset when
+ caching SHDR->sh_addr = 0.
+ * offline.c (dwfl_offline_section_address): Never called for sh_addr
+ really at 0, don't check for it. Use MOD->debug directly, not symfile.
+
+ * dwfl_module_getdwarf.c (load_symtab): Return success properly when
+ we've found SHT_SYMTAB.
+
+ * relocate.c (relocate_getsym): New function.
+ (__libdwfl_relocate): Use it.
+ (__libdwfl_relocate_value): Take new Elf * argument. Make SYMSHSTRNDX
+ be a pointer instead of value; cache getshstrndx result there.
+ * libdwflP.h: Update decl.
+ * derelocate.c (cache_sections): Update caller.
+ Always work on the main file, not the symfile.
+ (dwfl_module_address_section): Likewise.
+ * dwfl_module_getsym.c (dwfl_module_getsym): Update caller.
+
+2007-10-07 Roland McGrath <roland@redhat.com>
+
+ * offline.c (process_archive): Initialize MOD.
+
+ * linux-kernel-modules.c (get_release): New function, broken out of ...
+ (report_kernel): ... here. Call it.
+ (try_kernel_name): Take new arg TRY_DEBUG, only try ".debug" if set.
+ (find_kernel_elf): Update caller.
+ (report_kernel_archive): New function.
+ (dwfl_linux_kernel_report_offline): Call it.
+
+ * offline.c (process_file): Take new arg PREDICATE, pass it down.
+ (process_archive): Likewise.
+ (process_archive_member): Likewise. When nonnull, let the predicate
+ decide whether to use this member.
+ (__libdwfl_report_offline): New function, broken out of ...
+ (dwfl_report_offline): ... here. Call it.
+ * libdwflP.h: Declare it.
+
+ * offline.c (process_archive, process_archive_member): New functions.
+ (process_elf, process_file): New functions, broken out of ...
+ (dwfl_report_offline): ... here. Call process_file, which recurses on
+ ELF_K_AR files.
+
+ * dwfl_report_elf.c (__libdwfl_report_elf): New, broken out of ...
+ (dwfl_report_elf): ... here. Call it.
+ * libdwflP.h: Declare it.
+
+2007-10-06 Roland McGrath <roland@redhat.com>
+
+ * derelocate.c (dwfl_module_relocations): Don't call
+ dwfl_module_getdwarf.
+
+ * derelocate.c (find_section): Use __libdwfl_seterrno, not
+ __libdw_seterrno.
+
+ * relocate.c (__libdwfl_relocate_value): Abuse sh_offset, not
+ SHF_ALLOC, to cache sh_addr resolved to 0.
+
+ * dwfl_report_elf.c (dwfl_report_elf): When an ET_REL file has sh_addr
+ values nonzero already, just use its existing layout.
+
+ * relocate.c (__libdwfl_relocate): Clear size of reloc section in its
+ in-core shdr after applying it.
+
+2007-10-04 Ulrich Drepper <drepper@redhat.com>
+
+ * linux-kernel-modules.c (dwfl_linux_kernel_report_kernel): Fake
+ initialization of notes variable.
+
+2007-10-04 Roland McGrath <roland@redhat.com>
+
+ * linux-kernel-modules.c (intuit_kernel_bounds): Take new arg NOTES,
+ fill in with vaddr of "__start_notes" symbol if found.
+ (check_notes): New function.
+ (check_kernel_notes): New function.
+ (dwfl_linux_kernel_report_kernel): Call it.
+ (check_module_notes): New function.
+ (dwfl_linux_kernel_report_modules): Call it.
+
+ * linux-kernel-modules.c (dwfl_linux_kernel_find_elf):
+ Try dwfl_build_id_find_elf first.
+
+ * linux-kernel-modules.c (report_kernel): Don't leak FD if !REPORT.
+ Set kernel module e_type to ET_DYN.
+
+2007-10-03 Roland McGrath <roland@redhat.com>
+
+ * find-debuginfo.c (validate): New function, broken out of ...
+ (find_debuginfo_in_path): ... here. New function, broken out of ...
+ (dwfl_standard_find_debuginfo): ... here. Call it, after trying
+ dwfl_build_id_find_debuginfo first.
+
+ * dwfl_build_id_find_elf.c: New file.
+ * dwfl_build_id_find_debuginfo.c: New file.
+ * Makefile.am (libdwfl_a_SOURCES): Add them.
+ * libdwfl.h: Declare them.
+ * libdwflP.h: Add INTDECLs.
+
+ * dwfl_module_build_id.c: New file.
+ * dwfl_module_report_build_id.c: New file.
+ * Makefile.am (libdwfl_a_SOURCES): Add them.
+ * libdwfl.h: Declare them.
+ * libdwflP.h (struct Dwfl_Module): New members build_id_bits,
+ build_id_len, build_id_vaddr. Declare __libdwfl_find_build_id.
+ * dwfl_module.c (__libdwfl_module_free): Free MOD->build_id_bits.
+
+ * dwfl_module_getdwarf.c (find_offsets): New function.
+ (find_dynsym): New function, calls that.
+ (find_symtab): Call it.
+
+2007-09-11 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_addrsym.c: Prefer a later global symbol at the same
+ address if its st_size is smaller.
+
+2007-08-13 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_addrsym.c: Add dead initializer for stupid compiler.
+
+2007-08-12 Roland McGrath <roland@redhat.com>
+
+ * linux-kernel-modules.c (dwfl_linux_kernel_report_offline): Don't use
+ FTS_LOGICAL.
+
+ * elf-from-memory.c (elf_from_remote_memory): Don't reset LOADBASE on
+ a second phdr if it happens to match EHDR_VMA exactly.
+
+2007-08-08 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_addrsym.c: Don't use STT_SECTION, STT_FILE symbols and
+ those with no names. Rewrite best symbol algorithm not to assume a
+ sorted table and to be smarter handling sizeless symbols.
+
+2007-07-16 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module.c (dwfl_report_module): Increment DWFL->nmodules when
+ reviving an existing module.
+
+2007-06-08 Roland McGrath <roland@redhat.com>
+
+ * libdwflP.h: Fix #ifndef for config.h to use PACKAGE_NAME.
+
+2007-05-17 Roland McGrath <roland@redhat.com>
+
+ * linux-kernel-modules.c (dwfl_linux_kernel_report_offline): Look at
+ whole /lib/modules/VERSION tree, not just /lib/modules/VERSION/kernel.
+ (dwfl_linux_kernel_find_elf): Likewise.
+
+ * linux-kernel-modules.c (dwfl_linux_kernel_report_modules): Use
+ getline and sscanf instead of fscanf.
+
+2007-05-08 Roland McGrath <roland@redhat.com>
+
+ * offline.c (dwfl_offline_section_address): Don't assume section
+ numbers match between stripped and debuginfo files. Instead, assume
+ only that the ordering among SHF_ALLOC sections matches.
+
+ * linux-kernel-modules.c (report_kernel): Change RELEASE argument to
+ pointer to string.
+ (dwfl_linux_kernel_report_offline): Update caller.
+ (dwfl_linux_kernel_report_kernel): Likewise.
+
+2007-04-23 Roland McGrath <roland@redhat.com>
+
+ * argp-std.c (options): Fix group title string.
+
+ * argp-std.c (parse_opt): Handle ARGP_KEY_ERROR, free the Dwfl.
+ Update via STATE->input every time we set STATE->hook, not only at
+ ARGP_KEY_SUCCESS.
+
+ * dwfl_module.c (free_file): Free FILE->name.
+
+2007-04-16 Roland McGrath <roland@redhat.com>
+
+ * derelocate.c (cache_sections): Apply bias to sh_addr.
+ (compare_secrefs): Fix address comparison to avoid signed overflow.
+ (find_section): New function, broken out of ...
+ (dwfl_module_relocate_address): ... here, call it.
+ (check_module): New function, broken out of ...
+ (dwfl_module_relocate_address): ... here, call it.
+ (dwfl_module_address_section): New function.
+ * libdwfl.h: Declare it.
+
+2007-03-26 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module.c (__libdwfl_module_free): Free MOD itself.
+
+2007-03-18 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_getdwarf.c (find_debuglink): New function, broken out of
+ (find_debuginfo): ... here. Call it.
+ Don't return error for libelf errors finding .gnu_debuglink section.
+
+2007-03-12 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module.c (dwfl_report_begin_add): New function broken out of ...
+ (dwfl_report_begin): ... here. Call it.
+ * libdwfl.h: Declare it.
+ * libdwflP.h: Add INTDECL.
+
+ * elf-from-memory.c (elf_from_remote_memory): Fix 32/64 typo.
+
+ * offline.c: Comment typo fix.
+
+2007-03-04 Roland McGrath <roland@redhat.com>
+
+ * linux-kernel-modules.c (KERNEL_MODNAME): New macro for "kernel".
+ (find_kernel_elf): New function, broken out of ...
+ (report_kernel): ... here. Call it.
+ (dwfl_linux_kernel_find_elf): Use it for module named KERNEL_MODNAME.
+ (intuit_kernel_bounds): New function, grovel /proc/kallsyms to guess
+ virtual address bounds of kernel from symbols rounded to page size.
+ (dwfl_linux_kernel_report_kernel): Use that if it works, before
+ resorting to report_kernel.
+
+ * dwfl_module_getdwarf.c (open_elf): Set MOD->e_type to ET_DYN for an
+ ET_EXEC file with nonzero bias.
+
+ * dwfl_module_addrname.c (dwfl_module_addrname): Just call
+ dwfl_module_addrsym. Guts moved to ...
+ * dwfl_module_addrsym.c: ... here; new file.
+ * Makefile.am (libdwfl_a_SOURCES): Add it.
+ * libdwfl.h: Declare dwfl_module_addrsym.
+ * libdwflP.h: Add INTDECL.
+
+2007-03-03 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module.c (free_file): New function, broken out of ...
+ (__libdwfl_module_free): ... here. In it, close fd after elf_end.
+
+ * dwfl_module_getdwarf.c (open_elf): Close fd and reset to -1
+ on libelf failure.
+
+2007-03-02 Roland McGrath <roland@redhat.com>
+
+ * linux-kernel-modules.c: Fix bogus error test for asprintf call.
+
+2007-02-02 Roland McGrath <roland@redhat.com>
+
+ * dwfl_addrmodule.c (dwfl_addrmodule): Match a module's high boundary
+ address exactly if it's no other module's low boundary.
+
+ * dwfl_module_addrname.c (dwfl_module_addrname): If no symbol's value
+ and size cover the address, select the closest symbol with st_size==0
+ that lies in the same section.
+
+2007-01-29 Roland McGrath <roland@redhat.com>
+
+ * dwfl_version.c (dwfl_version): Return PACKAGE_VERSION,
+ not PACKAGE_STRING.
+
+2007-01-20 Roland McGrath <roland@redhat.com>
+
+ * relocate.c (__libdwfl_relocate_value): Treat section_address of -1
+ as omitted, not 0.
+ * libdwfl.h (Dwfl_Callbacks): Update comment.
+ * derelocate.c (cache_sections): Don't ignore sh_addr == 0 sections.
+ * linux-kernel-modules.c (dwfl_linux_kernel_module_section_address):
+ For ignored missing section, use -1 instead of 0.
+ * offline.c (dwfl_offline_section_address): Expect a call for 0.
+
+2007-01-19 Roland McGrath <roland@redhat.com>
+
+ * argp-std.c (parse_opt): For -e, reset DWFL->offline_next_address to
+ zero so a lone -e foo.so is shown without address bias.
+
+2007-01-10 Roland McGrath <roland@redhat.com>
+
+ * linux-kernel-modules.c (report_kernel): Check asprintf return value
+ directly instead of via side effect, to silence warn_unused_result.
+ (dwfl_linux_kernel_report_offline): Likewise.
+ (dwfl_linux_kernel_find_elf): Likewise.
+ (dwfl_linux_kernel_module_section_address): Likewise.
+ * find-debuginfo.c (try_open): Likewise.
+ * linux-proc-maps.c (find_sysinfo_ehdr): Likewise.
+ (dwfl_linux_proc_report): Likewise.
+
+ * libdwfl.h (dwfl_begin): Require nonnull argument.
+
+2006-12-27 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module.c (compare_modules): Fix address comparison to avoid
+ signed overflow. Patch by Frank Ch. Eigler <fche@redhat.com>.
+
+2006-10-30 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module.c (dwfl_report_module): Comment typo fix.
+
+2006-09-05 Roland McGrath <roland@redhat.com>
+
+ * derelocate.c (cache_sections): Use alloca instead of variable-sized
+ auto array, in function already using alloca.
+
+2006-08-14 Roland McGrath <roland@redhat.com>
+
+ * linux-kernel-modules.c (try_kernel_name): If the call to
+ dwfl_standard_find_debuginfo produces no results, try it again
+ with NULL as DEBUGLINK_FILE to try *FNAME with .debug suffix.
+
+ * find-debuginfo.c (DEFAULT_DEBUGINFO_PATH): Macro moved ...
+ * libdwflP.h: ... to here.
+ * linux-kernel-modules.c (try_kernel_name): Skip manual open if it
+ repeats the first thing dwfl_standard_find_debuginfo will try.
+
+ * linux-kernel-modules.c (MODULE_SECT_NAME_LEN): New macro.
+ (dwfl_linux_kernel_module_section_address): If a /sys file is missing
+ and the section name is >= MODULE_SECT_NAME_LEN, try truncating the
+ section name.
+
+2006-07-12 Ulrich Drepper <drepper@redhat.com>
+
+ * cu.c: Adjust for internal_function_def removal.
+ * dwfl_error.c: Likewise.
+ * dwfl_module.c: Likewise.
+ * dwfl_module_getdwarf.c: Likewise.
+ * lines.c: Likewise.
+ * relocate.c: Likewise.
+
+2006-07-11 Ulrich Drepper <drepper@redhat.com>
+
+ * dwfl_module.c (compare_modules): Don't return GElf_Sxword value,
+ it can overflow the return value type.
+ Patch by Tim Moore <timoore@redhat.com>.
+
+2006-06-28 Roland McGrath <roland@redhat.com>
+
+ * libdwfl.h: Cosmetic changes.
+
+ * dwfl_line_comp_dir.c: New file.
+ * Makefile.am (libdwfl_a_SOURCES): Add it.
+ * libdwfl.h: Declare dwfl_line_comp_dir.
+
+ * dwfl_lineinfo.c (dwfl_lineinfo): Remove stray extern in defn.
+
+ * dwfl_linecu.c: New file.
+ * Makefile.am (libdwfl_a_SOURCES): Add it.
+ * libdwfl.h: Declare dwfl_linecu.
+
+ * libdwflP.h (dwfl_linecu_inline): Function renamed from dwfl_linecu.
+ (dwfl_linecu): Define as macro.
+
+ * relocate.c (__libdwfl_relocate): Use dwfl_module_getsym.
+
+ * dwfl_module_getdwarf.c (dwfl_module_getsymtab): New function.
+ (dwfl_module_addrname): Function moved ...
+ * dwfl_module_addrname.c: ... here, new file.
+ * dwfl_module_getsym.c: New file.
+ * Makefile.am (libdwfl_a_SOURCES): Add them.
+ * libdwfl.h: Declare dwfl_module_getsymtab, dwfl_module_getsym.
+ * libdwflP.h: Add INTDECLs.
+
+2006-06-27 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module.c (dwfl_report_end): Whitespace fix.
+
+2006-06-13 Roland McGrath <roland@redhat.com>
+
+ * elf-from-memory.c (elf_from_remote_memory): Fix 32/64 typo.
+ Use __libdwfl_seterrno for elf_memory failure.
+
+2006-05-22 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_return_value_location.c
+ (dwfl_module_return_value_location): Use __libdwfl_module_getebl.
+
+2006-05-27 Ulrich Drepper <drepper@redhat.com>
+
+ * libdwfl.h: Add extern "C".
+
+2006-05-22 Ulrich Drepper <drepper@redhat.com>
+
+ * cu.c (addrarange): Handle files without aranges information.
+
+2006-05-16 Ulrich Drepper <drepper@redhat.com>
+
+ * dwfl_addrmodule.c (dwfl_addrmodule): Also return NULL of
+ ->modules is NULL.
+
+2006-02-26 Roland McGrath <roland@redhat.com>
+
+ * dwfl_version.c: New file.
+ * Makefile.am (libdwfl_a_SOURCES): Add it.
+ * libdwfl.h: Declare dwfl_version.
+
+ * offline.c (dwfl_report_offline): Account for dwfl_report_elf having
+ aligned up from DWFL->offline_next_address when checking for overlap.
+
+2005-12-22 Roland McGrath <roland@redhat.com>
+
+ * argp-std.c (parse_opt): Call dwfl_end in failure cases.
+
+ * linux-proc-maps.c (proc_maps_report): New function, broken out of ...
+ (dwfl_linux_proc_report): ... here. Call it.
+ (dwfl_linux_proc_maps_report): New function.
+ * libdwfl.h: Declare it.
+ * libdwflP.h: Add INTDECL.
+ * argp-std.c (options, parse_opt): Grok -M/--linux-process-map.
+
+ * dwfl_nextcu.c (dwfl_nextcu): Don't fail when dwfl_module_getdwarf
+ failed with DWFL_E_NO_DWARF.
+
+2005-11-26 Roland McGrath <roland@redhat.com>
+
+ * dwfl_end.c (dwfl_end): Free the DWFL itself.
+
+2005-11-25 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_getdwarf.c (__libdwfl_module_getebl): New function.
+ (load_dw): Use it.
+ * dwfl_module_register_names.c (dwfl_module_register_names): Likewise.
+ * libdwflP.h: Declare it.
+
+ * dwfl_module_register_names.c: New file.
+ * Makefile.am (libdwfl_a_SOURCES): Add it.
+ * libdwfl.h: Declare dwfl_module_register_names.
+
+2005-11-21 Roland McGrath <roland@redhat.com>
+
+ * linux-kernel-modules.c (dwfl_linux_kernel_module_section_address):
+ Don't leak malloc'd file name.
+ If a /sys/.../sections file is missing and starts with ".init",
+ try the variant with "_init" too; catches PPC64 kernel braindamage.
+
+2005-11-15 Roland McGrath <roland@redhat.com>
+
+ * libdwfl.h: Comment fixes.
+
+ * dwfl_module_return_value_location.c: Add unlikely for error case.
+
+2005-11-13 Roland McGrath <roland@redhat.com>
+
+ * dwfl_return_value_location.c: New file.
+ * Makefile.am (libdwfl_a_SOURCES): Add it.
+ * libdwfl.h: Declare dwfl_module_return_value_location.
+ * libdwflP.h (DWFL_ERRORS): Add DWFL_E_WEIRD_TYPE.
+
+2005-10-20 Roland McGrath <roland@redhat.com>
+
+ * libdwflP.h (DWFL_ERRORS): New error UNKNOWN_MACHINE.
+ * relocate.c (__libdwfl_relocate): Return DWFL_E_UNKNOWN_MACHINE
+ instead of DWFL_E_BADRELTYPE if ebl_get_elfmachine yields EM_NONE.
+
+2005-10-01 Roland McGrath <roland@redhat.com>
+
+ * linux-kernel-modules.c (report_kernel): Return ENOENT if we fail
+ with errno 0.
+
+2005-09-19 Roland McGrath <roland@redhat.com>
+
+ * linux-kernel-modules.c (dwfl_linux_kernel_report_modules): Use
+ PRIx64 instead of PRIi64, lest addresses with high bits set overflow
+ the signed integer reading; they will just have to be in hexadecimal.
+ (dwfl_linux_kernel_module_section_address): Likewise.
+
+2005-08-28 Ulrich Drepper <drepper@redhat.com>
+
+ * Makefile.am (%.os): Use COMPILE.os.
+ (COMPILE.os): Filter out gconv options.
+
+2005-08-25 Roland McGrath <roland@redhat.com>
+
+ * cu.c (__libdwfl_nextcu): Return success when dwarf_nextcu hits end.
+ * dwfl_nextcu.c (dwfl_nextcu): Skip modules with no dwarf info.
+
+2005-08-24 Roland McGrath <roland@redhat.com>
+
+ * dwfl_lineinfo.c (dwfl_lineinfo): Add bias, don't subtract it.
+
+ * argp-std.c [_MUDFLAP] (__libdwfl_argp_mudflap_options): New function,
+ magic initializer to set -heur-stack-bound option.
+
+2005-08-22 Roland McGrath <roland@redhat.com>
+
+ * dwfl_validate_address.c: New file.
+ * Makefile.am (libdwfl_a_SOURCES): Add it.
+ * libdwfl.h: Declare dwfl_validate_address.
+
+ * derelocate.c (dwfl_module_relocate_address): Add INTDEF.
+ * libdwflP.h: Add INTDECL.
+
+ * dwfl_module_getdwarf.c (find_symtab): Use elf_getdata instead of
+ elf_rawdata for symbol-related sections.
+
+ * offline.c (dwfl_report_offline): Move offline_next_address outside
+ module's range, in case it's an ET_EXEC using fixed segment locations.
+ * libdwfl.h: Update comment.
+
+ * dwfl_report_elf.c (dwfl_report_elf): Align BASE to first segment's
+ required alignment.
+
+2005-08-20 Roland McGrath <roland@redhat.com>
+
+ * linux-kernel-modules.c (report_kernel): Take new argument PREDICATE,
+ function to choose whether to report.
+ (dwfl_linux_kernel_report_offline): Likewise.
+ * libdwfl.h: Update decl.
+ * argp-std.c (parse_opt): Update caller.
+
+ * dwfl_getsrclines.c: New file.
+ * dwfl_onesrcline.c: New file.
+ * Makefile.am (libdwfl_a_SOURCES): Add them.
+ * libdwfl.h: Declare dwfl_getsrclines, dwfl_onesrcline.
+
+ * linux-kernel-modules.c (dwfl_linux_kernel_find_elf): Don't leak
+ MODULESDIR[0]. Call fts_close on failure.
+
+ * dwfl_module_getdwarf.c (load_dw): Take dwfl_file * instead of Elf *.
+ Close ET_REL file descriptors after relocation.
+ (find_dw): Update caller.
+ * offline.c (dwfl_report_offline): Get the file into memory and close
+ the file descriptor.
+
+ * dwfl_module_getdwarf.c (find_debuginfo): Do nothing when
+ MOD->debug.elf is already set.
+
+ * find-debuginfo.c (try_open): Use TEMP_FAILURE_RETRY.
+ (dwfl_standard_find_debuginfo): Fail on errors not ENOENT or ENOTDIR.
+
+ * argp-std.c (options, parse_opt): Grok -K/--offline-kernel, use
+ dwfl_linux_kernel_report_offline with offline_callbacks.
+
+ * linux-kernel-modules.c (report_kernel): New function, broken out of
+ ...
+ (dwfl_linux_kernel_report_kernel): ... here. Use it.
+ (dwfl_linux_kernel_report_offline): New function.
+ * libdwfl.h: Declare it.
+ * libdwflP.h: Add INTDECL.
+
+2005-08-19 Roland McGrath <roland@redhat.com>
+
+ Use standard debuginfo search path to look for vmlinux.
+ * find-debuginfo.c (dwfl_standard_find_debuginfo): Don't check CRC if
+ passed zero.
+ * linux-kernel-modules.c (try_kernel_name): New function, broken out
+ of ...
+ (dwfl_linux_kernel_report_kernel): ... here. Use it.
+
+ * argp-std.c (offline_callbacks): New variable.
+ (parse_opt): Use it for -e. Allow multiple -e options.
+
+ * offline.c: New file.
+ * Makefile.am (libdwfl_a_SOURCES): Add it.
+ * libdwfl.h: Declare dwfl_offline_section_address, dwfl_report_offline.
+ * libdwflP.h: Add INTDECLs.
+ (OFFLINE_REDZONE): New macro.
+ (struct Dwfl): New member `offline_next_address'.
+ * dwfl_begin.c (dwfl_begin): Initialize it.
+ * dwfl_module.c (dwfl_report_begin): Likewise.
+
+ * dwfl_report_elf.c (dwfl_report_elf): Accept all types. When ET_REL,
+ do a nominal absolute section layout starting at BASE.
+ * libdwfl.h: Update comment.
+
+2005-08-18 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_getsrc_file.c (dwfl_module_getsrc_file): Do
+ dwfl_module_getdwarf if necessary.
+
+ * dwfl_report_elf.c (dwfl_report_elf): Permit ET_REL with BASE==0.
+ * libdwfl.h: Update comment.
+
+ * derelocate.c: New file.
+ * Makefile.am (libdwfl_a_SOURCES): Add it.
+
+ * libdwflP.h (struct Dwfl_Module): isrel -> e_type.
+ * dwfl_report_elf.c (dwfl_report_elf): Initialize it.
+ * dwfl_module_getdwarf.c (open_elf): Update initialization.
+ (load_dw, dwfl_module_addrname): Update uses.
+ * relocate.c (__libdwfl_relocate): Likewise.
+
+2005-08-04 Roland McGrath <roland@redhat.com>
+
+ * libdwfl.h (Dwfl_Callbacks.section_address): Take additional
+ arguments SHNDX, SHDR.
+ (dwfl_linux_kernel_module_section_address): Update prototype.
+ * relocate.c (__libdwfl_relocate_value): Update caller.
+ * linux-kernel-modules.c (dwfl_linux_kernel_module_section_address):
+ Take the new arguments.
+
+2005-08-10 Roland McGrath <roland@redhat.com>
+
+ * relocate.c (__libdwfl_relocate): Take argument DEBUGFILE,
+ use it instead of MOD->debug.file.
+ * libdwflP.h: Update decl.
+ * dwfl_module_getdwarf.c (load_dw): Update caller.
+ Fixes bug #165598.
+
+2005-08-09 Roland McGrath <roland@redhat.com>
+
+ * libdwflP.h: Include ../libdw/libdwP.h for its INTDECLs.
+ * cu.c: Use INTUSE on dwarf_* calls.
+ * dwfl_error.c: Likewise.
+ * dwfl_module.c: Likewise.
+ * dwfl_module_getdwarf.c: Likewise.
+ * dwfl_module_getsrc_file.c: Likewise.
+ * lines.c: Likewise.
+
+2005-08-07 Roland McGrath <roland@redhat.com>
+
+ * linux-kernel-modules.c (dwfl_linux_kernel_find_elf): When module
+ names contain '_' or '-', look for files named either "foo-bar.ko"
+ or "foo_bar.ko".
+
+2005-07-29 Roland McGrath <roland@redhat.com>
+
+ * loc2c.c: File removed.
+ * loc2c.h: File removed.
+ * loc2c-runtime.h: File removed.
+ * test2.c: File removed.
+ * Makefile.am (EXTRA_DIST): Variable removed.
+ (noinst_HEADERS): Remove loc2c.h from here.
+
+2005-07-28 Ulrich Drepper <drepper@redhat.com>
+
+ * libdwfl.h: Add a few missing extern for function prototypes.
+
+ * libdwfl_crc32.c: New file.
+ * libdwfl_crc32_file.c: New file.
+ * libdwflP.h: Declare the new functions.
+ * Makefile.am (libdwfl_a_SOURCES): Add libdwfl_crc32.c and
+ libdwfl_crc32_file.c.
+ * libdwfl/find-debuginfo.c (check_crc): Use __libdwfl_crc32_file
+ instead of crc32_file.
+
+2005-07-28 Roland McGrath <roland@redhat.com>
+
+ * ptest.c: Moved to ../tests/dwflmodtest.c.
+
+ * Makefile.am (noinst_PROGRAMS): Variable removed.
+ (libdwfl_so_SOURCES, libdwfl_LIBS, libdwfl_so_LDADD): Likewise.
+ (EXTRA_DIST, ptest_LDADD, test2_LDADD): Likewise.
+ (libdwfl): Don't use libdwfl.so any more.
+ (libdwfl.so, install, uninstall): Targets removed.
+ (test2_SOURCES): Define EXTRA_DIST instead of this.
+ * libdwfl.map: File removed.
+
+ * libdwfl.h: Use "" for libdw.h #include.
+
+2005-07-27 Roland McGrath <roland@redhat.com>
+
+ * libdwfl.map: Add dwfl_getmodules.
+
+2005-07-23 Ulrich Drepper <drepper@redhat.com>
+
+ * Makefile.am: Fix rules to allow building with mudflap.
+
+2005-07-21 Roland McGrath <roland@redhat.com>
+
+ * Makefile.am (noinst_HEADERS): Add loc2c.c.
+
+ * test2.c (main): Check sscanf result to quiet warning.
+
+2005-07-20 Roland McGrath <roland@redhat.com>
+
+ * libdwfl-branch merged, creating this direcotry.
diff --git a/libdwfl/Makefile.am b/libdwfl/Makefile.am
new file mode 100644
index 0000000..89ca92e
--- /dev/null
+++ b/libdwfl/Makefile.am
@@ -0,0 +1,92 @@
+## Makefile.am for libdwfl library subdirectory in elfutils.
+##
+## Process this file with automake to create Makefile.in
+##
+## Copyright (C) 2005-2010, 2013 Red Hat, Inc.
+## This file is part of elfutils.
+##
+## This file is free software; you can redistribute it and/or modify
+## it under the terms of either
+##
+## * the GNU Lesser General Public License as published by the Free
+## Software Foundation; either version 3 of the License, or (at
+## your option) any later version
+##
+## or
+##
+## * the GNU General Public License as published by the Free
+## Software Foundation; either version 2 of the License, or (at
+## your option) any later version
+##
+## or both in parallel, as here.
+##
+## elfutils is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+## General Public License for more details.
+##
+## You should have received copies of the GNU General Public License and
+## the GNU Lesser General Public License along with this program. If
+## not, see <http://www.gnu.org/licenses/>.
+##
+include $(top_srcdir)/config/eu.am
+AM_CPPFLAGS += -I$(srcdir) -I$(srcdir)/../libelf -I$(srcdir)/../libebl \
+ -I$(srcdir)/../libdw -I$(srcdir)/../libdwelf
+VERSION = 1
+
+noinst_LIBRARIES = libdwfl.a
+noinst_LIBRARIES += libdwfl_pic.a
+
+pkginclude_HEADERS = libdwfl.h
+
+libdwfl_a_SOURCES = dwfl_begin.c dwfl_end.c dwfl_error.c dwfl_version.c \
+ dwfl_module.c dwfl_report_elf.c relocate.c \
+ dwfl_module_build_id.c dwfl_module_report_build_id.c \
+ derelocate.c offline.c segment.c \
+ dwfl_module_info.c dwfl_getmodules.c dwfl_getdwarf.c \
+ dwfl_module_getdwarf.c dwfl_module_getelf.c \
+ dwfl_validate_address.c \
+ argp-std.c find-debuginfo.c \
+ dwfl_build_id_find_elf.c \
+ dwfl_build_id_find_debuginfo.c \
+ linux-kernel-modules.c linux-proc-maps.c \
+ dwfl_addrmodule.c dwfl_addrdwarf.c \
+ cu.c dwfl_module_nextcu.c dwfl_nextcu.c dwfl_cumodule.c \
+ dwfl_module_addrdie.c dwfl_addrdie.c \
+ lines.c dwfl_lineinfo.c dwfl_line_comp_dir.c \
+ dwfl_linemodule.c dwfl_linecu.c dwfl_dwarf_line.c \
+ dwfl_getsrclines.c dwfl_onesrcline.c \
+ dwfl_module_getsrc.c dwfl_getsrc.c \
+ dwfl_module_getsrc_file.c \
+ libdwfl_crc32.c libdwfl_crc32_file.c \
+ elf-from-memory.c \
+ dwfl_module_dwarf_cfi.c dwfl_module_eh_cfi.c \
+ dwfl_module_getsym.c \
+ dwfl_module_addrname.c dwfl_module_addrsym.c \
+ dwfl_module_return_value_location.c \
+ dwfl_module_register_names.c \
+ dwfl_segment_report_module.c \
+ link_map.c core-file.c open.c image-header.c \
+ dwfl_frame.c frame_unwind.c dwfl_frame_pc.c \
+ linux-pid-attach.c linux-core-attach.c dwfl_frame_regs.c \
+ gzip.c
+
+if BZLIB
+libdwfl_a_SOURCES += bzip2.c
+endif
+if LZMA
+libdwfl_a_SOURCES += lzma.c
+endif
+
+libdwfl = $(libdw)
+libdw = ../libdw/libdw.so
+libelf = ../libelf/libelf.so
+libebl = ../libebl/libebl.a
+libeu = ../lib/libeu.a
+
+libdwfl_pic_a_SOURCES =
+am_libdwfl_pic_a_OBJECTS = $(libdwfl_a_SOURCES:.c=.os)
+
+noinst_HEADERS = libdwflP.h
+
+CLEANFILES += $(am_libdwfl_pic_a_OBJECTS)
diff --git a/libdwfl/argp-std.c b/libdwfl/argp-std.c
new file mode 100644
index 0000000..8ee9158
--- /dev/null
+++ b/libdwfl/argp-std.c
@@ -0,0 +1,383 @@
+/* Standard argp argument parsers for tools using libdwfl.
+ Copyright (C) 2005-2010, 2012, 2015 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include <argp.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <libintl.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+/* gettext helper macros. */
+#define _(Str) dgettext ("elfutils", Str)
+
+
+#define OPT_DEBUGINFO 0x100
+#define OPT_COREFILE 0x101
+
+static const struct argp_option options[] =
+{
+ { NULL, 0, NULL, 0, N_("Input selection options:"), 0 },
+ { "executable", 'e', "FILE", 0, N_("Find addresses in FILE"), 0 },
+ { "core", OPT_COREFILE, "COREFILE", 0,
+ N_("Find addresses from signatures found in COREFILE"), 0 },
+ { "pid", 'p', "PID", 0,
+ N_("Find addresses in files mapped into process PID"), 0 },
+ { "linux-process-map", 'M', "FILE", 0,
+ N_("Find addresses in files mapped as read from FILE"
+ " in Linux /proc/PID/maps format"), 0 },
+ { "kernel", 'k', NULL, 0, N_("Find addresses in the running kernel"), 0 },
+ { "offline-kernel", 'K', "RELEASE", OPTION_ARG_OPTIONAL,
+ N_("Kernel with all modules"), 0 },
+ { "debuginfo-path", OPT_DEBUGINFO, "PATH", 0,
+ N_("Search path for separate debuginfo files"), 0 },
+ { NULL, 0, NULL, 0, NULL, 0 }
+};
+
+static char *debuginfo_path;
+
+static const Dwfl_Callbacks offline_callbacks =
+ {
+ .find_debuginfo = INTUSE(dwfl_standard_find_debuginfo),
+ .debuginfo_path = &debuginfo_path,
+
+ .section_address = INTUSE(dwfl_offline_section_address),
+
+ /* We use this table for core files too. */
+ .find_elf = INTUSE(dwfl_build_id_find_elf),
+ };
+
+static const Dwfl_Callbacks proc_callbacks =
+ {
+ .find_debuginfo = INTUSE(dwfl_standard_find_debuginfo),
+ .debuginfo_path = &debuginfo_path,
+
+ .find_elf = INTUSE(dwfl_linux_proc_find_elf),
+ };
+
+static const Dwfl_Callbacks kernel_callbacks =
+ {
+ .find_debuginfo = INTUSE(dwfl_standard_find_debuginfo),
+ .debuginfo_path = &debuginfo_path,
+
+ .find_elf = INTUSE(dwfl_linux_kernel_find_elf),
+ .section_address = INTUSE(dwfl_linux_kernel_module_section_address),
+ };
+
+/* Structure held at state->HOOK. */
+struct parse_opt
+{
+ Dwfl *dwfl;
+ /* The -e|--executable parameter. */
+ const char *e;
+ /* The --core parameter. */
+ const char *core;
+};
+
+static inline void
+failure (Dwfl *dwfl, int errnum, const char *msg, struct argp_state *state)
+{
+ if (dwfl != NULL)
+ dwfl_end (dwfl);
+ if (errnum == -1)
+ argp_failure (state, EXIT_FAILURE, 0, "%s: %s",
+ msg, INTUSE(dwfl_errmsg) (-1));
+ else
+ argp_failure (state, EXIT_FAILURE, errnum, "%s", msg);
+}
+
+static inline error_t
+fail (Dwfl *dwfl, int errnum, const char *msg, struct argp_state *state)
+{
+ failure (dwfl, errnum, msg, state);
+ return errnum == -1 ? EIO : errnum;
+}
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case ARGP_KEY_INIT:
+ {
+ assert (state->hook == NULL);
+ struct parse_opt *opt = calloc (1, sizeof (*opt));
+ if (opt == NULL)
+ failure (NULL, DWFL_E_ERRNO, "calloc", state);
+ state->hook = opt;
+ }
+ break;
+
+ case OPT_DEBUGINFO:
+ debuginfo_path = arg;
+ break;
+
+ case 'e':
+ {
+ struct parse_opt *opt = state->hook;
+ Dwfl *dwfl = opt->dwfl;
+ if (dwfl == NULL)
+ {
+ dwfl = INTUSE(dwfl_begin) (&offline_callbacks);
+ if (dwfl == NULL)
+ return fail (dwfl, -1, arg, state);
+ opt->dwfl = dwfl;
+
+ /* Start at zero so if there is just one -e foo.so,
+ the DSO is shown without address bias. */
+ dwfl->offline_next_address = 0;
+ }
+ if (dwfl->callbacks != &offline_callbacks)
+ {
+ toomany:
+ argp_error (state, "%s",
+ _("only one of -e, -p, -k, -K, or --core allowed"));
+ return EINVAL;
+ }
+ opt->e = arg;
+ }
+ break;
+
+ case 'p':
+ {
+ struct parse_opt *opt = state->hook;
+ if (opt->dwfl == NULL)
+ {
+ Dwfl *dwfl = INTUSE(dwfl_begin) (&proc_callbacks);
+ int result = INTUSE(dwfl_linux_proc_report) (dwfl, atoi (arg));
+ if (result != 0)
+ return fail (dwfl, result, arg, state);
+
+ /* Non-fatal to not be able to attach to process, ignore error. */
+ INTUSE(dwfl_linux_proc_attach) (dwfl, atoi (arg), false);
+
+ opt->dwfl = dwfl;
+ }
+ else
+ goto toomany;
+ }
+ break;
+
+ case 'M':
+ {
+ struct parse_opt *opt = state->hook;
+ if (opt->dwfl == NULL)
+ {
+ FILE *f = fopen (arg, "r");
+ if (f == NULL)
+ {
+ int code = errno;
+ argp_failure (state, EXIT_FAILURE, code,
+ "cannot open '%s'", arg);
+ return code;
+ }
+ Dwfl *dwfl = INTUSE(dwfl_begin) (&proc_callbacks);
+ int result = INTUSE(dwfl_linux_proc_maps_report) (dwfl, f);
+ fclose (f);
+ if (result != 0)
+ return fail (dwfl, result, arg, state);
+ opt->dwfl = dwfl;
+ }
+ else
+ goto toomany;
+ }
+ break;
+
+ case OPT_COREFILE:
+ {
+ struct parse_opt *opt = state->hook;
+ Dwfl *dwfl = opt->dwfl;
+ if (dwfl == NULL)
+ opt->dwfl = dwfl = INTUSE(dwfl_begin) (&offline_callbacks);
+ /* Permit -e and --core together. */
+ else if (dwfl->callbacks != &offline_callbacks)
+ goto toomany;
+ opt->core = arg;
+ }
+ break;
+
+ case 'k':
+ {
+ struct parse_opt *opt = state->hook;
+ if (opt->dwfl == NULL)
+ {
+ Dwfl *dwfl = INTUSE(dwfl_begin) (&kernel_callbacks);
+ int result = INTUSE(dwfl_linux_kernel_report_kernel) (dwfl);
+ if (result != 0)
+ return fail (dwfl, result, _("cannot load kernel symbols"), state);
+ result = INTUSE(dwfl_linux_kernel_report_modules) (dwfl);
+ if (result != 0)
+ /* Non-fatal to have no modules since we do have the kernel. */
+ argp_failure (state, 0, result, _("cannot find kernel modules"));
+ opt->dwfl = dwfl;
+ }
+ else
+ goto toomany;
+ }
+ break;
+
+ case 'K':
+ {
+ struct parse_opt *opt = state->hook;
+ if (opt->dwfl == NULL)
+ {
+ Dwfl *dwfl = INTUSE(dwfl_begin) (&offline_callbacks);
+ int result = INTUSE(dwfl_linux_kernel_report_offline) (dwfl, arg,
+ NULL);
+ if (result != 0)
+ return fail (dwfl, result, _("cannot find kernel or modules"), state);
+ opt->dwfl = dwfl;
+ }
+ else
+ goto toomany;
+ }
+ break;
+
+ case ARGP_KEY_SUCCESS:
+ {
+ struct parse_opt *opt = state->hook;
+ Dwfl *dwfl = opt->dwfl;
+
+ if (dwfl == NULL)
+ {
+ /* Default if no -e, -p, or -k, is "-e a.out". */
+ arg = "a.out";
+ dwfl = INTUSE(dwfl_begin) (&offline_callbacks);
+ if (INTUSE(dwfl_report_offline) (dwfl, "", arg, -1) == NULL)
+ return fail (dwfl, -1, arg, state);
+ opt->dwfl = dwfl;
+ }
+
+ if (opt->core)
+ {
+ int fd = open (opt->core, O_RDONLY);
+ if (fd < 0)
+ {
+ int code = errno;
+ argp_failure (state, EXIT_FAILURE, code,
+ "cannot open '%s'", opt->core);
+ return code;
+ }
+
+ Elf *core;
+ Dwfl_Error error = __libdw_open_file (&fd, &core, true, false);
+ if (error != DWFL_E_NOERROR)
+ {
+ argp_failure (state, EXIT_FAILURE, 0,
+ _("cannot read ELF core file: %s"),
+ INTUSE(dwfl_errmsg) (error));
+ return error == DWFL_E_ERRNO ? errno : EIO;
+ }
+
+ int result = INTUSE(dwfl_core_file_report) (dwfl, core, opt->e);
+ if (result < 0)
+ {
+ elf_end (core);
+ close (fd);
+ return fail (dwfl, result, opt->core, state);
+ }
+
+ /* Non-fatal to not be able to attach to core, ignore error. */
+ INTUSE(dwfl_core_file_attach) (dwfl, core);
+
+ /* Store core Elf and fd in Dwfl to expose with dwfl_end. */
+ if (dwfl->user_core == NULL)
+ {
+ dwfl->user_core = calloc (1, sizeof (struct Dwfl_User_Core));
+ if (dwfl->user_core == NULL)
+ {
+ argp_failure (state, EXIT_FAILURE, 0,
+ _("Not enough memory"));
+ return ENOMEM;
+ }
+ }
+ dwfl->user_core->core = core;
+ dwfl->user_core->fd = fd;
+
+ if (result == 0)
+ {
+ argp_failure (state, EXIT_FAILURE, 0,
+ _("No modules recognized in core file"));
+ return ENOENT;
+ }
+ }
+ else if (opt->e)
+ {
+ if (INTUSE(dwfl_report_offline) (dwfl, "", opt->e, -1) == NULL)
+ return fail (dwfl, -1, opt->e, state);
+ }
+
+ /* One of the three flavors has done dwfl_begin and some reporting
+ if we got here. Tie up the Dwfl and return it to the caller of
+ argp_parse. */
+
+ int result = INTUSE(dwfl_report_end) (dwfl, NULL, NULL);
+ assert (result == 0);
+
+ /* Update the input all along, so a parent parser can see it.
+ As we free OPT the update below will be no longer active. */
+ *(Dwfl **) state->input = dwfl;
+ free (opt);
+ state->hook = NULL;
+ }
+ break;
+
+ case ARGP_KEY_ERROR:
+ {
+ struct parse_opt *opt = state->hook;
+ dwfl_end (opt->dwfl);
+ free (opt);
+ state->hook = NULL;
+ }
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ /* Update the input all along, so a parent parser can see it. */
+ struct parse_opt *opt = state->hook;
+ if (opt)
+ *(Dwfl **) state->input = opt->dwfl;
+
+ return 0;
+}
+
+static const struct argp libdwfl_argp =
+ { .options = options, .parser = parse_opt };
+
+const struct argp *
+dwfl_standard_argp (void)
+{
+ return &libdwfl_argp;
+}
diff --git a/libdwfl/bzip2.c b/libdwfl/bzip2.c
new file mode 100644
index 0000000..8ad4ee5
--- /dev/null
+++ b/libdwfl/bzip2.c
@@ -0,0 +1,4 @@
+/* bzlib is almost just like zlib. */
+
+#define BZLIB
+#include "gzip.c"
diff --git a/libdwfl/core-file.c b/libdwfl/core-file.c
new file mode 100644
index 0000000..84cb89a
--- /dev/null
+++ b/libdwfl/core-file.c
@@ -0,0 +1,624 @@
+/* Core file handling.
+ Copyright (C) 2008-2010, 2013, 2015 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include "../libelf/libelfP.h" /* For NOTE_ALIGN. */
+#undef _
+#include "libdwflP.h"
+#include <gelf.h>
+
+#include <unistd.h>
+#include <endian.h>
+#include <byteswap.h>
+#include "system.h"
+
+
+/* On failure return, we update *NEXT to point back at OFFSET. */
+static inline Elf *
+do_fail (int error, off_t *next, off_t offset)
+{
+ if (next != NULL)
+ *next = offset;
+ //__libelf_seterrno (error);
+ __libdwfl_seterrno (DWFL_E (LIBELF, error));
+ return NULL;
+}
+
+#define fail(error) do_fail (error, next, offset)
+
+/* This is a prototype of what a new libelf interface might be.
+ This implementation is pessimal for non-mmap cases and should
+ be replaced by more diddling inside libelf internals. */
+static Elf *
+elf_begin_rand (Elf *parent, off_t offset, off_t size, off_t *next)
+{
+ if (parent == NULL)
+ return NULL;
+
+ off_t min = (parent->kind == ELF_K_ELF ?
+ (parent->class == ELFCLASS32
+ ? sizeof (Elf32_Ehdr) : sizeof (Elf64_Ehdr))
+ : parent->kind == ELF_K_AR ? SARMAG
+ : 0);
+
+ if (unlikely (offset < min)
+ || unlikely (offset >= (off_t) parent->maximum_size))
+ return fail (ELF_E_RANGE);
+
+ /* For an archive, fetch just the size field
+ from the archive header to override SIZE. */
+ if (parent->kind == ELF_K_AR)
+ {
+ struct ar_hdr h = { .ar_size = "" };
+
+ if (unlikely (parent->maximum_size - offset < sizeof h))
+ return fail (ELF_E_RANGE);
+
+ if (parent->map_address != NULL)
+ memcpy (h.ar_size, parent->map_address + parent->start_offset + offset,
+ sizeof h.ar_size);
+ else if (unlikely (pread_retry (parent->fildes,
+ h.ar_size, sizeof (h.ar_size),
+ parent->start_offset + offset
+ + offsetof (struct ar_hdr, ar_size))
+ != sizeof (h.ar_size)))
+ return fail (ELF_E_READ_ERROR);
+
+ offset += sizeof h;
+
+ char *endp;
+ size = strtoll (h.ar_size, &endp, 10);
+ if (unlikely (endp == h.ar_size)
+ || unlikely ((off_t) parent->maximum_size - offset < size))
+ return fail (ELF_E_INVALID_ARCHIVE);
+ }
+
+ if (unlikely ((off_t) parent->maximum_size - offset < size))
+ return fail (ELF_E_RANGE);
+
+ /* Even if we fail at this point, update *NEXT to point past the file. */
+ if (next != NULL)
+ *next = offset + size;
+
+ if (unlikely (offset == 0)
+ && unlikely (size == (off_t) parent->maximum_size))
+ return elf_clone (parent, parent->cmd);
+
+ /* Note the image is guaranteed live only as long as PARENT
+ lives. Using elf_memory is quite suboptimal if the whole
+ file is not mmap'd. We really should have something like
+ a generalization of the archive support. */
+ Elf_Data *data = elf_getdata_rawchunk (parent, offset, size, ELF_T_BYTE);
+ if (data == NULL)
+ return NULL;
+ assert ((off_t) data->d_size == size);
+ return elf_memory (data->d_buf, size);
+}
+
+
+int
+dwfl_report_core_segments (Dwfl *dwfl, Elf *elf, size_t phnum, GElf_Phdr *notes)
+{
+ if (unlikely (dwfl == NULL))
+ return -1;
+
+ int result = 0;
+
+ if (notes != NULL)
+ notes->p_type = PT_NULL;
+
+ for (size_t ndx = 0; result >= 0 && ndx < phnum; ++ndx)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (elf, ndx, &phdr_mem);
+ if (unlikely (phdr == NULL))
+ {
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ return -1;
+ }
+ switch (phdr->p_type)
+ {
+ case PT_LOAD:
+ result = dwfl_report_segment (dwfl, ndx, phdr, 0, NULL);
+ break;
+
+ case PT_NOTE:
+ if (notes != NULL)
+ {
+ *notes = *phdr;
+ notes = NULL;
+ }
+ break;
+ }
+ }
+
+ return result;
+}
+
+/* Never read more than this much without mmap. */
+#define MAX_EAGER_COST 8192
+
+/* Dwfl_Module_Callback passed to and called by dwfl_segment_report_module
+ to read in a segment as ELF image directly if possible or indicate an
+ attempt must be made to read in the while segment right now. */
+static bool
+core_file_read_eagerly (Dwfl_Module *mod,
+ void **userdata __attribute__ ((unused)),
+ const char *name __attribute__ ((unused)),
+ Dwarf_Addr start __attribute__ ((unused)),
+ void **buffer, size_t *buffer_available,
+ GElf_Off cost, GElf_Off worthwhile,
+ GElf_Off whole,
+ GElf_Off contiguous __attribute__ ((unused)),
+ void *arg, Elf **elfp)
+{
+ Elf *core = arg;
+
+ /* The available buffer is often the whole segment when the core file
+ was mmap'd if used together with the dwfl_elf_phdr_memory_callback.
+ Which means that if it is complete we can just construct the whole
+ ELF image right now without having to read in anything more. */
+ if (whole <= *buffer_available)
+ {
+ /* All there ever was, we already have on hand. */
+
+ if (core->map_address == NULL)
+ {
+ /* We already malloc'd the buffer. */
+ *elfp = elf_memory (*buffer, whole);
+ if (unlikely (*elfp == NULL))
+ return false;
+
+ (*elfp)->flags |= ELF_F_MALLOCED;
+ *buffer = NULL;
+ *buffer_available = 0;
+ return true;
+ }
+
+ /* We can use the image inside the core file directly. */
+ *elfp = elf_begin_rand (core, *buffer - core->map_address, whole, NULL);
+ *buffer = NULL;
+ *buffer_available = 0;
+ return *elfp != NULL;
+ }
+
+ /* We don't have the whole file. Which either means the core file
+ wasn't mmap'd, but needs to still be read in, or that the segment
+ is truncated. Figure out if this is better than nothing. */
+
+ if (worthwhile == 0)
+ /* Caller doesn't think so. */
+ return false;
+
+ /*
+ XXX would like to fall back to partial file via memory
+ when build id find_elf fails
+ also, link_map name may give file name from disk better than partial here
+ requires find_elf hook re-doing the magic to fall back if no file found
+ */
+
+ if (whole > MAX_EAGER_COST && mod->build_id_len > 0)
+ /* We can't cheaply read the whole file here, so we'd
+ be using a partial file. But there is a build ID that could
+ help us find the whole file, which might be more useful than
+ what we have. We'll just rely on that. */
+ return false;
+
+ /* The file is either small (most likely the vdso) or big and incomplete,
+ but we don't have a build-id. */
+
+ if (core->map_address != NULL)
+ /* It's cheap to get, so get it. */
+ return true;
+
+ /* Only use it if there isn't too much to be read. */
+ return cost <= MAX_EAGER_COST;
+}
+
+static inline void
+update_end (GElf_Phdr *pphdr, const GElf_Off align,
+ GElf_Off *pend, GElf_Addr *pend_vaddr)
+{
+ *pend = (pphdr->p_offset + pphdr->p_filesz + align - 1) & -align;
+ *pend_vaddr = (pphdr->p_vaddr + pphdr->p_memsz + align - 1) & -align;
+}
+
+/* Use following contiguous segments to get towards SIZE. */
+static inline bool
+do_more (size_t size, GElf_Phdr *pphdr, const GElf_Off align,
+ Elf *elf, GElf_Off start, int *pndx,
+ GElf_Off *pend, GElf_Addr *pend_vaddr)
+{
+ while (*pend <= start || *pend - start < size)
+ {
+ if (pphdr->p_filesz < pphdr->p_memsz)
+ /* This segment is truncated, so no following one helps us. */
+ return false;
+
+ if (unlikely (gelf_getphdr (elf, (*pndx)++, pphdr) == NULL))
+ return false;
+
+ if (pphdr->p_type == PT_LOAD)
+ {
+ if (pphdr->p_offset > *pend
+ || pphdr->p_vaddr > *pend_vaddr)
+ /* It's discontiguous! */
+ return false;
+
+ update_end (pphdr, align, pend, pend_vaddr);
+ }
+ }
+ return true;
+}
+
+#define more(size) do_more (size, &phdr, align, elf, start, &ndx, &end, &end_vaddr)
+
+bool
+dwfl_elf_phdr_memory_callback (Dwfl *dwfl, int ndx,
+ void **buffer, size_t *buffer_available,
+ GElf_Addr vaddr,
+ size_t minread,
+ void *arg)
+{
+ Elf *elf = arg;
+
+ if (ndx == -1)
+ {
+ /* Called for cleanup. */
+ if (elf->map_address == NULL)
+ free (*buffer);
+ *buffer = NULL;
+ *buffer_available = 0;
+ return false;
+ }
+
+ const GElf_Off align = dwfl->segment_align ?: 1;
+ GElf_Phdr phdr;
+
+ do
+ if (unlikely (gelf_getphdr (elf, ndx++, &phdr) == NULL))
+ return false;
+ while (phdr.p_type != PT_LOAD
+ || ((phdr.p_vaddr + phdr.p_memsz + align - 1) & -align) <= vaddr);
+
+ GElf_Off start = vaddr - phdr.p_vaddr + phdr.p_offset;
+ GElf_Off end;
+ GElf_Addr end_vaddr;
+
+ update_end (&phdr, align, &end, &end_vaddr);
+
+ /* We need at least this much. */
+ if (! more (minread))
+ return false;
+
+ /* See how much more we can get of what the caller wants. */
+ (void) more (*buffer_available);
+
+ /* If it's already on hand anyway, use as much as there is. */
+ if (elf->map_address != NULL)
+ (void) more (elf->maximum_size - start);
+
+ /* Make sure we don't look past the end of the actual file,
+ even if the headers tell us to. */
+ if (unlikely (end > elf->maximum_size))
+ end = elf->maximum_size;
+
+ /* If the file is too small, there is nothing at all to get. */
+ if (unlikely (start >= end))
+ return false;
+
+ if (elf->map_address != NULL)
+ {
+ void *contents = elf->map_address + elf->start_offset + start;
+ size_t size = end - start;
+
+ if (minread == 0) /* String mode. */
+ {
+ const void *eos = memchr (contents, '\0', size);
+ if (unlikely (eos == NULL) || unlikely (eos == contents))
+ return false;
+ size = eos + 1 - contents;
+ }
+
+ if (*buffer == NULL)
+ {
+ *buffer = contents;
+ *buffer_available = size;
+ }
+ else
+ {
+ *buffer_available = MIN (size, *buffer_available);
+ memcpy (*buffer, contents, *buffer_available);
+ }
+ }
+ else
+ {
+ void *into = *buffer;
+ if (*buffer == NULL)
+ {
+ *buffer_available = MIN (minread ?: 512,
+ MAX (4096, MIN (end - start,
+ *buffer_available)));
+ into = malloc (*buffer_available);
+ if (unlikely (into == NULL))
+ {
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return false;
+ }
+ }
+
+ ssize_t nread = pread_retry (elf->fildes, into, *buffer_available, start);
+ if (nread < (ssize_t) minread)
+ {
+ if (into != *buffer)
+ free (into);
+ if (nread < 0)
+ __libdwfl_seterrno (DWFL_E_ERRNO);
+ return false;
+ }
+
+ if (minread == 0) /* String mode. */
+ {
+ const void *eos = memchr (into, '\0', nread);
+ if (unlikely (eos == NULL) || unlikely (eos == into))
+ {
+ if (*buffer == NULL)
+ free (into);
+ return false;
+ }
+ nread = eos + 1 - into;
+ }
+
+ if (*buffer == NULL)
+ *buffer = into;
+ *buffer_available = nread;
+ }
+
+ return true;
+}
+
+/* Free the contents of R_DEBUG_INFO without the R_DEBUG_INFO memory itself. */
+
+static void
+clear_r_debug_info (struct r_debug_info *r_debug_info)
+{
+ while (r_debug_info->module != NULL)
+ {
+ struct r_debug_info_module *module = r_debug_info->module;
+ r_debug_info->module = module->next;
+ elf_end (module->elf);
+ if (module->fd != -1)
+ close (module->fd);
+ free (module);
+ }
+}
+
+bool
+internal_function
+__libdwfl_dynamic_vaddr_get (Elf *elf, GElf_Addr *vaddrp)
+{
+ size_t phnum;
+ if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
+ return false;
+ for (size_t i = 0; i < phnum; ++i)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
+ if (unlikely (phdr == NULL))
+ return false;
+ if (phdr->p_type == PT_DYNAMIC)
+ {
+ *vaddrp = phdr->p_vaddr;
+ return true;
+ }
+ }
+ return false;
+}
+
+int
+dwfl_core_file_report (Dwfl *dwfl, Elf *elf, const char *executable)
+{
+ size_t phnum;
+ if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
+ {
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ return -1;
+ }
+
+ if (dwfl->user_core != NULL)
+ free (dwfl->user_core->executable_for_core);
+ if (executable == NULL)
+ {
+ if (dwfl->user_core != NULL)
+ dwfl->user_core->executable_for_core = NULL;
+ }
+ else
+ {
+ if (dwfl->user_core == NULL)
+ {
+ dwfl->user_core = calloc (1, sizeof (struct Dwfl_User_Core));
+ if (dwfl->user_core == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return -1;
+ }
+ dwfl->user_core->fd = -1;
+ }
+ dwfl->user_core->executable_for_core = strdup (executable);
+ if (dwfl->user_core->executable_for_core == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return -1;
+ }
+ }
+
+ /* First report each PT_LOAD segment. */
+ GElf_Phdr notes_phdr;
+ int ndx = dwfl_report_core_segments (dwfl, elf, phnum, ¬es_phdr);
+ if (unlikely (ndx <= 0))
+ return ndx;
+
+ /* Next, we should follow the chain from DT_DEBUG. */
+
+ const void *auxv = NULL;
+ const void *note_file = NULL;
+ size_t auxv_size = 0;
+ size_t note_file_size = 0;
+ if (likely (notes_phdr.p_type == PT_NOTE))
+ {
+ /* PT_NOTE -> NT_AUXV -> AT_PHDR -> PT_DYNAMIC -> DT_DEBUG */
+
+ Elf_Data *notes = elf_getdata_rawchunk (elf,
+ notes_phdr.p_offset,
+ notes_phdr.p_filesz,
+ ELF_T_NHDR);
+ if (likely (notes != NULL))
+ {
+ size_t pos = 0;
+ GElf_Nhdr nhdr;
+ size_t name_pos;
+ size_t desc_pos;
+ while ((pos = gelf_getnote (notes, pos, &nhdr,
+ &name_pos, &desc_pos)) > 0)
+ if (nhdr.n_namesz == sizeof "CORE"
+ && !memcmp (notes->d_buf + name_pos, "CORE", sizeof "CORE"))
+ {
+ if (nhdr.n_type == NT_AUXV)
+ {
+ auxv = notes->d_buf + desc_pos;
+ auxv_size = nhdr.n_descsz;
+ }
+ if (nhdr.n_type == NT_FILE)
+ {
+ note_file = notes->d_buf + desc_pos;
+ note_file_size = nhdr.n_descsz;
+ }
+ }
+ }
+ }
+
+ /* Now we have NT_AUXV contents. From here on this processing could be
+ used for a live process with auxv read from /proc. */
+
+ struct r_debug_info r_debug_info;
+ memset (&r_debug_info, 0, sizeof r_debug_info);
+ int retval = dwfl_link_map_report (dwfl, auxv, auxv_size,
+ dwfl_elf_phdr_memory_callback, elf,
+ &r_debug_info);
+ int listed = retval > 0 ? retval : 0;
+
+ /* Now sniff segment contents for modules hinted by information gathered
+ from DT_DEBUG. */
+
+ ndx = 0;
+ do
+ {
+ int seg = dwfl_segment_report_module (dwfl, ndx, NULL,
+ &dwfl_elf_phdr_memory_callback, elf,
+ core_file_read_eagerly, elf,
+ note_file, note_file_size,
+ &r_debug_info);
+ if (unlikely (seg < 0))
+ {
+ clear_r_debug_info (&r_debug_info);
+ return seg;
+ }
+ if (seg > ndx)
+ {
+ ndx = seg;
+ ++listed;
+ }
+ else
+ ++ndx;
+ }
+ while (ndx < (int) phnum);
+
+ /* Now report the modules from dwfl_link_map_report which were not filtered
+ out by dwfl_segment_report_module. */
+
+ Dwfl_Module **lastmodp = &dwfl->modulelist;
+ while (*lastmodp != NULL)
+ lastmodp = &(*lastmodp)->next;
+ for (struct r_debug_info_module *module = r_debug_info.module;
+ module != NULL; module = module->next)
+ {
+ if (module->elf == NULL)
+ continue;
+ GElf_Addr file_dynamic_vaddr;
+ if (! __libdwfl_dynamic_vaddr_get (module->elf, &file_dynamic_vaddr))
+ continue;
+ Dwfl_Module *mod;
+ mod = __libdwfl_report_elf (dwfl, basename (module->name), module->name,
+ module->fd, module->elf,
+ module->l_ld - file_dynamic_vaddr,
+ true, true);
+ if (mod == NULL)
+ continue;
+ ++listed;
+ module->elf = NULL;
+ module->fd = -1;
+ /* Move this module to the end of the list, so that we end
+ up with a list in the same order as the link_map chain. */
+ if (mod->next != NULL)
+ {
+ if (*lastmodp != mod)
+ {
+ lastmodp = &dwfl->modulelist;
+ while (*lastmodp != mod)
+ lastmodp = &(*lastmodp)->next;
+ }
+ *lastmodp = mod->next;
+ mod->next = NULL;
+ while (*lastmodp != NULL)
+ lastmodp = &(*lastmodp)->next;
+ *lastmodp = mod;
+ }
+ lastmodp = &mod->next;
+ }
+
+ clear_r_debug_info (&r_debug_info);
+
+ /* We return the number of modules we found if we found any.
+ If we found none, we return -1 instead of 0 if there was an
+ error rather than just nothing found. */
+ return listed > 0 ? listed : retval;
+}
+INTDEF (dwfl_core_file_report)
+NEW_VERSION (dwfl_core_file_report, ELFUTILS_0.158)
+
+#ifdef SYMBOL_VERSIONING
+int _compat_without_executable_dwfl_core_file_report (Dwfl *dwfl, Elf *elf);
+COMPAT_VERSION_NEWPROTO (dwfl_core_file_report, ELFUTILS_0.146,
+ without_executable)
+
+int
+_compat_without_executable_dwfl_core_file_report (Dwfl *dwfl, Elf *elf)
+{
+ return dwfl_core_file_report (dwfl, elf, NULL);
+}
+#endif
diff --git a/libdwfl/cu.c b/libdwfl/cu.c
new file mode 100644
index 0000000..7aa23b5
--- /dev/null
+++ b/libdwfl/cu.c
@@ -0,0 +1,322 @@
+/* Keeping track of DWARF compilation units in libdwfl.
+ Copyright (C) 2005-2010, 2015 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include "../libdw/libdwP.h"
+#include "../libdw/memory-access.h"
+#include <search.h>
+
+
+static inline Dwarf_Arange *
+dwar (Dwfl_Module *mod, unsigned int idx)
+{
+ return &mod->dw->aranges->info[mod->aranges[idx].arange];
+}
+
+
+static Dwfl_Error
+addrarange (Dwfl_Module *mod, Dwarf_Addr addr, struct dwfl_arange **arange)
+{
+ if (mod->aranges == NULL)
+ {
+ struct dwfl_arange *aranges = NULL;
+ Dwarf_Aranges *dwaranges = NULL;
+ size_t naranges;
+ if (INTUSE(dwarf_getaranges) (mod->dw, &dwaranges, &naranges) != 0)
+ return DWFL_E_LIBDW;
+
+ /* If the module has no aranges (when no code is included) we
+ allocate nothing. */
+ if (naranges != 0)
+ {
+ aranges = malloc (naranges * sizeof *aranges);
+ if (unlikely (aranges == NULL))
+ return DWFL_E_NOMEM;
+
+ /* libdw has sorted its list by address, which is how we want it.
+ But the sorted list is full of not-quite-contiguous runs pointing
+ to the same CU. We don't care about the little gaps inside the
+ module, we'll consider them part of the surrounding CU anyway.
+ Collect our own array with just one record for each run of ranges
+ pointing to one CU. */
+
+ naranges = 0;
+ Dwarf_Off lastcu = 0;
+ for (size_t i = 0; i < dwaranges->naranges; ++i)
+ if (i == 0 || dwaranges->info[i].offset != lastcu)
+ {
+ aranges[naranges].arange = i;
+ aranges[naranges].cu = NULL;
+ ++naranges;
+ lastcu = dwaranges->info[i].offset;
+ }
+ }
+
+ /* Store the final array, which is probably much smaller than before. */
+ mod->naranges = naranges;
+ mod->aranges = (realloc (aranges, naranges * sizeof aranges[0])
+ ?: aranges);
+ mod->lazycu += naranges;
+ }
+
+ /* The address must be inside the module to begin with. */
+ addr = dwfl_deadjust_dwarf_addr (mod, addr);
+
+ /* The ranges are sorted by address, so we can use binary search. */
+ size_t l = 0, u = mod->naranges;
+ while (l < u)
+ {
+ size_t idx = (l + u) / 2;
+ Dwarf_Addr start = dwar (mod, idx)->addr;
+ if (addr < start)
+ {
+ u = idx;
+ continue;
+ }
+ else if (addr > start)
+ {
+ if (idx + 1 < mod->naranges)
+ {
+ if (addr >= dwar (mod, idx + 1)->addr)
+ {
+ l = idx + 1;
+ continue;
+ }
+ }
+ else
+ {
+ /* It might be in the last range. */
+ const Dwarf_Arange *last
+ = &mod->dw->aranges->info[mod->dw->aranges->naranges - 1];
+ if (addr > last->addr + last->length)
+ break;
+ }
+ }
+
+ *arange = &mod->aranges[idx];
+ return DWFL_E_NOERROR;
+ }
+
+ return DWFL_E_ADDR_OUTOFRANGE;
+}
+
+
+static void
+nofree (void *arg)
+{
+ struct dwfl_cu *cu = arg;
+ if (cu == (void *) -1l)
+ return;
+
+ assert (cu->mod->lazycu == 0);
+}
+
+/* One reason fewer to keep the lazy lookup table for CUs. */
+static inline void
+less_lazy (Dwfl_Module *mod)
+{
+ if (--mod->lazycu > 0)
+ return;
+
+ /* We know about all the CUs now, we don't need this table. */
+ tdestroy (mod->lazy_cu_root, nofree);
+ mod->lazy_cu_root = NULL;
+}
+
+static inline Dwarf_Off
+cudie_offset (const struct dwfl_cu *cu)
+{
+ /* These are real CUs, so there never is a type_sig8. Note
+ initialization of dwkey.start and offset_size in intern_cu ()
+ to see why this calculates the same value for both key and
+ die.cu search items. */
+ return DIE_OFFSET_FROM_CU_OFFSET (cu->die.cu->start, cu->die.cu->offset_size,
+ 0);
+}
+
+static int
+compare_cukey (const void *a, const void *b)
+{
+ Dwarf_Off a_off = cudie_offset (a);
+ Dwarf_Off b_off = cudie_offset (b);
+ return (a_off < b_off) ? -1 : ((a_off > b_off) ? 1 : 0);
+}
+
+/* Intern the CU if necessary. */
+static Dwfl_Error
+intern_cu (Dwfl_Module *mod, Dwarf_Off cuoff, struct dwfl_cu **result)
+{
+ if (unlikely (cuoff + 4 >= mod->dw->sectiondata[IDX_debug_info]->d_size))
+ {
+ if (likely (mod->lazycu == 1))
+ {
+ /* This is the EOF marker. Now we have interned all the CUs.
+ One increment in MOD->lazycu counts not having hit EOF yet. */
+ *result = (void *) -1;
+ less_lazy (mod);
+ return DWFL_E_NOERROR;
+ }
+ else
+ {
+ /* Unexpected EOF, most likely a bogus aranges. */
+ return (DWFL_E (LIBDW, DWARF_E_INVALID_DWARF));
+ }
+ }
+
+ /* Make sure the cuoff points to a real DIE. */
+ Dwarf_Die cudie;
+ Dwarf_Die *die = INTUSE(dwarf_offdie) (mod->dw, cuoff, &cudie);
+ if (die == NULL)
+ return DWFL_E_LIBDW;
+
+ struct Dwarf_CU dwkey;
+ struct dwfl_cu key;
+ key.die.cu = &dwkey;
+ dwkey.offset_size = 0;
+ dwkey.start = cuoff - (3 * 0 - 4 + 3);
+ struct dwfl_cu **found = tsearch (&key, &mod->lazy_cu_root, &compare_cukey);
+ if (unlikely (found == NULL))
+ return DWFL_E_NOMEM;
+
+ if (*found == &key || *found == NULL)
+ {
+ /* This is a new entry, meaning we haven't looked at this CU. */
+
+ *found = NULL;
+
+ struct dwfl_cu *cu = malloc (sizeof *cu);
+ if (unlikely (cu == NULL))
+ return DWFL_E_NOMEM;
+
+ cu->mod = mod;
+ cu->next = NULL;
+ cu->lines = NULL;
+ cu->die = cudie;
+
+ struct dwfl_cu **newvec = realloc (mod->cu, ((mod->ncu + 1)
+ * sizeof (mod->cu[0])));
+ if (newvec == NULL)
+ {
+ free (cu);
+ return DWFL_E_NOMEM;
+ }
+ mod->cu = newvec;
+
+ mod->cu[mod->ncu++] = cu;
+ if (cu->die.cu->start == 0)
+ mod->first_cu = cu;
+
+ *found = cu;
+ }
+
+ *result = *found;
+ return DWFL_E_NOERROR;
+}
+
+
+/* Traverse all the CUs in the module. */
+
+Dwfl_Error
+internal_function
+__libdwfl_nextcu (Dwfl_Module *mod, struct dwfl_cu *lastcu,
+ struct dwfl_cu **cu)
+{
+ Dwarf_Off cuoff;
+ struct dwfl_cu **nextp;
+
+ if (lastcu == NULL)
+ {
+ /* Start the traversal. */
+ cuoff = 0;
+ nextp = &mod->first_cu;
+ }
+ else
+ {
+ /* Continue following LASTCU. */
+ cuoff = lastcu->die.cu->end;
+ nextp = &lastcu->next;
+ }
+
+ if (*nextp == NULL)
+ {
+ size_t cuhdrsz;
+ Dwarf_Off nextoff;
+ int end = INTUSE(dwarf_nextcu) (mod->dw, cuoff, &nextoff, &cuhdrsz,
+ NULL, NULL, NULL);
+ if (end < 0)
+ return DWFL_E_LIBDW;
+ if (end > 0)
+ {
+ *cu = NULL;
+ return DWFL_E_NOERROR;
+ }
+
+ Dwfl_Error result = intern_cu (mod, cuoff + cuhdrsz, nextp);
+ if (result != DWFL_E_NOERROR)
+ return result;
+
+ if (*nextp != (void *) -1
+ && (*nextp)->next == NULL && nextoff == (Dwarf_Off) -1l)
+ (*nextp)->next = (void *) -1l;
+ }
+
+ *cu = *nextp == (void *) -1l ? NULL : *nextp;
+ return DWFL_E_NOERROR;
+}
+
+
+/* Intern the CU arange points to, if necessary. */
+
+static Dwfl_Error
+arangecu (Dwfl_Module *mod, struct dwfl_arange *arange, struct dwfl_cu **cu)
+{
+ if (arange->cu == NULL)
+ {
+ const Dwarf_Arange *dwarange = &mod->dw->aranges->info[arange->arange];
+ Dwfl_Error result = intern_cu (mod, dwarange->offset, &arange->cu);
+ if (result != DWFL_E_NOERROR)
+ return result;
+ assert (arange->cu != NULL && arange->cu != (void *) -1l);
+ less_lazy (mod); /* Each arange with null ->cu counts once. */
+ }
+
+ *cu = arange->cu;
+ return DWFL_E_NOERROR;
+}
+
+Dwfl_Error
+internal_function
+__libdwfl_addrcu (Dwfl_Module *mod, Dwarf_Addr addr, struct dwfl_cu **cu)
+{
+ struct dwfl_arange *arange;
+ return addrarange (mod, addr, &arange) ?: arangecu (mod, arange, cu);
+}
diff --git a/libdwfl/derelocate.c b/libdwfl/derelocate.c
new file mode 100644
index 0000000..2f80b20
--- /dev/null
+++ b/libdwfl/derelocate.c
@@ -0,0 +1,434 @@
+/* Recover relocatibility for addresses computed from debug information.
+ Copyright (C) 2005-2010, 2013, 2015 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+struct dwfl_relocation
+{
+ size_t count;
+ struct
+ {
+ Elf_Scn *scn;
+ Elf_Scn *relocs;
+ const char *name;
+ GElf_Addr start, end;
+ } refs[0];
+};
+
+
+struct secref
+{
+ struct secref *next;
+ Elf_Scn *scn;
+ Elf_Scn *relocs;
+ const char *name;
+ GElf_Addr start, end;
+};
+
+static int
+compare_secrefs (const void *a, const void *b)
+{
+ struct secref *const *p1 = a;
+ struct secref *const *p2 = b;
+
+ /* No signed difference calculation is correct here, since the
+ terms are unsigned and could be more than INT64_MAX apart. */
+ if ((*p1)->start < (*p2)->start)
+ return -1;
+ if ((*p1)->start > (*p2)->start)
+ return 1;
+
+ if ((*p1)->end < (*p2)->end)
+ return -1;
+ if ((*p1)->end > (*p2)->end)
+ return 1;
+
+ /* Same start/end, then just compare which section came first. */
+ return elf_ndxscn ((*p1)->scn) - elf_ndxscn ((*p2)->scn);
+}
+
+static int
+cache_sections (Dwfl_Module *mod)
+{
+ if (likely (mod->reloc_info != NULL))
+ return mod->reloc_info->count;
+
+ struct secref *refs = NULL;
+ size_t nrefs = 0;
+
+ size_t shstrndx;
+ if (unlikely (elf_getshdrstrndx (mod->main.elf, &shstrndx) < 0))
+ {
+ elf_error:
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ nrefs = -1;
+ goto free_refs;
+ }
+
+ bool check_reloc_sections = false;
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr == NULL)
+ goto elf_error;
+
+ if ((shdr->sh_flags & SHF_ALLOC) && shdr->sh_addr == 0
+ && mod->e_type == ET_REL)
+ {
+ /* This section might not yet have been looked at. */
+ if (__libdwfl_relocate_value (mod, mod->main.elf, &shstrndx,
+ elf_ndxscn (scn),
+ &shdr->sh_addr) != DWFL_E_NOERROR)
+ continue;
+ shdr = gelf_getshdr (scn, &shdr_mem);
+ if (unlikely (shdr == NULL))
+ goto elf_error;
+ }
+
+ if (shdr->sh_flags & SHF_ALLOC)
+ {
+ const char *name = elf_strptr (mod->main.elf, shstrndx,
+ shdr->sh_name);
+ if (unlikely (name == NULL))
+ goto elf_error;
+
+ struct secref *newref = malloc (sizeof *newref);
+ if (unlikely (newref == NULL))
+ {
+ nomem:
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ nrefs = -1;
+ goto free_refs;
+ }
+
+ newref->scn = scn;
+ newref->relocs = NULL;
+ newref->name = name;
+ newref->start = dwfl_adjusted_address (mod, shdr->sh_addr);
+ newref->end = newref->start + shdr->sh_size;
+ newref->next = refs;
+ refs = newref;
+ ++nrefs;
+ }
+
+ if (mod->e_type == ET_REL
+ && shdr->sh_size != 0
+ && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
+ && mod->dwfl->callbacks->section_address != NULL)
+ {
+ if (shdr->sh_info < elf_ndxscn (scn))
+ {
+ /* We've already looked at the section these relocs apply to. */
+ Elf_Scn *tscn = elf_getscn (mod->main.elf, shdr->sh_info);
+ if (likely (tscn != NULL))
+ for (struct secref *sec = refs; sec != NULL; sec = sec->next)
+ if (sec->scn == tscn)
+ {
+ sec->relocs = scn;
+ break;
+ }
+ }
+ else
+ /* We'll have to do a second pass. */
+ check_reloc_sections = true;
+ }
+ }
+
+ mod->reloc_info = malloc (offsetof (struct dwfl_relocation, refs[nrefs]));
+ if (unlikely (mod->reloc_info == NULL))
+ goto nomem;
+
+ struct secref **sortrefs = malloc (nrefs * sizeof sortrefs[0]);
+ if (unlikely (sortrefs == NULL))
+ goto nomem;
+
+ for (size_t i = nrefs; i-- > 0; refs = refs->next)
+ sortrefs[i] = refs;
+ assert (refs == NULL);
+
+ qsort (sortrefs, nrefs, sizeof sortrefs[0], &compare_secrefs);
+
+ mod->reloc_info->count = nrefs;
+ for (size_t i = 0; i < nrefs; ++i)
+ {
+ mod->reloc_info->refs[i].name = sortrefs[i]->name;
+ mod->reloc_info->refs[i].scn = sortrefs[i]->scn;
+ mod->reloc_info->refs[i].relocs = sortrefs[i]->relocs;
+ mod->reloc_info->refs[i].start = sortrefs[i]->start;
+ mod->reloc_info->refs[i].end = sortrefs[i]->end;
+ free (sortrefs[i]);
+ }
+
+ free (sortrefs);
+
+ if (unlikely (check_reloc_sections))
+ {
+ /* There was a reloc section that preceded its target section.
+ So we have to scan again now that we have cached all the
+ possible target sections we care about. */
+
+ scn = NULL;
+ while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr == NULL)
+ goto elf_error;
+
+ if (shdr->sh_size != 0
+ && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA))
+ {
+ Elf_Scn *tscn = elf_getscn (mod->main.elf, shdr->sh_info);
+ if (likely (tscn != NULL))
+ for (size_t i = 0; i < nrefs; ++i)
+ if (mod->reloc_info->refs[i].scn == tscn)
+ {
+ mod->reloc_info->refs[i].relocs = scn;
+ break;
+ }
+ }
+ }
+ }
+
+free_refs:
+ while (refs != NULL)
+ {
+ struct secref *ref = refs;
+ refs = ref->next;
+ free (ref);
+ }
+
+ return nrefs;
+}
+
+
+int
+dwfl_module_relocations (Dwfl_Module *mod)
+{
+ if (mod == NULL)
+ return -1;
+
+ switch (mod->e_type)
+ {
+ case ET_REL:
+ return cache_sections (mod);
+
+ case ET_DYN:
+ return 1;
+
+ case ET_EXEC:
+ assert (mod->main.vaddr == mod->low_addr);
+ break;
+ }
+
+ return 0;
+}
+
+const char *
+dwfl_module_relocation_info (Dwfl_Module *mod, unsigned int idx,
+ Elf32_Word *shndxp)
+{
+ if (mod == NULL)
+ return NULL;
+
+ switch (mod->e_type)
+ {
+ case ET_REL:
+ break;
+
+ case ET_DYN:
+ if (idx != 0)
+ return NULL;
+ if (shndxp)
+ *shndxp = SHN_ABS;
+ return "";
+
+ default:
+ return NULL;
+ }
+
+ if (cache_sections (mod) < 0)
+ return NULL;
+
+ struct dwfl_relocation *sections = mod->reloc_info;
+
+ if (idx >= sections->count)
+ return NULL;
+
+ if (shndxp)
+ *shndxp = elf_ndxscn (sections->refs[idx].scn);
+
+ return sections->refs[idx].name;
+}
+
+/* Check that MOD is valid and make sure its relocation has been done. */
+static bool
+check_module (Dwfl_Module *mod)
+{
+ if (mod == NULL)
+ return true;
+
+ if (INTUSE(dwfl_module_getsymtab) (mod) < 0)
+ {
+ Dwfl_Error error = dwfl_errno ();
+ if (error != DWFL_E_NO_SYMTAB)
+ {
+ __libdwfl_seterrno (error);
+ return true;
+ }
+ }
+
+ if (mod->dw == NULL)
+ {
+ Dwarf_Addr bias;
+ if (INTUSE(dwfl_module_getdwarf) (mod, &bias) == NULL)
+ {
+ Dwfl_Error error = dwfl_errno ();
+ if (error != DWFL_E_NO_DWARF)
+ {
+ __libdwfl_seterrno (error);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+/* Find the index in MOD->reloc_info.refs containing *ADDR. */
+static int
+find_section (Dwfl_Module *mod, Dwarf_Addr *addr)
+{
+ if (cache_sections (mod) < 0)
+ return -1;
+
+ struct dwfl_relocation *sections = mod->reloc_info;
+
+ /* The sections are sorted by address, so we can use binary search. */
+ size_t l = 0, u = sections->count;
+ while (l < u)
+ {
+ size_t idx = (l + u) / 2;
+ if (*addr < sections->refs[idx].start)
+ u = idx;
+ else if (*addr > sections->refs[idx].end)
+ l = idx + 1;
+ else
+ {
+ /* Consider the limit of a section to be inside it, unless it's
+ inside the next one. A section limit address can appear in
+ line records. */
+ if (*addr == sections->refs[idx].end
+ && idx + 1 < sections->count
+ && *addr == sections->refs[idx + 1].start)
+ ++idx;
+
+ *addr -= sections->refs[idx].start;
+ return idx;
+ }
+ }
+
+ __libdwfl_seterrno (DWFL_E (LIBDW, DWARF_E_NO_MATCH));
+ return -1;
+}
+
+size_t
+internal_function
+__libdwfl_find_section_ndx (Dwfl_Module *mod, Dwarf_Addr *addr)
+{
+ int idx = find_section (mod, addr);
+ if (unlikely (idx == -1))
+ return SHN_UNDEF;
+
+ return elf_ndxscn (mod->reloc_info->refs[idx].scn);
+}
+
+int
+dwfl_module_relocate_address (Dwfl_Module *mod, Dwarf_Addr *addr)
+{
+ if (unlikely (check_module (mod)))
+ return -1;
+
+ switch (mod->e_type)
+ {
+ case ET_REL:
+ return find_section (mod, addr);
+
+ case ET_DYN:
+ /* All relative to first and only relocation base: module start. */
+ *addr -= mod->low_addr;
+ break;
+
+ default:
+ /* Already absolute, dwfl_module_relocations returned zero. We
+ shouldn't really have been called, but it's a harmless no-op. */
+ break;
+ }
+
+ return 0;
+}
+INTDEF (dwfl_module_relocate_address)
+
+Elf_Scn *
+dwfl_module_address_section (Dwfl_Module *mod, Dwarf_Addr *address,
+ Dwarf_Addr *bias)
+{
+ if (check_module (mod))
+ return NULL;
+
+ int idx = find_section (mod, address);
+ if (idx < 0)
+ return NULL;
+
+ if (mod->reloc_info->refs[idx].relocs != NULL)
+ {
+ assert (mod->e_type == ET_REL);
+
+ Elf_Scn *tscn = mod->reloc_info->refs[idx].scn;
+ Elf_Scn *relocscn = mod->reloc_info->refs[idx].relocs;
+ Dwfl_Error result = __libdwfl_relocate_section (mod, mod->main.elf,
+ relocscn, tscn, true);
+ if (likely (result == DWFL_E_NOERROR))
+ mod->reloc_info->refs[idx].relocs = NULL;
+ else
+ {
+ __libdwfl_seterrno (result);
+ return NULL;
+ }
+ }
+
+ *bias = dwfl_adjusted_address (mod, 0);
+ return mod->reloc_info->refs[idx].scn;
+}
+INTDEF (dwfl_module_address_section)
diff --git a/libdwfl/dwfl_addrdie.c b/libdwfl/dwfl_addrdie.c
new file mode 100644
index 0000000..c5b1d68
--- /dev/null
+++ b/libdwfl/dwfl_addrdie.c
@@ -0,0 +1,40 @@
+/* Fetch CU DIE from address.
+ Copyright (C) 2005 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+Dwarf_Die *
+dwfl_addrdie (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Addr *bias)
+{
+ return INTUSE(dwfl_module_addrdie) (INTUSE(dwfl_addrmodule) (dwfl, addr),
+ addr, bias);
+}
diff --git a/libdwfl/dwfl_addrdwarf.c b/libdwfl/dwfl_addrdwarf.c
new file mode 100644
index 0000000..4f9efab
--- /dev/null
+++ b/libdwfl/dwfl_addrdwarf.c
@@ -0,0 +1,41 @@
+/* Fetch libdw handle from address.
+ Copyright (C) 2005 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+Dwarf *
+dwfl_addrdwarf (Dwfl *dwfl, Dwarf_Addr address, Dwarf_Addr *bias)
+{
+ return INTUSE(dwfl_module_getdwarf) (INTUSE(dwfl_addrmodule) (dwfl, address),
+ bias);
+}
+INTDEF (dwfl_addrdwarf)
diff --git a/libdwfl/dwfl_addrmodule.c b/libdwfl/dwfl_addrmodule.c
new file mode 100644
index 0000000..abf1ff4
--- /dev/null
+++ b/libdwfl/dwfl_addrmodule.c
@@ -0,0 +1,42 @@
+/* Find module containing address.
+ Copyright (C) 2005, 2006, 2007, 2008 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+Dwfl_Module *
+dwfl_addrmodule (Dwfl *dwfl, Dwarf_Addr address)
+{
+ Dwfl_Module *mod;
+ (void) INTUSE(dwfl_addrsegment) (dwfl, address, &mod);
+ return mod;
+}
+INTDEF (dwfl_addrmodule)
diff --git a/libdwfl/dwfl_begin.c b/libdwfl/dwfl_begin.c
new file mode 100644
index 0000000..b03f5cf
--- /dev/null
+++ b/libdwfl/dwfl_begin.c
@@ -0,0 +1,55 @@
+/* Set up a session using libdwfl.
+ Copyright (C) 2005 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+Dwfl *
+dwfl_begin (const Dwfl_Callbacks *callbacks)
+{
+ if (elf_version (EV_CURRENT) == EV_NONE)
+ {
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ return NULL;
+ }
+
+ Dwfl *dwfl = calloc (1, sizeof *dwfl);
+ if (dwfl == NULL)
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ else
+ {
+ dwfl->callbacks = callbacks;
+ dwfl->offline_next_address = OFFLINE_REDZONE;
+ }
+
+ return dwfl;
+}
+INTDEF (dwfl_begin)
diff --git a/libdwfl/dwfl_build_id_find_debuginfo.c b/libdwfl/dwfl_build_id_find_debuginfo.c
new file mode 100644
index 0000000..273e5e5
--- /dev/null
+++ b/libdwfl/dwfl_build_id_find_debuginfo.c
@@ -0,0 +1,132 @@
+/* Find the debuginfo file for a module from its build ID.
+ Copyright (C) 2007, 2009, 2014 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include <unistd.h>
+
+
+int
+dwfl_build_id_find_debuginfo (Dwfl_Module *mod,
+ void **userdata __attribute__ ((unused)),
+ const char *modname __attribute__ ((unused)),
+ Dwarf_Addr base __attribute__ ((unused)),
+ const char *file __attribute__ ((unused)),
+ const char *debuglink __attribute__ ((unused)),
+ GElf_Word crc __attribute__ ((unused)),
+ char **debuginfo_file_name)
+{
+ int fd = -1;
+
+ /* Are we looking for a separate debug file for the main file or for
+ an alternate (dwz multi) debug file? Alternatively we could check
+ whether the dwbias == -1. */
+ if (mod->dw != NULL)
+ {
+ const void *build_id;
+ const char *altname;
+ ssize_t build_id_len = INTUSE(dwelf_dwarf_gnu_debugaltlink) (mod->dw,
+ &altname,
+ &build_id);
+ if (build_id_len > 0)
+ fd = __libdwfl_open_by_build_id (mod, true, debuginfo_file_name,
+ build_id_len, build_id);
+
+ if (fd >= 0)
+ {
+ /* We need to open an Elf handle on the file so we can check its
+ build ID note for validation. Backdoor the handle into the
+ module data structure since we had to open it early anyway. */
+ Dwfl_Error error = __libdw_open_file (&fd, &mod->alt_elf,
+ true, false);
+ if (error != DWFL_E_NOERROR)
+ __libdwfl_seterrno (error);
+ else
+ {
+ const void *alt_build_id;
+ ssize_t alt_len = INTUSE(dwelf_elf_gnu_build_id) (mod->alt_elf,
+ &alt_build_id);
+ if (alt_len > 0 && alt_len == build_id_len
+ && memcmp (build_id, alt_build_id, alt_len) == 0)
+ return fd;
+ else
+ {
+ /* A mismatch! */
+ elf_end (mod->alt_elf);
+ mod->alt_elf = NULL;
+ close (fd);
+ fd = -1;
+ }
+ free (*debuginfo_file_name);
+ *debuginfo_file_name = NULL;
+ errno = 0;
+ }
+ }
+ return fd;
+ }
+
+ /* We don't even have the Dwarf yet and it isn't in the main file.
+ Try to find separate debug file now using the module build id. */
+ const unsigned char *bits;
+ GElf_Addr vaddr;
+
+ if (INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr) > 0)
+ fd = __libdwfl_open_mod_by_build_id (mod, true, debuginfo_file_name);
+ if (fd >= 0)
+ {
+ /* We need to open an Elf handle on the file so we can check its
+ build ID note for validation. Backdoor the handle into the
+ module data structure since we had to open it early anyway. */
+ Dwfl_Error error = __libdw_open_file (&fd, &mod->debug.elf, true, false);
+ if (error != DWFL_E_NOERROR)
+ __libdwfl_seterrno (error);
+ else if (likely (__libdwfl_find_build_id (mod, false,
+ mod->debug.elf) == 2))
+ {
+ /* Also backdoor the gratuitous flag. */
+ mod->debug.valid = true;
+ return fd;
+ }
+ else
+ {
+ /* A mismatch! */
+ elf_end (mod->debug.elf);
+ mod->debug.elf = NULL;
+ close (fd);
+ fd = -1;
+ }
+ free (*debuginfo_file_name);
+ *debuginfo_file_name = NULL;
+ errno = 0;
+ }
+ return fd;
+}
+INTDEF (dwfl_build_id_find_debuginfo)
diff --git a/libdwfl/dwfl_build_id_find_elf.c b/libdwfl/dwfl_build_id_find_elf.c
new file mode 100644
index 0000000..ee0c164
--- /dev/null
+++ b/libdwfl/dwfl_build_id_find_elf.c
@@ -0,0 +1,198 @@
+/* Find an ELF file for a module from its build ID.
+ Copyright (C) 2007-2010, 2014, 2015 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include <inttypes.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "system.h"
+
+
+int
+internal_function
+__libdwfl_open_by_build_id (Dwfl_Module *mod, bool debug, char **file_name,
+ const size_t id_len, const uint8_t *id)
+{
+ /* We don't handle very short or really large build-ids. We need at
+ at least 3 and allow for up to 64 (normally ids are 20 long). */
+#define MIN_BUILD_ID_BYTES 3
+#define MAX_BUILD_ID_BYTES 64
+ if (id_len < MIN_BUILD_ID_BYTES || id_len > MAX_BUILD_ID_BYTES)
+ {
+ __libdwfl_seterrno (DWFL_E_WRONG_ID_ELF);
+ return -1;
+ }
+
+ /* Search debuginfo_path directories' .build-id/ subdirectories. */
+
+ char id_name[sizeof "/.build-id/" + 1 + MAX_BUILD_ID_BYTES * 2
+ + sizeof ".debug" - 1];
+ strcpy (id_name, "/.build-id/");
+ int n = snprintf (&id_name[sizeof "/.build-id/" - 1],
+ 4, "%02" PRIx8 "/", (uint8_t) id[0]);
+ assert (n == 3);
+ for (size_t i = 1; i < id_len; ++i)
+ {
+ n = snprintf (&id_name[sizeof "/.build-id/" - 1 + 3 + (i - 1) * 2],
+ 3, "%02" PRIx8, (uint8_t) id[i]);
+ assert (n == 2);
+ }
+ if (debug)
+ strcpy (&id_name[sizeof "/.build-id/" - 1 + 3 + (id_len - 1) * 2],
+ ".debug");
+
+ const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
+ char *path = strdup ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
+ ?: DEFAULT_DEBUGINFO_PATH);
+ if (path == NULL)
+ return -1;
+
+ int fd = -1;
+ char *dir;
+ char *paths = path;
+ while (fd < 0 && (dir = strsep (&paths, ":")) != NULL)
+ {
+ if (dir[0] == '+' || dir[0] == '-')
+ ++dir;
+
+ /* Only absolute directory names are useful to us. */
+ if (dir[0] != '/')
+ continue;
+
+ size_t dirlen = strlen (dir);
+ char *name = malloc (dirlen + sizeof id_name);
+ if (unlikely (name == NULL))
+ break;
+ memcpy (mempcpy (name, dir, dirlen), id_name, sizeof id_name);
+
+ fd = TEMP_FAILURE_RETRY (open (name, O_RDONLY));
+ if (fd >= 0)
+ {
+ if (*file_name != NULL)
+ free (*file_name);
+ *file_name = canonicalize_file_name (name);
+ if (*file_name == NULL)
+ {
+ *file_name = name;
+ name = NULL;
+ }
+ }
+ free (name);
+ }
+
+ free (path);
+
+ /* If we simply found nothing, clear errno. If we had some other error
+ with the file, report that. Possibly this should treat other errors
+ like ENOENT too. But ignoring all errors could mask some that should
+ be reported. */
+ if (fd < 0 && errno == ENOENT)
+ errno = 0;
+
+ return fd;
+}
+
+int
+internal_function
+__libdwfl_open_mod_by_build_id (Dwfl_Module *mod, bool debug, char **file_name)
+{
+ /* If *FILE_NAME was primed into the module, leave it there
+ as the fallback when we have nothing to offer. */
+ errno = 0;
+ if (mod->build_id_len <= 0)
+ return -1;
+
+ const size_t id_len = mod->build_id_len;
+ const uint8_t *id = mod->build_id_bits;
+
+ return __libdwfl_open_by_build_id (mod, debug, file_name, id_len, id);
+}
+
+int
+dwfl_build_id_find_elf (Dwfl_Module *mod,
+ void **userdata __attribute__ ((unused)),
+ const char *modname __attribute__ ((unused)),
+ Dwarf_Addr base __attribute__ ((unused)),
+ char **file_name, Elf **elfp)
+{
+ *elfp = NULL;
+ if (mod->is_executable
+ && mod->dwfl->user_core != NULL
+ && mod->dwfl->user_core->executable_for_core != NULL)
+ {
+ /* When dwfl_core_file_report was called with a non-NULL executable file
+ name this callback will replace the Dwfl_Module main.name with the
+ recorded executable file when MOD was identified as main executable
+ (which then triggers opening and reporting of the executable). */
+ const char *executable = mod->dwfl->user_core->executable_for_core;
+ int fd = open (executable, O_RDONLY);
+ if (fd >= 0)
+ {
+ *file_name = strdup (executable);
+ if (*file_name != NULL)
+ return fd;
+ else
+ close (fd);
+ }
+ }
+ int fd = __libdwfl_open_mod_by_build_id (mod, false, file_name);
+ if (fd >= 0)
+ {
+ Dwfl_Error error = __libdw_open_file (&fd, elfp, true, false);
+ if (error != DWFL_E_NOERROR)
+ __libdwfl_seterrno (error);
+ else if (__libdwfl_find_build_id (mod, false, *elfp) == 2)
+ {
+ /* This is a backdoor signal to short-circuit the ID refresh. */
+ mod->main.valid = true;
+ return fd;
+ }
+ else
+ {
+ /* This file does not contain the ID it should! */
+ elf_end (*elfp);
+ *elfp = NULL;
+ close (fd);
+ fd = -1;
+ }
+ free (*file_name);
+ *file_name = NULL;
+ }
+ else if (errno == 0 && mod->build_id_len > 0)
+ /* Setting this with no file yet loaded is a marker that
+ the build ID is authoritative even if we also know a
+ putative *FILE_NAME. */
+ mod->main.valid = true;
+
+ return fd;
+}
+INTDEF (dwfl_build_id_find_elf)
diff --git a/libdwfl/dwfl_cumodule.c b/libdwfl/dwfl_cumodule.c
new file mode 100644
index 0000000..2b593f2
--- /dev/null
+++ b/libdwfl/dwfl_cumodule.c
@@ -0,0 +1,40 @@
+/* Find the module for a CU DIE previously returned by libdwfl.
+ Copyright (C) 2005 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+Dwfl_Module *
+dwfl_cumodule (Dwarf_Die *cudie)
+{
+ struct dwfl_cu *cu = (struct dwfl_cu *) cudie;
+ return cu->mod;
+}
diff --git a/libdwfl/dwfl_dwarf_line.c b/libdwfl/dwfl_dwarf_line.c
new file mode 100644
index 0000000..e22e984
--- /dev/null
+++ b/libdwfl/dwfl_dwarf_line.c
@@ -0,0 +1,47 @@
+/* Get information from a source line record returned by libdwfl.
+ Copyright (C) 2010 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include "../libdw/libdwP.h"
+
+Dwarf_Line *
+dwfl_dwarf_line (Dwfl_Line *line, Dwarf_Addr *bias)
+{
+ if (line == NULL)
+ return NULL;
+
+ struct dwfl_cu *cu = dwfl_linecu (line);
+ const Dwarf_Line *info = &cu->die.cu->lines->info[line->idx];
+
+ *bias = dwfl_adjusted_dwarf_addr (cu->mod, 0);
+ return (Dwarf_Line *) info;
+}
diff --git a/libdwfl/dwfl_end.c b/libdwfl/dwfl_end.c
new file mode 100644
index 0000000..74ee9e0
--- /dev/null
+++ b/libdwfl/dwfl_end.c
@@ -0,0 +1,66 @@
+/* Finish a session using libdwfl.
+ Copyright (C) 2005, 2008, 2012-2013, 2015 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include <unistd.h>
+
+void
+dwfl_end (Dwfl *dwfl)
+{
+ if (dwfl == NULL)
+ return;
+
+ if (dwfl->process)
+ __libdwfl_process_free (dwfl->process);
+
+ free (dwfl->lookup_addr);
+ free (dwfl->lookup_module);
+ free (dwfl->lookup_segndx);
+
+ Dwfl_Module *next = dwfl->modulelist;
+ while (next != NULL)
+ {
+ Dwfl_Module *dead = next;
+ next = dead->next;
+ __libdwfl_module_free (dead);
+ }
+
+ if (dwfl->user_core != NULL)
+ {
+ free (dwfl->user_core->executable_for_core);
+ elf_end (dwfl->user_core->core);
+ if (dwfl->user_core->fd != -1)
+ close (dwfl->user_core->fd);
+ free (dwfl->user_core);
+ }
+ free (dwfl);
+}
diff --git a/libdwfl/dwfl_error.c b/libdwfl/dwfl_error.c
new file mode 100644
index 0000000..7bcf61c
--- /dev/null
+++ b/libdwfl/dwfl_error.c
@@ -0,0 +1,171 @@
+/* Error handling in libdwfl.
+ Copyright (C) 2005-2015 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+#include <libintl.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "libdwflP.h"
+
+
+/* The error number. */
+static __thread int global_error;
+
+
+int
+dwfl_errno (void)
+{
+ int result = global_error;
+ global_error = DWFL_E_NOERROR;
+ return result;
+}
+INTDEF (dwfl_errno)
+
+
+struct msgtable
+{
+#define DWFL_ERROR(name, text) char msg_##name[sizeof text];
+ DWFL_ERRORS
+#undef DWFL_ERROR
+};
+
+static const union
+{
+ struct msgtable table;
+ char strings[
+#define DWFL_ERROR(name, text) + sizeof text
+ DWFL_ERRORS
+#undef DWFL_ERROR
+ ];
+} msgtable =
+ {
+ .table =
+ {
+#define DWFL_ERROR(name, text) text,
+ DWFL_ERRORS
+#undef DWFL_ERROR
+ }
+ };
+#define msgstr (msgtable.strings)
+
+static const uint_fast16_t msgidx[] =
+{
+#define DWFL_ERROR(name, text) \
+ [DWFL_E_##name] = offsetof (struct msgtable, msg_##name),
+ DWFL_ERRORS
+#undef DWFL_ERROR
+};
+#define nmsgidx (sizeof msgidx / sizeof msgidx[0])
+
+
+static inline int
+canonicalize (Dwfl_Error error)
+{
+ unsigned int value;
+
+ switch (error)
+ {
+ default:
+ value = error;
+ if ((value &~ 0xffff) != 0)
+ break;
+ assert (value < nmsgidx);
+ break;
+ case DWFL_E_ERRNO:
+ value = DWFL_E (ERRNO, errno);
+ break;
+ case DWFL_E_LIBELF:
+ value = DWFL_E (LIBELF, elf_errno ());
+ break;
+ case DWFL_E_LIBDW:
+ value = DWFL_E (LIBDW, INTUSE(dwarf_errno) ());
+ break;
+#if 0
+ DWFL_E_LIBEBL:
+ value = DWFL_E (LIBEBL, ebl_errno ());
+ break;
+#endif
+ }
+
+ return value;
+}
+
+int
+internal_function
+__libdwfl_canon_error (Dwfl_Error error)
+{
+ return canonicalize (error);
+}
+
+void
+internal_function
+__libdwfl_seterrno (Dwfl_Error error)
+{
+ global_error = canonicalize (error);
+}
+
+
+const char *
+dwfl_errmsg (int error)
+{
+ if (error == 0 || error == -1)
+ {
+ int last_error = global_error;
+
+ if (error == 0 && last_error == 0)
+ return NULL;
+
+ error = last_error;
+ global_error = DWFL_E_NOERROR;
+ }
+
+ switch (error &~ 0xffff)
+ {
+ case OTHER_ERROR (ERRNO):
+ return strerror_r (error & 0xffff, "bad", 0);
+ case OTHER_ERROR (LIBELF):
+ return elf_errmsg (error & 0xffff);
+ case OTHER_ERROR (LIBDW):
+ return INTUSE(dwarf_errmsg) (error & 0xffff);
+#if 0
+ case OTHER_ERROR (LIBEBL):
+ return ebl_errmsg (error & 0xffff);
+#endif
+ }
+
+ return _(&msgstr[msgidx[(unsigned int) error < nmsgidx
+ ? error : DWFL_E_UNKNOWN_ERROR]]);
+}
+INTDEF (dwfl_errmsg)
diff --git a/libdwfl/dwfl_frame.c b/libdwfl/dwfl_frame.c
new file mode 100644
index 0000000..881f735
--- /dev/null
+++ b/libdwfl/dwfl_frame.c
@@ -0,0 +1,478 @@
+/* Get Dwarf Frame state for target PID or core file.
+ Copyright (C) 2013, 2014 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include <unistd.h>
+
+/* Set STATE->pc_set from STATE->regs according to the backend. Return true on
+ success, false on error. */
+static bool
+state_fetch_pc (Dwfl_Frame *state)
+{
+ switch (state->pc_state)
+ {
+ case DWFL_FRAME_STATE_PC_SET:
+ return true;
+ case DWFL_FRAME_STATE_PC_UNDEFINED:
+ abort ();
+ case DWFL_FRAME_STATE_ERROR:
+ {
+ Ebl *ebl = state->thread->process->ebl;
+ Dwarf_CIE abi_info;
+ if (ebl_abi_cfi (ebl, &abi_info) != 0)
+ {
+ __libdwfl_seterrno (DWFL_E_LIBEBL);
+ return false;
+ }
+ unsigned ra = abi_info.return_address_register;
+ /* dwarf_frame_state_reg_is_set is not applied here. */
+ if (ra >= ebl_frame_nregs (ebl))
+ {
+ __libdwfl_seterrno (DWFL_E_LIBEBL_BAD);
+ return false;
+ }
+ state->pc = state->regs[ra] + ebl_ra_offset (ebl);
+ state->pc_state = DWFL_FRAME_STATE_PC_SET;
+ }
+ return true;
+ }
+ abort ();
+}
+
+/* Do not call it on your own, to be used by thread_* functions only. */
+
+static void
+state_free (Dwfl_Frame *state)
+{
+ Dwfl_Thread *thread = state->thread;
+ assert (thread->unwound == state);
+ thread->unwound = state->unwound;
+ free (state);
+}
+
+static void
+thread_free_all_states (Dwfl_Thread *thread)
+{
+ while (thread->unwound)
+ state_free (thread->unwound);
+}
+
+static Dwfl_Frame *
+state_alloc (Dwfl_Thread *thread)
+{
+ assert (thread->unwound == NULL);
+ Ebl *ebl = thread->process->ebl;
+ size_t nregs = ebl_frame_nregs (ebl);
+ if (nregs == 0)
+ return NULL;
+ assert (nregs < sizeof (((Dwfl_Frame *) NULL)->regs_set) * 8);
+ Dwfl_Frame *state = malloc (sizeof (*state) + sizeof (*state->regs) * nregs);
+ if (state == NULL)
+ return NULL;
+ state->thread = thread;
+ state->signal_frame = false;
+ state->initial_frame = true;
+ state->pc_state = DWFL_FRAME_STATE_ERROR;
+ memset (state->regs_set, 0, sizeof (state->regs_set));
+ thread->unwound = state;
+ state->unwound = NULL;
+ return state;
+}
+
+void
+internal_function
+__libdwfl_process_free (Dwfl_Process *process)
+{
+ Dwfl *dwfl = process->dwfl;
+ if (process->callbacks->detach != NULL)
+ process->callbacks->detach (dwfl, process->callbacks_arg);
+ assert (dwfl->process == process);
+ dwfl->process = NULL;
+ if (process->ebl_close)
+ ebl_closebackend (process->ebl);
+ free (process);
+ dwfl->attacherr = DWFL_E_NOERROR;
+}
+
+/* Allocate new Dwfl_Process for DWFL. */
+static void
+process_alloc (Dwfl *dwfl)
+{
+ Dwfl_Process *process = malloc (sizeof (*process));
+ if (process == NULL)
+ return;
+ process->dwfl = dwfl;
+ dwfl->process = process;
+}
+
+bool
+dwfl_attach_state (Dwfl *dwfl, Elf *elf, pid_t pid,
+ const Dwfl_Thread_Callbacks *thread_callbacks, void *arg)
+{
+ if (dwfl->process != NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_ATTACH_STATE_CONFLICT);
+ return false;
+ }
+
+ /* Reset any previous error, we are just going to try again. */
+ dwfl->attacherr = DWFL_E_NOERROR;
+ /* thread_callbacks is declared NN */
+ if (thread_callbacks->next_thread == NULL
+ || thread_callbacks->set_initial_registers == NULL)
+ {
+ dwfl->attacherr = DWFL_E_INVALID_ARGUMENT;
+ fail:
+ dwfl->attacherr = __libdwfl_canon_error (dwfl->attacherr);
+ __libdwfl_seterrno (dwfl->attacherr);
+ return false;
+ }
+
+ Ebl *ebl;
+ bool ebl_close;
+ if (elf != NULL)
+ {
+ ebl = ebl_openbackend (elf);
+ ebl_close = true;
+ }
+ else
+ {
+ ebl = NULL;
+ for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next)
+ {
+ /* Reading of the vDSO or (deleted) modules may fail as
+ /proc/PID/mem is unreadable without PTRACE_ATTACH and
+ we may not be PTRACE_ATTACH-ed now. MOD would not be
+ re-read later to unwind it when we are already
+ PTRACE_ATTACH-ed to PID. This happens when this function
+ is called from dwfl_linux_proc_attach with elf == NULL.
+ __libdwfl_module_getebl will call __libdwfl_getelf which
+ will call the find_elf callback. */
+ if (strncmp (mod->name, "[vdso: ", 7) == 0
+ || strcmp (strrchr (mod->name, ' ') ?: "",
+ " (deleted)") == 0)
+ continue;
+ Dwfl_Error error = __libdwfl_module_getebl (mod);
+ if (error != DWFL_E_NOERROR)
+ continue;
+ ebl = mod->ebl;
+ break;
+ }
+ ebl_close = false;
+ }
+ if (ebl == NULL)
+ {
+ /* Not identified EBL from any of the modules. */
+ dwfl->attacherr = DWFL_E_PROCESS_NO_ARCH;
+ goto fail;
+ }
+ process_alloc (dwfl);
+ Dwfl_Process *process = dwfl->process;
+ if (process == NULL)
+ {
+ if (ebl_close)
+ ebl_closebackend (ebl);
+ dwfl->attacherr = DWFL_E_NOMEM;
+ goto fail;
+ }
+ process->ebl = ebl;
+ process->ebl_close = ebl_close;
+ process->pid = pid;
+ process->callbacks = thread_callbacks;
+ process->callbacks_arg = arg;
+ return true;
+}
+INTDEF(dwfl_attach_state)
+
+pid_t
+dwfl_pid (Dwfl *dwfl)
+{
+ if (dwfl->attacherr != DWFL_E_NOERROR)
+ {
+ __libdwfl_seterrno (dwfl->attacherr);
+ return -1;
+ }
+
+ if (dwfl->process == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_NO_ATTACH_STATE);
+ return -1;
+ }
+ return dwfl->process->pid;
+}
+INTDEF(dwfl_pid)
+
+Dwfl *
+dwfl_thread_dwfl (Dwfl_Thread *thread)
+{
+ return thread->process->dwfl;
+}
+INTDEF(dwfl_thread_dwfl)
+
+pid_t
+dwfl_thread_tid (Dwfl_Thread *thread)
+{
+ return thread->tid;
+}
+INTDEF(dwfl_thread_tid)
+
+Dwfl_Thread *
+dwfl_frame_thread (Dwfl_Frame *state)
+{
+ return state->thread;
+}
+INTDEF(dwfl_frame_thread)
+
+int
+dwfl_getthreads (Dwfl *dwfl, int (*callback) (Dwfl_Thread *thread, void *arg),
+ void *arg)
+{
+ if (dwfl->attacherr != DWFL_E_NOERROR)
+ {
+ __libdwfl_seterrno (dwfl->attacherr);
+ return -1;
+ }
+
+ Dwfl_Process *process = dwfl->process;
+ if (process == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_NO_ATTACH_STATE);
+ return -1;
+ }
+
+ Dwfl_Thread thread;
+ thread.process = process;
+ thread.unwound = NULL;
+ thread.callbacks_arg = NULL;
+ for (;;)
+ {
+ thread.tid = process->callbacks->next_thread (dwfl,
+ process->callbacks_arg,
+ &thread.callbacks_arg);
+ if (thread.tid < 0)
+ {
+ Dwfl_Error saved_errno = dwfl_errno ();
+ thread_free_all_states (&thread);
+ __libdwfl_seterrno (saved_errno);
+ return -1;
+ }
+ if (thread.tid == 0)
+ {
+ thread_free_all_states (&thread);
+ __libdwfl_seterrno (DWFL_E_NOERROR);
+ return 0;
+ }
+ int err = callback (&thread, arg);
+ if (err != DWARF_CB_OK)
+ {
+ thread_free_all_states (&thread);
+ return err;
+ }
+ assert (thread.unwound == NULL);
+ }
+ /* NOTREACHED */
+}
+INTDEF(dwfl_getthreads)
+
+struct one_arg
+{
+ pid_t tid;
+ bool seen;
+ int (*callback) (Dwfl_Thread *thread, void *arg);
+ void *arg;
+ int ret;
+};
+
+static int
+get_one_thread_cb (Dwfl_Thread *thread, void *arg)
+{
+ struct one_arg *oa = (struct one_arg *) arg;
+ if (! oa->seen && INTUSE(dwfl_thread_tid) (thread) == oa->tid)
+ {
+ oa->seen = true;
+ oa->ret = oa->callback (thread, oa->arg);
+ return DWARF_CB_ABORT;
+ }
+
+ return DWARF_CB_OK;
+}
+
+/* Note not currently exported, will be when there are more Dwfl_Thread
+ properties to query. Use dwfl_getthread_frames for now directly. */
+static int
+getthread (Dwfl *dwfl, pid_t tid,
+ int (*callback) (Dwfl_Thread *thread, void *arg),
+ void *arg)
+{
+ if (dwfl->attacherr != DWFL_E_NOERROR)
+ {
+ __libdwfl_seterrno (dwfl->attacherr);
+ return -1;
+ }
+
+ Dwfl_Process *process = dwfl->process;
+ if (process == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_NO_ATTACH_STATE);
+ return -1;
+ }
+
+ if (process->callbacks->get_thread != NULL)
+ {
+ Dwfl_Thread thread;
+ thread.process = process;
+ thread.unwound = NULL;
+ thread.callbacks_arg = NULL;
+
+ if (process->callbacks->get_thread (dwfl, tid, process->callbacks_arg,
+ &thread.callbacks_arg))
+ {
+ int err;
+ thread.tid = tid;
+ err = callback (&thread, arg);
+ thread_free_all_states (&thread);
+ return err;
+ }
+
+ return -1;
+ }
+
+ struct one_arg oa = { .tid = tid, .callback = callback,
+ .arg = arg, .seen = false };
+ int err = INTUSE(dwfl_getthreads) (dwfl, get_one_thread_cb, &oa);
+
+ if (err == DWARF_CB_ABORT && oa.seen)
+ return oa.ret;
+
+ if (err == DWARF_CB_OK && ! oa.seen)
+ {
+ errno = ESRCH;
+ __libdwfl_seterrno (DWFL_E_ERRNO);
+ return -1;
+ }
+
+ return err;
+}
+
+struct one_thread
+{
+ int (*callback) (Dwfl_Frame *frame, void *arg);
+ void *arg;
+};
+
+static int
+get_one_thread_frames_cb (Dwfl_Thread *thread, void *arg)
+{
+ struct one_thread *ot = (struct one_thread *) arg;
+ return INTUSE(dwfl_thread_getframes) (thread, ot->callback, ot->arg);
+}
+
+int
+dwfl_getthread_frames (Dwfl *dwfl, pid_t tid,
+ int (*callback) (Dwfl_Frame *frame, void *arg),
+ void *arg)
+{
+ struct one_thread ot = { .callback = callback, .arg = arg };
+ return getthread (dwfl, tid, get_one_thread_frames_cb, &ot);
+}
+INTDEF(dwfl_getthread_frames)
+
+int
+dwfl_thread_getframes (Dwfl_Thread *thread,
+ int (*callback) (Dwfl_Frame *state, void *arg),
+ void *arg)
+{
+ if (thread->unwound != NULL)
+ {
+ /* We had to be called from inside CALLBACK. */
+ __libdwfl_seterrno (DWFL_E_ATTACH_STATE_CONFLICT);
+ return -1;
+ }
+ Ebl *ebl = thread->process->ebl;
+ if (ebl_frame_nregs (ebl) == 0)
+ {
+ __libdwfl_seterrno (DWFL_E_NO_UNWIND);
+ return -1;
+ }
+ if (state_alloc (thread) == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return -1;
+ }
+ Dwfl_Process *process = thread->process;
+ if (! process->callbacks->set_initial_registers (thread,
+ thread->callbacks_arg))
+ {
+ thread_free_all_states (thread);
+ return -1;
+ }
+ if (! state_fetch_pc (thread->unwound))
+ {
+ if (process->callbacks->thread_detach)
+ process->callbacks->thread_detach (thread, thread->callbacks_arg);
+ thread_free_all_states (thread);
+ return -1;
+ }
+
+ Dwfl_Frame *state;
+ do
+ {
+ state = thread->unwound;
+ int err = callback (state, arg);
+ if (err != DWARF_CB_OK)
+ {
+ if (process->callbacks->thread_detach)
+ process->callbacks->thread_detach (thread, thread->callbacks_arg);
+ thread_free_all_states (thread);
+ return err;
+ }
+ __libdwfl_frame_unwind (state);
+ /* The old frame is no longer needed. */
+ state_free (thread->unwound);
+ state = thread->unwound;
+ }
+ while (state && state->pc_state == DWFL_FRAME_STATE_PC_SET);
+
+ Dwfl_Error err = dwfl_errno ();
+ if (process->callbacks->thread_detach)
+ process->callbacks->thread_detach (thread, thread->callbacks_arg);
+ if (state == NULL || state->pc_state == DWFL_FRAME_STATE_ERROR)
+ {
+ thread_free_all_states (thread);
+ __libdwfl_seterrno (err);
+ return -1;
+ }
+ assert (state->pc_state == DWFL_FRAME_STATE_PC_UNDEFINED);
+ thread_free_all_states (thread);
+ return 0;
+}
+INTDEF(dwfl_thread_getframes)
diff --git a/libdwfl/dwfl_frame_pc.c b/libdwfl/dwfl_frame_pc.c
new file mode 100644
index 0000000..296c815
--- /dev/null
+++ b/libdwfl/dwfl_frame_pc.c
@@ -0,0 +1,64 @@
+/* Get return address register value for frame.
+ Copyright (C) 2013 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+bool
+dwfl_frame_pc (Dwfl_Frame *state, Dwarf_Addr *pc, bool *isactivation)
+{
+ assert (state->pc_state == DWFL_FRAME_STATE_PC_SET);
+ *pc = state->pc;
+ ebl_normalize_pc (state->thread->process->ebl, pc);
+ if (isactivation)
+ {
+ /* Bottom frame? */
+ if (state->initial_frame)
+ *isactivation = true;
+ /* *ISACTIVATION is logical union of whether current or previous frame
+ state is SIGNAL_FRAME. */
+ else if (state->signal_frame)
+ *isactivation = true;
+ else
+ {
+ /* If the previous frame has unwound unsuccessfully just silently do
+ not consider it could be a SIGNAL_FRAME. */
+ __libdwfl_frame_unwind (state);
+ if (state->unwound == NULL
+ || state->unwound->pc_state != DWFL_FRAME_STATE_PC_SET)
+ *isactivation = false;
+ else
+ *isactivation = state->unwound->signal_frame;
+ }
+ }
+ return true;
+}
+INTDEF (dwfl_frame_pc)
diff --git a/libdwfl/dwfl_frame_regs.c b/libdwfl/dwfl_frame_regs.c
new file mode 100644
index 0000000..83b1abe
--- /dev/null
+++ b/libdwfl/dwfl_frame_regs.c
@@ -0,0 +1,61 @@
+/* Get Dwarf Frame state from modules present in DWFL.
+ Copyright (C) 2013 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+bool
+dwfl_thread_state_registers (Dwfl_Thread *thread, int firstreg,
+ unsigned nregs, const Dwarf_Word *regs)
+{
+ Dwfl_Frame *state = thread->unwound;
+ assert (state && state->unwound == NULL);
+ assert (state->initial_frame);
+ for (unsigned regno = firstreg; regno < firstreg + nregs; regno++)
+ if (! __libdwfl_frame_reg_set (state, regno, regs[regno - firstreg]))
+ {
+ __libdwfl_seterrno (DWFL_E_INVALID_REGISTER);
+ return false;
+ }
+ return true;
+}
+INTDEF(dwfl_thread_state_registers)
+
+void
+dwfl_thread_state_register_pc (Dwfl_Thread *thread, Dwarf_Word pc)
+{
+ Dwfl_Frame *state = thread->unwound;
+ assert (state && state->unwound == NULL);
+ assert (state->initial_frame);
+ state->pc = pc;
+ state->pc_state = DWFL_FRAME_STATE_PC_SET;
+}
+INTDEF(dwfl_thread_state_register_pc)
diff --git a/libdwfl/dwfl_getdwarf.c b/libdwfl/dwfl_getdwarf.c
new file mode 100644
index 0000000..edd088e
--- /dev/null
+++ b/libdwfl/dwfl_getdwarf.c
@@ -0,0 +1,63 @@
+/* Iterate through modules to fetch Dwarf information.
+ Copyright (C) 2005, 2008 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+struct module_callback_info
+{
+ int (*callback) (Dwfl_Module *, void **,
+ const char *, Dwarf_Addr,
+ Dwarf *, Dwarf_Addr, void *);
+ void *arg;
+};
+
+static int
+module_callback (Dwfl_Module *mod, void **userdata,
+ const char *name, Dwarf_Addr start, void *arg)
+{
+ const struct module_callback_info *info = arg;
+ Dwarf_Addr bias = 0;
+ Dwarf *dw = INTUSE(dwfl_module_getdwarf) (mod, &bias);
+ return (*info->callback) (mod, userdata, name, start, dw, bias, info->arg);
+}
+
+ptrdiff_t
+dwfl_getdwarf (Dwfl *dwfl,
+ int (*callback) (Dwfl_Module *, void **,
+ const char *, Dwarf_Addr,
+ Dwarf *, Dwarf_Addr, void *),
+ void *arg,
+ ptrdiff_t offset)
+{
+ struct module_callback_info info = { callback, arg };
+ return INTUSE(dwfl_getmodules) (dwfl, &module_callback, &info, offset);
+}
diff --git a/libdwfl/dwfl_getmodules.c b/libdwfl/dwfl_getmodules.c
new file mode 100644
index 0000000..243cb04
--- /dev/null
+++ b/libdwfl/dwfl_getmodules.c
@@ -0,0 +1,96 @@
+/* Iterate through modules.
+ Copyright (C) 2005, 2008 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+ptrdiff_t
+dwfl_getmodules (Dwfl *dwfl,
+ int (*callback) (Dwfl_Module *, void **,
+ const char *, Dwarf_Addr, void *),
+ void *arg,
+ ptrdiff_t offset)
+{
+ if (dwfl == NULL)
+ return -1;
+
+ /* We iterate through the linked list when it's all we have.
+ But continuing from an offset is slow that way. So when
+ DWFL->lookup_module is populated, we can instead keep our
+ place by jumping directly into the array. Since the actions
+ of a callback could cause it to get populated, we must
+ choose the style of place-holder when we return an offset,
+ and we encode the choice in the low bits of that value. */
+
+ Dwfl_Module *m = dwfl->modulelist;
+
+ if ((offset & 3) == 1)
+ {
+ offset >>= 2;
+ for (ptrdiff_t pos = 0; pos < offset; ++pos)
+ if (m == NULL)
+ return -1;
+ else
+ m = m->next;
+ }
+ else if (((offset & 3) == 2) && likely (dwfl->lookup_module != NULL))
+ {
+ offset >>= 2;
+
+ if ((size_t) offset - 1 == dwfl->lookup_elts)
+ return 0;
+
+ if (unlikely ((size_t) offset - 1 > dwfl->lookup_elts))
+ return -1;
+
+ m = dwfl->lookup_module[offset - 1];
+ if (unlikely (m == NULL))
+ return -1;
+ }
+ else if (offset != 0)
+ {
+ __libdwfl_seterrno (DWFL_E_BADSTROFF);
+ return -1;
+ }
+
+ while (m != NULL)
+ {
+ int ok = (*callback) (MODCB_ARGS (m), arg);
+ ++offset;
+ m = m->next;
+ if (ok != DWARF_CB_OK)
+ return ((dwfl->lookup_module == NULL) ? ((offset << 2) | 1)
+ : (((m == NULL ? (ptrdiff_t) dwfl->lookup_elts + 1
+ : m->segment + 1) << 2) | 2));
+ }
+ return 0;
+}
+INTDEF (dwfl_getmodules)
diff --git a/libdwfl/dwfl_getsrc.c b/libdwfl/dwfl_getsrc.c
new file mode 100644
index 0000000..d853aed
--- /dev/null
+++ b/libdwfl/dwfl_getsrc.c
@@ -0,0 +1,40 @@
+/* Find source location for PC address.
+ Copyright (C) 2005 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+Dwfl_Line *
+dwfl_getsrc (Dwfl *dwfl, Dwarf_Addr addr)
+{
+ return INTUSE(dwfl_module_getsrc) (INTUSE(dwfl_addrmodule) (dwfl, addr),
+ addr);
+}
diff --git a/libdwfl/dwfl_getsrclines.c b/libdwfl/dwfl_getsrclines.c
new file mode 100644
index 0000000..1ce78fc
--- /dev/null
+++ b/libdwfl/dwfl_getsrclines.c
@@ -0,0 +1,52 @@
+/* Fetch source line information for CU.
+ Copyright (C) 2005, 2013 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+int
+dwfl_getsrclines (Dwarf_Die *cudie, size_t *nlines)
+{
+ struct dwfl_cu *cu = (struct dwfl_cu *) cudie;
+
+ if (cu->lines == NULL)
+ {
+ Dwfl_Error error = __libdwfl_cu_getsrclines (cu);
+ if (error != DWFL_E_NOERROR)
+ {
+ __libdwfl_seterrno (error);
+ return -1;
+ }
+ }
+
+ *nlines = cu->die.cu->lines->nlines;
+ return 0;
+}
diff --git a/libdwfl/dwfl_line_comp_dir.c b/libdwfl/dwfl_line_comp_dir.c
new file mode 100644
index 0000000..77c3fdf
--- /dev/null
+++ b/libdwfl/dwfl_line_comp_dir.c
@@ -0,0 +1,47 @@
+/* Get information from a source line record returned by libdwfl.
+ Copyright (C) 2005, 2006 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include <dwarf.h>
+
+const char *
+dwfl_line_comp_dir (Dwfl_Line *line)
+{
+ if (line == NULL)
+ return NULL;
+
+ struct dwfl_cu *cu = dwfl_linecu (line);
+ Dwarf_Attribute attr_mem;
+ return INTUSE(dwarf_formstring) (INTUSE(dwarf_attr) (&cu->die,
+ DW_AT_comp_dir,
+ &attr_mem));
+}
diff --git a/libdwfl/dwfl_linecu.c b/libdwfl/dwfl_linecu.c
new file mode 100644
index 0000000..2043b17
--- /dev/null
+++ b/libdwfl/dwfl_linecu.c
@@ -0,0 +1,45 @@
+/* Fetch the module containing a source line record returned by libdwfl.
+ Copyright (C) 2006 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+#undef dwfl_linecu
+
+Dwarf_Die *
+dwfl_linecu (Dwfl_Line *line)
+{
+ if (line == NULL)
+ return NULL;
+
+ struct dwfl_cu *cu = dwfl_linecu_inline (line);
+ return &cu->die;
+}
diff --git a/libdwfl/dwfl_lineinfo.c b/libdwfl/dwfl_lineinfo.c
new file mode 100644
index 0000000..9618712
--- /dev/null
+++ b/libdwfl/dwfl_lineinfo.c
@@ -0,0 +1,65 @@
+/* Get information from a source line record returned by libdwfl.
+ Copyright (C) 2005-2010, 2015 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include "../libdw/libdwP.h"
+
+const char *
+dwfl_lineinfo (Dwfl_Line *line, Dwarf_Addr *addr, int *linep, int *colp,
+ Dwarf_Word *mtime, Dwarf_Word *length)
+{
+ if (line == NULL)
+ return NULL;
+
+ struct dwfl_cu *cu = dwfl_linecu (line);
+ const Dwarf_Line *info = &cu->die.cu->lines->info[line->idx];
+
+ if (addr != NULL)
+ *addr = dwfl_adjusted_dwarf_addr (cu->mod, info->addr);
+ if (linep != NULL)
+ *linep = info->line;
+ if (colp != NULL)
+ *colp = info->column;
+
+ if (unlikely (info->file >= info->files->nfiles))
+ {
+ __libdwfl_seterrno (DWFL_E (LIBDW, DWARF_E_INVALID_DWARF));
+ return NULL;
+ }
+
+ struct Dwarf_Fileinfo_s *file = &info->files->info[info->file];
+ if (mtime != NULL)
+ *mtime = file->mtime;
+ if (length != NULL)
+ *length = file->length;
+ return file->name;
+}
diff --git a/libdwfl/dwfl_linemodule.c b/libdwfl/dwfl_linemodule.c
new file mode 100644
index 0000000..d243f0d
--- /dev/null
+++ b/libdwfl/dwfl_linemodule.c
@@ -0,0 +1,42 @@
+/* Fetch the module containing a source line record returned by libdwfl.
+ Copyright (C) 2005 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+Dwfl_Module *
+dwfl_linemodule (Dwfl_Line *line)
+{
+ if (line == NULL)
+ return NULL;
+
+ return dwfl_linecu (line)->mod;
+}
diff --git a/libdwfl/dwfl_module.c b/libdwfl/dwfl_module.c
new file mode 100644
index 0000000..510bd69
--- /dev/null
+++ b/libdwfl/dwfl_module.c
@@ -0,0 +1,242 @@
+/* Maintenance of module list in libdwfl.
+ Copyright (C) 2005, 2006, 2007, 2008, 2014, 2015 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include "../libdw/cfi.h"
+#include <search.h>
+#include <unistd.h>
+
+static void
+free_cu (struct dwfl_cu *cu)
+{
+ if (cu->lines != NULL)
+ free (cu->lines);
+ free (cu);
+}
+
+static void
+nofree (void *arg __attribute__ ((unused)))
+{
+}
+
+static void
+free_file (struct dwfl_file *file)
+{
+ free (file->name);
+
+ /* Close the fd only on the last reference. */
+ if (file->elf != NULL && elf_end (file->elf) == 0 && file->fd != -1)
+ close (file->fd);
+}
+
+void
+internal_function
+__libdwfl_module_free (Dwfl_Module *mod)
+{
+ if (mod->lazy_cu_root != NULL)
+ tdestroy (mod->lazy_cu_root, nofree);
+
+ if (mod->aranges != NULL)
+ free (mod->aranges);
+
+ if (mod->cu != NULL)
+ {
+ for (size_t i = 0; i < mod->ncu; ++i)
+ free_cu (mod->cu[i]);
+ free (mod->cu);
+ }
+
+ /* We might have primed the Dwarf_CFI ebl cache with our own ebl
+ in __libdwfl_set_cfi. Make sure we don't free it twice. */
+ if (mod->eh_cfi != NULL)
+ {
+ if (mod->eh_cfi->ebl != NULL && mod->eh_cfi->ebl == mod->ebl)
+ mod->eh_cfi->ebl = NULL;
+ dwarf_cfi_end (mod->eh_cfi);
+ }
+
+ if (mod->dwarf_cfi != NULL)
+ {
+ if (mod->dwarf_cfi->ebl != NULL && mod->dwarf_cfi->ebl == mod->ebl)
+ mod->dwarf_cfi->ebl = NULL;
+ /* We don't need to explicitly destroy the dwarf_cfi.
+ That will be done by dwarf_end. */
+ }
+
+ if (mod->dw != NULL)
+ {
+ INTUSE(dwarf_end) (mod->dw);
+ if (mod->alt != NULL)
+ {
+ INTUSE(dwarf_end) (mod->alt);
+ if (mod->alt_elf != NULL)
+ elf_end (mod->alt_elf);
+ if (mod->alt_fd != -1)
+ close (mod->alt_fd);
+ }
+ }
+
+ if (mod->ebl != NULL)
+ ebl_closebackend (mod->ebl);
+
+ if (mod->debug.elf != mod->main.elf)
+ free_file (&mod->debug);
+ free_file (&mod->main);
+ free_file (&mod->aux_sym);
+
+ if (mod->build_id_bits != NULL)
+ free (mod->build_id_bits);
+
+ if (mod->reloc_info != NULL)
+ free (mod->reloc_info);
+
+ free (mod->name);
+ free (mod);
+}
+
+void
+dwfl_report_begin_add (Dwfl *dwfl __attribute__ ((unused)))
+{
+ /* The lookup table will be cleared on demand, there is nothing we need
+ to do here. */
+}
+INTDEF (dwfl_report_begin_add)
+
+void
+dwfl_report_begin (Dwfl *dwfl)
+{
+ /* Clear the segment lookup table. */
+ dwfl->lookup_elts = 0;
+
+ for (Dwfl_Module *m = dwfl->modulelist; m != NULL; m = m->next)
+ m->gc = true;
+
+ dwfl->offline_next_address = OFFLINE_REDZONE;
+}
+INTDEF (dwfl_report_begin)
+
+static inline Dwfl_Module *
+use (Dwfl_Module *mod, Dwfl_Module **tailp, Dwfl *dwfl)
+{
+ mod->next = *tailp;
+ *tailp = mod;
+
+ if (unlikely (dwfl->lookup_module != NULL))
+ {
+ free (dwfl->lookup_module);
+ dwfl->lookup_module = NULL;
+ }
+
+ return mod;
+}
+
+/* Report that a module called NAME spans addresses [START, END).
+ Returns the module handle, either existing or newly allocated,
+ or returns a null pointer for an allocation error. */
+Dwfl_Module *
+dwfl_report_module (Dwfl *dwfl, const char *name,
+ GElf_Addr start, GElf_Addr end)
+{
+ Dwfl_Module **tailp = &dwfl->modulelist, **prevp = tailp;
+
+ for (Dwfl_Module *m = *prevp; m != NULL; m = *(prevp = &m->next))
+ {
+ if (m->low_addr == start && m->high_addr == end
+ && !strcmp (m->name, name))
+ {
+ /* This module is still here. Move it to the place in the list
+ after the last module already reported. */
+ *prevp = m->next;
+ m->gc = false;
+ return use (m, tailp, dwfl);
+ }
+
+ if (! m->gc)
+ tailp = &m->next;
+ }
+
+ Dwfl_Module *mod = calloc (1, sizeof *mod);
+ if (mod == NULL)
+ goto nomem;
+
+ mod->name = strdup (name);
+ if (mod->name == NULL)
+ {
+ free (mod);
+ nomem:
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return NULL;
+ }
+
+ mod->low_addr = start;
+ mod->high_addr = end;
+ mod->dwfl = dwfl;
+
+ return use (mod, tailp, dwfl);
+}
+INTDEF (dwfl_report_module)
+
+
+/* Finish reporting the current set of modules to the library.
+ If REMOVED is not null, it's called for each module that
+ existed before but was not included in the current report.
+ Returns a nonzero return value from the callback.
+ DWFL cannot be used until this function has returned zero. */
+int
+dwfl_report_end (Dwfl *dwfl,
+ int (*removed) (Dwfl_Module *, void *,
+ const char *, Dwarf_Addr,
+ void *arg),
+ void *arg)
+{
+ Dwfl_Module **tailp = &dwfl->modulelist;
+ while (*tailp != NULL)
+ {
+ Dwfl_Module *m = *tailp;
+ if (m->gc && removed != NULL)
+ {
+ int result = (*removed) (MODCB_ARGS (m), arg);
+ if (result != 0)
+ return result;
+ }
+ if (m->gc)
+ {
+ *tailp = m->next;
+ __libdwfl_module_free (m);
+ }
+ else
+ tailp = &m->next;
+ }
+
+ return 0;
+}
+INTDEF (dwfl_report_end)
diff --git a/libdwfl/dwfl_module_addrdie.c b/libdwfl/dwfl_module_addrdie.c
new file mode 100644
index 0000000..b44ec13
--- /dev/null
+++ b/libdwfl/dwfl_module_addrdie.c
@@ -0,0 +1,49 @@
+/* Fetch the CU DIE for a PC address in a given module.
+ Copyright (C) 2005 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+Dwarf_Die *
+dwfl_module_addrdie (Dwfl_Module *mod, Dwarf_Addr addr, Dwarf_Addr *bias)
+{
+ if (INTUSE(dwfl_module_getdwarf) (mod, bias) == NULL)
+ return NULL;
+
+ struct dwfl_cu *cu;
+ Dwfl_Error error = __libdwfl_addrcu (mod, addr, &cu);
+ if (likely (error == DWFL_E_NOERROR))
+ return &cu->die;
+
+ __libdwfl_seterrno (error);
+ return NULL;
+}
+INTDEF (dwfl_module_addrdie)
diff --git a/libdwfl/dwfl_module_addrname.c b/libdwfl/dwfl_module_addrname.c
new file mode 100644
index 0000000..3142b3e
--- /dev/null
+++ b/libdwfl/dwfl_module_addrname.c
@@ -0,0 +1,42 @@
+/* Find debugging and symbol information for a module in libdwfl.
+ Copyright (C) 2005, 2006, 2007, 2013 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+const char *
+dwfl_module_addrname (Dwfl_Module *mod, GElf_Addr addr)
+{
+ GElf_Off off;
+ GElf_Sym sym;
+ return INTUSE(dwfl_module_addrinfo) (mod, addr, &off, &sym,
+ NULL, NULL, NULL);
+}
diff --git a/libdwfl/dwfl_module_addrsym.c b/libdwfl/dwfl_module_addrsym.c
new file mode 100644
index 0000000..db302e6
--- /dev/null
+++ b/libdwfl/dwfl_module_addrsym.c
@@ -0,0 +1,332 @@
+/* Find debugging and symbol information for a module in libdwfl.
+ Copyright (C) 2005-2013 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+struct search_state
+{
+ Dwfl_Module *mod;
+ GElf_Addr addr;
+
+ GElf_Sym *closest_sym;
+ bool adjust_st_value;
+ GElf_Word addr_shndx;
+ Elf *addr_symelf;
+
+ /* Keep track of the closest symbol we have seen so far.
+ Here we store only symbols with nonzero st_size. */
+ const char *closest_name;
+ GElf_Addr closest_value;
+ GElf_Word closest_shndx;
+ Elf *closest_elf;
+
+ /* Keep track of an eligible symbol with st_size == 0 as a fallback. */
+ const char *sizeless_name;
+ GElf_Sym sizeless_sym;
+ GElf_Addr sizeless_value;
+ GElf_Word sizeless_shndx;
+ Elf *sizeless_elf;
+
+ /* Keep track of the lowest address a relevant sizeless symbol could have. */
+ GElf_Addr min_label;
+};
+
+/* Return true iff we consider ADDR to lie in the same section as SYM. */
+static inline bool
+same_section (struct search_state *state,
+ GElf_Addr value, Elf *symelf, GElf_Word shndx)
+{
+ /* For absolute symbols and the like, only match exactly. */
+ if (shndx >= SHN_LORESERVE)
+ return value == state->addr;
+
+ /* If value might not be st_value, the shndx of the symbol might
+ not match the section of the value. Explicitly look both up. */
+ if (! state->adjust_st_value)
+ {
+ Dwarf_Addr v;
+ if (state->addr_shndx == SHN_UNDEF)
+ {
+ v = state->addr;
+ state->addr_shndx = __libdwfl_find_section_ndx (state->mod, &v);
+ }
+
+ v = value;
+ return state->addr_shndx == __libdwfl_find_section_ndx (state->mod, &v);
+ }
+
+ /* Figure out what section ADDR lies in. */
+ if (state->addr_shndx == SHN_UNDEF || state->addr_symelf != symelf)
+ {
+ GElf_Addr mod_addr = dwfl_deadjust_st_value (state->mod, symelf,
+ state->addr);
+ Elf_Scn *scn = NULL;
+ state->addr_shndx = SHN_ABS;
+ state->addr_symelf = symelf;
+ while ((scn = elf_nextscn (symelf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (likely (shdr != NULL)
+ && mod_addr >= shdr->sh_addr
+ && mod_addr < shdr->sh_addr + shdr->sh_size)
+ {
+ state->addr_shndx = elf_ndxscn (scn);
+ break;
+ }
+ }
+ }
+
+ return shndx == state->addr_shndx && state->addr_symelf == symelf;
+}
+
+/* Return GELF_ST_BIND as higher-is-better integer. */
+static inline int
+binding_value (const GElf_Sym *symp)
+{
+ switch (GELF_ST_BIND (symp->st_info))
+ {
+ case STB_GLOBAL:
+ return 3;
+ case STB_WEAK:
+ return 2;
+ case STB_LOCAL:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/* Try one symbol and associated value from the search table. */
+static inline void
+try_sym_value (struct search_state *state,
+ GElf_Addr value, GElf_Sym *sym,
+ const char *name, GElf_Word shndx,
+ Elf *elf, bool resolved)
+{
+ /* Even if we don't choose this symbol, its existence excludes
+ any sizeless symbol (assembly label) that is below its upper
+ bound. */
+ if (value + sym->st_size > state->min_label)
+ state->min_label = value + sym->st_size;
+
+ if (sym->st_size == 0 || state->addr - value < sym->st_size)
+ {
+ /* This symbol is a better candidate than the current one
+ if it's closer to ADDR or is global when it was local. */
+ if (state->closest_name == NULL
+ || state->closest_value < value
+ || binding_value (state->closest_sym) < binding_value (sym))
+ {
+ if (sym->st_size != 0)
+ {
+ *state->closest_sym = *sym;
+ state->closest_value = value;
+ state->closest_shndx = shndx;
+ state->closest_elf = elf;
+ state->closest_name = name;
+ }
+ else if (state->closest_name == NULL
+ && value >= state->min_label
+ && same_section (state, value,
+ resolved ? state->mod->main.elf : elf,
+ shndx))
+ {
+ /* Handwritten assembly symbols sometimes have no
+ st_size. If no symbol with proper size includes
+ the address, we'll use the closest one that is in
+ the same section as ADDR. */
+ state->sizeless_sym = *sym;
+ state->sizeless_value = value;
+ state->sizeless_shndx = shndx;
+ state->sizeless_elf = elf;
+ state->sizeless_name = name;
+ }
+ }
+ /* When the beginning of its range is no closer,
+ the end of its range might be. Otherwise follow
+ GELF_ST_BIND preference. If all are equal prefer
+ the first symbol found. */
+ else if (sym->st_size != 0
+ && state->closest_value == value
+ && ((state->closest_sym->st_size > sym->st_size
+ && (binding_value (state->closest_sym)
+ <= binding_value (sym)))
+ || (state->closest_sym->st_size >= sym->st_size
+ && (binding_value (state->closest_sym)
+ < binding_value (sym)))))
+ {
+ *state->closest_sym = *sym;
+ state->closest_value = value;
+ state->closest_shndx = shndx;
+ state->closest_elf = elf;
+ state->closest_name = name;
+ }
+ }
+}
+
+/* Look through the symbol table for a matching symbol. */
+static inline void
+search_table (struct search_state *state, int start, int end)
+{
+ for (int i = start; i < end; ++i)
+ {
+ GElf_Sym sym;
+ GElf_Addr value;
+ GElf_Word shndx;
+ Elf *elf;
+ bool resolved;
+ const char *name = __libdwfl_getsym (state->mod, i, &sym, &value,
+ &shndx, &elf, NULL,
+ &resolved,
+ state->adjust_st_value);
+ if (name != NULL && name[0] != '\0'
+ && sym.st_shndx != SHN_UNDEF
+ && value <= state->addr
+ && GELF_ST_TYPE (sym.st_info) != STT_SECTION
+ && GELF_ST_TYPE (sym.st_info) != STT_FILE
+ && GELF_ST_TYPE (sym.st_info) != STT_TLS)
+ {
+ try_sym_value (state, value, &sym, name, shndx, elf, resolved);
+
+ /* If this is an addrinfo variant and the value could be
+ resolved then also try matching the (adjusted) st_value. */
+ if (resolved && state->mod->e_type != ET_REL)
+ {
+ GElf_Addr adjusted_st_value;
+ adjusted_st_value = dwfl_adjusted_st_value (state->mod, elf,
+ sym.st_value);
+ if (value != adjusted_st_value
+ && adjusted_st_value <= state->addr)
+ try_sym_value (state, adjusted_st_value, &sym, name, shndx,
+ elf, false);
+ }
+ }
+ }
+}
+
+/* Returns the name of the symbol "closest" to ADDR.
+ Never returns symbols at addresses above ADDR. */
+const char *
+internal_function
+__libdwfl_addrsym (Dwfl_Module *_mod, GElf_Addr _addr, GElf_Off *off,
+ GElf_Sym *_closest_sym, GElf_Word *shndxp,
+ Elf **elfp, Dwarf_Addr *biasp, bool _adjust_st_value)
+{
+ int syments = INTUSE(dwfl_module_getsymtab) (_mod);
+ if (syments < 0)
+ return NULL;
+
+ struct search_state state =
+ {
+ .addr = _addr,
+ .mod = _mod,
+ .closest_sym = _closest_sym,
+ .adjust_st_value = _adjust_st_value,
+ .addr_shndx = SHN_UNDEF,
+ .addr_symelf = NULL,
+ .closest_name = NULL,
+ .closest_value = 0,
+ .closest_shndx = SHN_UNDEF,
+ .closest_elf = NULL,
+ .sizeless_name = NULL,
+ .sizeless_sym = { 0, 0, 0, 0, 0, SHN_UNDEF },
+ .sizeless_value = 0,
+ .sizeless_shndx = SHN_UNDEF,
+ .sizeless_elf = NULL,
+ .min_label = 0
+ };
+
+ /* First go through global symbols. mod->first_global and
+ mod->aux_first_global are setup by dwfl_module_getsymtab to the
+ index of the first global symbol in those symbol tables. Both
+ are non-zero when the table exist, except when there is only a
+ dynsym table loaded through phdrs, then first_global is zero and
+ there will be no auxiliary table. All symbols with local binding
+ come first in the symbol table, then all globals. The zeroth,
+ null entry, in the auxiliary table is skipped if there is a main
+ table. */
+ int first_global = INTUSE (dwfl_module_getsymtab_first_global) (state.mod);
+ if (first_global < 0)
+ return NULL;
+ search_table (&state, first_global == 0 ? 1 : first_global, syments);
+
+ /* If we found nothing searching the global symbols, then try the locals.
+ Unless we have a global sizeless symbol that matches exactly. */
+ if (state.closest_name == NULL && first_global > 1
+ && (state.sizeless_name == NULL || state.sizeless_value != state.addr))
+ search_table (&state, 1, first_global);
+
+ /* If we found no proper sized symbol to use, fall back to the best
+ candidate sizeless symbol we found, if any. */
+ if (state.closest_name == NULL
+ && state.sizeless_name != NULL
+ && state.sizeless_value >= state.min_label)
+ {
+ *state.closest_sym = state.sizeless_sym;
+ state.closest_value = state.sizeless_value;
+ state.closest_shndx = state.sizeless_shndx;
+ state.closest_elf = state.sizeless_elf;
+ state.closest_name = state.sizeless_name;
+ }
+
+ *off = state.addr - state.closest_value;
+
+ if (shndxp != NULL)
+ *shndxp = state.closest_shndx;
+ if (elfp != NULL)
+ *elfp = state.closest_elf;
+ if (biasp != NULL)
+ *biasp = dwfl_adjusted_st_value (state.mod, state.closest_elf, 0);
+ return state.closest_name;
+}
+
+
+const char *
+dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr,
+ GElf_Sym *closest_sym, GElf_Word *shndxp)
+{
+ GElf_Off off;
+ return __libdwfl_addrsym (mod, addr, &off, closest_sym, shndxp,
+ NULL, NULL, true);
+}
+INTDEF (dwfl_module_addrsym)
+
+const char
+*dwfl_module_addrinfo (Dwfl_Module *mod, GElf_Addr address,
+ GElf_Off *offset, GElf_Sym *sym,
+ GElf_Word *shndxp, Elf **elfp, Dwarf_Addr *bias)
+{
+ return __libdwfl_addrsym (mod, address, offset, sym, shndxp, elfp, bias,
+ false);
+}
+INTDEF (dwfl_module_addrinfo)
diff --git a/libdwfl/dwfl_module_build_id.c b/libdwfl/dwfl_module_build_id.c
new file mode 100644
index 0000000..6ca9376
--- /dev/null
+++ b/libdwfl/dwfl_module_build_id.c
@@ -0,0 +1,121 @@
+/* Return build ID information for a module.
+ Copyright (C) 2007-2010, 2014 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+static int
+found_build_id (Dwfl_Module *mod, bool set,
+ const void *bits, int len, GElf_Addr vaddr)
+{
+ if (!set)
+ /* When checking bits, we do not compare VADDR because the
+ address found in a debuginfo file may not match the main
+ file as modified by prelink. */
+ return 1 + (mod->build_id_len == len
+ && !memcmp (bits, mod->build_id_bits, len));
+
+ void *copy = malloc (len);
+ if (unlikely (copy == NULL))
+ {
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return -1;
+ }
+
+ mod->build_id_bits = memcpy (copy, bits, len);
+ mod->build_id_vaddr = vaddr;
+ mod->build_id_len = len;
+ return len;
+}
+
+int
+internal_function
+__libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf)
+{
+ const void *build_id_bits;
+ GElf_Addr build_id_elfaddr;
+ int build_id_len;
+
+ /* For mod == NULL use dwelf_elf_gnu_build_id directly. */
+ assert (mod != NULL);
+
+ int result = __libdwfl_find_elf_build_id (mod, elf, &build_id_bits,
+ &build_id_elfaddr, &build_id_len);
+ if (result <= 0)
+ return result;
+
+ GElf_Addr build_id_vaddr = build_id_elfaddr + (build_id_elfaddr != 0
+ ? mod->main_bias : 0);
+ return found_build_id (mod, set, build_id_bits, build_id_len, build_id_vaddr);
+}
+
+int
+dwfl_module_build_id (Dwfl_Module *mod,
+ const unsigned char **bits, GElf_Addr *vaddr)
+{
+ if (mod == NULL)
+ return -1;
+
+ if (mod->build_id_len == 0 && mod->main.elf != NULL)
+ {
+ /* We have the file, but have not examined it yet. */
+ int result = __libdwfl_find_build_id (mod, true, mod->main.elf);
+ if (result <= 0)
+ {
+ mod->build_id_len = -1; /* Cache negative result. */
+ return result;
+ }
+ }
+
+ if (mod->build_id_len <= 0)
+ return 0;
+
+ *bits = mod->build_id_bits;
+ *vaddr = mod->build_id_vaddr;
+ return mod->build_id_len;
+}
+INTDEF (dwfl_module_build_id)
+NEW_VERSION (dwfl_module_build_id, ELFUTILS_0.138)
+
+#ifdef SYMBOL_VERSIONING
+COMPAT_VERSION (dwfl_module_build_id, ELFUTILS_0.130, vaddr_at_end)
+
+int
+_compat_vaddr_at_end_dwfl_module_build_id (Dwfl_Module *mod,
+ const unsigned char **bits,
+ GElf_Addr *vaddr)
+{
+ int result = INTUSE(dwfl_module_build_id) (mod, bits, vaddr);
+ if (result > 0)
+ *vaddr += (result + 3) & -4;
+ return result;
+}
+#endif
diff --git a/libdwfl/dwfl_module_dwarf_cfi.c b/libdwfl/dwfl_module_dwarf_cfi.c
new file mode 100644
index 0000000..0e5b435
--- /dev/null
+++ b/libdwfl/dwfl_module_dwarf_cfi.c
@@ -0,0 +1,73 @@
+/* Find DWARF CFI for a module in libdwfl.
+ Copyright (C) 2009-2010 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include "../libdw/cfi.h"
+
+Dwarf_CFI *
+internal_function
+__libdwfl_set_cfi (Dwfl_Module *mod, Dwarf_CFI **slot, Dwarf_CFI *cfi)
+{
+ if (cfi != NULL && cfi->ebl == NULL)
+ {
+ Dwfl_Error error = __libdwfl_module_getebl (mod);
+ if (error == DWFL_E_NOERROR)
+ cfi->ebl = mod->ebl;
+ else
+ {
+ if (slot == &mod->eh_cfi)
+ INTUSE(dwarf_cfi_end) (cfi);
+ __libdwfl_seterrno (error);
+ return NULL;
+ }
+ }
+
+ return *slot = cfi;
+}
+
+Dwarf_CFI *
+dwfl_module_dwarf_cfi (Dwfl_Module *mod, Dwarf_Addr *bias)
+{
+ if (mod == NULL)
+ return NULL;
+
+ if (mod->dwarf_cfi != NULL)
+ {
+ *bias = dwfl_adjusted_dwarf_addr (mod, 0);
+ return mod->dwarf_cfi;
+ }
+
+ return __libdwfl_set_cfi (mod, &mod->dwarf_cfi,
+ INTUSE(dwarf_getcfi)
+ (INTUSE(dwfl_module_getdwarf) (mod, bias)));
+}
+INTDEF (dwfl_module_dwarf_cfi)
diff --git a/libdwfl/dwfl_module_eh_cfi.c b/libdwfl/dwfl_module_eh_cfi.c
new file mode 100644
index 0000000..c296e39
--- /dev/null
+++ b/libdwfl/dwfl_module_eh_cfi.c
@@ -0,0 +1,59 @@
+/* Find EH CFI for a module in libdwfl.
+ Copyright (C) 2009-2010 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include "../libdw/cfi.h"
+
+Dwarf_CFI *
+dwfl_module_eh_cfi (Dwfl_Module *mod, Dwarf_Addr *bias)
+{
+ if (mod == NULL)
+ return NULL;
+
+ if (mod->eh_cfi != NULL)
+ {
+ *bias = dwfl_adjusted_address (mod, 0);
+ return mod->eh_cfi;
+ }
+
+ __libdwfl_getelf (mod);
+ if (mod->elferr != DWFL_E_NOERROR)
+ {
+ __libdwfl_seterrno (mod->elferr);
+ return NULL;
+ }
+
+ *bias = dwfl_adjusted_address (mod, 0);
+ return __libdwfl_set_cfi (mod, &mod->eh_cfi,
+ INTUSE(dwarf_getcfi_elf) (mod->main.elf));
+}
+INTDEF (dwfl_module_eh_cfi)
diff --git a/libdwfl/dwfl_module_getdwarf.c b/libdwfl/dwfl_module_getdwarf.c
new file mode 100644
index 0000000..9775ace
--- /dev/null
+++ b/libdwfl/dwfl_module_getdwarf.c
@@ -0,0 +1,1494 @@
+/* Find debugging and symbol information for a module in libdwfl.
+ Copyright (C) 2005-2012, 2014, 2015 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include <inttypes.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include "../libdw/libdwP.h" /* DWARF_E_* values are here. */
+#include "../libelf/libelfP.h"
+#include "system.h"
+
+static inline Dwfl_Error
+open_elf_file (Elf **elf, int *fd, char **name)
+{
+ if (*elf == NULL)
+ {
+ /* CBFAIL uses errno if it's set, so clear it first in case we don't
+ set it with an open failure below. */
+ errno = 0;
+
+ /* If there was a pre-primed file name left that the callback left
+ behind, try to open that file name. */
+ if (*fd < 0 && *name != NULL)
+ *fd = TEMP_FAILURE_RETRY (open (*name, O_RDONLY));
+
+ if (*fd < 0)
+ return CBFAIL;
+
+ return __libdw_open_file (fd, elf, true, false);
+ }
+ else if (unlikely (elf_kind (*elf) != ELF_K_ELF))
+ {
+ elf_end (*elf);
+ *elf = NULL;
+ close (*fd);
+ *fd = -1;
+ return DWFL_E_BADELF;
+ }
+
+ /* Elf file already open and looks fine. */
+ return DWFL_E_NOERROR;
+}
+
+/* Open libelf FILE->fd and compute the load base of ELF as loaded in MOD.
+ When we return success, FILE->elf and FILE->vaddr are set up. */
+static inline Dwfl_Error
+open_elf (Dwfl_Module *mod, struct dwfl_file *file)
+{
+ Dwfl_Error error = open_elf_file (&file->elf, &file->fd, &file->name);
+ if (error != DWFL_E_NOERROR)
+ return error;
+
+ GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (file->elf, &ehdr_mem);
+ if (ehdr == NULL)
+ {
+ elf_error:
+ elf_end (file->elf);
+ file->elf = NULL;
+ close (file->fd);
+ file->fd = -1;
+ return DWFL_E (LIBELF, elf_errno ());
+ }
+
+ if (ehdr->e_type != ET_REL)
+ {
+ /* In any non-ET_REL file, we compute the "synchronization address".
+
+ We start with the address at the end of the first PT_LOAD
+ segment. When prelink converts REL to RELA in an ET_DYN
+ file, it expands the space between the beginning of the
+ segment and the actual code/data addresses. Since that
+ change wasn't made in the debug file, the distance from
+ p_vaddr to an address of interest (in an st_value or DWARF
+ data) now differs between the main and debug files. The
+ distance from address_sync to an address of interest remains
+ consistent.
+
+ If there are no section headers at all (full stripping), then
+ the end of the first segment is a valid synchronization address.
+ This cannot happen in a prelinked file, since prelink itself
+ relies on section headers for prelinking and for undoing it.
+ (If you do full stripping on a prelinked file, then you get what
+ you deserve--you can neither undo the prelinking, nor expect to
+ line it up with a debug file separated before prelinking.)
+
+ However, when prelink processes an ET_EXEC file, it can do
+ something different. There it juggles the "special" sections
+ (SHT_DYNSYM et al) to make space for the additional prelink
+ special sections. Sometimes it will do this by moving a special
+ section like .dynstr after the real program sections in the first
+ PT_LOAD segment--i.e. to the end. That changes the end address of
+ the segment, so it no longer lines up correctly and is not a valid
+ synchronization address to use. Because of this, we need to apply
+ a different prelink-savvy means to discover the synchronization
+ address when there is a separate debug file and a prelinked main
+ file. That is done in find_debuginfo, below. */
+
+ size_t phnum;
+ if (unlikely (elf_getphdrnum (file->elf, &phnum) != 0))
+ goto elf_error;
+
+ file->vaddr = file->address_sync = 0;
+ for (size_t i = 0; i < phnum; ++i)
+ {
+ GElf_Phdr ph_mem;
+ GElf_Phdr *ph = gelf_getphdr (file->elf, i, &ph_mem);
+ if (unlikely (ph == NULL))
+ goto elf_error;
+ if (ph->p_type == PT_LOAD)
+ {
+ file->vaddr = ph->p_vaddr & -ph->p_align;
+ file->address_sync = ph->p_vaddr + ph->p_memsz;
+ break;
+ }
+ }
+ }
+
+ /* We only want to set the module e_type explictly once, derived from
+ the main ELF file. (It might be changed for the kernel, because
+ that is special - see below.) open_elf is always called first for
+ the main ELF file, because both find_dw and find_symtab call
+ __libdwfl_getelf first to open the main file. So don't let debug
+ or aux files override the module e_type. The kernel heuristic
+ below could otherwise trigger for non-kernel/non-main files, since
+ their phdrs might not match the actual load addresses. */
+ if (file == &mod->main)
+ {
+ mod->e_type = ehdr->e_type;
+
+ /* Relocatable Linux kernels are ET_EXEC but act like ET_DYN. */
+ if (mod->e_type == ET_EXEC && file->vaddr != mod->low_addr)
+ mod->e_type = ET_DYN;
+ }
+ else
+ assert (mod->main.elf != NULL);
+
+ return DWFL_E_NOERROR;
+}
+
+/* We have an authoritative build ID for this module MOD, so don't use
+ a file by name that doesn't match that ID. */
+static void
+mod_verify_build_id (Dwfl_Module *mod)
+{
+ assert (mod->build_id_len > 0);
+
+ switch (__builtin_expect (__libdwfl_find_build_id (mod, false,
+ mod->main.elf), 2))
+ {
+ case 2:
+ /* Build ID matches as it should. */
+ return;
+
+ case -1: /* ELF error. */
+ mod->elferr = INTUSE(dwfl_errno) ();
+ break;
+
+ case 0: /* File has no build ID note. */
+ case 1: /* FIle has a build ID that does not match. */
+ mod->elferr = DWFL_E_WRONG_ID_ELF;
+ break;
+
+ default:
+ abort ();
+ }
+
+ /* We get here when it was the right ELF file. Clear it out. */
+ elf_end (mod->main.elf);
+ mod->main.elf = NULL;
+ if (mod->main.fd >= 0)
+ {
+ close (mod->main.fd);
+ mod->main.fd = -1;
+ }
+}
+
+/* Find the main ELF file for this module and open libelf on it.
+ When we return success, MOD->main.elf and MOD->main.bias are set up. */
+void
+internal_function
+__libdwfl_getelf (Dwfl_Module *mod)
+{
+ if (mod->main.elf != NULL /* Already done. */
+ || mod->elferr != DWFL_E_NOERROR) /* Cached failure. */
+ return;
+
+ mod->main.fd = (*mod->dwfl->callbacks->find_elf) (MODCB_ARGS (mod),
+ &mod->main.name,
+ &mod->main.elf);
+ const bool fallback = mod->main.elf == NULL && mod->main.fd < 0;
+ mod->elferr = open_elf (mod, &mod->main);
+ if (mod->elferr != DWFL_E_NOERROR)
+ return;
+
+ if (!mod->main.valid)
+ {
+ /* Clear any explicitly reported build ID, just in case it was wrong.
+ We'll fetch it from the file when asked. */
+ free (mod->build_id_bits);
+ mod->build_id_bits = NULL;
+ mod->build_id_len = 0;
+ }
+ else if (fallback)
+ mod_verify_build_id (mod);
+
+ mod->main_bias = mod->e_type == ET_REL ? 0 : mod->low_addr - mod->main.vaddr;
+}
+
+static inline void
+consider_shdr (GElf_Addr interp,
+ GElf_Word sh_type,
+ GElf_Xword sh_flags,
+ GElf_Addr sh_addr,
+ GElf_Xword sh_size,
+ GElf_Addr *phighest)
+{
+ if ((sh_flags & SHF_ALLOC)
+ && ((sh_type == SHT_PROGBITS && sh_addr != interp)
+ || sh_type == SHT_NOBITS))
+ {
+ const GElf_Addr sh_end = sh_addr + sh_size;
+ if (sh_end > *phighest)
+ *phighest = sh_end;
+ }
+}
+
+/* If the main file might have been prelinked, then we need to
+ discover the correct synchronization address between the main and
+ debug files. Because of prelink's section juggling, we cannot rely
+ on the address_sync computed from PT_LOAD segments (see open_elf).
+
+ We will attempt to discover a synchronization address based on the
+ section headers instead. But finding a section address that is
+ safe to use requires identifying which sections are SHT_PROGBITS.
+ We can do that in the main file, but in the debug file all the
+ allocated sections have been transformed into SHT_NOBITS so we have
+ lost the means to match them up correctly.
+
+ The only method left to us is to decode the .gnu.prelink_undo
+ section in the prelinked main file. This shows what the sections
+ looked like before prelink juggled them--when they still had a
+ direct correspondence to the debug file. */
+static Dwfl_Error
+find_prelink_address_sync (Dwfl_Module *mod, struct dwfl_file *file)
+{
+ /* The magic section is only identified by name. */
+ size_t shstrndx;
+ if (elf_getshdrstrndx (mod->main.elf, &shstrndx) < 0)
+ return DWFL_E_LIBELF;
+
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (unlikely (shdr == NULL))
+ return DWFL_E_LIBELF;
+ if (shdr->sh_type == SHT_PROGBITS
+ && !(shdr->sh_flags & SHF_ALLOC)
+ && shdr->sh_name != 0)
+ {
+ const char *secname = elf_strptr (mod->main.elf, shstrndx,
+ shdr->sh_name);
+ if (unlikely (secname == NULL))
+ return DWFL_E_LIBELF;
+ if (!strcmp (secname, ".gnu.prelink_undo"))
+ break;
+ }
+ }
+
+ if (scn == NULL)
+ /* There was no .gnu.prelink_undo section. */
+ return DWFL_E_NOERROR;
+
+ Elf_Data *undodata = elf_rawdata (scn, NULL);
+ if (unlikely (undodata == NULL))
+ return DWFL_E_LIBELF;
+
+ /* Decode the section. It consists of the original ehdr, phdrs,
+ and shdrs (but omits section 0). */
+
+ union
+ {
+ Elf32_Ehdr e32;
+ Elf64_Ehdr e64;
+ } ehdr;
+ Elf_Data dst =
+ {
+ .d_buf = &ehdr,
+ .d_size = sizeof ehdr,
+ .d_type = ELF_T_EHDR,
+ .d_version = EV_CURRENT
+ };
+ Elf_Data src = *undodata;
+ src.d_size = gelf_fsize (mod->main.elf, ELF_T_EHDR, 1, EV_CURRENT);
+ src.d_type = ELF_T_EHDR;
+ if (unlikely (gelf_xlatetom (mod->main.elf, &dst, &src,
+ elf_getident (mod->main.elf, NULL)[EI_DATA])
+ == NULL))
+ return DWFL_E_LIBELF;
+
+ size_t shentsize = gelf_fsize (mod->main.elf, ELF_T_SHDR, 1, EV_CURRENT);
+ size_t phentsize = gelf_fsize (mod->main.elf, ELF_T_PHDR, 1, EV_CURRENT);
+
+ uint_fast16_t phnum;
+ uint_fast16_t shnum;
+ if (ehdr.e32.e_ident[EI_CLASS] == ELFCLASS32)
+ {
+ if (ehdr.e32.e_shentsize != shentsize
+ || ehdr.e32.e_phentsize != phentsize)
+ return DWFL_E_BAD_PRELINK;
+ phnum = ehdr.e32.e_phnum;
+ shnum = ehdr.e32.e_shnum;
+ }
+ else
+ {
+ if (ehdr.e64.e_shentsize != shentsize
+ || ehdr.e64.e_phentsize != phentsize)
+ return DWFL_E_BAD_PRELINK;
+ phnum = ehdr.e64.e_phnum;
+ shnum = ehdr.e64.e_shnum;
+ }
+
+ /* Since prelink does not store the zeroth section header in the undo
+ section, it cannot support SHN_XINDEX encoding. */
+ if (unlikely (shnum >= SHN_LORESERVE) || unlikely(shnum == 0)
+ || unlikely (undodata->d_size != (src.d_size
+ + phnum * phentsize
+ + (shnum - 1) * shentsize)))
+ return DWFL_E_BAD_PRELINK;
+
+ --shnum;
+
+ /* We look at the allocated SHT_PROGBITS (or SHT_NOBITS) sections. (Most
+ every file will have some SHT_PROGBITS sections, but it's possible to
+ have one with nothing but .bss, i.e. SHT_NOBITS.) The special sections
+ that can be moved around have different sh_type values--except for
+ .interp, the section that became the PT_INTERP segment. So we exclude
+ the SHT_PROGBITS section whose address matches the PT_INTERP p_vaddr.
+ For this reason, we must examine the phdrs first to find PT_INTERP. */
+
+ GElf_Addr main_interp = 0;
+ {
+ size_t main_phnum;
+ if (unlikely (elf_getphdrnum (mod->main.elf, &main_phnum)))
+ return DWFL_E_LIBELF;
+ for (size_t i = 0; i < main_phnum; ++i)
+ {
+ GElf_Phdr phdr;
+ if (unlikely (gelf_getphdr (mod->main.elf, i, &phdr) == NULL))
+ return DWFL_E_LIBELF;
+ if (phdr.p_type == PT_INTERP)
+ {
+ main_interp = phdr.p_vaddr;
+ break;
+ }
+ }
+ }
+
+ src.d_buf += src.d_size;
+ src.d_type = ELF_T_PHDR;
+ src.d_size = phnum * phentsize;
+
+ GElf_Addr undo_interp = 0;
+ bool class32 = ehdr.e32.e_ident[EI_CLASS] == ELFCLASS32;
+ {
+ size_t phdr_size = class32 ? sizeof (Elf32_Phdr) : sizeof (Elf64_Phdr);
+ if (unlikely (phnum > SIZE_MAX / phdr_size))
+ return DWFL_E_NOMEM;
+ const size_t phdrs_bytes = phnum * phdr_size;
+ void *phdrs = malloc (phdrs_bytes);
+ if (unlikely (phdrs == NULL))
+ return DWFL_E_NOMEM;
+ dst.d_buf = phdrs;
+ dst.d_size = phdrs_bytes;
+ if (unlikely (gelf_xlatetom (mod->main.elf, &dst, &src,
+ ehdr.e32.e_ident[EI_DATA]) == NULL))
+ {
+ free (phdrs);
+ return DWFL_E_LIBELF;
+ }
+ if (class32)
+ {
+ Elf32_Phdr (*p32)[phnum] = phdrs;
+ for (uint_fast16_t i = 0; i < phnum; ++i)
+ if ((*p32)[i].p_type == PT_INTERP)
+ {
+ undo_interp = (*p32)[i].p_vaddr;
+ break;
+ }
+ }
+ else
+ {
+ Elf64_Phdr (*p64)[phnum] = phdrs;
+ for (uint_fast16_t i = 0; i < phnum; ++i)
+ if ((*p64)[i].p_type == PT_INTERP)
+ {
+ undo_interp = (*p64)[i].p_vaddr;
+ break;
+ }
+ }
+ free (phdrs);
+ }
+
+ if (unlikely ((main_interp == 0) != (undo_interp == 0)))
+ return DWFL_E_BAD_PRELINK;
+
+ src.d_buf += src.d_size;
+ src.d_type = ELF_T_SHDR;
+ src.d_size = gelf_fsize (mod->main.elf, ELF_T_SHDR, shnum, EV_CURRENT);
+
+ size_t shdr_size = class32 ? sizeof (Elf32_Shdr) : sizeof (Elf64_Shdr);
+ if (unlikely (shnum > SIZE_MAX / shdr_size))
+ return DWFL_E_NOMEM;
+ const size_t shdrs_bytes = shnum * shdr_size;
+ void *shdrs = malloc (shdrs_bytes);
+ if (unlikely (shdrs == NULL))
+ return DWFL_E_NOMEM;
+ dst.d_buf = shdrs;
+ dst.d_size = shdrs_bytes;
+ if (unlikely (gelf_xlatetom (mod->main.elf, &dst, &src,
+ ehdr.e32.e_ident[EI_DATA]) == NULL))
+ {
+ free (shdrs);
+ return DWFL_E_LIBELF;
+ }
+
+ /* Now we can look at the original section headers of the main file
+ before it was prelinked. First we'll apply our method to the main
+ file sections as they are after prelinking, to calculate the
+ synchronization address of the main file. Then we'll apply that
+ same method to the saved section headers, to calculate the matching
+ synchronization address of the debug file.
+
+ The method is to consider SHF_ALLOC sections that are either
+ SHT_PROGBITS or SHT_NOBITS, excluding the section whose sh_addr
+ matches the PT_INTERP p_vaddr. The special sections that can be
+ moved by prelink have other types, except for .interp (which
+ becomes PT_INTERP). The "real" sections cannot move as such, but
+ .bss can be split into .dynbss and .bss, with the total memory
+ image remaining the same but being spread across the two sections.
+ So we consider the highest section end, which still matches up. */
+
+ GElf_Addr highest;
+
+ highest = 0;
+ scn = NULL;
+ while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
+ {
+ GElf_Shdr sh_mem;
+ GElf_Shdr *sh = gelf_getshdr (scn, &sh_mem);
+ if (unlikely (sh == NULL))
+ {
+ free (shdrs);
+ return DWFL_E_LIBELF;
+ }
+ consider_shdr (main_interp, sh->sh_type, sh->sh_flags,
+ sh->sh_addr, sh->sh_size, &highest);
+ }
+ if (highest > mod->main.vaddr)
+ {
+ mod->main.address_sync = highest;
+
+ highest = 0;
+ if (class32)
+ {
+ Elf32_Shdr (*s32)[shnum] = shdrs;
+ for (size_t i = 0; i < shnum; ++i)
+ consider_shdr (undo_interp, (*s32)[i].sh_type,
+ (*s32)[i].sh_flags, (*s32)[i].sh_addr,
+ (*s32)[i].sh_size, &highest);
+ }
+ else
+ {
+ Elf64_Shdr (*s64)[shnum] = shdrs;
+ for (size_t i = 0; i < shnum; ++i)
+ consider_shdr (undo_interp, (*s64)[i].sh_type,
+ (*s64)[i].sh_flags, (*s64)[i].sh_addr,
+ (*s64)[i].sh_size, &highest);
+ }
+
+ if (highest > file->vaddr)
+ file->address_sync = highest;
+ else
+ {
+ free (shdrs);
+ return DWFL_E_BAD_PRELINK;
+ }
+ }
+
+ free (shdrs);
+
+ return DWFL_E_NOERROR;
+}
+
+/* Find the separate debuginfo file for this module and open libelf on it.
+ When we return success, MOD->debug is set up. */
+static Dwfl_Error
+find_debuginfo (Dwfl_Module *mod)
+{
+ if (mod->debug.elf != NULL)
+ return DWFL_E_NOERROR;
+
+ GElf_Word debuglink_crc = 0;
+ const char *debuglink_file;
+ debuglink_file = INTUSE(dwelf_elf_gnu_debuglink) (mod->main.elf,
+ &debuglink_crc);
+
+ mod->debug.fd = (*mod->dwfl->callbacks->find_debuginfo) (MODCB_ARGS (mod),
+ mod->main.name,
+ debuglink_file,
+ debuglink_crc,
+ &mod->debug.name);
+ Dwfl_Error result = open_elf (mod, &mod->debug);
+ if (result == DWFL_E_NOERROR && mod->debug.address_sync != 0)
+ result = find_prelink_address_sync (mod, &mod->debug);
+ return result;
+}
+
+/* Try to find the alternative debug link for the given DWARF and set
+ it if found. Only called when mod->dw is already setup but still
+ might need an alternative (dwz multi) debug file. filename is either
+ the main or debug name from which the Dwarf was created. */
+static void
+find_debug_altlink (Dwfl_Module *mod, const char *filename)
+{
+ assert (mod->dw != NULL);
+
+ const char *altname;
+ const void *build_id;
+ ssize_t build_id_len = INTUSE(dwelf_dwarf_gnu_debugaltlink) (mod->dw,
+ &altname,
+ &build_id);
+
+ if (build_id_len > 0)
+ {
+ /* We could store altfile in the module, but don't really need it. */
+ char *altfile = NULL;
+ mod->alt_fd = (*mod->dwfl->callbacks->find_debuginfo) (MODCB_ARGS (mod),
+ filename,
+ altname,
+ 0,
+ &altfile);
+
+ /* The (internal) callbacks might just set mod->alt_elf directly
+ because they open the Elf anyway for sanity checking.
+ Otherwise open either the given file name or use the fd
+ returned. */
+ Dwfl_Error error = open_elf_file (&mod->alt_elf, &mod->alt_fd,
+ &altfile);
+ if (error == DWFL_E_NOERROR)
+ {
+ mod->alt = INTUSE(dwarf_begin_elf) (mod->alt_elf,
+ DWARF_C_READ, NULL);
+ if (mod->alt == NULL)
+ {
+ elf_end (mod->alt_elf);
+ mod->alt_elf = NULL;
+ close (mod->alt_fd);
+ mod->alt_fd = -1;
+ }
+ else
+ dwarf_setalt (mod->dw, mod->alt);
+ }
+
+ free (altfile); /* See above, we don't really need it. */
+ }
+}
+
+/* Try to find a symbol table in FILE.
+ Returns DWFL_E_NOERROR if a proper one is found.
+ Returns DWFL_E_NO_SYMTAB if not, but still sets results for SHT_DYNSYM. */
+static Dwfl_Error
+load_symtab (struct dwfl_file *file, struct dwfl_file **symfile,
+ Elf_Scn **symscn, Elf_Scn **xndxscn,
+ size_t *syments, int *first_global, GElf_Word *strshndx)
+{
+ bool symtab = false;
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (file->elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem, *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr != NULL)
+ switch (shdr->sh_type)
+ {
+ case SHT_SYMTAB:
+ if (shdr->sh_entsize == 0)
+ break;
+ symtab = true;
+ *symscn = scn;
+ *symfile = file;
+ *strshndx = shdr->sh_link;
+ *syments = shdr->sh_size / shdr->sh_entsize;
+ *first_global = shdr->sh_info;
+ if (*xndxscn != NULL)
+ return DWFL_E_NOERROR;
+ break;
+
+ case SHT_DYNSYM:
+ if (symtab)
+ break;
+ /* Use this if need be, but keep looking for SHT_SYMTAB. */
+ if (shdr->sh_entsize == 0)
+ break;
+ *symscn = scn;
+ *symfile = file;
+ *strshndx = shdr->sh_link;
+ *syments = shdr->sh_size / shdr->sh_entsize;
+ *first_global = shdr->sh_info;
+ break;
+
+ case SHT_SYMTAB_SHNDX:
+ *xndxscn = scn;
+ if (symtab)
+ return DWFL_E_NOERROR;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (symtab)
+ /* We found one, though no SHT_SYMTAB_SHNDX to go with it. */
+ return DWFL_E_NOERROR;
+
+ /* We found no SHT_SYMTAB, so any SHT_SYMTAB_SHNDX was bogus.
+ We might have found an SHT_DYNSYM and set *SYMSCN et al though. */
+ *xndxscn = NULL;
+ return DWFL_E_NO_SYMTAB;
+}
+
+
+/* Translate addresses into file offsets.
+ OFFS[*] start out zero and remain zero if unresolved. */
+static void
+find_offsets (Elf *elf, GElf_Addr main_bias, size_t phnum, size_t n,
+ GElf_Addr addrs[n], GElf_Off offs[n])
+{
+ size_t unsolved = n;
+ for (size_t i = 0; i < phnum; ++i)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
+ if (phdr != NULL && phdr->p_type == PT_LOAD && phdr->p_memsz > 0)
+ for (size_t j = 0; j < n; ++j)
+ if (offs[j] == 0
+ && addrs[j] >= phdr->p_vaddr + main_bias
+ && addrs[j] - (phdr->p_vaddr + main_bias) < phdr->p_filesz)
+ {
+ offs[j] = addrs[j] - (phdr->p_vaddr + main_bias) + phdr->p_offset;
+ if (--unsolved == 0)
+ break;
+ }
+ }
+}
+
+/* Various addresses we might want to pull from the dynamic segment. */
+enum
+{
+ i_symtab,
+ i_strtab,
+ i_hash,
+ i_gnu_hash,
+ i_max
+};
+
+/* Translate pointers into file offsets. ADJUST is either zero
+ in case the dynamic segment wasn't adjusted or mod->main_bias.
+ Will set mod->symfile if the translated offsets can be used as
+ symbol table. */
+static void
+translate_offs (GElf_Addr adjust,
+ Dwfl_Module *mod, size_t phnum,
+ GElf_Addr addrs[i_max], GElf_Xword strsz,
+ GElf_Ehdr *ehdr)
+{
+ GElf_Off offs[i_max] = { 0, };
+ find_offsets (mod->main.elf, adjust, phnum, i_max, addrs, offs);
+
+ /* Figure out the size of the symbol table. */
+ if (offs[i_hash] != 0)
+ {
+ /* In the original format, .hash says the size of .dynsym. */
+
+ size_t entsz = SH_ENTSIZE_HASH (ehdr);
+ Elf_Data *data = elf_getdata_rawchunk (mod->main.elf,
+ offs[i_hash] + entsz, entsz,
+ (entsz == 4
+ ? ELF_T_WORD : ELF_T_XWORD));
+ if (data != NULL)
+ mod->syments = (entsz == 4
+ ? *(const GElf_Word *) data->d_buf
+ : *(const GElf_Xword *) data->d_buf);
+ }
+ if (offs[i_gnu_hash] != 0 && mod->syments == 0)
+ {
+ /* In the new format, we can derive it with some work. */
+
+ const struct
+ {
+ Elf32_Word nbuckets;
+ Elf32_Word symndx;
+ Elf32_Word maskwords;
+ Elf32_Word shift2;
+ } *header;
+
+ Elf_Data *data = elf_getdata_rawchunk (mod->main.elf, offs[i_gnu_hash],
+ sizeof *header, ELF_T_WORD);
+ if (data != NULL)
+ {
+ header = data->d_buf;
+ Elf32_Word nbuckets = header->nbuckets;
+ Elf32_Word symndx = header->symndx;
+ GElf_Off buckets_at = (offs[i_gnu_hash] + sizeof *header
+ + (gelf_getclass (mod->main.elf)
+ * sizeof (Elf32_Word)
+ * header->maskwords));
+
+ // elf_getdata_rawchunk takes a size_t, make sure it
+ // doesn't overflow.
+#if SIZE_MAX <= UINT32_MAX
+ if (nbuckets > SIZE_MAX / sizeof (Elf32_Word))
+ data = NULL;
+ else
+#endif
+ data = elf_getdata_rawchunk (mod->main.elf, buckets_at,
+ nbuckets * sizeof (Elf32_Word),
+ ELF_T_WORD);
+ if (data != NULL && symndx < nbuckets)
+ {
+ const Elf32_Word *const buckets = data->d_buf;
+ Elf32_Word maxndx = symndx;
+ for (Elf32_Word bucket = 0; bucket < nbuckets; ++bucket)
+ if (buckets[bucket] > maxndx)
+ maxndx = buckets[bucket];
+
+ GElf_Off hasharr_at = (buckets_at
+ + nbuckets * sizeof (Elf32_Word));
+ hasharr_at += (maxndx - symndx) * sizeof (Elf32_Word);
+ do
+ {
+ data = elf_getdata_rawchunk (mod->main.elf,
+ hasharr_at,
+ sizeof (Elf32_Word),
+ ELF_T_WORD);
+ if (data != NULL
+ && (*(const Elf32_Word *) data->d_buf & 1u))
+ {
+ mod->syments = maxndx + 1;
+ break;
+ }
+ ++maxndx;
+ hasharr_at += sizeof (Elf32_Word);
+ }
+ while (data != NULL);
+ }
+ }
+ }
+ if (offs[i_strtab] > offs[i_symtab] && mod->syments == 0)
+ mod->syments = ((offs[i_strtab] - offs[i_symtab])
+ / gelf_fsize (mod->main.elf,
+ ELF_T_SYM, 1, EV_CURRENT));
+
+ if (mod->syments > 0)
+ {
+ mod->symdata = elf_getdata_rawchunk (mod->main.elf,
+ offs[i_symtab],
+ gelf_fsize (mod->main.elf,
+ ELF_T_SYM,
+ mod->syments,
+ EV_CURRENT),
+ ELF_T_SYM);
+ if (mod->symdata != NULL)
+ {
+ mod->symstrdata = elf_getdata_rawchunk (mod->main.elf,
+ offs[i_strtab],
+ strsz,
+ ELF_T_BYTE);
+ if (mod->symstrdata == NULL)
+ mod->symdata = NULL;
+ }
+ if (mod->symdata == NULL)
+ mod->symerr = DWFL_E (LIBELF, elf_errno ());
+ else
+ {
+ mod->symfile = &mod->main;
+ mod->symerr = DWFL_E_NOERROR;
+ }
+ }
+}
+
+/* Try to find a dynamic symbol table via phdrs. */
+static void
+find_dynsym (Dwfl_Module *mod)
+{
+ GElf_Ehdr ehdr_mem;
+ GElf_Ehdr *ehdr = gelf_getehdr (mod->main.elf, &ehdr_mem);
+
+ size_t phnum;
+ if (unlikely (elf_getphdrnum (mod->main.elf, &phnum) != 0))
+ return;
+
+ for (size_t i = 0; i < phnum; ++i)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (mod->main.elf, i, &phdr_mem);
+ if (phdr == NULL)
+ break;
+
+ if (phdr->p_type == PT_DYNAMIC)
+ {
+ /* Examine the dynamic section for the pointers we need. */
+
+ Elf_Data *data = elf_getdata_rawchunk (mod->main.elf,
+ phdr->p_offset, phdr->p_filesz,
+ ELF_T_DYN);
+ if (data == NULL)
+ continue;
+
+ GElf_Addr addrs[i_max] = { 0, };
+ GElf_Xword strsz = 0;
+ size_t n = data->d_size / gelf_fsize (mod->main.elf,
+ ELF_T_DYN, 1, EV_CURRENT);
+ for (size_t j = 0; j < n; ++j)
+ {
+ GElf_Dyn dyn_mem;
+ GElf_Dyn *dyn = gelf_getdyn (data, j, &dyn_mem);
+ if (dyn != NULL)
+ switch (dyn->d_tag)
+ {
+ case DT_SYMTAB:
+ addrs[i_symtab] = dyn->d_un.d_ptr;
+ continue;
+
+ case DT_HASH:
+ addrs[i_hash] = dyn->d_un.d_ptr;
+ continue;
+
+ case DT_GNU_HASH:
+ addrs[i_gnu_hash] = dyn->d_un.d_ptr;
+ continue;
+
+ case DT_STRTAB:
+ addrs[i_strtab] = dyn->d_un.d_ptr;
+ continue;
+
+ case DT_STRSZ:
+ strsz = dyn->d_un.d_val;
+ continue;
+
+ default:
+ continue;
+
+ case DT_NULL:
+ break;
+ }
+ break;
+ }
+
+ /* First try unadjusted, like ELF files from disk, vdso.
+ Then try for already adjusted dynamic section, like ELF
+ from remote memory. */
+ translate_offs (0, mod, phnum, addrs, strsz, ehdr);
+ if (mod->symfile == NULL)
+ translate_offs (mod->main_bias, mod, phnum, addrs, strsz, ehdr);
+
+ return;
+ }
+ }
+}
+
+
+#if USE_LZMA
+/* Try to find the offset between the main file and .gnu_debugdata. */
+static bool
+find_aux_address_sync (Dwfl_Module *mod)
+{
+ /* Don't trust the phdrs in the minisymtab elf file to be setup correctly.
+ The address_sync is equal to the main file it is embedded in at first. */
+ mod->aux_sym.address_sync = mod->main.address_sync;
+
+ /* Adjust address_sync for the difference in entry addresses, attempting to
+ account for ELF relocation changes after aux was split. */
+ GElf_Ehdr ehdr_main, ehdr_aux;
+ if (unlikely (gelf_getehdr (mod->main.elf, &ehdr_main) == NULL)
+ || unlikely (gelf_getehdr (mod->aux_sym.elf, &ehdr_aux) == NULL))
+ return false;
+ mod->aux_sym.address_sync += ehdr_aux.e_entry - ehdr_main.e_entry;
+
+ /* The shdrs are setup OK to make find_prelink_address_sync () do the right
+ thing, which is possibly more reliable, but it needs .gnu.prelink_undo. */
+ if (mod->aux_sym.address_sync != 0)
+ return find_prelink_address_sync (mod, &mod->aux_sym) == DWFL_E_NOERROR;
+
+ return true;
+}
+#endif
+
+/* Try to find the auxiliary symbol table embedded in the main elf file
+ section .gnu_debugdata. Only matters if the symbol information comes
+ from the main file dynsym. No harm done if not found. */
+static void
+find_aux_sym (Dwfl_Module *mod __attribute__ ((unused)),
+ Elf_Scn **aux_symscn __attribute__ ((unused)),
+ Elf_Scn **aux_xndxscn __attribute__ ((unused)),
+ GElf_Word *aux_strshndx __attribute__ ((unused)))
+{
+ /* Since a .gnu_debugdata section is compressed using lzma don't do
+ anything unless we have support for that. */
+#if USE_LZMA
+ Elf *elf = mod->main.elf;
+
+ size_t shstrndx;
+ if (elf_getshdrstrndx (elf, &shstrndx) < 0)
+ return;
+
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr == NULL)
+ return;
+
+ const char *name = elf_strptr (elf, shstrndx, shdr->sh_name);
+ if (name == NULL)
+ return;
+
+ if (!strcmp (name, ".gnu_debugdata"))
+ break;
+ }
+
+ if (scn == NULL)
+ return;
+
+ /* Found the .gnu_debugdata section. Uncompress the lzma image and
+ turn it into an ELF image. */
+ Elf_Data *rawdata = elf_rawdata (scn, NULL);
+ if (rawdata == NULL)
+ return;
+
+ Dwfl_Error error;
+ void *buffer = NULL;
+ size_t size = 0;
+ error = __libdw_unlzma (-1, 0, rawdata->d_buf, rawdata->d_size,
+ &buffer, &size);
+ if (error == DWFL_E_NOERROR)
+ {
+ if (unlikely (size == 0))
+ free (buffer);
+ else
+ {
+ mod->aux_sym.elf = elf_memory (buffer, size);
+ if (mod->aux_sym.elf == NULL)
+ free (buffer);
+ else
+ {
+ mod->aux_sym.fd = -1;
+ mod->aux_sym.elf->flags |= ELF_F_MALLOCED;
+ if (open_elf (mod, &mod->aux_sym) != DWFL_E_NOERROR)
+ return;
+ if (! find_aux_address_sync (mod))
+ {
+ elf_end (mod->aux_sym.elf);
+ mod->aux_sym.elf = NULL;
+ return;
+ }
+
+ /* So far, so good. Get minisymtab table data and cache it. */
+ bool minisymtab = false;
+ scn = NULL;
+ while ((scn = elf_nextscn (mod->aux_sym.elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem, *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr != NULL)
+ switch (shdr->sh_type)
+ {
+ case SHT_SYMTAB:
+ minisymtab = true;
+ *aux_symscn = scn;
+ *aux_strshndx = shdr->sh_link;
+ mod->aux_syments = shdr->sh_size / shdr->sh_entsize;
+ mod->aux_first_global = shdr->sh_info;
+ if (*aux_xndxscn != NULL)
+ return;
+ break;
+
+ case SHT_SYMTAB_SHNDX:
+ *aux_xndxscn = scn;
+ if (minisymtab)
+ return;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (minisymtab)
+ /* We found one, though no SHT_SYMTAB_SHNDX to go with it. */
+ return;
+
+ /* We found no SHT_SYMTAB, so everything else is bogus. */
+ *aux_xndxscn = NULL;
+ *aux_strshndx = 0;
+ mod->aux_syments = 0;
+ elf_end (mod->aux_sym.elf);
+ mod->aux_sym.elf = NULL;
+ return;
+ }
+ }
+ }
+ else
+ free (buffer);
+#endif
+}
+
+/* Try to find a symbol table in either MOD->main.elf or MOD->debug.elf. */
+static void
+find_symtab (Dwfl_Module *mod)
+{
+ if (mod->symdata != NULL || mod->aux_symdata != NULL /* Already done. */
+ || mod->symerr != DWFL_E_NOERROR) /* Cached previous failure. */
+ return;
+
+ __libdwfl_getelf (mod);
+ mod->symerr = mod->elferr;
+ if (mod->symerr != DWFL_E_NOERROR)
+ return;
+
+ /* First see if the main ELF file has the debugging information. */
+ Elf_Scn *symscn = NULL, *xndxscn = NULL;
+ Elf_Scn *aux_symscn = NULL, *aux_xndxscn = NULL;
+ GElf_Word strshndx, aux_strshndx = 0;
+ mod->symerr = load_symtab (&mod->main, &mod->symfile, &symscn,
+ &xndxscn, &mod->syments, &mod->first_global,
+ &strshndx);
+ switch (mod->symerr)
+ {
+ default:
+ return;
+
+ case DWFL_E_NOERROR:
+ break;
+
+ case DWFL_E_NO_SYMTAB:
+ /* Now we have to look for a separate debuginfo file. */
+ mod->symerr = find_debuginfo (mod);
+ switch (mod->symerr)
+ {
+ default:
+ return;
+
+ case DWFL_E_NOERROR:
+ mod->symerr = load_symtab (&mod->debug, &mod->symfile, &symscn,
+ &xndxscn, &mod->syments,
+ &mod->first_global, &strshndx);
+ break;
+
+ case DWFL_E_CB: /* The find_debuginfo hook failed. */
+ mod->symerr = DWFL_E_NO_SYMTAB;
+ break;
+ }
+
+ switch (mod->symerr)
+ {
+ default:
+ return;
+
+ case DWFL_E_NOERROR:
+ break;
+
+ case DWFL_E_NO_SYMTAB:
+ /* There might be an auxiliary table. */
+ find_aux_sym (mod, &aux_symscn, &aux_xndxscn, &aux_strshndx);
+
+ if (symscn != NULL)
+ {
+ /* We still have the dynamic symbol table. */
+ mod->symerr = DWFL_E_NOERROR;
+ break;
+ }
+
+ if (aux_symscn != NULL)
+ {
+ /* We still have the auxiliary symbol table. */
+ mod->symerr = DWFL_E_NOERROR;
+ goto aux_cache;
+ }
+
+ /* Last ditch, look for dynamic symbols without section headers. */
+ find_dynsym (mod);
+ return;
+ }
+ break;
+ }
+
+ /* This does some sanity checks on the string table section. */
+ if (elf_strptr (mod->symfile->elf, strshndx, 0) == NULL)
+ {
+ elferr:
+ mod->symdata = NULL;
+ mod->syments = 0;
+ mod->first_global = 0;
+ mod->symerr = DWFL_E (LIBELF, elf_errno ());
+ goto aux_cleanup; /* This cleans up some more and tries find_dynsym. */
+ }
+
+ /* Cache the data; MOD->syments and MOD->first_global were set
+ above. If any of the sections is compressed, uncompress it
+ first. Only the string data setion could theoretically be
+ compressed GNU style (as .zdebug_str). Everything else only ELF
+ gabi style (SHF_COMPRESSED). */
+
+ Elf_Scn *symstrscn = elf_getscn (mod->symfile->elf, strshndx);
+ if (symstrscn == NULL)
+ goto elferr;
+
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (symstrscn, &shdr_mem);
+ if (shdr == NULL)
+ goto elferr;
+
+ size_t shstrndx;
+ if (elf_getshdrstrndx (mod->symfile->elf, &shstrndx) < 0)
+ goto elferr;
+
+ const char *sname = elf_strptr (mod->symfile->elf, shstrndx, shdr->sh_name);
+ if (sname == NULL)
+ goto elferr;
+
+ if (strncmp (sname, ".zdebug", strlen (".zdebug")) == 0)
+ /* Try to uncompress, but it might already have been, an error
+ might just indicate, already uncompressed. */
+ elf_compress_gnu (symstrscn, 0, 0);
+
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ if (elf_compress (symstrscn, 0, 0) < 0)
+ goto elferr;
+
+ mod->symstrdata = elf_getdata (symstrscn, NULL);
+ if (mod->symstrdata == NULL || mod->symstrdata->d_buf == NULL)
+ goto elferr;
+
+ if (xndxscn == NULL)
+ mod->symxndxdata = NULL;
+ else
+ {
+ shdr = gelf_getshdr (xndxscn, &shdr_mem);
+ if (shdr == NULL)
+ goto elferr;
+
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ if (elf_compress (xndxscn, 0, 0) < 0)
+ goto elferr;
+
+ mod->symxndxdata = elf_getdata (xndxscn, NULL);
+ if (mod->symxndxdata == NULL || mod->symxndxdata->d_buf == NULL)
+ goto elferr;
+ }
+
+ shdr = gelf_getshdr (symscn, &shdr_mem);
+ if (shdr == NULL)
+ goto elferr;
+
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ if (elf_compress (symscn, 0, 0) < 0)
+ goto elferr;
+
+ mod->symdata = elf_getdata (symscn, NULL);
+ if (mod->symdata == NULL || mod->symdata->d_buf == NULL)
+ goto elferr;
+
+ // Sanity check number of symbols.
+ shdr = gelf_getshdr (symscn, &shdr_mem);
+ if (shdr == NULL || shdr->sh_entsize == 0
+ || mod->syments > mod->symdata->d_size / shdr->sh_entsize
+ || (size_t) mod->first_global > mod->syments)
+ goto elferr;
+
+ /* Cache any auxiliary symbol info, when it fails, just ignore aux_sym. */
+ if (aux_symscn != NULL)
+ {
+ aux_cache:
+ /* This does some sanity checks on the string table section. */
+ if (elf_strptr (mod->aux_sym.elf, aux_strshndx, 0) == NULL)
+ {
+ aux_cleanup:
+ mod->aux_syments = 0;
+ elf_end (mod->aux_sym.elf);
+ mod->aux_sym.elf = NULL;
+ /* We thought we had something through shdrs, but it failed...
+ Last ditch, look for dynamic symbols without section headers. */
+ find_dynsym (mod);
+ return;
+ }
+
+ Elf_Scn *aux_strscn = elf_getscn (mod->aux_sym.elf, aux_strshndx);
+ if (aux_strscn == NULL)
+ goto elferr;
+
+ shdr = gelf_getshdr (aux_strscn, &shdr_mem);
+ if (shdr == NULL)
+ goto elferr;
+
+ size_t aux_shstrndx;
+ if (elf_getshdrstrndx (mod->aux_sym.elf, &aux_shstrndx) < 0)
+ goto elferr;
+
+ sname = elf_strptr (mod->aux_sym.elf, aux_shstrndx,
+ shdr->sh_name);
+ if (sname == NULL)
+ goto elferr;
+
+ if (strncmp (sname, ".zdebug", strlen (".zdebug")) == 0)
+ /* Try to uncompress, but it might already have been, an error
+ might just indicate, already uncompressed. */
+ elf_compress_gnu (aux_strscn, 0, 0);
+
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ if (elf_compress (aux_strscn, 0, 0) < 0)
+ goto elferr;
+
+ mod->aux_symstrdata = elf_getdata (aux_strscn, NULL);
+ if (mod->aux_symstrdata == NULL || mod->aux_symstrdata->d_buf == NULL)
+ goto aux_cleanup;
+
+ if (aux_xndxscn == NULL)
+ mod->aux_symxndxdata = NULL;
+ else
+ {
+ shdr = gelf_getshdr (aux_xndxscn, &shdr_mem);
+ if (shdr == NULL)
+ goto elferr;
+
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ if (elf_compress (aux_xndxscn, 0, 0) < 0)
+ goto elferr;
+
+ mod->aux_symxndxdata = elf_getdata (aux_xndxscn, NULL);
+ if (mod->aux_symxndxdata == NULL
+ || mod->aux_symxndxdata->d_buf == NULL)
+ goto aux_cleanup;
+ }
+
+ shdr = gelf_getshdr (aux_symscn, &shdr_mem);
+ if (shdr == NULL)
+ goto elferr;
+
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ if (elf_compress (aux_symscn, 0, 0) < 0)
+ goto elferr;
+
+ mod->aux_symdata = elf_getdata (aux_symscn, NULL);
+ if (mod->aux_symdata == NULL || mod->aux_symdata->d_buf == NULL)
+ goto aux_cleanup;
+
+ // Sanity check number of aux symbols.
+ shdr = gelf_getshdr (aux_symscn, &shdr_mem);
+ if (mod->aux_syments > mod->aux_symdata->d_size / shdr->sh_entsize
+ || (size_t) mod->aux_first_global > mod->aux_syments)
+ goto aux_cleanup;
+ }
+}
+
+
+/* Try to open a libebl backend for MOD. */
+Dwfl_Error
+internal_function
+__libdwfl_module_getebl (Dwfl_Module *mod)
+{
+ if (mod->ebl == NULL)
+ {
+ __libdwfl_getelf (mod);
+ if (mod->elferr != DWFL_E_NOERROR)
+ return mod->elferr;
+
+ mod->ebl = ebl_openbackend (mod->main.elf);
+ if (mod->ebl == NULL)
+ return DWFL_E_LIBEBL;
+ }
+ return DWFL_E_NOERROR;
+}
+
+/* Try to start up libdw on DEBUGFILE. */
+static Dwfl_Error
+load_dw (Dwfl_Module *mod, struct dwfl_file *debugfile)
+{
+ if (mod->e_type == ET_REL && !debugfile->relocated)
+ {
+ const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
+
+ /* The debugging sections have to be relocated. */
+ if (cb->section_address == NULL)
+ return DWFL_E_NOREL;
+
+ Dwfl_Error error = __libdwfl_module_getebl (mod);
+ if (error != DWFL_E_NOERROR)
+ return error;
+
+ find_symtab (mod);
+ Dwfl_Error result = mod->symerr;
+ if (result == DWFL_E_NOERROR)
+ result = __libdwfl_relocate (mod, debugfile->elf, true);
+ if (result != DWFL_E_NOERROR)
+ return result;
+
+ /* Don't keep the file descriptors around. */
+ if (mod->main.fd != -1 && elf_cntl (mod->main.elf, ELF_C_FDREAD) == 0)
+ {
+ close (mod->main.fd);
+ mod->main.fd = -1;
+ }
+ if (debugfile->fd != -1 && elf_cntl (debugfile->elf, ELF_C_FDREAD) == 0)
+ {
+ close (debugfile->fd);
+ debugfile->fd = -1;
+ }
+ }
+
+ mod->dw = INTUSE(dwarf_begin_elf) (debugfile->elf, DWARF_C_READ, NULL);
+ if (mod->dw == NULL)
+ {
+ int err = INTUSE(dwarf_errno) ();
+ return err == DWARF_E_NO_DWARF ? DWFL_E_NO_DWARF : DWFL_E (LIBDW, err);
+ }
+
+ /* Until we have iterated through all CU's, we might do lazy lookups. */
+ mod->lazycu = 1;
+
+ return DWFL_E_NOERROR;
+}
+
+/* Try to start up libdw on either the main file or the debuginfo file. */
+static void
+find_dw (Dwfl_Module *mod)
+{
+ if (mod->dw != NULL /* Already done. */
+ || mod->dwerr != DWFL_E_NOERROR) /* Cached previous failure. */
+ return;
+
+ __libdwfl_getelf (mod);
+ mod->dwerr = mod->elferr;
+ if (mod->dwerr != DWFL_E_NOERROR)
+ return;
+
+ /* First see if the main ELF file has the debugging information. */
+ mod->dwerr = load_dw (mod, &mod->main);
+ switch (mod->dwerr)
+ {
+ case DWFL_E_NOERROR:
+ mod->debug.elf = mod->main.elf;
+ mod->debug.address_sync = mod->main.address_sync;
+
+ /* The Dwarf might need an alt debug file, find that now after
+ everything about the debug file has been setup (the
+ find_debuginfo callback might need it). */
+ find_debug_altlink (mod, mod->main.name);
+ return;
+
+ case DWFL_E_NO_DWARF:
+ break;
+
+ default:
+ goto canonicalize;
+ }
+
+ /* Now we have to look for a separate debuginfo file. */
+ mod->dwerr = find_debuginfo (mod);
+ switch (mod->dwerr)
+ {
+ case DWFL_E_NOERROR:
+ mod->dwerr = load_dw (mod, &mod->debug);
+ if (mod->dwerr == DWFL_E_NOERROR)
+ {
+ /* The Dwarf might need an alt debug file, find that now after
+ everything about the debug file has been setup (the
+ find_debuginfo callback might need it). */
+ find_debug_altlink (mod, mod->debug.name);
+ return;
+ }
+
+ break;
+
+ case DWFL_E_CB: /* The find_debuginfo hook failed. */
+ mod->dwerr = DWFL_E_NO_DWARF;
+ return;
+
+ default:
+ break;
+ }
+
+ canonicalize:
+ mod->dwerr = __libdwfl_canon_error (mod->dwerr);
+}
+
+Dwarf *
+dwfl_module_getdwarf (Dwfl_Module *mod, Dwarf_Addr *bias)
+{
+ if (mod == NULL)
+ return NULL;
+
+ find_dw (mod);
+ if (mod->dwerr == DWFL_E_NOERROR)
+ {
+ /* If dwfl_module_getelf was used previously, then partial apply
+ relocation to miscellaneous sections in the debug file too. */
+ if (mod->e_type == ET_REL
+ && mod->main.relocated && ! mod->debug.relocated)
+ {
+ mod->debug.relocated = true;
+ if (mod->debug.elf != mod->main.elf)
+ (void) __libdwfl_relocate (mod, mod->debug.elf, false);
+ }
+
+ *bias = dwfl_adjusted_dwarf_addr (mod, 0);
+ return mod->dw;
+ }
+
+ __libdwfl_seterrno (mod->dwerr);
+ return NULL;
+}
+INTDEF (dwfl_module_getdwarf)
+
+int
+dwfl_module_getsymtab (Dwfl_Module *mod)
+{
+ if (mod == NULL)
+ return -1;
+
+ find_symtab (mod);
+ if (mod->symerr == DWFL_E_NOERROR)
+ /* We will skip the auxiliary zero entry if there is another one. */
+ return (mod->syments + mod->aux_syments
+ - (mod->syments > 0 && mod->aux_syments > 0 ? 1 : 0));
+
+ __libdwfl_seterrno (mod->symerr);
+ return -1;
+}
+INTDEF (dwfl_module_getsymtab)
+
+int
+dwfl_module_getsymtab_first_global (Dwfl_Module *mod)
+{
+ if (mod == NULL)
+ return -1;
+
+ find_symtab (mod);
+ if (mod->symerr == DWFL_E_NOERROR)
+ {
+ /* All local symbols should come before all global symbols. If
+ we have an auxiliary table make sure all the main locals come
+ first, then all aux locals, then all main globals and finally all
+ aux globals. And skip the auxiliary table zero undefined
+ entry. */
+ int skip_aux_zero = (mod->syments > 0 && mod->aux_syments > 0) ? 1 : 0;
+ return mod->first_global + mod->aux_first_global - skip_aux_zero;
+ }
+
+ __libdwfl_seterrno (mod->symerr);
+ return -1;
+}
+INTDEF (dwfl_module_getsymtab_first_global)
diff --git a/libdwfl/dwfl_module_getelf.c b/libdwfl/dwfl_module_getelf.c
new file mode 100644
index 0000000..6358de4
--- /dev/null
+++ b/libdwfl/dwfl_module_getelf.c
@@ -0,0 +1,71 @@
+/* Find debugging and symbol information for a module in libdwfl.
+ Copyright (C) 2009-2010 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+Elf *
+dwfl_module_getelf (Dwfl_Module *mod, GElf_Addr *loadbase)
+{
+ if (mod == NULL)
+ return NULL;
+
+ __libdwfl_getelf (mod);
+ if (mod->elferr == DWFL_E_NOERROR)
+ {
+ if (mod->e_type == ET_REL && ! mod->main.relocated)
+ {
+ /* Before letting them get at the Elf handle,
+ apply all the relocations we know how to. */
+
+ mod->main.relocated = true;
+ if (likely (__libdwfl_module_getebl (mod) == DWFL_E_NOERROR))
+ {
+ (void) __libdwfl_relocate (mod, mod->main.elf, false);
+
+ if (mod->debug.elf == mod->main.elf)
+ mod->debug.relocated = true;
+ else if (mod->debug.elf != NULL && ! mod->debug.relocated)
+ {
+ mod->debug.relocated = true;
+ (void) __libdwfl_relocate (mod, mod->debug.elf, false);
+ }
+ }
+ }
+
+ *loadbase = dwfl_adjusted_address (mod, 0);
+ return mod->main.elf;
+ }
+
+ __libdwfl_seterrno (mod->elferr);
+ return NULL;
+}
+INTDEF (dwfl_module_getelf)
diff --git a/libdwfl/dwfl_module_getsrc.c b/libdwfl/dwfl_module_getsrc.c
new file mode 100644
index 0000000..fc99b16
--- /dev/null
+++ b/libdwfl/dwfl_module_getsrc.c
@@ -0,0 +1,85 @@
+/* Find source location for PC address in module.
+ Copyright (C) 2005, 2008, 2014 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include "../libdw/libdwP.h"
+
+Dwfl_Line *
+dwfl_module_getsrc (Dwfl_Module *mod, Dwarf_Addr addr)
+{
+ Dwarf_Addr bias;
+ if (INTUSE(dwfl_module_getdwarf) (mod, &bias) == NULL)
+ return NULL;
+
+ struct dwfl_cu *cu;
+ Dwfl_Error error = __libdwfl_addrcu (mod, addr, &cu);
+ if (likely (error == DWFL_E_NOERROR))
+ error = __libdwfl_cu_getsrclines (cu);
+ if (likely (error == DWFL_E_NOERROR))
+ {
+ Dwarf_Lines *lines = cu->die.cu->lines;
+ size_t nlines = lines->nlines;
+ if (nlines > 0)
+ {
+ /* This is guaranteed for us by libdw read_srclines. */
+ assert(lines->info[nlines - 1].end_sequence);
+
+ /* Now we look at the module-relative address. */
+ addr -= bias;
+
+ /* The lines are sorted by address, so we can use binary search. */
+ size_t l = 0, u = nlines - 1;
+ while (l < u)
+ {
+ size_t idx = u - (u - l) / 2;
+ Dwarf_Line *line = &lines->info[idx];
+ if (addr < line->addr)
+ u = idx - 1;
+ else
+ l = idx;
+ }
+
+ /* The last line which is less than or equal to addr is what
+ we want, unless it is the end_sequence which is after the
+ current line sequence. */
+ Dwarf_Line *line = &lines->info[l];
+ if (! line->end_sequence && line->addr <= addr)
+ return &cu->lines->idx[l];
+ }
+
+ error = DWFL_E_ADDR_OUTOFRANGE;
+ }
+
+ __libdwfl_seterrno (error);
+ return NULL;
+}
+INTDEF (dwfl_module_getsrc)
diff --git a/libdwfl/dwfl_module_getsrc_file.c b/libdwfl/dwfl_module_getsrc_file.c
new file mode 100644
index 0000000..cea2ba4
--- /dev/null
+++ b/libdwfl/dwfl_module_getsrc_file.c
@@ -0,0 +1,178 @@
+/* Find matching source locations in a module.
+ Copyright (C) 2005 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include "../libdw/libdwP.h"
+
+
+static inline const char *
+dwfl_dwarf_line_file (const Dwarf_Line *line)
+{
+ return line->files->info[line->file].name;
+}
+
+static inline Dwarf_Line *
+dwfl_line (const Dwfl_Line *line)
+{
+ return &dwfl_linecu (line)->die.cu->lines->info[line->idx];
+}
+
+static inline const char *
+dwfl_line_file (const Dwfl_Line *line)
+{
+ return dwfl_dwarf_line_file (dwfl_line (line));
+}
+
+int
+dwfl_module_getsrc_file (Dwfl_Module *mod,
+ const char *fname, int lineno, int column,
+ Dwfl_Line ***srcsp, size_t *nsrcs)
+{
+ if (mod == NULL)
+ return -1;
+
+ if (mod->dw == NULL)
+ {
+ Dwarf_Addr bias;
+ if (INTUSE(dwfl_module_getdwarf) (mod, &bias) == NULL)
+ return -1;
+ }
+
+ bool is_basename = strchr (fname, '/') == NULL;
+
+ size_t max_match = *nsrcs ?: ~0u;
+ size_t act_match = *nsrcs;
+ size_t cur_match = 0;
+ Dwfl_Line **match = *nsrcs == 0 ? NULL : *srcsp;
+
+ struct dwfl_cu *cu = NULL;
+ Dwfl_Error error;
+ while ((error = __libdwfl_nextcu (mod, cu, &cu)) == DWFL_E_NOERROR
+ && cu != NULL
+ && (error = __libdwfl_cu_getsrclines (cu)) == DWFL_E_NOERROR)
+ {
+ /* Search through all the line number records for a matching
+ file and line/column number. If any of the numbers is zero,
+ no match is performed. */
+ const char *lastfile = NULL;
+ bool lastmatch = false;
+ for (size_t cnt = 0; cnt < cu->die.cu->lines->nlines; ++cnt)
+ {
+ Dwarf_Line *line = &cu->die.cu->lines->info[cnt];
+
+ if (unlikely (line->file >= line->files->nfiles))
+ {
+ if (*nsrcs == 0)
+ free (match);
+ __libdwfl_seterrno (DWFL_E (LIBDW, DWARF_E_INVALID_DWARF));
+ return -1;
+ }
+ else
+ {
+ const char *file = dwfl_dwarf_line_file (line);
+ if (file != lastfile)
+ {
+ /* Match the name with the name the user provided. */
+ lastfile = file;
+ lastmatch = !strcmp (is_basename ? basename (file) : file,
+ fname);
+ }
+ }
+ if (!lastmatch)
+ continue;
+
+ /* See whether line and possibly column match. */
+ if (lineno != 0
+ && (lineno > line->line
+ || (column != 0 && column > line->column)))
+ /* Cannot match. */
+ continue;
+
+ /* Determine whether this is the best match so far. */
+ size_t inner;
+ for (inner = 0; inner < cur_match; ++inner)
+ if (dwfl_line_file (match[inner])
+ == dwfl_dwarf_line_file (line))
+ break;
+ if (inner < cur_match
+ && (dwfl_line (match[inner])->line != line->line
+ || dwfl_line (match[inner])->line != lineno
+ || (column != 0
+ && (dwfl_line (match[inner])->column != line->column
+ || dwfl_line (match[inner])->column != column))))
+ {
+ /* We know about this file already. If this is a better
+ match for the line number, use it. */
+ if (dwfl_line (match[inner])->line >= line->line
+ && (dwfl_line (match[inner])->line != line->line
+ || dwfl_line (match[inner])->column >= line->column))
+ /* Use the new line. Otherwise the old one. */
+ match[inner] = &cu->lines->idx[cnt];
+ continue;
+ }
+
+ if (cur_match < max_match)
+ {
+ if (cur_match == act_match)
+ {
+ /* Enlarge the array for the results. */
+ act_match += 10;
+ Dwfl_Line **newp = realloc (match,
+ act_match
+ * sizeof (Dwfl_Line *));
+ if (newp == NULL)
+ {
+ free (match);
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return -1;
+ }
+ match = newp;
+ }
+
+ match[cur_match++] = &cu->lines->idx[cnt];
+ }
+ }
+ }
+
+ if (cur_match > 0)
+ {
+ assert (*nsrcs == 0 || *srcsp == match);
+
+ *nsrcs = cur_match;
+ *srcsp = match;
+
+ return 0;
+ }
+
+ __libdwfl_seterrno (DWFL_E_NO_MATCH);
+ return -1;
+}
diff --git a/libdwfl/dwfl_module_getsym.c b/libdwfl/dwfl_module_getsym.c
new file mode 100644
index 0000000..8de9a3e
--- /dev/null
+++ b/libdwfl/dwfl_module_getsym.c
@@ -0,0 +1,220 @@
+/* Find debugging and symbol information for a module in libdwfl.
+ Copyright (C) 2006-2014 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+const char *
+internal_function
+__libdwfl_getsym (Dwfl_Module *mod, int ndx, GElf_Sym *sym, GElf_Addr *addr,
+ GElf_Word *shndxp, Elf **elfp, Dwarf_Addr *biasp,
+ bool *resolved, bool adjust_st_value)
+{
+ if (unlikely (mod == NULL))
+ return NULL;
+
+ if (unlikely (mod->symdata == NULL))
+ {
+ int result = INTUSE(dwfl_module_getsymtab) (mod);
+ if (result < 0)
+ return NULL;
+ }
+
+ /* All local symbols should come before all global symbols. If we
+ have an auxiliary table make sure all the main locals come first,
+ then all aux locals, then all main globals and finally all aux globals.
+ And skip the auxiliary table zero undefined entry. */
+ GElf_Word shndx;
+ int tndx = ndx;
+ int skip_aux_zero = (mod->syments > 0 && mod->aux_syments > 0) ? 1 : 0;
+ Elf *elf;
+ Elf_Data *symdata;
+ Elf_Data *symxndxdata;
+ Elf_Data *symstrdata;
+ if (mod->aux_symdata == NULL
+ || ndx < mod->first_global)
+ {
+ /* main symbol table (locals). */
+ tndx = ndx;
+ elf = mod->symfile->elf;
+ symdata = mod->symdata;
+ symxndxdata = mod->symxndxdata;
+ symstrdata = mod->symstrdata;
+ }
+ else if (ndx < mod->first_global + mod->aux_first_global - skip_aux_zero)
+ {
+ /* aux symbol table (locals). */
+ tndx = ndx - mod->first_global + skip_aux_zero;
+ elf = mod->aux_sym.elf;
+ symdata = mod->aux_symdata;
+ symxndxdata = mod->aux_symxndxdata;
+ symstrdata = mod->aux_symstrdata;
+ }
+ else if ((size_t) ndx < mod->syments + mod->aux_first_global - skip_aux_zero)
+ {
+ /* main symbol table (globals). */
+ tndx = ndx - mod->aux_first_global + skip_aux_zero;
+ elf = mod->symfile->elf;
+ symdata = mod->symdata;
+ symxndxdata = mod->symxndxdata;
+ symstrdata = mod->symstrdata;
+ }
+ else
+ {
+ /* aux symbol table (globals). */
+ tndx = ndx - mod->syments + skip_aux_zero;
+ elf = mod->aux_sym.elf;
+ symdata = mod->aux_symdata;
+ symxndxdata = mod->aux_symxndxdata;
+ symstrdata = mod->aux_symstrdata;
+ }
+ sym = gelf_getsymshndx (symdata, symxndxdata, tndx, sym, &shndx);
+
+ if (unlikely (sym == NULL))
+ {
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ return NULL;
+ }
+
+ if (sym->st_shndx != SHN_XINDEX)
+ shndx = sym->st_shndx;
+
+ /* Figure out whether this symbol points into an SHF_ALLOC section. */
+ bool alloc = true;
+ if ((shndxp != NULL || mod->e_type != ET_REL)
+ && (sym->st_shndx == SHN_XINDEX
+ || (sym->st_shndx < SHN_LORESERVE && sym->st_shndx != SHN_UNDEF)))
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (elf_getscn (elf, shndx), &shdr_mem);
+ alloc = unlikely (shdr == NULL) || (shdr->sh_flags & SHF_ALLOC);
+ }
+
+ /* In case of an value in an allocated section the main Elf Ebl
+ might know where the real value is (e.g. for function
+ descriptors). */
+
+ char *ident;
+ GElf_Addr st_value = sym->st_value & ebl_func_addr_mask (mod->ebl);
+ *resolved = false;
+ if (! adjust_st_value && mod->e_type != ET_REL && alloc
+ && (GELF_ST_TYPE (sym->st_info) == STT_FUNC
+ || (GELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC
+ && (ident = elf_getident (elf, NULL)) != NULL
+ && ident[EI_OSABI] == ELFOSABI_LINUX)))
+ {
+ if (likely (__libdwfl_module_getebl (mod) == DWFL_E_NOERROR))
+ {
+ if (elf != mod->main.elf)
+ {
+ st_value = dwfl_adjusted_st_value (mod, elf, st_value);
+ st_value = dwfl_deadjust_st_value (mod, mod->main.elf, st_value);
+ }
+
+ *resolved = ebl_resolve_sym_value (mod->ebl, &st_value);
+ if (! *resolved)
+ st_value = sym->st_value;
+ }
+ }
+
+ if (shndxp != NULL)
+ /* Yield -1 in case of a non-SHF_ALLOC section. */
+ *shndxp = alloc ? shndx : (GElf_Word) -1;
+
+ switch (sym->st_shndx)
+ {
+ case SHN_ABS: /* XXX sometimes should use bias?? */
+ case SHN_UNDEF:
+ case SHN_COMMON:
+ break;
+
+ default:
+ if (mod->e_type == ET_REL)
+ {
+ /* In an ET_REL file, the symbol table values are relative
+ to the section, not to the module's load base. */
+ size_t symshstrndx = SHN_UNDEF;
+ Dwfl_Error result = __libdwfl_relocate_value (mod, elf,
+ &symshstrndx,
+ shndx, &st_value);
+ if (unlikely (result != DWFL_E_NOERROR))
+ {
+ __libdwfl_seterrno (result);
+ return NULL;
+ }
+ }
+ else if (alloc)
+ /* Apply the bias to the symbol value. */
+ st_value = dwfl_adjusted_st_value (mod,
+ *resolved ? mod->main.elf : elf,
+ st_value);
+ break;
+ }
+
+ if (adjust_st_value)
+ sym->st_value = st_value;
+
+ if (addr != NULL)
+ *addr = st_value;
+
+ if (unlikely (sym->st_name >= symstrdata->d_size))
+ {
+ __libdwfl_seterrno (DWFL_E_BADSTROFF);
+ return NULL;
+ }
+ if (elfp)
+ *elfp = elf;
+ if (biasp)
+ *biasp = dwfl_adjusted_st_value (mod, elf, 0);
+ return (const char *) symstrdata->d_buf + sym->st_name;
+}
+
+const char *
+dwfl_module_getsym_info (Dwfl_Module *mod, int ndx,
+ GElf_Sym *sym, GElf_Addr *addr,
+ GElf_Word *shndxp,
+ Elf **elfp, Dwarf_Addr *bias)
+{
+ bool resolved;
+ return __libdwfl_getsym (mod, ndx, sym, addr, shndxp, elfp, bias,
+ &resolved, false);
+}
+INTDEF (dwfl_module_getsym_info)
+
+const char *
+dwfl_module_getsym (Dwfl_Module *mod, int ndx,
+ GElf_Sym *sym, GElf_Word *shndxp)
+{
+ bool resolved;
+ return __libdwfl_getsym (mod, ndx, sym, NULL, shndxp, NULL, NULL,
+ &resolved, true);
+}
+INTDEF (dwfl_module_getsym)
diff --git a/libdwfl/dwfl_module_info.c b/libdwfl/dwfl_module_info.c
new file mode 100644
index 0000000..af1faab
--- /dev/null
+++ b/libdwfl/dwfl_module_info.c
@@ -0,0 +1,65 @@
+/* Return information about a module.
+ Copyright (C) 2005-2010 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+const char *
+dwfl_module_info (Dwfl_Module *mod, void ***userdata,
+ Dwarf_Addr *start, Dwarf_Addr *end,
+ Dwarf_Addr *dwbias, Dwarf_Addr *symbias,
+ const char **mainfile, const char **debugfile)
+{
+ if (mod == NULL)
+ return NULL;
+
+ if (userdata)
+ *userdata = &mod->userdata;
+ if (start)
+ *start = mod->low_addr;
+ if (end)
+ *end = mod->high_addr;
+
+ if (dwbias)
+ *dwbias = (mod->debug.elf == NULL ? (Dwarf_Addr) -1
+ : dwfl_adjusted_dwarf_addr (mod, 0));
+ if (symbias)
+ *symbias = (mod->symfile == NULL ? (Dwarf_Addr) -1
+ : dwfl_adjusted_st_value (mod, mod->symfile->elf, 0));
+
+ if (mainfile)
+ *mainfile = mod->main.name;
+
+ if (debugfile)
+ *debugfile = mod->debug.name;
+
+ return mod->name;
+}
diff --git a/libdwfl/dwfl_module_nextcu.c b/libdwfl/dwfl_module_nextcu.c
new file mode 100644
index 0000000..32ee6bc
--- /dev/null
+++ b/libdwfl/dwfl_module_nextcu.c
@@ -0,0 +1,48 @@
+/* Iterate through DWARF compilation units in a module.
+ Copyright (C) 2005 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+Dwarf_Die *
+dwfl_module_nextcu (Dwfl_Module *mod, Dwarf_Die *lastcu, Dwarf_Addr *bias)
+{
+ if (INTUSE(dwfl_module_getdwarf) (mod, bias) == NULL)
+ return NULL;
+
+ struct dwfl_cu *cu;
+ Dwfl_Error error = __libdwfl_nextcu (mod, (struct dwfl_cu *) lastcu, &cu);
+ if (likely (error == DWFL_E_NOERROR))
+ return &cu->die; /* Same as a cast, so ok for null too. */
+
+ __libdwfl_seterrno (error);
+ return NULL;
+}
diff --git a/libdwfl/dwfl_module_register_names.c b/libdwfl/dwfl_module_register_names.c
new file mode 100644
index 0000000..9ea0937
--- /dev/null
+++ b/libdwfl/dwfl_module_register_names.c
@@ -0,0 +1,82 @@
+/* Enumerate DWARF register numbers and their names.
+ Copyright (C) 2005, 2006 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+
+int
+dwfl_module_register_names (Dwfl_Module *mod,
+ int (*func) (void *, int, const char *,
+ const char *, const char *,
+ int, int),
+ void *arg)
+{
+ if (unlikely (mod == NULL))
+ return -1;
+
+ if (unlikely (mod->ebl == NULL))
+ {
+ Dwfl_Error error = __libdwfl_module_getebl (mod);
+ if (error != DWFL_E_NOERROR)
+ {
+ __libdwfl_seterrno (error);
+ return -1;
+ }
+ }
+
+ int nregs = ebl_register_info (mod->ebl, -1, NULL, 0,
+ NULL, NULL, NULL, NULL);
+ int result = 0;
+ for (int regno = 0; regno < nregs && likely (result == 0); ++regno)
+ {
+ char name[32];
+ const char *setname = NULL;
+ const char *prefix = NULL;
+ int bits = -1;
+ int type = -1;
+ ssize_t len = ebl_register_info (mod->ebl, regno, name, sizeof name,
+ &prefix, &setname, &bits, &type);
+ if (unlikely (len < 0))
+ {
+ __libdwfl_seterrno (DWFL_E_LIBEBL);
+ result = -1;
+ break;
+ }
+ if (likely (len > 0))
+ {
+ assert (len > 1); /* Backend should never yield "". */
+ result = (*func) (arg, regno, setname, prefix, name, bits, type);
+ }
+ }
+
+ return result;
+}
diff --git a/libdwfl/dwfl_module_report_build_id.c b/libdwfl/dwfl_module_report_build_id.c
new file mode 100644
index 0000000..31e17c8
--- /dev/null
+++ b/libdwfl/dwfl_module_report_build_id.c
@@ -0,0 +1,84 @@
+/* Report build ID information for a module.
+ Copyright (C) 2007, 2008 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+// XXX vs report changed module: punting old file
+int
+dwfl_module_report_build_id (Dwfl_Module *mod,
+ const unsigned char *bits, size_t len,
+ GElf_Addr vaddr)
+{
+ if (mod == NULL)
+ return -1;
+
+ if (mod->main.elf != NULL)
+ {
+ /* Once we know about a file, we won't take any lies about
+ its contents. The only permissible call is a no-op. */
+
+ if ((size_t) mod->build_id_len == len
+ && (mod->build_id_vaddr == vaddr || vaddr == 0)
+ && !memcmp (bits, mod->build_id_bits, len))
+ return 0;
+
+ __libdwfl_seterrno (DWFL_E_ALREADY_ELF);
+ return -1;
+ }
+
+ if (vaddr != 0 && (vaddr < mod->low_addr || vaddr + len > mod->high_addr))
+ {
+ __libdwfl_seterrno (DWFL_E_ADDR_OUTOFRANGE);
+ return -1;
+ }
+
+ void *copy = NULL;
+ if (len > 0)
+ {
+ copy = malloc (len);
+ if (unlikely (copy == NULL))
+ {
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return -1;
+ }
+ memcpy (copy, bits, len);
+ }
+
+ free (mod->build_id_bits);
+
+ mod->build_id_bits = copy;
+ mod->build_id_len = len;
+ mod->build_id_vaddr = vaddr;
+
+ return 0;
+}
+INTDEF (dwfl_module_report_build_id)
diff --git a/libdwfl/dwfl_module_return_value_location.c b/libdwfl/dwfl_module_return_value_location.c
new file mode 100644
index 0000000..ff6f56f
--- /dev/null
+++ b/libdwfl/dwfl_module_return_value_location.c
@@ -0,0 +1,66 @@
+/* Return location expression to find return value given a function type DIE.
+ Copyright (C) 2005, 2006 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+
+int
+dwfl_module_return_value_location (Dwfl_Module *mod, Dwarf_Die *functypedie,
+ const Dwarf_Op **locops)
+{
+ if (mod == NULL)
+ return -1;
+
+ if (mod->ebl == NULL)
+ {
+ Dwfl_Error error = __libdwfl_module_getebl (mod);
+ if (error != DWFL_E_NOERROR)
+ {
+ __libdwfl_seterrno (error);
+ return -1;
+ }
+ }
+
+ int nops = ebl_return_value_location (mod->ebl, functypedie, locops);
+ if (unlikely (nops < 0))
+ {
+ if (nops == -1)
+ __libdwfl_seterrno (DWFL_E_LIBDW);
+ else if (nops == -2)
+ __libdwfl_seterrno (DWFL_E_WEIRD_TYPE);
+ else
+ __libdwfl_seterrno (DWFL_E_LIBEBL);
+ nops = -1;
+ }
+
+ return nops;
+}
diff --git a/libdwfl/dwfl_nextcu.c b/libdwfl/dwfl_nextcu.c
new file mode 100644
index 0000000..64bd521
--- /dev/null
+++ b/libdwfl/dwfl_nextcu.c
@@ -0,0 +1,86 @@
+/* Iterate through DWARF compilation units across all modules.
+ Copyright (C) 2005-2010 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+Dwarf_Die *
+dwfl_nextcu (Dwfl *dwfl, Dwarf_Die *lastcu, Dwarf_Addr *bias)
+{
+ if (dwfl == NULL)
+ return NULL;
+
+ struct dwfl_cu *cu = (struct dwfl_cu *) lastcu;
+ Dwfl_Module *mod;
+
+ if (cu == NULL)
+ {
+ mod = dwfl->modulelist;
+ goto nextmod;
+ }
+ else
+ mod = cu->mod;
+
+ Dwfl_Error error;
+ do
+ {
+ error = __libdwfl_nextcu (mod, cu, &cu);
+ if (error != DWFL_E_NOERROR)
+ break;
+
+ if (cu != NULL)
+ {
+ *bias = dwfl_adjusted_dwarf_addr (mod, 0);
+ return &cu->die;
+ }
+
+ do
+ {
+ mod = mod->next;
+
+ nextmod:
+ if (mod == NULL)
+ return NULL;
+
+ if (mod->dwerr == DWFL_E_NOERROR
+ && (mod->dw != NULL
+ || INTUSE(dwfl_module_getdwarf) (mod, bias) != NULL))
+ break;
+ }
+ while (mod->dwerr == DWFL_E_NO_DWARF);
+ error = mod->dwerr;
+ }
+ while (error == DWFL_E_NOERROR);
+
+ __libdwfl_seterrno (error);
+ return NULL;
+}
+INTDEF (dwfl_nextcu)
diff --git a/libdwfl/dwfl_onesrcline.c b/libdwfl/dwfl_onesrcline.c
new file mode 100644
index 0000000..b1e7055
--- /dev/null
+++ b/libdwfl/dwfl_onesrcline.c
@@ -0,0 +1,60 @@
+/* Return one of the sources lines of a CU.
+ Copyright (C) 2005 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+Dwfl_Line *
+dwfl_onesrcline (Dwarf_Die *cudie, size_t idx)
+{
+ struct dwfl_cu *cu = (struct dwfl_cu *) cudie;
+
+ if (cudie == NULL)
+ return NULL;
+
+ if (cu->lines == NULL)
+ {
+ Dwfl_Error error = __libdwfl_cu_getsrclines (cu);
+ if (error != DWFL_E_NOERROR)
+ {
+ __libdwfl_seterrno (error);
+ return NULL;
+ }
+ }
+
+ if (idx >= cu->die.cu->lines->nlines)
+ {
+ __libdwfl_seterrno (DWFL_E (LIBDW, DWARF_E_INVALID_LINE_IDX));
+ return NULL;
+ }
+
+ return &cu->lines->idx[idx];
+}
diff --git a/libdwfl/dwfl_report_elf.c b/libdwfl/dwfl_report_elf.c
new file mode 100644
index 0000000..3fc9384
--- /dev/null
+++ b/libdwfl/dwfl_report_elf.c
@@ -0,0 +1,342 @@
+/* Report a module to libdwfl based on ELF program headers.
+ Copyright (C) 2005-2010 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include <fcntl.h>
+#include <unistd.h>
+
+
+/* We start every ET_REL module at a moderately aligned boundary.
+ This keeps the low addresses easy to read compared to a layout
+ starting at 0 (as when using -e). It also makes it unlikely
+ that a middle section will have a larger alignment and require
+ rejiggering (see below). */
+#define REL_MIN_ALIGN ((GElf_Xword) 0x100)
+
+bool
+internal_function
+__libdwfl_elf_address_range (Elf *elf, GElf_Addr base, bool add_p_vaddr,
+ bool sanity, GElf_Addr *vaddrp,
+ GElf_Addr *address_syncp, GElf_Addr *startp,
+ GElf_Addr *endp, GElf_Addr *biasp,
+ GElf_Half *e_typep)
+{
+ GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
+ if (ehdr == NULL)
+ {
+ elf_error:
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ return false;
+ }
+
+ GElf_Addr vaddr = 0;
+ GElf_Addr address_sync = 0;
+ GElf_Addr start = 0, end = 0, bias = 0;
+ switch (ehdr->e_type)
+ {
+ case ET_REL:
+ /* For a relocatable object, we do an arbitrary section layout.
+ By updating the section header in place, we leave the layout
+ information to be found by relocation. */
+
+ start = end = base = (base + REL_MIN_ALIGN - 1) & -REL_MIN_ALIGN;
+
+ bool first = true;
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (unlikely (shdr == NULL))
+ goto elf_error;
+
+ if (shdr->sh_flags & SHF_ALLOC)
+ {
+ const GElf_Xword align = shdr->sh_addralign ?: 1;
+ const GElf_Addr next = (end + align - 1) & -align;
+ if (shdr->sh_addr == 0
+ /* Once we've started doing layout we have to do it all,
+ unless we just layed out the first section at 0 when
+ it already was at 0. */
+ || (bias == 0 && end > start && end != next))
+ {
+ shdr->sh_addr = next;
+ if (end == base)
+ /* This is the first section assigned a location.
+ Use its aligned address as the module's base. */
+ start = base = shdr->sh_addr;
+ else if (unlikely (base & (align - 1)))
+ {
+ /* If BASE has less than the maximum alignment of
+ any section, we eat more than the optimal amount
+ of padding and so make the module's apparent
+ size come out larger than it would when placed
+ at zero. So reset the layout with a better base. */
+
+ start = end = base = (base + align - 1) & -align;
+ Elf_Scn *prev_scn = NULL;
+ do
+ {
+ prev_scn = elf_nextscn (elf, prev_scn);
+ GElf_Shdr prev_shdr_mem;
+ GElf_Shdr *prev_shdr = gelf_getshdr (prev_scn,
+ &prev_shdr_mem);
+ if (unlikely (prev_shdr == NULL))
+ goto elf_error;
+ if (prev_shdr->sh_flags & SHF_ALLOC)
+ {
+ const GElf_Xword prev_align
+ = prev_shdr->sh_addralign ?: 1;
+
+ prev_shdr->sh_addr
+ = (end + prev_align - 1) & -prev_align;
+ end = prev_shdr->sh_addr + prev_shdr->sh_size;
+
+ if (unlikely (! gelf_update_shdr (prev_scn,
+ prev_shdr)))
+ goto elf_error;
+ }
+ }
+ while (prev_scn != scn);
+ continue;
+ }
+
+ end = shdr->sh_addr + shdr->sh_size;
+ if (likely (shdr->sh_addr != 0)
+ && unlikely (! gelf_update_shdr (scn, shdr)))
+ goto elf_error;
+ }
+ else
+ {
+ /* The address is already assigned. Just track it. */
+ if (first || end < shdr->sh_addr + shdr->sh_size)
+ end = shdr->sh_addr + shdr->sh_size;
+ if (first || bias > shdr->sh_addr)
+ /* This is the lowest address in the module. */
+ bias = shdr->sh_addr;
+
+ if ((shdr->sh_addr - bias + base) & (align - 1))
+ /* This section winds up misaligned using BASE.
+ Adjust BASE upwards to make it congruent to
+ the lowest section address in the file modulo ALIGN. */
+ base = (((base + align - 1) & -align)
+ + (bias & (align - 1)));
+ }
+
+ first = false;
+ }
+ }
+
+ if (bias != 0)
+ {
+ /* The section headers had nonzero sh_addr values. The layout
+ was already done. We've just collected the total span.
+ Now just compute the bias from the requested base. */
+ start = base;
+ end = end - bias + start;
+ bias = start - bias;
+ }
+ break;
+
+ /* Everything else has to have program headers. */
+
+ case ET_EXEC:
+ case ET_CORE:
+ /* An assigned base address is meaningless for these. */
+ base = 0;
+ add_p_vaddr = true;
+ FALLTHROUGH;
+ case ET_DYN:
+ default:;
+ size_t phnum;
+ if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
+ goto elf_error;
+ for (size_t i = 0; i < phnum; ++i)
+ {
+ GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
+ if (unlikely (ph == NULL))
+ goto elf_error;
+ if (ph->p_type == PT_LOAD)
+ {
+ vaddr = ph->p_vaddr & -ph->p_align;
+ address_sync = ph->p_vaddr + ph->p_memsz;
+ break;
+ }
+ }
+ if (add_p_vaddr)
+ {
+ start = base + vaddr;
+ bias = base;
+ }
+ else
+ {
+ start = base;
+ bias = base - vaddr;
+ }
+
+ for (size_t i = phnum; i-- > 0;)
+ {
+ GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
+ if (unlikely (ph == NULL))
+ goto elf_error;
+ if (ph->p_type == PT_LOAD
+ && ph->p_vaddr + ph->p_memsz > 0)
+ {
+ end = bias + (ph->p_vaddr + ph->p_memsz);
+ break;
+ }
+ }
+
+ if (end == 0 && sanity)
+ {
+ __libdwfl_seterrno (DWFL_E_NO_PHDR);
+ return false;
+ }
+ break;
+ }
+ if (vaddrp)
+ *vaddrp = vaddr;
+ if (address_syncp)
+ *address_syncp = address_sync;
+ if (startp)
+ *startp = start;
+ if (endp)
+ *endp = end;
+ if (biasp)
+ *biasp = bias;
+ if (e_typep)
+ *e_typep = ehdr->e_type;
+ return true;
+}
+
+Dwfl_Module *
+internal_function
+__libdwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name,
+ int fd, Elf *elf, GElf_Addr base, bool add_p_vaddr,
+ bool sanity)
+{
+ GElf_Addr vaddr, address_sync, start, end, bias;
+ GElf_Half e_type;
+ if (! __libdwfl_elf_address_range (elf, base, add_p_vaddr, sanity, &vaddr,
+ &address_sync, &start, &end, &bias,
+ &e_type))
+ return NULL;
+ Dwfl_Module *m = INTUSE(dwfl_report_module) (dwfl, name, start, end);
+ if (m != NULL)
+ {
+ if (m->main.name == NULL)
+ {
+ m->main.name = strdup (file_name);
+ m->main.fd = fd;
+ }
+ else if ((fd >= 0 && m->main.fd != fd)
+ || strcmp (m->main.name, file_name))
+ {
+ overlap:
+ m->gc = true;
+ __libdwfl_seterrno (DWFL_E_OVERLAP);
+ return NULL;
+ }
+
+ /* Preinstall the open ELF handle for the module. */
+ if (m->main.elf == NULL)
+ {
+ m->main.elf = elf;
+ m->main.vaddr = vaddr;
+ m->main.address_sync = address_sync;
+ m->main_bias = bias;
+ m->e_type = e_type;
+ }
+ else
+ {
+ elf_end (elf);
+ if (m->main_bias != bias
+ || m->main.vaddr != vaddr || m->main.address_sync != address_sync)
+ goto overlap;
+ }
+ }
+ return m;
+}
+
+Dwfl_Module *
+dwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name, int fd,
+ GElf_Addr base, bool add_p_vaddr)
+{
+ bool closefd = false;
+ if (fd < 0)
+ {
+ closefd = true;
+ fd = open (file_name, O_RDONLY);
+ if (fd < 0)
+ {
+ __libdwfl_seterrno (DWFL_E_ERRNO);
+ return NULL;
+ }
+ }
+
+ Elf *elf;
+ Dwfl_Error error = __libdw_open_file (&fd, &elf, closefd, false);
+ if (error != DWFL_E_NOERROR)
+ {
+ __libdwfl_seterrno (error);
+ return NULL;
+ }
+
+ Dwfl_Module *mod = __libdwfl_report_elf (dwfl, name, file_name,
+ fd, elf, base, add_p_vaddr, true);
+ if (mod == NULL)
+ {
+ elf_end (elf);
+ if (closefd)
+ close (fd);
+ }
+
+ return mod;
+}
+INTDEF (dwfl_report_elf)
+NEW_VERSION (dwfl_report_elf, ELFUTILS_0.156)
+
+#ifdef SYMBOL_VERSIONING
+Dwfl_Module *
+ _compat_without_add_p_vaddr_dwfl_report_elf (Dwfl *dwfl, const char *name,
+ const char *file_name, int fd,
+ GElf_Addr base);
+COMPAT_VERSION_NEWPROTO (dwfl_report_elf, ELFUTILS_0.122, without_add_p_vaddr)
+
+Dwfl_Module *
+_compat_without_add_p_vaddr_dwfl_report_elf (Dwfl *dwfl, const char *name,
+ const char *file_name, int fd,
+ GElf_Addr base)
+{
+ return dwfl_report_elf (dwfl, name, file_name, fd, base, true);
+}
+#endif
diff --git a/libdwfl/dwfl_segment_report_module.c b/libdwfl/dwfl_segment_report_module.c
new file mode 100644
index 0000000..207a257
--- /dev/null
+++ b/libdwfl/dwfl_segment_report_module.c
@@ -0,0 +1,939 @@
+/* Sniff out modules from ELF headers visible in memory segments.
+ Copyright (C) 2008-2012, 2014, 2015 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include "../libelf/libelfP.h" /* For NOTE_ALIGN. */
+#undef _
+#include "libdwflP.h"
+#include "common.h"
+
+#include <elf.h>
+#include <gelf.h>
+#include <inttypes.h>
+#include <endian.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <system.h>
+
+
+/* A good size for the initial read from memory, if it's not too costly.
+ This more than covers the phdrs and note segment in the average 64-bit
+ binary. */
+
+#define INITIAL_READ 1024
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+# define MY_ELFDATA ELFDATA2LSB
+#else
+# define MY_ELFDATA ELFDATA2MSB
+#endif
+
+
+/* Return user segment index closest to ADDR but not above it.
+ If NEXT, return the closest to ADDR but not below it. */
+static int
+addr_segndx (Dwfl *dwfl, size_t segment, GElf_Addr addr, bool next)
+{
+ int ndx = -1;
+ do
+ {
+ if (dwfl->lookup_segndx[segment] >= 0)
+ ndx = dwfl->lookup_segndx[segment];
+ if (++segment >= dwfl->lookup_elts - 1)
+ return next ? ndx + 1 : ndx;
+ }
+ while (dwfl->lookup_addr[segment] < addr);
+
+ if (next)
+ {
+ while (dwfl->lookup_segndx[segment] < 0)
+ if (++segment >= dwfl->lookup_elts - 1)
+ return ndx + 1;
+ ndx = dwfl->lookup_segndx[segment];
+ }
+
+ return ndx;
+}
+
+/* Return whether there is SZ bytes available at PTR till END. */
+
+static bool
+buf_has_data (const void *ptr, const void *end, size_t sz)
+{
+ return ptr < end && (size_t) (end - ptr) >= sz;
+}
+
+/* Read SZ bytes into *RETP from *PTRP (limited by END) in format EI_DATA.
+ Function comes from src/readelf.c . */
+
+static bool
+buf_read_ulong (unsigned char ei_data, size_t sz,
+ const void **ptrp, const void *end, uint64_t *retp)
+{
+ if (! buf_has_data (*ptrp, end, sz))
+ return false;
+
+ union
+ {
+ uint64_t u64;
+ uint32_t u32;
+ } u;
+
+ memcpy (&u, *ptrp, sz);
+ (*ptrp) += sz;
+
+ if (retp == NULL)
+ return true;
+
+ if (MY_ELFDATA != ei_data)
+ {
+ if (sz == 4)
+ CONVERT (u.u32);
+ else
+ CONVERT (u.u64);
+ }
+ if (sz == 4)
+ *retp = u.u32;
+ else
+ *retp = u.u64;
+ return true;
+}
+
+/* Try to find matching entry for module from address MODULE_START to
+ MODULE_END in NT_FILE note located at NOTE_FILE of NOTE_FILE_SIZE
+ bytes in format EI_CLASS and EI_DATA. */
+
+static const char *
+handle_file_note (GElf_Addr module_start, GElf_Addr module_end,
+ unsigned char ei_class, unsigned char ei_data,
+ const void *note_file, size_t note_file_size)
+{
+ if (note_file == NULL)
+ return NULL;
+
+ size_t sz;
+ switch (ei_class)
+ {
+ case ELFCLASS32:
+ sz = 4;
+ break;
+ case ELFCLASS64:
+ sz = 8;
+ break;
+ default:
+ return NULL;
+ }
+
+ const void *ptr = note_file;
+ const void *end = note_file + note_file_size;
+ uint64_t count;
+ if (! buf_read_ulong (ei_data, sz, &ptr, end, &count))
+ return NULL;
+ if (! buf_read_ulong (ei_data, sz, &ptr, end, NULL)) // page_size
+ return NULL;
+
+ uint64_t maxcount = (size_t) (end - ptr) / (3 * sz);
+ if (count > maxcount)
+ return NULL;
+
+ /* Where file names are stored. */
+ const char *fptr = ptr + 3 * count * sz;
+
+ ssize_t firstix = -1;
+ ssize_t lastix = -1;
+ for (size_t mix = 0; mix < count; mix++)
+ {
+ uint64_t mstart, mend, moffset;
+ if (! buf_read_ulong (ei_data, sz, &ptr, fptr, &mstart)
+ || ! buf_read_ulong (ei_data, sz, &ptr, fptr, &mend)
+ || ! buf_read_ulong (ei_data, sz, &ptr, fptr, &moffset))
+ return NULL;
+ if (mstart == module_start && moffset == 0)
+ firstix = lastix = mix;
+ if (firstix != -1 && mstart < module_end)
+ lastix = mix;
+ if (mend >= module_end)
+ break;
+ }
+ if (firstix == -1)
+ return NULL;
+
+ const char *retval = NULL;
+ for (ssize_t mix = 0; mix <= lastix; mix++)
+ {
+ const char *fnext = memchr (fptr, 0, (const char *) end - fptr);
+ if (fnext == NULL)
+ return NULL;
+ if (mix == firstix)
+ retval = fptr;
+ if (firstix < mix && mix <= lastix && strcmp (fptr, retval) != 0)
+ return NULL;
+ fptr = fnext + 1;
+ }
+ return retval;
+}
+
+/* Return true iff we are certain ELF cannot match BUILD_ID of
+ BUILD_ID_LEN bytes. Pass DISK_FILE_HAS_BUILD_ID as false if it is
+ certain ELF does not contain build-id (it is only a performance hit
+ to pass it always as true). */
+
+static bool
+invalid_elf (Elf *elf, bool disk_file_has_build_id,
+ const void *build_id, size_t build_id_len)
+{
+ if (! disk_file_has_build_id && build_id_len > 0)
+ {
+ /* Module found in segments with build-id is more reliable
+ than a module found via DT_DEBUG on disk without any
+ build-id. */
+ return true;
+ }
+ if (disk_file_has_build_id && build_id_len > 0)
+ {
+ const void *elf_build_id;
+ ssize_t elf_build_id_len;
+
+ /* If there is a build id in the elf file, check it. */
+ elf_build_id_len = INTUSE(dwelf_elf_gnu_build_id) (elf, &elf_build_id);
+ if (elf_build_id_len > 0)
+ {
+ if (build_id_len != (size_t) elf_build_id_len
+ || memcmp (build_id, elf_build_id, build_id_len) != 0)
+ return true;
+ }
+ }
+ return false;
+}
+
+int
+dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
+ Dwfl_Memory_Callback *memory_callback,
+ void *memory_callback_arg,
+ Dwfl_Module_Callback *read_eagerly,
+ void *read_eagerly_arg,
+ const void *note_file, size_t note_file_size,
+ const struct r_debug_info *r_debug_info)
+{
+ size_t segment = ndx;
+
+ if (segment >= dwfl->lookup_elts)
+ segment = dwfl->lookup_elts - 1;
+
+ while (segment > 0
+ && (dwfl->lookup_segndx[segment] > ndx
+ || dwfl->lookup_segndx[segment] == -1))
+ --segment;
+
+ while (dwfl->lookup_segndx[segment] < ndx)
+ if (++segment == dwfl->lookup_elts)
+ return 0;
+
+ GElf_Addr start = dwfl->lookup_addr[segment];
+
+ inline bool segment_read (int segndx,
+ void **buffer, size_t *buffer_available,
+ GElf_Addr addr, size_t minread)
+ {
+ return ! (*memory_callback) (dwfl, segndx, buffer, buffer_available,
+ addr, minread, memory_callback_arg);
+ }
+
+ inline void release_buffer (void **buffer, size_t *buffer_available)
+ {
+ if (*buffer != NULL)
+ (void) segment_read (-1, buffer, buffer_available, 0, 0);
+ }
+
+ /* First read in the file header and check its sanity. */
+
+ void *buffer = NULL;
+ size_t buffer_available = INITIAL_READ;
+ Elf *elf = NULL;
+ int fd = -1;
+
+ /* We might have to reserve some memory for the phdrs. Set to NULL
+ here so we can always safely free it. */
+ void *phdrsp = NULL;
+
+ inline int finish (void)
+ {
+ free (phdrsp);
+ release_buffer (&buffer, &buffer_available);
+ if (elf != NULL)
+ elf_end (elf);
+ if (fd != -1)
+ close (fd);
+ return ndx;
+ }
+
+ if (segment_read (ndx, &buffer, &buffer_available,
+ start, sizeof (Elf64_Ehdr))
+ || memcmp (buffer, ELFMAG, SELFMAG) != 0)
+ return finish ();
+
+ inline bool read_portion (void **data, size_t *data_size,
+ GElf_Addr vaddr, size_t filesz)
+ {
+ if (vaddr - start + filesz > buffer_available
+ /* If we're in string mode, then don't consider the buffer we have
+ sufficient unless it contains the terminator of the string. */
+ || (filesz == 0 && memchr (vaddr - start + buffer, '\0',
+ buffer_available - (vaddr - start)) == NULL))
+ {
+ *data = NULL;
+ *data_size = filesz;
+ return segment_read (addr_segndx (dwfl, segment, vaddr, false),
+ data, data_size, vaddr, filesz);
+ }
+
+ /* We already have this whole note segment from our initial read. */
+ *data = vaddr - start + buffer;
+ *data_size = 0;
+ return false;
+ }
+
+ inline void finish_portion (void **data, size_t *data_size)
+ {
+ if (*data_size != 0)
+ release_buffer (data, data_size);
+ }
+
+ /* Extract the information we need from the file header. */
+ const unsigned char *e_ident;
+ unsigned char ei_class;
+ unsigned char ei_data;
+ uint16_t e_type;
+ union
+ {
+ Elf32_Ehdr e32;
+ Elf64_Ehdr e64;
+ } ehdr;
+ GElf_Off phoff;
+ uint_fast16_t phnum;
+ uint_fast16_t phentsize;
+ GElf_Off shdrs_end;
+ Elf_Data xlatefrom =
+ {
+ .d_type = ELF_T_EHDR,
+ .d_buf = (void *) buffer,
+ .d_version = EV_CURRENT,
+ };
+ Elf_Data xlateto =
+ {
+ .d_type = ELF_T_EHDR,
+ .d_buf = &ehdr,
+ .d_size = sizeof ehdr,
+ .d_version = EV_CURRENT,
+ };
+ e_ident = ((const unsigned char *) buffer);
+ ei_class = e_ident[EI_CLASS];
+ ei_data = e_ident[EI_DATA];
+ switch (ei_class)
+ {
+ case ELFCLASS32:
+ xlatefrom.d_size = sizeof (Elf32_Ehdr);
+ if (elf32_xlatetom (&xlateto, &xlatefrom, ei_data) == NULL)
+ return finish ();
+ e_type = ehdr.e32.e_type;
+ phoff = ehdr.e32.e_phoff;
+ phnum = ehdr.e32.e_phnum;
+ phentsize = ehdr.e32.e_phentsize;
+ if (phentsize != sizeof (Elf32_Phdr))
+ return finish ();
+ shdrs_end = ehdr.e32.e_shoff + ehdr.e32.e_shnum * ehdr.e32.e_shentsize;
+ break;
+
+ case ELFCLASS64:
+ xlatefrom.d_size = sizeof (Elf64_Ehdr);
+ if (elf64_xlatetom (&xlateto, &xlatefrom, ei_data) == NULL)
+ return finish ();
+ e_type = ehdr.e64.e_type;
+ phoff = ehdr.e64.e_phoff;
+ phnum = ehdr.e64.e_phnum;
+ phentsize = ehdr.e64.e_phentsize;
+ if (phentsize != sizeof (Elf64_Phdr))
+ return finish ();
+ shdrs_end = ehdr.e64.e_shoff + ehdr.e64.e_shnum * ehdr.e64.e_shentsize;
+ break;
+
+ default:
+ return finish ();
+ }
+
+ /* The file header tells where to find the program headers.
+ These are what we need to find the boundaries of the module.
+ Without them, we don't have a module to report. */
+
+ if (phnum == 0)
+ return finish ();
+
+ xlatefrom.d_type = xlateto.d_type = ELF_T_PHDR;
+ xlatefrom.d_size = phnum * phentsize;
+
+ void *ph_buffer = NULL;
+ size_t ph_buffer_size = 0;
+ if (read_portion (&ph_buffer, &ph_buffer_size,
+ start + phoff, xlatefrom.d_size))
+ return finish ();
+
+ xlatefrom.d_buf = ph_buffer;
+
+ bool class32 = ei_class == ELFCLASS32;
+ size_t phdr_size = class32 ? sizeof (Elf32_Phdr) : sizeof (Elf64_Phdr);
+ if (unlikely (phnum > SIZE_MAX / phdr_size))
+ return finish ();
+ const size_t phdrsp_bytes = phnum * phdr_size;
+ phdrsp = malloc (phdrsp_bytes);
+ if (unlikely (phdrsp == NULL))
+ return finish ();
+
+ xlateto.d_buf = phdrsp;
+ xlateto.d_size = phdrsp_bytes;
+
+ /* Track the bounds of the file visible in memory. */
+ GElf_Off file_trimmed_end = 0; /* Proper p_vaddr + p_filesz end. */
+ GElf_Off file_end = 0; /* Rounded up to effective page size. */
+ GElf_Off contiguous = 0; /* Visible as contiguous file from START. */
+ GElf_Off total_filesz = 0; /* Total size of data to read. */
+
+ /* Collect the bias between START and the containing PT_LOAD's p_vaddr. */
+ GElf_Addr bias = 0;
+ bool found_bias = false;
+
+ /* Collect the unbiased bounds of the module here. */
+ GElf_Addr module_start = -1l;
+ GElf_Addr module_end = 0;
+ GElf_Addr module_address_sync = 0;
+
+ /* If we see PT_DYNAMIC, record it here. */
+ GElf_Addr dyn_vaddr = 0;
+ GElf_Xword dyn_filesz = 0;
+
+ /* Collect the build ID bits here. */
+ void *build_id = NULL;
+ size_t build_id_len = 0;
+ GElf_Addr build_id_vaddr = 0;
+
+ /* Consider a PT_NOTE we've found in the image. */
+ inline void consider_notes (GElf_Addr vaddr, GElf_Xword filesz)
+ {
+ /* If we have already seen a build ID, we don't care any more. */
+ if (build_id != NULL || filesz == 0)
+ return;
+
+ void *data;
+ size_t data_size;
+ if (read_portion (&data, &data_size, vaddr, filesz))
+ return;
+
+ assert (sizeof (Elf32_Nhdr) == sizeof (Elf64_Nhdr));
+
+ void *notes;
+ if (ei_data == MY_ELFDATA)
+ notes = data;
+ else
+ {
+ notes = malloc (filesz);
+ if (unlikely (notes == NULL))
+ return;
+ xlatefrom.d_type = xlateto.d_type = ELF_T_NHDR;
+ xlatefrom.d_buf = (void *) data;
+ xlatefrom.d_size = filesz;
+ xlateto.d_buf = notes;
+ xlateto.d_size = filesz;
+ if (elf32_xlatetom (&xlateto, &xlatefrom,
+ ehdr.e32.e_ident[EI_DATA]) == NULL)
+ goto done;
+ }
+
+ const GElf_Nhdr *nh = notes;
+ while ((const void *) nh < (const void *) notes + filesz)
+ {
+ const void *note_name = nh + 1;
+ const void *note_desc = note_name + NOTE_ALIGN (nh->n_namesz);
+ if (unlikely ((size_t) ((const void *) notes + filesz
+ - note_desc) < nh->n_descsz))
+ break;
+
+ if (nh->n_type == NT_GNU_BUILD_ID
+ && nh->n_descsz > 0
+ && nh->n_namesz == sizeof "GNU"
+ && !memcmp (note_name, "GNU", sizeof "GNU"))
+ {
+ build_id_vaddr = note_desc - (const void *) notes + vaddr;
+ build_id_len = nh->n_descsz;
+ build_id = malloc (nh->n_descsz);
+ if (likely (build_id != NULL))
+ memcpy (build_id, note_desc, build_id_len);
+ break;
+ }
+
+ nh = note_desc + NOTE_ALIGN (nh->n_descsz);
+ }
+
+ done:
+ if (notes != data)
+ free (notes);
+ finish_portion (&data, &data_size);
+ }
+
+ /* Consider each of the program headers we've read from the image. */
+ inline void consider_phdr (GElf_Word type,
+ GElf_Addr vaddr, GElf_Xword memsz,
+ GElf_Off offset, GElf_Xword filesz,
+ GElf_Xword align)
+ {
+ switch (type)
+ {
+ case PT_DYNAMIC:
+ dyn_vaddr = vaddr;
+ dyn_filesz = filesz;
+ break;
+
+ case PT_NOTE:
+ /* We calculate from the p_offset of the note segment,
+ because we don't yet know the bias for its p_vaddr. */
+ consider_notes (start + offset, filesz);
+ break;
+
+ case PT_LOAD:
+ align = dwfl->segment_align > 1 ? dwfl->segment_align : align ?: 1;
+
+ GElf_Addr vaddr_end = (vaddr + memsz + align - 1) & -align;
+ GElf_Addr filesz_vaddr = filesz < memsz ? vaddr + filesz : vaddr_end;
+ GElf_Off filesz_offset = filesz_vaddr - vaddr + offset;
+
+ if (file_trimmed_end < offset + filesz)
+ {
+ file_trimmed_end = offset + filesz;
+
+ /* Trim the last segment so we don't bother with zeros
+ in the last page that are off the end of the file.
+ However, if the extra bit in that page includes the
+ section headers, keep them. */
+ if (shdrs_end <= filesz_offset && shdrs_end > file_trimmed_end)
+ {
+ filesz += shdrs_end - file_trimmed_end;
+ file_trimmed_end = shdrs_end;
+ }
+ }
+
+ total_filesz += filesz;
+
+ if (file_end < filesz_offset)
+ {
+ file_end = filesz_offset;
+ if (filesz_vaddr - start == filesz_offset)
+ contiguous = file_end;
+ }
+
+ if (!found_bias && (offset & -align) == 0
+ && likely (filesz_offset >= phoff + phnum * phentsize))
+ {
+ bias = start - vaddr;
+ found_bias = true;
+ }
+
+ if ((vaddr & -align) < module_start)
+ {
+ module_start = vaddr & -align;
+ module_address_sync = vaddr + memsz;
+ }
+
+ if (module_end < vaddr_end)
+ module_end = vaddr_end;
+ break;
+ }
+ }
+
+ Elf32_Phdr (*p32)[phnum] = phdrsp;
+ Elf64_Phdr (*p64)[phnum] = phdrsp;
+ if (ei_class == ELFCLASS32)
+ {
+ if (elf32_xlatetom (&xlateto, &xlatefrom, ei_data) == NULL)
+ found_bias = false; /* Trigger error check. */
+ else
+ for (uint_fast16_t i = 0; i < phnum; ++i)
+ consider_phdr ((*p32)[i].p_type,
+ (*p32)[i].p_vaddr, (*p32)[i].p_memsz,
+ (*p32)[i].p_offset, (*p32)[i].p_filesz,
+ (*p32)[i].p_align);
+ }
+ else
+ {
+ if (elf64_xlatetom (&xlateto, &xlatefrom, ei_data) == NULL)
+ found_bias = false; /* Trigger error check. */
+ else
+ for (uint_fast16_t i = 0; i < phnum; ++i)
+ consider_phdr ((*p64)[i].p_type,
+ (*p64)[i].p_vaddr, (*p64)[i].p_memsz,
+ (*p64)[i].p_offset, (*p64)[i].p_filesz,
+ (*p64)[i].p_align);
+ }
+
+ finish_portion (&ph_buffer, &ph_buffer_size);
+
+ /* We must have seen the segment covering offset 0, or else the ELF
+ header we read at START was not produced by these program headers. */
+ if (unlikely (!found_bias))
+ {
+ free (build_id);
+ return finish ();
+ }
+
+ /* Now we know enough to report a module for sure: its bounds. */
+ module_start += bias;
+ module_end += bias;
+
+ dyn_vaddr += bias;
+
+ /* NAME found from link map has precedence over DT_SONAME possibly read
+ below. */
+ bool name_is_final = false;
+
+ /* Try to match up DYN_VADDR against L_LD as found in link map.
+ Segments sniffing may guess invalid address as the first read-only memory
+ mapping may not be dumped to the core file (if ELF headers are not dumped)
+ and the ELF header is dumped first with the read/write mapping of the same
+ file at higher addresses. */
+ if (r_debug_info != NULL)
+ for (const struct r_debug_info_module *module = r_debug_info->module;
+ module != NULL; module = module->next)
+ if (module_start <= module->l_ld && module->l_ld < module_end)
+ {
+ /* L_LD read from link map must be right while DYN_VADDR is unsafe.
+ Therefore subtract DYN_VADDR and add L_LD to get a possibly
+ corrective displacement for all addresses computed so far. */
+ GElf_Addr fixup = module->l_ld - dyn_vaddr;
+ if ((fixup & (dwfl->segment_align - 1)) == 0
+ && module_start + fixup <= module->l_ld
+ && module->l_ld < module_end + fixup)
+ {
+ module_start += fixup;
+ module_end += fixup;
+ dyn_vaddr += fixup;
+ bias += fixup;
+ if (module->name[0] != '\0')
+ {
+ name = basename (module->name);
+ name_is_final = true;
+ }
+ break;
+ }
+ }
+
+ if (r_debug_info != NULL)
+ {
+ bool skip_this_module = false;
+ for (struct r_debug_info_module *module = r_debug_info->module;
+ module != NULL; module = module->next)
+ if ((module_end > module->start && module_start < module->end)
+ || dyn_vaddr == module->l_ld)
+ {
+ if (module->elf != NULL
+ && invalid_elf (module->elf, module->disk_file_has_build_id,
+ build_id, build_id_len))
+ {
+ elf_end (module->elf);
+ close (module->fd);
+ module->elf = NULL;
+ module->fd = -1;
+ }
+ if (module->elf != NULL)
+ {
+ /* Ignore this found module if it would conflict in address
+ space with any already existing module of DWFL. */
+ skip_this_module = true;
+ }
+ }
+ if (skip_this_module)
+ {
+ free (build_id);
+ return finish ();
+ }
+ }
+
+ const char *file_note_name = handle_file_note (module_start, module_end,
+ ei_class, ei_data,
+ note_file, note_file_size);
+ if (file_note_name)
+ {
+ name = file_note_name;
+ name_is_final = true;
+ bool invalid = false;
+ fd = open (name, O_RDONLY);
+ if (fd >= 0)
+ {
+ Dwfl_Error error = __libdw_open_file (&fd, &elf, true, false);
+ if (error == DWFL_E_NOERROR)
+ invalid = invalid_elf (elf, true /* disk_file_has_build_id */,
+ build_id, build_id_len);
+ }
+ if (invalid)
+ {
+ /* The file was there, but the build_id didn't match. We
+ still want to report the module, but need to get the ELF
+ some other way if possible. */
+ close (fd);
+ fd = -1;
+ elf_end (elf);
+ elf = NULL;
+ }
+ }
+
+ /* Our return value now says to skip the segments contained
+ within the module. */
+ ndx = addr_segndx (dwfl, segment, module_end, true);
+
+ /* Examine its .dynamic section to get more interesting details.
+ If it has DT_SONAME, we'll use that as the module name.
+ If it has a DT_DEBUG, then it's actually a PIE rather than a DSO.
+ We need its DT_STRTAB and DT_STRSZ to decipher DT_SONAME,
+ and they also tell us the essential portion of the file
+ for fetching symbols. */
+ GElf_Addr soname_stroff = 0;
+ GElf_Addr dynstr_vaddr = 0;
+ GElf_Xword dynstrsz = 0;
+ bool execlike = false;
+ inline bool consider_dyn (GElf_Sxword tag, GElf_Xword val)
+ {
+ switch (tag)
+ {
+ default:
+ return false;
+
+ case DT_DEBUG:
+ execlike = true;
+ break;
+
+ case DT_SONAME:
+ soname_stroff = val;
+ break;
+
+ case DT_STRTAB:
+ dynstr_vaddr = val;
+ break;
+
+ case DT_STRSZ:
+ dynstrsz = val;
+ break;
+ }
+
+ return soname_stroff != 0 && dynstr_vaddr != 0 && dynstrsz != 0;
+ }
+
+ const size_t dyn_entsize = (ei_class == ELFCLASS32
+ ? sizeof (Elf32_Dyn) : sizeof (Elf64_Dyn));
+ void *dyn_data = NULL;
+ size_t dyn_data_size = 0;
+ if (dyn_filesz != 0 && dyn_filesz % dyn_entsize == 0
+ && ! read_portion (&dyn_data, &dyn_data_size, dyn_vaddr, dyn_filesz))
+ {
+ void *dyns = malloc (dyn_filesz);
+ Elf32_Dyn (*d32)[dyn_filesz / sizeof (Elf32_Dyn)] = dyns;
+ Elf64_Dyn (*d64)[dyn_filesz / sizeof (Elf64_Dyn)] = dyns;
+ if (unlikely (dyns == NULL))
+ return finish ();
+
+ xlatefrom.d_type = xlateto.d_type = ELF_T_DYN;
+ xlatefrom.d_buf = (void *) dyn_data;
+ xlatefrom.d_size = dyn_filesz;
+ xlateto.d_buf = dyns;
+ xlateto.d_size = dyn_filesz;
+
+ if (ei_class == ELFCLASS32)
+ {
+ if (elf32_xlatetom (&xlateto, &xlatefrom, ei_data) != NULL)
+ for (size_t i = 0; i < dyn_filesz / sizeof (Elf32_Dyn); ++i)
+ if (consider_dyn ((*d32)[i].d_tag, (*d32)[i].d_un.d_val))
+ break;
+ }
+ else
+ {
+ if (elf64_xlatetom (&xlateto, &xlatefrom, ei_data) != NULL)
+ for (size_t i = 0; i < dyn_filesz / sizeof (Elf64_Dyn); ++i)
+ if (consider_dyn ((*d64)[i].d_tag, (*d64)[i].d_un.d_val))
+ break;
+ }
+ free (dyns);
+ }
+ finish_portion (&dyn_data, &dyn_data_size);
+
+ /* We'll use the name passed in or a stupid default if not DT_SONAME. */
+ if (name == NULL)
+ name = e_type == ET_EXEC ? "[exe]" : execlike ? "[pie]" : "[dso]";
+
+ void *soname = NULL;
+ size_t soname_size = 0;
+ if (! name_is_final && dynstrsz != 0 && dynstr_vaddr != 0)
+ {
+ /* We know the bounds of the .dynstr section.
+
+ The DYNSTR_VADDR pointer comes from the .dynamic section
+ (DT_STRTAB, detected above). Ordinarily the dynamic linker
+ will have adjusted this pointer in place so it's now an
+ absolute address. But sometimes .dynamic is read-only (in
+ vDSOs and odd architectures), and sometimes the adjustment
+ just hasn't happened yet in the memory image we looked at.
+ So treat DYNSTR_VADDR as an absolute address if it falls
+ within the module bounds, or try applying the phdr bias
+ when that adjusts it to fall within the module bounds. */
+
+ if ((dynstr_vaddr < module_start || dynstr_vaddr >= module_end)
+ && dynstr_vaddr + bias >= module_start
+ && dynstr_vaddr + bias < module_end)
+ dynstr_vaddr += bias;
+
+ if (unlikely (dynstr_vaddr + dynstrsz > module_end))
+ dynstrsz = 0;
+
+ /* Try to get the DT_SONAME string. */
+ if (soname_stroff != 0 && soname_stroff + 1 < dynstrsz
+ && ! read_portion (&soname, &soname_size,
+ dynstr_vaddr + soname_stroff, 0))
+ name = soname;
+ }
+
+ /* Now that we have chosen the module's name and bounds, report it.
+ If we found a build ID, report that too. */
+
+ Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, name,
+ module_start, module_end);
+
+ // !execlike && ET_EXEC is PIE.
+ // execlike && !ET_EXEC is a static executable.
+ if (mod != NULL && (execlike || ehdr.e32.e_type == ET_EXEC))
+ mod->is_executable = true;
+
+ if (likely (mod != NULL) && build_id != NULL
+ && unlikely (INTUSE(dwfl_module_report_build_id) (mod,
+ build_id,
+ build_id_len,
+ build_id_vaddr)))
+ {
+ mod->gc = true;
+ mod = NULL;
+ }
+
+ /* At this point we do not need BUILD_ID or NAME any more.
+ They have been copied. */
+ free (build_id);
+ finish_portion (&soname, &soname_size);
+
+ if (unlikely (mod == NULL))
+ {
+ ndx = -1;
+ return finish ();
+ }
+
+ /* We have reported the module. Now let the caller decide whether we
+ should read the whole thing in right now. */
+
+ const GElf_Off cost = (contiguous < file_trimmed_end ? total_filesz
+ : buffer_available >= contiguous ? 0
+ : contiguous - buffer_available);
+ const GElf_Off worthwhile = ((dynstr_vaddr == 0 || dynstrsz == 0) ? 0
+ : dynstr_vaddr + dynstrsz - start);
+ const GElf_Off whole = MAX (file_trimmed_end, shdrs_end);
+
+ if (elf == NULL
+ && (*read_eagerly) (MODCB_ARGS (mod), &buffer, &buffer_available,
+ cost, worthwhile, whole, contiguous,
+ read_eagerly_arg, &elf)
+ && elf == NULL)
+ {
+ /* The caller wants to read the whole file in right now, but hasn't
+ done it for us. Fill in a local image of the virtual file. */
+
+ void *contents = calloc (1, file_trimmed_end);
+ if (unlikely (contents == NULL))
+ return finish ();
+
+ inline void final_read (size_t offset, GElf_Addr vaddr, size_t size)
+ {
+ void *into = contents + offset;
+ size_t read_size = size;
+ (void) segment_read (addr_segndx (dwfl, segment, vaddr, false),
+ &into, &read_size, vaddr, size);
+ }
+
+ if (contiguous < file_trimmed_end)
+ {
+ /* We can't use the memory image verbatim as the file image.
+ So we'll be reading into a local image of the virtual file. */
+
+ inline void read_phdr (GElf_Word type, GElf_Addr vaddr,
+ GElf_Off offset, GElf_Xword filesz)
+ {
+ if (type == PT_LOAD)
+ final_read (offset, vaddr + bias, filesz);
+ }
+
+ if (ei_class == ELFCLASS32)
+ for (uint_fast16_t i = 0; i < phnum; ++i)
+ read_phdr ((*p32)[i].p_type, (*p32)[i].p_vaddr,
+ (*p32)[i].p_offset, (*p32)[i].p_filesz);
+ else
+ for (uint_fast16_t i = 0; i < phnum; ++i)
+ read_phdr ((*p64)[i].p_type, (*p64)[i].p_vaddr,
+ (*p64)[i].p_offset, (*p64)[i].p_filesz);
+ }
+ else
+ {
+ /* The whole file sits contiguous in memory,
+ but the caller didn't want to just do it. */
+
+ const size_t have = MIN (buffer_available, file_trimmed_end);
+ memcpy (contents, buffer, have);
+
+ if (have < file_trimmed_end)
+ final_read (have, start + have, file_trimmed_end - have);
+ }
+
+ elf = elf_memory (contents, file_trimmed_end);
+ if (unlikely (elf == NULL))
+ free (contents);
+ else
+ elf->flags |= ELF_F_MALLOCED;
+ }
+
+ if (elf != NULL)
+ {
+ /* Install the file in the module. */
+ mod->main.elf = elf;
+ elf = NULL;
+ fd = -1;
+ mod->main.vaddr = module_start - bias;
+ mod->main.address_sync = module_address_sync;
+ mod->main_bias = bias;
+ }
+
+ return finish ();
+}
diff --git a/libdwfl/dwfl_validate_address.c b/libdwfl/dwfl_validate_address.c
new file mode 100644
index 0000000..15e2602
--- /dev/null
+++ b/libdwfl/dwfl_validate_address.c
@@ -0,0 +1,65 @@
+/* Validate an address and the relocatability of an offset from it.
+ Copyright (C) 2005 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+int
+dwfl_validate_address (Dwfl *dwfl, Dwarf_Addr address, Dwarf_Sword offset)
+{
+ Dwfl_Module *mod = INTUSE(dwfl_addrmodule) (dwfl, address);
+ if (mod == NULL)
+ return -1;
+
+ Dwarf_Addr relative = address;
+ int idx = INTUSE(dwfl_module_relocate_address) (mod, &relative);
+ if (idx < 0)
+ return -1;
+
+ if (offset != 0)
+ {
+ int offset_idx = -1;
+ relative = address + offset;
+ if (relative >= mod->low_addr && relative <= mod->high_addr)
+ {
+ offset_idx = INTUSE(dwfl_module_relocate_address) (mod, &relative);
+ if (offset_idx < 0)
+ return -1;
+ }
+ if (offset_idx != idx)
+ {
+ __libdwfl_seterrno (DWFL_E_ADDR_OUTOFRANGE);
+ return -1;
+ }
+ }
+
+ return 0;
+}
diff --git a/libdwfl/dwfl_version.c b/libdwfl/dwfl_version.c
new file mode 100644
index 0000000..c27d4f6
--- /dev/null
+++ b/libdwfl/dwfl_version.c
@@ -0,0 +1,39 @@
+/* Return implementation's version string suitable for printing.
+ Copyright (C) 2006, 2007 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+const char *
+dwfl_version (Dwfl *dwfl __attribute__ ((unused)))
+{
+ return PACKAGE_VERSION;
+}
diff --git a/libdwfl/elf-from-memory.c b/libdwfl/elf-from-memory.c
new file mode 100644
index 0000000..12a0a1b
--- /dev/null
+++ b/libdwfl/elf-from-memory.c
@@ -0,0 +1,394 @@
+/* Reconstruct an ELF file by reading the segments out of remote memory.
+ Copyright (C) 2005-2011, 2014, 2015 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include "../libelf/libelfP.h"
+#undef _
+
+#include "libdwflP.h"
+
+#include <gelf.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Reconstruct an ELF file by reading the segments out of remote memory
+ based on the ELF file header at EHDR_VMA and the ELF program headers it
+ points to. If not null, *LOADBASEP is filled in with the difference
+ between the addresses from which the segments were read, and the
+ addresses the file headers put them at.
+
+ The function READ_MEMORY is called to copy at least MINREAD and at most
+ MAXREAD bytes from the remote memory at target address ADDRESS into the
+ local buffer at DATA; it should return -1 for errors (with code in
+ `errno'), 0 if it failed to read at least MINREAD bytes due to EOF, or
+ the number of bytes read if >= MINREAD. ARG is passed through.
+
+ PAGESIZE is the minimum page size and alignment used for the PT_LOAD
+ segments. */
+
+Elf *
+elf_from_remote_memory (GElf_Addr ehdr_vma,
+ GElf_Xword pagesize,
+ GElf_Addr *loadbasep,
+ ssize_t (*read_memory) (void *arg, void *data,
+ GElf_Addr address,
+ size_t minread,
+ size_t maxread),
+ void *arg)
+{
+ /* We might have to reserve some memory for the phdrs. Set to NULL
+ here so we can always safely free it. */
+ void *phdrsp = NULL;
+
+ /* First read in the file header and check its sanity. */
+
+ const size_t initial_bufsize = 256;
+ unsigned char *buffer = malloc (initial_bufsize);
+ if (unlikely (buffer == NULL))
+ {
+ no_memory:
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return NULL;
+ }
+
+ ssize_t nread = (*read_memory) (arg, buffer, ehdr_vma,
+ sizeof (Elf32_Ehdr), initial_bufsize);
+ if (nread <= 0)
+ {
+ read_error:
+ free (buffer);
+ free (phdrsp);
+ __libdwfl_seterrno (nread < 0 ? DWFL_E_ERRNO : DWFL_E_TRUNCATED);
+ return NULL;
+ }
+
+ if (memcmp (buffer, ELFMAG, SELFMAG) != 0)
+ {
+ bad_elf:
+ free (buffer);
+ free (phdrsp);
+ __libdwfl_seterrno (DWFL_E_BADELF);
+ return NULL;
+ }
+
+ /* Extract the information we need from the file header. */
+
+ union
+ {
+ Elf32_Ehdr e32;
+ Elf64_Ehdr e64;
+ } ehdr;
+ Elf_Data xlatefrom =
+ {
+ .d_type = ELF_T_EHDR,
+ .d_buf = buffer,
+ .d_version = EV_CURRENT,
+ };
+ Elf_Data xlateto =
+ {
+ .d_type = ELF_T_EHDR,
+ .d_buf = &ehdr,
+ .d_size = sizeof ehdr,
+ .d_version = EV_CURRENT,
+ };
+
+ GElf_Off phoff;
+ uint_fast16_t phnum;
+ uint_fast16_t phentsize;
+ GElf_Off shdrs_end;
+
+ switch (buffer[EI_CLASS])
+ {
+ case ELFCLASS32:
+ xlatefrom.d_size = sizeof (Elf32_Ehdr);
+ if (elf32_xlatetom (&xlateto, &xlatefrom, buffer[EI_DATA]) == NULL)
+ {
+ libelf_error:
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ return NULL;
+ }
+ phoff = ehdr.e32.e_phoff;
+ phnum = ehdr.e32.e_phnum;
+ phentsize = ehdr.e32.e_phentsize;
+ if (phentsize != sizeof (Elf32_Phdr) || phnum == 0)
+ goto bad_elf;
+ shdrs_end = ehdr.e32.e_shoff + ehdr.e32.e_shnum * ehdr.e32.e_shentsize;
+ break;
+
+ case ELFCLASS64:
+ xlatefrom.d_size = sizeof (Elf64_Ehdr);
+ if (elf64_xlatetom (&xlateto, &xlatefrom, buffer[EI_DATA]) == NULL)
+ goto libelf_error;
+ phoff = ehdr.e64.e_phoff;
+ phnum = ehdr.e64.e_phnum;
+ phentsize = ehdr.e64.e_phentsize;
+ if (phentsize != sizeof (Elf64_Phdr) || phnum == 0)
+ goto bad_elf;
+ shdrs_end = ehdr.e64.e_shoff + ehdr.e64.e_shnum * ehdr.e64.e_shentsize;
+ break;
+
+ default:
+ goto bad_elf;
+ }
+
+
+ /* The file header tells where to find the program headers.
+ These are what we use to actually choose what to read. */
+
+ xlatefrom.d_type = xlateto.d_type = ELF_T_PHDR;
+ xlatefrom.d_size = phnum * phentsize;
+
+ if ((size_t) nread >= phoff + phnum * phentsize)
+ /* We already have all the phdrs from the initial read. */
+ xlatefrom.d_buf = buffer + phoff;
+ else
+ {
+ /* Read in the program headers. */
+
+ if (initial_bufsize < (size_t)phnum * phentsize)
+ {
+ unsigned char *newbuf = realloc (buffer, phnum * phentsize);
+ if (newbuf == NULL)
+ {
+ free (buffer);
+ free (phdrsp);
+ goto no_memory;
+ }
+ buffer = newbuf;
+ }
+ nread = (*read_memory) (arg, buffer, ehdr_vma + phoff,
+ phnum * phentsize, phnum * phentsize);
+ if (nread <= 0)
+ goto read_error;
+
+ xlatefrom.d_buf = buffer;
+ }
+
+ bool class32 = ehdr.e32.e_ident[EI_CLASS] == ELFCLASS32;
+ size_t phdr_size = class32 ? sizeof (Elf32_Phdr) : sizeof (Elf64_Phdr);
+ if (unlikely (phnum > SIZE_MAX / phdr_size))
+ {
+ free (buffer);
+ goto no_memory;
+ }
+ const size_t phdrsp_bytes = phnum * phdr_size;
+ phdrsp = malloc (phdrsp_bytes);
+ if (unlikely (phdrsp == NULL))
+ {
+ free (buffer);
+ goto no_memory;
+ }
+
+ xlateto.d_buf = phdrsp;
+ xlateto.d_size = phdrsp_bytes;
+
+ /* Scan for PT_LOAD segments to find the total size of the file image. */
+ size_t contents_size = 0;
+ GElf_Off segments_end = 0;
+ GElf_Off segments_end_mem = 0;
+ GElf_Addr loadbase = ehdr_vma;
+ bool found_base = false;
+ Elf32_Phdr (*p32)[phnum] = phdrsp;
+ Elf64_Phdr (*p64)[phnum] = phdrsp;
+ switch (ehdr.e32.e_ident[EI_CLASS])
+ {
+ /* Sanity checks segments and calculates segment_end,
+ segments_end, segments_end_mem and loadbase (if not
+ found_base yet). Returns true if sanity checking failed,
+ false otherwise. */
+ inline bool handle_segment (GElf_Addr vaddr, GElf_Off offset,
+ GElf_Xword filesz, GElf_Xword memsz)
+ {
+ /* Sanity check the segment load aligns with the pagesize. */
+ if (((vaddr - offset) & (pagesize - 1)) != 0)
+ return true;
+
+ GElf_Off segment_end = ((offset + filesz + pagesize - 1)
+ & -pagesize);
+
+ if (segment_end > (GElf_Off) contents_size)
+ contents_size = segment_end;
+
+ if (!found_base && (offset & -pagesize) == 0)
+ {
+ loadbase = ehdr_vma - (vaddr & -pagesize);
+ found_base = true;
+ }
+
+ segments_end = offset + filesz;
+ segments_end_mem = offset + memsz;
+ return false;
+ }
+
+ case ELFCLASS32:
+ if (elf32_xlatetom (&xlateto, &xlatefrom,
+ ehdr.e32.e_ident[EI_DATA]) == NULL)
+ goto libelf_error;
+ for (uint_fast16_t i = 0; i < phnum; ++i)
+ if ((*p32)[i].p_type == PT_LOAD)
+ if (handle_segment ((*p32)[i].p_vaddr, (*p32)[i].p_offset,
+ (*p32)[i].p_filesz, (*p32)[i].p_memsz))
+ goto bad_elf;
+ break;
+
+ case ELFCLASS64:
+ if (elf64_xlatetom (&xlateto, &xlatefrom,
+ ehdr.e64.e_ident[EI_DATA]) == NULL)
+ goto libelf_error;
+ for (uint_fast16_t i = 0; i < phnum; ++i)
+ if ((*p64)[i].p_type == PT_LOAD)
+ if (handle_segment ((*p64)[i].p_vaddr, (*p64)[i].p_offset,
+ (*p64)[i].p_filesz, (*p64)[i].p_memsz))
+ goto bad_elf;
+ break;
+
+ default:
+ abort ();
+ break;
+ }
+
+ /* Trim the last segment so we don't bother with zeros in the last page
+ that are off the end of the file. However, if the extra bit in that
+ page includes the section headers and the memory isn't extended (which
+ might indicate it will have been reused otherwise), keep them. */
+ if ((GElf_Off) contents_size > segments_end
+ && (GElf_Off) contents_size >= shdrs_end
+ && segments_end == segments_end_mem)
+ {
+ contents_size = segments_end;
+ if ((GElf_Off) contents_size < shdrs_end)
+ contents_size = shdrs_end;
+ }
+ else
+ contents_size = segments_end;
+
+ free (buffer);
+
+ /* Now we know the size of the whole image we want read in. */
+ buffer = calloc (1, contents_size);
+ if (buffer == NULL)
+ {
+ free (phdrsp);
+ goto no_memory;
+ }
+
+ switch (ehdr.e32.e_ident[EI_CLASS])
+ {
+ /* Reads the given segment. Returns true if reading fails,
+ false otherwise. */
+ inline bool handle_segment (GElf_Addr vaddr, GElf_Off offset,
+ GElf_Xword filesz)
+ {
+ GElf_Off start = offset & -pagesize;
+ GElf_Off end = (offset + filesz + pagesize - 1) & -pagesize;
+ if (end > (GElf_Off) contents_size)
+ end = contents_size;
+ nread = (*read_memory) (arg, buffer + start,
+ (loadbase + vaddr) & -pagesize,
+ end - start, end - start);
+ return nread <= 0;
+ }
+
+ case ELFCLASS32:
+ for (uint_fast16_t i = 0; i < phnum; ++i)
+ if ((*p32)[i].p_type == PT_LOAD)
+ if (handle_segment ((*p32)[i].p_vaddr, (*p32)[i].p_offset,
+ (*p32)[i].p_filesz))
+ goto read_error;
+
+ /* If the segments visible in memory didn't include the section
+ headers, then clear them from the file header. */
+ if (contents_size < shdrs_end)
+ {
+ ehdr.e32.e_shoff = 0;
+ ehdr.e32.e_shnum = 0;
+ ehdr.e32.e_shstrndx = 0;
+ }
+
+ /* This will normally have been in the first PT_LOAD segment. But it
+ conceivably could be missing, and we might have just changed it. */
+ xlatefrom.d_type = xlateto.d_type = ELF_T_EHDR;
+ xlatefrom.d_size = xlateto.d_size = sizeof ehdr.e32;
+ xlatefrom.d_buf = &ehdr.e32;
+ xlateto.d_buf = buffer;
+ if (elf32_xlatetof (&xlateto, &xlatefrom,
+ ehdr.e32.e_ident[EI_DATA]) == NULL)
+ goto libelf_error;
+ break;
+
+ case ELFCLASS64:
+ for (uint_fast16_t i = 0; i < phnum; ++i)
+ if ((*p64)[i].p_type == PT_LOAD)
+ if (handle_segment ((*p64)[i].p_vaddr, (*p64)[i].p_offset,
+ (*p64)[i].p_filesz))
+ goto read_error;
+
+ /* If the segments visible in memory didn't include the section
+ headers, then clear them from the file header. */
+ if (contents_size < shdrs_end)
+ {
+ ehdr.e64.e_shoff = 0;
+ ehdr.e64.e_shnum = 0;
+ ehdr.e64.e_shstrndx = 0;
+ }
+
+ /* This will normally have been in the first PT_LOAD segment. But it
+ conceivably could be missing, and we might have just changed it. */
+ xlatefrom.d_type = xlateto.d_type = ELF_T_EHDR;
+ xlatefrom.d_size = xlateto.d_size = sizeof ehdr.e64;
+ xlatefrom.d_buf = &ehdr.e64;
+ xlateto.d_buf = buffer;
+ if (elf64_xlatetof (&xlateto, &xlatefrom,
+ ehdr.e64.e_ident[EI_DATA]) == NULL)
+ goto libelf_error;
+ break;
+
+ default:
+ abort ();
+ break;
+ }
+
+ free (phdrsp);
+ phdrsp = NULL;
+
+ /* Now we have the image. Open libelf on it. */
+
+ Elf *elf = elf_memory ((char *) buffer, contents_size);
+ if (elf == NULL)
+ {
+ free (buffer);
+ goto libelf_error;
+ }
+
+ elf->flags |= ELF_F_MALLOCED;
+ if (loadbasep != NULL)
+ *loadbasep = loadbase;
+ return elf;
+}
diff --git a/libdwfl/find-debuginfo.c b/libdwfl/find-debuginfo.c
new file mode 100644
index 0000000..6d5a42a
--- /dev/null
+++ b/libdwfl/find-debuginfo.c
@@ -0,0 +1,402 @@
+/* Standard find_debuginfo callback for libdwfl.
+ Copyright (C) 2005-2010, 2014, 2015 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include "system.h"
+
+
+/* Try to open [DIR/][SUBDIR/]DEBUGLINK, return file descriptor or -1.
+ On success, *DEBUGINFO_FILE_NAME has the malloc'd name of the open file. */
+static int
+try_open (const struct stat *main_stat,
+ const char *dir, const char *subdir, const char *debuglink,
+ char **debuginfo_file_name)
+{
+ char *fname;
+ if (dir == NULL && subdir == NULL)
+ {
+ fname = strdup (debuglink);
+ if (unlikely (fname == NULL))
+ return -1;
+ }
+ else if ((subdir == NULL ? asprintf (&fname, "%s/%s", dir, debuglink)
+ : dir == NULL ? asprintf (&fname, "%s/%s", subdir, debuglink)
+ : asprintf (&fname, "%s/%s/%s", dir, subdir, debuglink)) < 0)
+ return -1;
+
+ struct stat st;
+ int fd = TEMP_FAILURE_RETRY (open (fname, O_RDONLY));
+ if (fd < 0)
+ free (fname);
+ else if (fstat (fd, &st) == 0
+ && st.st_ino == main_stat->st_ino
+ && st.st_dev == main_stat->st_dev)
+ {
+ /* This is the main file by another name. Don't look at it again. */
+ free (fname);
+ close (fd);
+ errno = ENOENT;
+ fd = -1;
+ }
+ else
+ *debuginfo_file_name = fname;
+
+ return fd;
+}
+
+/* Return true iff the FD's contents CRC matches DEBUGLINK_CRC. */
+static inline bool
+check_crc (int fd, GElf_Word debuglink_crc)
+{
+ uint32_t file_crc;
+ return (__libdwfl_crc32_file (fd, &file_crc) == 0
+ && file_crc == debuglink_crc);
+}
+
+static bool
+validate (Dwfl_Module *mod, int fd, bool check, GElf_Word debuglink_crc)
+{
+ /* For alt debug files always check the build-id from the Dwarf and alt. */
+ if (mod->dw != NULL)
+ {
+ bool valid = false;
+ const void *build_id;
+ const char *altname;
+ ssize_t build_id_len = INTUSE(dwelf_dwarf_gnu_debugaltlink) (mod->dw,
+ &altname,
+ &build_id);
+ if (build_id_len > 0)
+ {
+ /* We need to open an Elf handle on the file so we can check its
+ build ID note for validation. Backdoor the handle into the
+ module data structure since we had to open it early anyway. */
+ Dwfl_Error error = __libdw_open_file (&fd, &mod->alt_elf,
+ false, false);
+ if (error != DWFL_E_NOERROR)
+ __libdwfl_seterrno (error);
+ else
+ {
+ const void *alt_build_id;
+ ssize_t alt_len = INTUSE(dwelf_elf_gnu_build_id) (mod->alt_elf,
+ &alt_build_id);
+ if (alt_len > 0 && alt_len == build_id_len
+ && memcmp (build_id, alt_build_id, alt_len) == 0)
+ valid = true;
+ else
+ {
+ /* A mismatch! */
+ elf_end (mod->alt_elf);
+ mod->alt_elf = NULL;
+ close (fd);
+ fd = -1;
+ }
+ }
+ }
+ return valid;
+ }
+
+ /* If we have a build ID, check only that. */
+ if (mod->build_id_len > 0)
+ {
+ /* We need to open an Elf handle on the file so we can check its
+ build ID note for validation. Backdoor the handle into the
+ module data structure since we had to open it early anyway. */
+
+ mod->debug.valid = false;
+ Dwfl_Error error = __libdw_open_file (&fd, &mod->debug.elf, false, false);
+ if (error != DWFL_E_NOERROR)
+ __libdwfl_seterrno (error);
+ else if (likely (__libdwfl_find_build_id (mod, false,
+ mod->debug.elf) == 2))
+ /* Also backdoor the gratuitous flag. */
+ mod->debug.valid = true;
+ else
+ {
+ /* A mismatch! */
+ elf_end (mod->debug.elf);
+ mod->debug.elf = NULL;
+ close (fd);
+ fd = -1;
+ }
+
+ return mod->debug.valid;
+ }
+
+ return !check || check_crc (fd, debuglink_crc);
+}
+
+static int
+find_debuginfo_in_path (Dwfl_Module *mod, const char *file_name,
+ const char *debuglink_file, GElf_Word debuglink_crc,
+ char **debuginfo_file_name)
+{
+ bool cancheck = debuglink_crc != (GElf_Word) 0;
+
+ const char *file_basename = file_name == NULL ? NULL : basename (file_name);
+ char *localname = NULL;
+
+ /* We invent a debuglink .debug name if NULL, but then want to try the
+ basename too. */
+ bool debuglink_null = debuglink_file == NULL;
+ if (debuglink_null)
+ {
+ /* For a alt debug multi file we need a name, for a separate debug
+ name we may be able to fall back on file_basename.debug. */
+ if (file_basename == NULL || mod->dw != NULL)
+ {
+ errno = 0;
+ return -1;
+ }
+
+ size_t len = strlen (file_basename);
+ localname = malloc (len + sizeof ".debug");
+ if (unlikely (localname == NULL))
+ return -1;
+ memcpy (localname, file_basename, len);
+ memcpy (&localname[len], ".debug", sizeof ".debug");
+ debuglink_file = localname;
+ cancheck = false;
+ }
+
+ /* Look for a file named DEBUGLINK_FILE in the directories
+ indicated by the debug directory path setting. */
+
+ const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
+ char *localpath = strdup ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
+ ?: DEFAULT_DEBUGINFO_PATH);
+ if (unlikely (localpath == NULL))
+ {
+ free (localname);
+ return -1;
+ }
+
+ /* A leading - or + in the whole path sets whether to check file CRCs. */
+ bool defcheck = true;
+ char *path = localpath;
+ if (path[0] == '-' || path[0] == '+')
+ {
+ defcheck = path[0] == '+';
+ ++path;
+ }
+
+ /* XXX dev/ino should be cached in struct dwfl_file. */
+ struct stat main_stat;
+ if (unlikely ((mod->main.fd != -1 ? fstat (mod->main.fd, &main_stat)
+ : file_name != NULL ? stat (file_name, &main_stat)
+ : -1) < 0))
+ {
+ main_stat.st_dev = 0;
+ main_stat.st_ino = 0;
+ }
+
+ char *file_dirname = (file_basename == file_name ? NULL
+ : strndup (file_name, file_basename - 1 - file_name));
+ if (file_basename != file_name && file_dirname == NULL)
+ {
+ free (localpath);
+ free (localname);
+ return -1;
+ }
+ char *p;
+ while ((p = strsep (&path, ":")) != NULL)
+ {
+ /* A leading - or + says whether to check file CRCs for this element. */
+ bool check = defcheck;
+ if (*p == '+' || *p == '-')
+ check = *p++ == '+';
+ check = check && cancheck;
+
+ /* Try the basename too, if we made up the debuglink name and this
+ is not the main directory. */
+ bool try_file_basename;
+
+ const char *dir, *subdir, *file;
+ switch (p[0])
+ {
+ case '\0':
+ /* An empty entry says to try the main file's directory. */
+ dir = file_dirname;
+ subdir = NULL;
+ file = debuglink_file;
+ try_file_basename = false;
+ break;
+ case '/':
+ /* An absolute path says to look there for a subdirectory
+ named by the main file's absolute directory. This cannot
+ be applied to a relative file name. For alt debug files
+ it means to look for the basename file in that dir or the
+ .dwz subdir (see below). */
+ if (mod->dw == NULL
+ && (file_dirname == NULL || file_dirname[0] != '/'))
+ continue;
+ dir = p;
+ if (mod->dw == NULL)
+ {
+ subdir = file_dirname;
+ /* We want to explore all sub-subdirs. Chop off one slash
+ at a time. */
+ explore_dir:
+ subdir = strchr (subdir, '/');
+ if (subdir != NULL)
+ subdir = subdir + 1;
+ if (subdir && *subdir == 0)
+ continue;
+ file = debuglink_file;
+ }
+ else
+ {
+ subdir = NULL;
+ file = basename (debuglink_file);
+ }
+ try_file_basename = debuglink_null;
+ break;
+ default:
+ /* A relative path says to try a subdirectory of that name
+ in the main file's directory. */
+ dir = file_dirname;
+ subdir = p;
+ file = debuglink_file;
+ try_file_basename = debuglink_null;
+ break;
+ }
+
+ char *fname = NULL;
+ int fd = try_open (&main_stat, dir, subdir, file, &fname);
+ if (fd < 0 && try_file_basename)
+ fd = try_open (&main_stat, dir, subdir, file_basename, &fname);
+ if (fd < 0)
+ switch (errno)
+ {
+ case ENOENT:
+ case ENOTDIR:
+ /* If we are looking for the alt file also try the .dwz subdir.
+ But only if this is the empty or absolute path. */
+ if (mod->dw != NULL && (p[0] == '\0' || p[0] == '/'))
+ {
+ fd = try_open (&main_stat, dir, ".dwz",
+ basename (file), &fname);
+ if (fd < 0)
+ {
+ if (errno != ENOENT && errno != ENOTDIR)
+ goto fail_free;
+ else
+ continue;
+ }
+ break;
+ }
+ /* If possible try again with a sub-subdir. */
+ if (mod->dw == NULL && subdir)
+ goto explore_dir;
+ continue;
+ default:
+ goto fail_free;
+ }
+ if (validate (mod, fd, check, debuglink_crc))
+ {
+ free (localpath);
+ free (localname);
+ free (file_dirname);
+ *debuginfo_file_name = fname;
+ return fd;
+ }
+ free (fname);
+ close (fd);
+ }
+
+ /* No dice. */
+ errno = 0;
+fail_free:
+ free (localpath);
+ free (localname);
+ free (file_dirname);
+ return -1;
+}
+
+int
+dwfl_standard_find_debuginfo (Dwfl_Module *mod,
+ void **userdata __attribute__ ((unused)),
+ const char *modname __attribute__ ((unused)),
+ GElf_Addr base __attribute__ ((unused)),
+ const char *file_name,
+ const char *debuglink_file,
+ GElf_Word debuglink_crc,
+ char **debuginfo_file_name)
+{
+ /* First try by build ID if we have one. If that succeeds or fails
+ other than just by finding nothing, that's all we do. */
+ const unsigned char *bits;
+ GElf_Addr vaddr;
+ if (INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr) > 0)
+ {
+ /* Dropping most arguments means we cannot rely on them in
+ dwfl_build_id_find_debuginfo. But leave it that way since
+ some user code out there also does this, so we'll have to
+ handle it anyway. */
+ int fd = INTUSE(dwfl_build_id_find_debuginfo) (mod,
+ NULL, NULL, 0,
+ NULL, NULL, 0,
+ debuginfo_file_name);
+
+ /* Did the build_id callback find something or report an error?
+ Then we are done. Otherwise fallback on path based search. */
+ if (fd >= 0
+ || (mod->dw == NULL && mod->debug.elf != NULL)
+ || (mod->dw != NULL && mod->alt_elf != NULL)
+ || errno != 0)
+ return fd;
+ }
+
+ /* Failing that, search the path by name. */
+ int fd = find_debuginfo_in_path (mod, file_name,
+ debuglink_file, debuglink_crc,
+ debuginfo_file_name);
+
+ if (fd < 0 && errno == 0 && file_name != NULL)
+ {
+ /* If FILE_NAME is a symlink, the debug file might be associated
+ with the symlink target name instead. */
+
+ char *canon = canonicalize_file_name (file_name);
+ if (canon != NULL && strcmp (file_name, canon))
+ fd = find_debuginfo_in_path (mod, canon,
+ debuglink_file, debuglink_crc,
+ debuginfo_file_name);
+ free (canon);
+ }
+
+ return fd;
+}
+INTDEF (dwfl_standard_find_debuginfo)
diff --git a/libdwfl/frame_unwind.c b/libdwfl/frame_unwind.c
new file mode 100644
index 0000000..eaea495
--- /dev/null
+++ b/libdwfl/frame_unwind.c
@@ -0,0 +1,758 @@
+/* Get previous frame state for an existing frame state.
+ Copyright (C) 2013, 2014, 2016 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "cfi.h"
+#include <stdlib.h>
+#include "libdwflP.h"
+#include "../libdw/dwarf.h"
+#include <system.h>
+
+/* Maximum number of DWARF expression stack slots before returning an error. */
+#define DWARF_EXPR_STACK_MAX 0x100
+
+/* Maximum number of DWARF expression executed operations before returning an
+ error. */
+#define DWARF_EXPR_STEPS_MAX 0x1000
+
+bool
+internal_function
+__libdwfl_frame_reg_get (Dwfl_Frame *state, unsigned regno, Dwarf_Addr *val)
+{
+ Ebl *ebl = state->thread->process->ebl;
+ if (! ebl_dwarf_to_regno (ebl, ®no))
+ return false;
+ if (regno >= ebl_frame_nregs (ebl))
+ return false;
+ if ((state->regs_set[regno / sizeof (*state->regs_set) / 8]
+ & ((uint64_t) 1U << (regno % (sizeof (*state->regs_set) * 8)))) == 0)
+ return false;
+ if (val)
+ *val = state->regs[regno];
+ return true;
+}
+
+bool
+internal_function
+__libdwfl_frame_reg_set (Dwfl_Frame *state, unsigned regno, Dwarf_Addr val)
+{
+ Ebl *ebl = state->thread->process->ebl;
+ if (! ebl_dwarf_to_regno (ebl, ®no))
+ return false;
+ if (regno >= ebl_frame_nregs (ebl))
+ return false;
+ /* For example i386 user_regs_struct has signed fields. */
+ if (ebl_get_elfclass (ebl) == ELFCLASS32)
+ val &= 0xffffffff;
+ state->regs_set[regno / sizeof (*state->regs_set) / 8] |=
+ ((uint64_t) 1U << (regno % (sizeof (*state->regs_set) * 8)));
+ state->regs[regno] = val;
+ return true;
+}
+
+static bool
+state_get_reg (Dwfl_Frame *state, unsigned regno, Dwarf_Addr *val)
+{
+ if (! __libdwfl_frame_reg_get (state, regno, val))
+ {
+ __libdwfl_seterrno (DWFL_E_INVALID_REGISTER);
+ return false;
+ }
+ return true;
+}
+
+static int
+bra_compar (const void *key_voidp, const void *elem_voidp)
+{
+ Dwarf_Word offset = (uintptr_t) key_voidp;
+ const Dwarf_Op *op = elem_voidp;
+ return (offset > op->offset) - (offset < op->offset);
+}
+
+struct eval_stack {
+ Dwarf_Addr *addrs;
+ size_t used;
+ size_t allocated;
+};
+
+static bool
+do_push (struct eval_stack *stack, Dwarf_Addr val)
+{
+ if (stack->used >= DWARF_EXPR_STACK_MAX)
+ {
+ __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+ return false;
+ }
+ if (stack->used == stack->allocated)
+ {
+ stack->allocated = MAX (stack->allocated * 2, 32);
+ Dwarf_Addr *new_addrs;
+ new_addrs = realloc (stack->addrs,
+ stack->allocated * sizeof (*stack->addrs));
+ if (new_addrs == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return false;
+ }
+ stack->addrs = new_addrs;
+ }
+ stack->addrs[stack->used++] = val;
+ return true;
+}
+
+static bool
+do_pop (struct eval_stack *stack, Dwarf_Addr *val)
+{
+ if (stack->used == 0)
+ {
+ __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+ return false;
+ }
+ *val = stack->addrs[--stack->used];
+ return true;
+}
+
+/* If FRAME is NULL is are computing CFI frame base. In such case another
+ DW_OP_call_frame_cfa is no longer permitted. */
+
+static bool
+expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
+ size_t nops, Dwarf_Addr *result, Dwarf_Addr bias)
+{
+ Dwfl_Process *process = state->thread->process;
+ if (nops == 0)
+ {
+ __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+ return false;
+ }
+ struct eval_stack stack =
+ {
+ .addrs = NULL,
+ .used = 0,
+ .allocated = 0
+ };
+
+#define pop(x) do_pop(&stack, x)
+#define push(x) do_push(&stack, x)
+
+ Dwarf_Addr val1, val2;
+ bool is_location = false;
+ size_t steps_count = 0;
+ for (const Dwarf_Op *op = ops; op < ops + nops; op++)
+ {
+ if (++steps_count > DWARF_EXPR_STEPS_MAX)
+ {
+ __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+ return false;
+ }
+ switch (op->atom)
+ {
+ /* DW_OP_* order matches libgcc/unwind-dw2.c execute_stack_op: */
+ case DW_OP_lit0 ... DW_OP_lit31:
+ if (! push (op->atom - DW_OP_lit0))
+ {
+ free (stack.addrs);
+ return false;
+ }
+ break;
+ case DW_OP_addr:
+ if (! push (op->number + bias))
+ {
+ free (stack.addrs);
+ return false;
+ }
+ break;
+ case DW_OP_GNU_encoded_addr:
+ /* Missing support in the rest of elfutils. */
+ __libdwfl_seterrno (DWFL_E_UNSUPPORTED_DWARF);
+ return false;
+ case DW_OP_const1u:
+ case DW_OP_const1s:
+ case DW_OP_const2u:
+ case DW_OP_const2s:
+ case DW_OP_const4u:
+ case DW_OP_const4s:
+ case DW_OP_const8u:
+ case DW_OP_const8s:
+ case DW_OP_constu:
+ case DW_OP_consts:
+ if (! push (op->number))
+ {
+ free (stack.addrs);
+ return false;
+ }
+ break;
+ case DW_OP_reg0 ... DW_OP_reg31:
+ if (! state_get_reg (state, op->atom - DW_OP_reg0, &val1)
+ || ! push (val1))
+ {
+ free (stack.addrs);
+ return false;
+ }
+ break;
+ case DW_OP_regx:
+ if (! state_get_reg (state, op->number, &val1) || ! push (val1))
+ {
+ free (stack.addrs);
+ return false;
+ }
+ break;
+ case DW_OP_breg0 ... DW_OP_breg31:
+ if (! state_get_reg (state, op->atom - DW_OP_breg0, &val1))
+ {
+ free (stack.addrs);
+ return false;
+ }
+ val1 += op->number;
+ if (! push (val1))
+ {
+ free (stack.addrs);
+ return false;
+ }
+ break;
+ case DW_OP_bregx:
+ if (! state_get_reg (state, op->number, &val1))
+ {
+ free (stack.addrs);
+ return false;
+ }
+ val1 += op->number2;
+ if (! push (val1))
+ {
+ free (stack.addrs);
+ return false;
+ }
+ break;
+ case DW_OP_dup:
+ if (! pop (&val1) || ! push (val1) || ! push (val1))
+ {
+ free (stack.addrs);
+ return false;
+ }
+ break;
+ case DW_OP_drop:
+ if (! pop (&val1))
+ {
+ free (stack.addrs);
+ return false;
+ }
+ break;
+ case DW_OP_pick:
+ if (stack.used <= op->number)
+ {
+ free (stack.addrs);
+ __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+ return false;
+ }
+ if (! push (stack.addrs[stack.used - 1 - op->number]))
+ {
+ free (stack.addrs);
+ return false;
+ }
+ break;
+ case DW_OP_over:
+ if (! pop (&val1) || ! pop (&val2)
+ || ! push (val2) || ! push (val1) || ! push (val2))
+ {
+ free (stack.addrs);
+ return false;
+ }
+ break;
+ case DW_OP_swap:
+ if (! pop (&val1) || ! pop (&val2) || ! push (val1) || ! push (val2))
+ {
+ free (stack.addrs);
+ return false;
+ }
+ break;
+ case DW_OP_rot:
+ {
+ Dwarf_Addr val3;
+ if (! pop (&val1) || ! pop (&val2) || ! pop (&val3)
+ || ! push (val1) || ! push (val3) || ! push (val2))
+ {
+ free (stack.addrs);
+ return false;
+ }
+ }
+ break;
+ case DW_OP_deref:
+ case DW_OP_deref_size:
+ if (process->callbacks->memory_read == NULL)
+ {
+ free (stack.addrs);
+ __libdwfl_seterrno (DWFL_E_INVALID_ARGUMENT);
+ return false;
+ }
+ if (! pop (&val1)
+ || ! process->callbacks->memory_read (process->dwfl, val1, &val1,
+ process->callbacks_arg))
+ {
+ free (stack.addrs);
+ return false;
+ }
+ if (op->atom == DW_OP_deref_size)
+ {
+ const int elfclass = frame->cache->e_ident[EI_CLASS];
+ const unsigned addr_bytes = elfclass == ELFCLASS32 ? 4 : 8;
+ if (op->number > addr_bytes)
+ {
+ free (stack.addrs);
+ __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+ return false;
+ }
+#if BYTE_ORDER == BIG_ENDIAN
+ if (op->number == 0)
+ val1 = 0;
+ else
+ val1 >>= (addr_bytes - op->number) * 8;
+#else
+ if (op->number < 8)
+ val1 &= (1 << (op->number * 8)) - 1;
+#endif
+ }
+ if (! push (val1))
+ {
+ free (stack.addrs);
+ return false;
+ }
+ break;
+#define UNOP(atom, expr) \
+ case atom: \
+ if (! pop (&val1) || ! push (expr)) \
+ { \
+ free (stack.addrs); \
+ return false; \
+ } \
+ break;
+ UNOP (DW_OP_abs, llabs ((int64_t) val1))
+ UNOP (DW_OP_neg, -(int64_t) val1)
+ UNOP (DW_OP_not, ~val1)
+#undef UNOP
+ case DW_OP_plus_uconst:
+ if (! pop (&val1) || ! push (val1 + op->number))
+ {
+ free (stack.addrs);
+ return false;
+ }
+ break;
+#define BINOP(atom, op) \
+ case atom: \
+ if (! pop (&val2) || ! pop (&val1) || ! push (val1 op val2)) \
+ { \
+ free (stack.addrs); \
+ return false; \
+ } \
+ break;
+#define BINOP_SIGNED(atom, op) \
+ case atom: \
+ if (! pop (&val2) || ! pop (&val1) \
+ || ! push ((int64_t) val1 op (int64_t) val2)) \
+ { \
+ free (stack.addrs); \
+ return false; \
+ } \
+ break;
+ BINOP (DW_OP_and, &)
+ case DW_OP_div:
+ if (! pop (&val2) || ! pop (&val1))
+ {
+ free (stack.addrs);
+ return false;
+ }
+ if (val2 == 0)
+ {
+ free (stack.addrs);
+ __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+ return false;
+ }
+ if (! push ((int64_t) val1 / (int64_t) val2))
+ {
+ free (stack.addrs);
+ return false;
+ }
+ break;
+ BINOP (DW_OP_minus, -)
+ case DW_OP_mod:
+ if (! pop (&val2) || ! pop (&val1))
+ {
+ free (stack.addrs);
+ return false;
+ }
+ if (val2 == 0)
+ {
+ free (stack.addrs);
+ __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+ return false;
+ }
+ if (! push (val1 % val2))
+ {
+ free (stack.addrs);
+ return false;
+ }
+ break;
+ BINOP (DW_OP_mul, *)
+ BINOP (DW_OP_or, |)
+ BINOP (DW_OP_plus, +)
+ BINOP (DW_OP_shl, <<)
+ BINOP (DW_OP_shr, >>)
+ BINOP_SIGNED (DW_OP_shra, >>)
+ BINOP (DW_OP_xor, ^)
+ BINOP_SIGNED (DW_OP_le, <=)
+ BINOP_SIGNED (DW_OP_ge, >=)
+ BINOP_SIGNED (DW_OP_eq, ==)
+ BINOP_SIGNED (DW_OP_lt, <)
+ BINOP_SIGNED (DW_OP_gt, >)
+ BINOP_SIGNED (DW_OP_ne, !=)
+#undef BINOP
+#undef BINOP_SIGNED
+ case DW_OP_bra:
+ if (! pop (&val1))
+ {
+ free (stack.addrs);
+ return false;
+ }
+ if (val1 == 0)
+ break;
+ FALLTHROUGH;
+ case DW_OP_skip:;
+ Dwarf_Word offset = op->offset + 1 + 2 + (int16_t) op->number;
+ const Dwarf_Op *found = bsearch ((void *) (uintptr_t) offset, ops, nops,
+ sizeof (*ops), bra_compar);
+ if (found == NULL)
+ {
+ free (stack.addrs);
+ /* PPC32 vDSO has such invalid operations. */
+ __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+ return false;
+ }
+ /* Undo the 'for' statement increment. */
+ op = found - 1;
+ break;
+ case DW_OP_nop:
+ break;
+ /* DW_OP_* not listed in libgcc/unwind-dw2.c execute_stack_op: */
+ case DW_OP_call_frame_cfa:;
+ // Not used by CFI itself but it is synthetized by elfutils internation.
+ Dwarf_Op *cfa_ops;
+ size_t cfa_nops;
+ Dwarf_Addr cfa;
+ if (frame == NULL
+ || dwarf_frame_cfa (frame, &cfa_ops, &cfa_nops) != 0
+ || ! expr_eval (state, NULL, cfa_ops, cfa_nops, &cfa, bias)
+ || ! push (cfa))
+ {
+ __libdwfl_seterrno (DWFL_E_LIBDW);
+ free (stack.addrs);
+ return false;
+ }
+ is_location = true;
+ break;
+ case DW_OP_stack_value:
+ // Not used by CFI itself but it is synthetized by elfutils internation.
+ is_location = false;
+ break;
+ default:
+ __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+ return false;
+ }
+ }
+ if (! pop (result))
+ {
+ free (stack.addrs);
+ return false;
+ }
+ free (stack.addrs);
+ if (is_location)
+ {
+ if (process->callbacks->memory_read == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_INVALID_ARGUMENT);
+ return false;
+ }
+ if (! process->callbacks->memory_read (process->dwfl, *result, result,
+ process->callbacks_arg))
+ return false;
+ }
+ return true;
+#undef push
+#undef pop
+}
+
+static Dwfl_Frame *
+new_unwound (Dwfl_Frame *state)
+{
+ assert (state->unwound == NULL);
+ Dwfl_Thread *thread = state->thread;
+ Dwfl_Process *process = thread->process;
+ Ebl *ebl = process->ebl;
+ size_t nregs = ebl_frame_nregs (ebl);
+ assert (nregs > 0);
+ Dwfl_Frame *unwound;
+ unwound = malloc (sizeof (*unwound) + sizeof (*unwound->regs) * nregs);
+ if (unlikely (unwound == NULL))
+ return NULL;
+ state->unwound = unwound;
+ unwound->thread = thread;
+ unwound->unwound = NULL;
+ unwound->signal_frame = false;
+ unwound->initial_frame = false;
+ unwound->pc_state = DWFL_FRAME_STATE_ERROR;
+ memset (unwound->regs_set, 0, sizeof (unwound->regs_set));
+ return unwound;
+}
+
+/* The logic is to call __libdwfl_seterrno for any CFI bytecode interpretation
+ error so one can easily catch the problem with a debugger. Still there are
+ archs with invalid CFI for some registers where the registers are never used
+ later. Therefore we continue unwinding leaving the registers undefined. */
+
+static void
+handle_cfi (Dwfl_Frame *state, Dwarf_Addr pc, Dwarf_CFI *cfi, Dwarf_Addr bias)
+{
+ Dwarf_Frame *frame;
+ if (INTUSE(dwarf_cfi_addrframe) (cfi, pc, &frame) != 0)
+ {
+ __libdwfl_seterrno (DWFL_E_LIBDW);
+ return;
+ }
+
+ Dwfl_Frame *unwound = new_unwound (state);
+ if (unwound == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return;
+ }
+
+ unwound->signal_frame = frame->fde->cie->signal_frame;
+ Dwfl_Thread *thread = state->thread;
+ Dwfl_Process *process = thread->process;
+ Ebl *ebl = process->ebl;
+ size_t nregs = ebl_frame_nregs (ebl);
+ assert (nregs > 0);
+
+ /* The return register is special for setting the unwound->pc_state. */
+ unsigned ra = frame->fde->cie->return_address_register;
+ bool ra_set = false;
+ ebl_dwarf_to_regno (ebl, &ra);
+
+ for (unsigned regno = 0; regno < nregs; regno++)
+ {
+ Dwarf_Op reg_ops_mem[3], *reg_ops;
+ size_t reg_nops;
+ if (dwarf_frame_register (frame, regno, reg_ops_mem, ®_ops,
+ ®_nops) != 0)
+ {
+ __libdwfl_seterrno (DWFL_E_LIBDW);
+ continue;
+ }
+ Dwarf_Addr regval;
+ if (reg_nops == 0)
+ {
+ if (reg_ops == reg_ops_mem)
+ {
+ /* REGNO is undefined. */
+ if (regno == ra)
+ unwound->pc_state = DWFL_FRAME_STATE_PC_UNDEFINED;
+ continue;
+ }
+ else if (reg_ops == NULL)
+ {
+ /* REGNO is same-value. */
+ if (! state_get_reg (state, regno, ®val))
+ continue;
+ }
+ else
+ {
+ __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+ continue;
+ }
+ }
+ else if (! expr_eval (state, frame, reg_ops, reg_nops, ®val, bias))
+ {
+ /* PPC32 vDSO has various invalid operations, ignore them. The
+ register will look as unset causing an error later, if used.
+ But PPC32 does not use such registers. */
+ continue;
+ }
+
+ /* Some architectures encode some extra info in the return address. */
+ if (regno == frame->fde->cie->return_address_register)
+ regval &= ebl_func_addr_mask (ebl);
+
+ /* This is another strange PPC[64] case. There are two
+ registers numbers that can represent the same DWARF return
+ register number. We only want one to actually set the return
+ register value. But we always want to override the value if
+ the register is the actual CIE return address register. */
+ if (ra_set && regno != frame->fde->cie->return_address_register)
+ {
+ unsigned r = regno;
+ if (ebl_dwarf_to_regno (ebl, &r) && r == ra)
+ continue;
+ }
+
+ if (! __libdwfl_frame_reg_set (unwound, regno, regval))
+ {
+ __libdwfl_seterrno (DWFL_E_INVALID_REGISTER);
+ continue;
+ }
+ else if (! ra_set)
+ {
+ unsigned r = regno;
+ if (ebl_dwarf_to_regno (ebl, &r) && r == ra)
+ ra_set = true;
+ }
+ }
+ if (unwound->pc_state == DWFL_FRAME_STATE_ERROR
+ && __libdwfl_frame_reg_get (unwound,
+ frame->fde->cie->return_address_register,
+ &unwound->pc))
+ {
+ /* PPC32 __libc_start_main properly CFI-unwinds PC as zero. Currently
+ none of the archs supported for unwinding have zero as a valid PC. */
+ if (unwound->pc == 0)
+ unwound->pc_state = DWFL_FRAME_STATE_PC_UNDEFINED;
+ else
+ {
+ unwound->pc_state = DWFL_FRAME_STATE_PC_SET;
+ /* In SPARC the return address register actually contains
+ the address of the call instruction instead of the return
+ address. Therefore we add here an offset defined by the
+ backend. Most likely 0. */
+ unwound->pc += ebl_ra_offset (ebl);
+ }
+ }
+ free (frame);
+}
+
+static bool
+setfunc (int firstreg, unsigned nregs, const Dwarf_Word *regs, void *arg)
+{
+ Dwfl_Frame *state = arg;
+ Dwfl_Frame *unwound = state->unwound;
+ if (firstreg < 0)
+ {
+ assert (firstreg == -1);
+ assert (nregs == 1);
+ assert (unwound->pc_state == DWFL_FRAME_STATE_PC_UNDEFINED);
+ unwound->pc = *regs;
+ unwound->pc_state = DWFL_FRAME_STATE_PC_SET;
+ return true;
+ }
+ while (nregs--)
+ if (! __libdwfl_frame_reg_set (unwound, firstreg++, *regs++))
+ return false;
+ return true;
+}
+
+static bool
+getfunc (int firstreg, unsigned nregs, Dwarf_Word *regs, void *arg)
+{
+ Dwfl_Frame *state = arg;
+ assert (firstreg >= 0);
+ while (nregs--)
+ if (! __libdwfl_frame_reg_get (state, firstreg++, regs++))
+ return false;
+ return true;
+}
+
+static bool
+readfunc (Dwarf_Addr addr, Dwarf_Word *datap, void *arg)
+{
+ Dwfl_Frame *state = arg;
+ Dwfl_Thread *thread = state->thread;
+ Dwfl_Process *process = thread->process;
+ return process->callbacks->memory_read (process->dwfl, addr, datap,
+ process->callbacks_arg);
+}
+
+void
+internal_function
+__libdwfl_frame_unwind (Dwfl_Frame *state)
+{
+ if (state->unwound)
+ return;
+ /* Do not ask dwfl_frame_pc for ISACTIVATION, it would try to unwind STATE
+ which would deadlock us. */
+ Dwarf_Addr pc;
+ bool ok = INTUSE(dwfl_frame_pc) (state, &pc, NULL);
+ assert (ok);
+ /* Check whether this is the initial frame or a signal frame.
+ Then we need to unwind from the original, unadjusted PC. */
+ if (! state->initial_frame && ! state->signal_frame)
+ pc--;
+ Dwfl_Module *mod = INTUSE(dwfl_addrmodule) (state->thread->process->dwfl, pc);
+ if (mod == NULL)
+ __libdwfl_seterrno (DWFL_E_NO_DWARF);
+ else
+ {
+ Dwarf_Addr bias;
+ Dwarf_CFI *cfi_eh = INTUSE(dwfl_module_eh_cfi) (mod, &bias);
+ if (cfi_eh)
+ {
+ handle_cfi (state, pc - bias, cfi_eh, bias);
+ if (state->unwound)
+ return;
+ }
+ Dwarf_CFI *cfi_dwarf = INTUSE(dwfl_module_dwarf_cfi) (mod, &bias);
+ if (cfi_dwarf)
+ {
+ handle_cfi (state, pc - bias, cfi_dwarf, bias);
+ if (state->unwound)
+ return;
+ }
+ }
+ assert (state->unwound == NULL);
+ Dwfl_Thread *thread = state->thread;
+ Dwfl_Process *process = thread->process;
+ Ebl *ebl = process->ebl;
+ if (new_unwound (state) == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return;
+ }
+ state->unwound->pc_state = DWFL_FRAME_STATE_PC_UNDEFINED;
+ // &Dwfl_Frame.signal_frame cannot be passed as it is a bitfield.
+ bool signal_frame = false;
+ if (! ebl_unwind (ebl, pc, setfunc, getfunc, readfunc, state, &signal_frame))
+ {
+ // Discard the unwind attempt. During next __libdwfl_frame_unwind call
+ // we may have for example the appropriate Dwfl_Module already mapped.
+ assert (state->unwound->unwound == NULL);
+ free (state->unwound);
+ state->unwound = NULL;
+ // __libdwfl_seterrno has been called above.
+ return;
+ }
+ assert (state->unwound->pc_state == DWFL_FRAME_STATE_PC_SET);
+ state->unwound->signal_frame = signal_frame;
+}
diff --git a/libdwfl/gzip.c b/libdwfl/gzip.c
new file mode 100644
index 0000000..c2c13ba
--- /dev/null
+++ b/libdwfl/gzip.c
@@ -0,0 +1,328 @@
+/* Decompression support for libdwfl: zlib (gzip) and/or bzlib (bzip2).
+ Copyright (C) 2009 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include "system.h"
+
+#include <unistd.h>
+
+#ifdef LZMA
+# define USE_INFLATE 1
+# include <lzma.h>
+# define unzip __libdw_unlzma
+# define DWFL_E_ZLIB DWFL_E_LZMA
+# define MAGIC "\xFD" "7zXZ\0" /* XZ file format. */
+# define MAGIC2 "\x5d\0" /* Raw LZMA format. */
+# define Z(what) LZMA_##what
+# define LZMA_ERRNO LZMA_PROG_ERROR
+# define z_stream lzma_stream
+# define inflateInit(z) lzma_auto_decoder (z, 1 << 30, 0)
+# define do_inflate(z) lzma_code (z, LZMA_RUN)
+# define inflateEnd(z) lzma_end (z)
+#elif defined BZLIB
+# define USE_INFLATE 1
+# include <bzlib.h>
+# define unzip __libdw_bunzip2
+# define DWFL_E_ZLIB DWFL_E_BZLIB
+# define MAGIC "BZh"
+# define Z(what) BZ_##what
+# define BZ_ERRNO BZ_IO_ERROR
+# define z_stream bz_stream
+# define inflateInit(z) BZ2_bzDecompressInit (z, 0, 0)
+# define do_inflate(z) BZ2_bzDecompress (z)
+# define inflateEnd(z) BZ2_bzDecompressEnd (z)
+#else
+# define USE_INFLATE 0
+# define crc32 loser_crc32
+# include <zlib.h>
+# define unzip __libdw_gunzip
+# define MAGIC "\037\213"
+# define Z(what) Z_##what
+#endif
+
+#define READ_SIZE (1 << 20)
+
+struct unzip_state {
+#if !USE_INFLATE
+ gzFile zf;
+#endif
+ size_t mapped_size;
+ void **whole;
+ void *buffer;
+ size_t size;
+ void *input_buffer;
+ off_t input_pos;
+};
+
+static inline bool
+bigger_buffer (struct unzip_state *state, size_t start)
+{
+ size_t more = state->size ? state->size * 2 : start;
+ char *b = realloc (state->buffer, more);
+ while (unlikely (b == NULL) && more >= state->size + 1024)
+ b = realloc (state->buffer, more -= 1024);
+ if (unlikely (b == NULL))
+ return false;
+ state->buffer = b;
+ state->size = more;
+ return true;
+}
+
+static inline void
+smaller_buffer (struct unzip_state *state, size_t end)
+{
+ state->buffer =
+ realloc (state->buffer, end) ?: end == 0 ? NULL : state->buffer;
+ state->size = end;
+}
+
+static inline Dwfl_Error
+fail (struct unzip_state *state, Dwfl_Error failure)
+{
+ if (state->input_pos == (off_t) state->mapped_size)
+ *state->whole = state->input_buffer;
+ else
+ {
+ free (state->input_buffer);
+ *state->whole = NULL;
+ }
+ free (state->buffer);
+ return failure;
+}
+
+static inline Dwfl_Error
+zlib_fail (struct unzip_state *state, int result)
+{
+ switch (result)
+ {
+ case Z (MEM_ERROR):
+ return fail (state, DWFL_E_NOMEM);
+ case Z (ERRNO):
+ return fail (state, DWFL_E_ERRNO);
+ default:
+ return fail (state, DWFL_E_ZLIB);
+ }
+}
+
+#if !USE_INFLATE
+static Dwfl_Error
+open_stream (int fd, off_t start_offset, struct unzip_state *state)
+{
+ int d = dup (fd);
+ if (unlikely (d < 0))
+ return DWFL_E_BADELF;
+ if (start_offset != 0)
+ {
+ off_t off = lseek (d, start_offset, SEEK_SET);
+ if (off != start_offset)
+ {
+ close (d);
+ return DWFL_E_BADELF;
+ }
+ }
+ state->zf = gzdopen (d, "r");
+ if (unlikely (state->zf == NULL))
+ {
+ close (d);
+ return zlib_fail (state, Z (MEM_ERROR));
+ }
+
+ /* From here on, zlib will close D. */
+
+ return DWFL_E_NOERROR;
+}
+#endif
+
+/* If this is not a compressed image, return DWFL_E_BADELF.
+ If we uncompressed it into *WHOLE, *WHOLE_SIZE, return DWFL_E_NOERROR.
+ Otherwise return an error for bad compressed data or I/O failure.
+ If we return an error after reading the first part of the file,
+ leave that portion malloc'd in *WHOLE, *WHOLE_SIZE. If *WHOLE
+ is not null on entry, we'll use it in lieu of repeating a read. */
+
+Dwfl_Error internal_function
+unzip (int fd, off_t start_offset,
+ void *mapped, size_t _mapped_size,
+ void **_whole, size_t *whole_size)
+{
+ struct unzip_state state =
+ {
+#if !USE_INFLATE
+ .zf = NULL,
+#endif
+ .mapped_size = _mapped_size,
+ .whole = _whole,
+ .buffer = NULL,
+ .size = 0,
+ .input_buffer = NULL,
+ .input_pos = 0
+ };
+
+ if (mapped == NULL)
+ {
+ if (*state.whole == NULL)
+ {
+ state.input_buffer = malloc (READ_SIZE);
+ if (unlikely (state.input_buffer == NULL))
+ return DWFL_E_NOMEM;
+
+ ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE, start_offset);
+ if (unlikely (n < 0))
+ return zlib_fail (&state, Z (ERRNO));
+
+ state.input_pos = n;
+ mapped = state.input_buffer;
+ state.mapped_size = n;
+ }
+ else
+ {
+ state.input_buffer = *state.whole;
+ state.input_pos = state.mapped_size = *whole_size;
+ }
+ }
+
+#define NOMAGIC(magic) \
+ (state.mapped_size <= sizeof magic || \
+ memcmp (mapped, magic, sizeof magic - 1))
+
+ /* First, look at the header. */
+ if (NOMAGIC (MAGIC)
+#ifdef MAGIC2
+ && NOMAGIC (MAGIC2)
+#endif
+ )
+ /* Not a compressed file. */
+ return DWFL_E_BADELF;
+
+#if USE_INFLATE
+
+ /* This style actually only works with bzlib and liblzma.
+ The stupid zlib interface has nothing to grok the
+ gzip file headers except the slow gzFile interface. */
+
+ z_stream z = { .next_in = mapped, .avail_in = state.mapped_size };
+ int result = inflateInit (&z);
+ if (result != Z (OK))
+ {
+ inflateEnd (&z);
+ return zlib_fail (&state, result);
+ }
+
+ do
+ {
+ if (z.avail_in == 0 && state.input_buffer != NULL)
+ {
+ ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE,
+ start_offset + state.input_pos);
+ if (unlikely (n < 0))
+ {
+ inflateEnd (&z);
+ return zlib_fail (&state, Z (ERRNO));
+ }
+ z.next_in = state.input_buffer;
+ z.avail_in = n;
+ state.input_pos += n;
+ }
+ if (z.avail_out == 0)
+ {
+ ptrdiff_t pos = (void *) z.next_out - state.buffer;
+ if (!bigger_buffer (&state, z.avail_in))
+ {
+ result = Z (MEM_ERROR);
+ break;
+ }
+ z.next_out = state.buffer + pos;
+ z.avail_out = state.size - pos;
+ }
+ }
+ while ((result = do_inflate (&z)) == Z (OK));
+
+#ifdef BZLIB
+ uint64_t total_out = (((uint64_t) z.total_out_hi32 << 32)
+ | z.total_out_lo32);
+ smaller_buffer (&state, total_out);
+#else
+ smaller_buffer (&state, z.total_out);
+#endif
+
+ inflateEnd (&z);
+
+ if (result != Z (STREAM_END))
+ return zlib_fail (&state, result);
+
+#else /* gzip only. */
+
+ /* Let the decompression library read the file directly. */
+
+ Dwfl_Error result = open_stream (fd, start_offset, &state);
+
+ if (result == DWFL_E_NOERROR && gzdirect (state.zf))
+ {
+ gzclose (state.zf);
+ return fail (&state, DWFL_E_BADELF);
+ }
+
+ if (result != DWFL_E_NOERROR)
+ return fail (&state, result);
+
+ ptrdiff_t pos = 0;
+ while (1)
+ {
+ if (!bigger_buffer (&state, 1024))
+ {
+ gzclose (state.zf);
+ return zlib_fail (&state, Z (MEM_ERROR));
+ }
+ int n = gzread (state.zf, state.buffer + pos, state.size - pos);
+ if (n < 0)
+ {
+ int code;
+ gzerror (state.zf, &code);
+ gzclose (state.zf);
+ return zlib_fail (&state, code);
+ }
+ if (n == 0)
+ break;
+ pos += n;
+ }
+
+ gzclose (state.zf);
+ smaller_buffer (&state, pos);
+#endif
+
+ free (state.input_buffer);
+
+ *state.whole = state.buffer;
+ *whole_size = state.size;
+
+ return DWFL_E_NOERROR;
+}
diff --git a/libdwfl/image-header.c b/libdwfl/image-header.c
new file mode 100644
index 0000000..25fbfd9
--- /dev/null
+++ b/libdwfl/image-header.c
@@ -0,0 +1,105 @@
+/* Linux kernel image support for libdwfl.
+ Copyright (C) 2009-2011 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include "system.h"
+
+#include <unistd.h>
+#include <endian.h>
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+# define LE16(x) (x)
+#else
+# define LE16(x) bswap_16 (x)
+#endif
+
+/* See Documentation/x86/boot.txt in Linux kernel sources
+ for an explanation of these format details. */
+
+#define MAGIC1 0xaa55
+#define MAGIC2 0x53726448 /* "HdrS" little-endian */
+#define MIN_VERSION 0x0208
+
+#define H_START (H_SETUP_SECTS & -4)
+#define H_SETUP_SECTS 0x1f1
+#define H_MAGIC1 0x1fe
+#define H_MAGIC2 0x202
+#define H_VERSION 0x206
+#define H_PAYLOAD_OFFSET 0x248
+#define H_PAYLOAD_LENGTH 0x24c
+#define H_END 0x250
+#define H_READ_SIZE (H_END - H_START)
+
+Dwfl_Error
+internal_function
+__libdw_image_header (int fd, off_t *start_offset,
+ void *mapped, size_t mapped_size)
+{
+ if (likely (mapped_size > H_END))
+ {
+ const void *header = mapped;
+ char header_buffer[H_READ_SIZE];
+ if (header == NULL)
+ {
+ ssize_t n = pread_retry (fd, header_buffer, H_READ_SIZE,
+ *start_offset + H_START);
+ if (n < 0)
+ return DWFL_E_ERRNO;
+ if (n < H_READ_SIZE)
+ return DWFL_E_BADELF;
+
+ header = header_buffer - H_START;
+ }
+
+ if (*(uint16_t *) (header + H_MAGIC1) == LE16 (MAGIC1)
+ && *(uint32_t *) (header + H_MAGIC2) == LE32 (MAGIC2)
+ && LE16 (*(uint16_t *) (header + H_VERSION)) >= MIN_VERSION)
+ {
+ /* The magic numbers match and the version field is sufficient.
+ Extract the payload bounds. */
+
+ uint32_t offset = LE32 (*(uint32_t *) (header + H_PAYLOAD_OFFSET));
+ uint32_t length = LE32 (*(uint32_t *) (header + H_PAYLOAD_LENGTH));
+
+ offset += ((*(uint8_t *) (header + H_SETUP_SECTS) ?: 4) + 1) * 512;
+
+ if (offset > H_END && offset < mapped_size
+ && mapped_size - offset >= length)
+ {
+ /* It looks kosher. Use it! */
+ *start_offset += offset;
+ return DWFL_E_NOERROR;
+ }
+ }
+ }
+ return DWFL_E_BADELF;
+}
diff --git a/libdwfl/libdwfl.h b/libdwfl/libdwfl.h
new file mode 100644
index 0000000..a0c1d35
--- /dev/null
+++ b/libdwfl/libdwfl.h
@@ -0,0 +1,814 @@
+/* Interfaces for libdwfl.
+ Copyright (C) 2005-2010, 2013 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _LIBDWFL_H
+#define _LIBDWFL_H 1
+
+#include "libdw.h"
+#include <stdio.h>
+
+/* Handle for a session using the library. */
+typedef struct Dwfl Dwfl;
+
+/* Handle for a module. */
+typedef struct Dwfl_Module Dwfl_Module;
+
+/* Handle describing a line record. */
+typedef struct Dwfl_Line Dwfl_Line;
+
+/* This holds information common for all the frames of one backtrace for
+ a partical thread/task/TID. Several threads belong to one Dwfl. */
+typedef struct Dwfl_Thread Dwfl_Thread;
+
+/* This holds everything we know about the state of the frame at a particular
+ PC location described by an FDE belonging to Dwfl_Thread. */
+typedef struct Dwfl_Frame Dwfl_Frame;
+
+/* Callbacks. */
+typedef struct
+{
+ int (*find_elf) (Dwfl_Module *mod, void **userdata,
+ const char *modname, Dwarf_Addr base,
+ char **file_name, Elf **elfp);
+
+ int (*find_debuginfo) (Dwfl_Module *mod, void **userdata,
+ const char *modname, Dwarf_Addr base,
+ const char *file_name,
+ const char *debuglink_file, GElf_Word debuglink_crc,
+ char **debuginfo_file_name);
+
+ /* Fill *ADDR with the loaded address of the section called SECNAME in
+ the given module. Use (Dwarf_Addr) -1 if this section is omitted from
+ accessible memory. This is called exactly once for each SHF_ALLOC
+ section that relocations affecting DWARF data refer to, so it can
+ easily be used to collect state about the sections referenced. */
+ int (*section_address) (Dwfl_Module *mod, void **userdata,
+ const char *modname, Dwarf_Addr base,
+ const char *secname,
+ GElf_Word shndx, const GElf_Shdr *shdr,
+ Dwarf_Addr *addr);
+
+ char **debuginfo_path; /* See dwfl_standard_find_debuginfo. */
+} Dwfl_Callbacks;
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Start a new session with the library. */
+extern Dwfl *dwfl_begin (const Dwfl_Callbacks *callbacks)
+ __nonnull_attribute__ (1);
+
+
+/* End a session. */
+extern void dwfl_end (Dwfl *);
+
+/* Return implementation's version string suitable for printing. */
+extern const char *dwfl_version (Dwfl *);
+
+/* Return error code of last failing function call. This value is kept
+ separately for each thread. */
+extern int dwfl_errno (void);
+
+/* Return error string for ERROR. If ERROR is zero, return error string
+ for most recent error or NULL if none occurred. If ERROR is -1 the
+ behaviour is similar to the last case except that not NULL but a legal
+ string is returned. */
+extern const char *dwfl_errmsg (int err);
+
+
+/* Start reporting the current set of segments and modules to the library.
+ All existing segments are wiped. Existing modules are marked to be
+ deleted, and will not be found via dwfl_addrmodule et al if they are not
+ re-reported before dwfl_report_end is called. */
+extern void dwfl_report_begin (Dwfl *dwfl);
+
+/* Report that segment NDX begins at PHDR->p_vaddr + BIAS.
+ If NDX is < 0, the value succeeding the last call's NDX
+ is used instead (zero on the first call).
+
+ If nonzero, the smallest PHDR->p_align value seen sets the
+ effective page size for the address space DWFL describes.
+ This is the granularity at which reported module boundary
+ addresses will be considered to fall in or out of a segment.
+
+ Returns -1 for errors, or NDX (or its assigned replacement) on success.
+
+ When NDX is the value succeeding the last call's NDX (or is implicitly
+ so as above), IDENT is nonnull and matches the value in the last call,
+ and the PHDR and BIAS values reflect a segment that would be contiguous,
+ in both memory and file, with the last segment reported, then this
+ segment may be coalesced internally with preceding segments. When given
+ an address inside this segment, dwfl_addrsegment may return the NDX of a
+ preceding contiguous segment. To prevent coalesced segments, always
+ pass a null pointer for IDENT.
+
+ The values passed are not stored (except to track coalescence).
+ The only information that can be extracted from DWFL later is the
+ mapping of an address to a segment index that starts at or below
+ it. Reporting segments at all is optional. Its only benefit to
+ the caller is to offer this quick lookup via dwfl_addrsegment,
+ or use other segment-based calls. */
+extern int dwfl_report_segment (Dwfl *dwfl, int ndx,
+ const GElf_Phdr *phdr, GElf_Addr bias,
+ const void *ident);
+
+/* Report that a module called NAME spans addresses [START, END).
+ Returns the module handle, either existing or newly allocated,
+ or returns a null pointer for an allocation error. */
+extern Dwfl_Module *dwfl_report_module (Dwfl *dwfl, const char *name,
+ Dwarf_Addr start, Dwarf_Addr end);
+
+/* Report a module to address BASE with start and end addresses computed
+ from the ELF program headers in the given file - see the table below.
+ FD may be -1 to open FILE_NAME. On success, FD is consumed by the
+ library, and the `find_elf' callback will not be used for this module.
+ ADD_P_VADDR BASE
+ ET_EXEC ignored ignored
+ ET_DYN false absolute address where to place the file
+ true start address relative to ELF's phdr p_vaddr
+ ET_REL ignored absolute address where to place the file
+ ET_CORE ignored ignored
+ ET_DYN ELF phdr p_vaddr address can be non-zero if the shared library
+ has been prelinked by tool prelink(8). */
+extern Dwfl_Module *dwfl_report_elf (Dwfl *dwfl, const char *name,
+ const char *file_name, int fd,
+ GElf_Addr base, bool add_p_vaddr);
+
+/* Similar, but report the module for offline use. All ET_EXEC files
+ being reported must be reported before any relocatable objects.
+ If this is used, dwfl_report_module and dwfl_report_elf may not be
+ used in the same reporting session. */
+extern Dwfl_Module *dwfl_report_offline (Dwfl *dwfl, const char *name,
+ const char *file_name, int fd);
+
+
+/* Finish reporting the current set of modules to the library.
+ If REMOVED is not null, it's called for each module that
+ existed before but was not included in the current report.
+ Returns a nonzero return value from the callback.
+ The callback may call dwfl_report_module; doing so with the
+ details of the module being removed prevents its removal.
+ DWFL cannot be used until this function has returned zero. */
+extern int dwfl_report_end (Dwfl *dwfl,
+ int (*removed) (Dwfl_Module *, void *,
+ const char *, Dwarf_Addr,
+ void *arg),
+ void *arg);
+
+/* Start reporting additional modules to the library. No calls but
+ dwfl_report_* can be made on DWFL until dwfl_report_end is called.
+ This is like dwfl_report_begin, but all the old modules are kept on.
+ More dwfl_report_* calls can follow to add more modules.
+ When dwfl_report_end is called, no old modules will be removed. */
+extern void dwfl_report_begin_add (Dwfl *dwfl);
+
+
+/* Return the name of the module, and for each non-null argument store
+ interesting details: *USERDATA is a location for storing your own
+ pointer, **USERDATA is initially null; *START and *END give the address
+ range covered by the module; *DWBIAS is the address bias for debugging
+ information, and *SYMBIAS for symbol table entries (either is -1 if not
+ yet accessed); *MAINFILE is the name of the ELF file, and *DEBUGFILE the
+ name of the debuginfo file (might be equal to *MAINFILE; either is null
+ if not yet accessed). */
+extern const char *dwfl_module_info (Dwfl_Module *mod, void ***userdata,
+ Dwarf_Addr *start, Dwarf_Addr *end,
+ Dwarf_Addr *dwbias, Dwarf_Addr *symbias,
+ const char **mainfile,
+ const char **debugfile);
+
+/* Iterate through the modules, starting the walk with OFFSET == 0.
+ Calls *CALLBACK for each module as long as it returns DWARF_CB_OK.
+ When *CALLBACK returns another value, the walk stops and the
+ return value can be passed as OFFSET to resume it. Returns 0 when
+ there are no more modules, or -1 for errors. */
+extern ptrdiff_t dwfl_getmodules (Dwfl *dwfl,
+ int (*callback) (Dwfl_Module *, void **,
+ const char *, Dwarf_Addr,
+ void *arg),
+ void *arg,
+ ptrdiff_t offset);
+
+/* Find the module containing the given address. */
+extern Dwfl_Module *dwfl_addrmodule (Dwfl *dwfl, Dwarf_Addr address);
+
+/* Find the segment, if any, and module, if any, containing ADDRESS.
+ Returns a segment index returned by dwfl_report_segment, or -1
+ if no segment matches the address. Regardless of the return value,
+ *MOD is always set to the module containing ADDRESS, or to null. */
+extern int dwfl_addrsegment (Dwfl *dwfl, Dwarf_Addr address, Dwfl_Module **mod);
+
+
+
+/* Report the known build ID bits associated with a module.
+ If VADDR is nonzero, it gives the absolute address where those
+ bits are found within the module. This can be called at any
+ time, but is usually used immediately after dwfl_report_module.
+ Once the module's main ELF file is opened, the ID note found
+ there takes precedence and cannot be changed. */
+extern int dwfl_module_report_build_id (Dwfl_Module *mod,
+ const unsigned char *bits, size_t len,
+ GElf_Addr vaddr)
+ __nonnull_attribute__ (2);
+
+/* Extract the build ID bits associated with a module.
+ Returns -1 for errors, 0 if no ID is known, or the number of ID bytes.
+ When an ID is found, *BITS points to it; *VADDR is the absolute address
+ at which the ID bits are found within the module, or 0 if unknown.
+
+ This returns 0 when the module's main ELF file has not yet been loaded
+ and its build ID bits were not reported. To ensure the ID is always
+ returned when determinable, call dwfl_module_getelf first. */
+extern int dwfl_module_build_id (Dwfl_Module *mod,
+ const unsigned char **bits, GElf_Addr *vaddr)
+ __nonnull_attribute__ (2, 3);
+
+
+/*** Standard callbacks ***/
+
+/* These standard find_elf and find_debuginfo callbacks are
+ controlled by a string specifying directories to look in.
+ If `debuginfo_path' is set in the Dwfl_Callbacks structure
+ and the char * it points to is not null, that supplies the
+ string. Otherwise a default path is used.
+
+ If the first character of the string is + or - that enables or
+ disables CRC32 checksum validation when it's necessary. The
+ remainder of the string is composed of elements separated by
+ colons. Each element can start with + or - to override the
+ global checksum behavior. This flag is never relevant when
+ working with build IDs, but it's always parsed in the path
+ string. The remainder of the element indicates a directory.
+
+ Searches by build ID consult only the elements naming absolute
+ directory paths. They look under those directories for a link
+ named ".build-id/xx/yy" or ".build-id/xx/yy.debug", where "xxyy"
+ is the lower-case hexadecimal representation of the ID bytes.
+
+ In searches for debuginfo by name, if the remainder of the
+ element is empty, the directory containing the main file is
+ tried; if it's an absolute path name, the absolute directory path
+ (and any subdirectory of that path) containing the main file is
+ taken as a subdirectory of this path; a relative path name is taken
+ as a subdirectory of the directory containing the main file.
+ Hence for /usr/bin/ls, the default string ":.debug:/usr/lib/debug"
+ says to look in /usr/bin, then /usr/bin/.debug, then the path subdirs
+ under /usr/lib/debug, in the order /usr/lib/debug/usr/bin, then
+ /usr/lib/debug/bin, and finally /usr/lib/debug, for the file name in
+ the .gnu_debuglink section (or "ls.debug" if none was found). */
+
+/* Standard find_elf callback function working solely on build ID.
+ This can be tried first by any find_elf callback, to use the
+ bits passed to dwfl_module_report_build_id, if any. */
+extern int dwfl_build_id_find_elf (Dwfl_Module *, void **,
+ const char *, Dwarf_Addr,
+ char **, Elf **);
+
+/* Standard find_debuginfo callback function working solely on build ID.
+ This can be tried first by any find_debuginfo callback,
+ to use the build ID bits from the main file when present. */
+extern int dwfl_build_id_find_debuginfo (Dwfl_Module *, void **,
+ const char *, Dwarf_Addr,
+ const char *, const char *,
+ GElf_Word, char **);
+
+/* Standard find_debuginfo callback function.
+ If a build ID is available, this tries first to use that.
+ If there is no build ID or no valid debuginfo found by ID,
+ it searches the debuginfo path by name, as described above.
+ Any file found in the path is validated by build ID if possible,
+ or else by CRC32 checksum if enabled, and skipped if it does not match. */
+extern int dwfl_standard_find_debuginfo (Dwfl_Module *, void **,
+ const char *, Dwarf_Addr,
+ const char *, const char *,
+ GElf_Word, char **);
+
+
+/* This callback must be used when using dwfl_offline_* to report modules,
+ if ET_REL is to be supported. */
+extern int dwfl_offline_section_address (Dwfl_Module *, void **,
+ const char *, Dwarf_Addr,
+ const char *, GElf_Word,
+ const GElf_Shdr *,
+ Dwarf_Addr *addr);
+
+
+/* Callbacks for working with kernel modules in the running Linux kernel. */
+extern int dwfl_linux_kernel_find_elf (Dwfl_Module *, void **,
+ const char *, Dwarf_Addr,
+ char **, Elf **);
+extern int dwfl_linux_kernel_module_section_address (Dwfl_Module *, void **,
+ const char *, Dwarf_Addr,
+ const char *, GElf_Word,
+ const GElf_Shdr *,
+ Dwarf_Addr *addr);
+
+/* Call dwfl_report_elf for the running Linux kernel.
+ Returns zero on success, -1 if dwfl_report_module failed,
+ or an errno code if opening the kernel binary failed. */
+extern int dwfl_linux_kernel_report_kernel (Dwfl *dwfl);
+
+/* Call dwfl_report_module for each kernel module in the running Linux kernel.
+ Returns zero on success, -1 if dwfl_report_module failed,
+ or an errno code if reading the list of modules failed. */
+extern int dwfl_linux_kernel_report_modules (Dwfl *dwfl);
+
+/* Report a kernel and its modules found on disk, for offline use.
+ If RELEASE starts with '/', it names a directory to look in;
+ if not, it names a directory to find under /lib/modules/;
+ if null, /lib/modules/`uname -r` is used.
+ Returns zero on success, -1 if dwfl_report_module failed,
+ or an errno code if finding the files on disk failed.
+
+ If PREDICATE is not null, it is called with each module to be reported;
+ its arguments are the module name, and the ELF file name or null if unknown,
+ and its return value should be zero to skip the module, one to report it,
+ or -1 to cause the call to fail and return errno. */
+extern int dwfl_linux_kernel_report_offline (Dwfl *dwfl, const char *release,
+ int (*predicate) (const char *,
+ const char *));
+
+/* Examine an ET_CORE file and report modules based on its contents.
+ This can follow a dwfl_report_offline call to bootstrap the
+ DT_DEBUG method of following the dynamic linker link_map chain, in
+ case the core file does not contain enough of the executable's text
+ segment to locate its PT_DYNAMIC in the dump. In such case you need to
+ supply non-NULL EXECUTABLE, otherwise dynamic libraries will not be loaded
+ into the DWFL map. This might call dwfl_report_elf on file names found in
+ the dump if reading some link_map files is the only way to ascertain those
+ modules' addresses. Returns the number of modules reported, or -1 for
+ errors. */
+extern int dwfl_core_file_report (Dwfl *dwfl, Elf *elf, const char *executable);
+
+/* Call dwfl_report_module for each file mapped into the address space of PID.
+ Returns zero on success, -1 if dwfl_report_module failed,
+ or an errno code if opening the proc files failed. */
+extern int dwfl_linux_proc_report (Dwfl *dwfl, pid_t pid);
+
+/* Similar, but reads an input stream in the format of Linux /proc/PID/maps
+ files giving module layout, not the file for a live process. */
+extern int dwfl_linux_proc_maps_report (Dwfl *dwfl, FILE *);
+
+/* Trivial find_elf callback for use with dwfl_linux_proc_report.
+ This uses the module name as a file name directly and tries to open it
+ if it begin with a slash, or handles the magic string "[vdso]". */
+extern int dwfl_linux_proc_find_elf (Dwfl_Module *mod, void **userdata,
+ const char *module_name, Dwarf_Addr base,
+ char **file_name, Elf **);
+
+/* Standard argument parsing for using a standard callback set. */
+struct argp;
+extern const struct argp *dwfl_standard_argp (void) __const_attribute__;
+
+
+/*** Relocation of addresses from Dwfl ***/
+
+/* Return the number of relocatable bases associated with the module,
+ which is zero for ET_EXEC and one for ET_DYN. Returns -1 for errors. */
+extern int dwfl_module_relocations (Dwfl_Module *mod);
+
+/* Return the relocation base index associated with the *ADDRESS location,
+ and adjust *ADDRESS to be an offset relative to that base.
+ Returns -1 for errors. */
+extern int dwfl_module_relocate_address (Dwfl_Module *mod,
+ Dwarf_Addr *address);
+
+/* Return the ELF section name for the given relocation base index;
+ if SHNDXP is not null, set *SHNDXP to the ELF section index.
+ For ET_DYN, returns "" and sets *SHNDXP to SHN_ABS; the relocation
+ base is the runtime start address reported for the module.
+ Returns null for errors. */
+extern const char *dwfl_module_relocation_info (Dwfl_Module *mod,
+ unsigned int idx,
+ GElf_Word *shndxp);
+
+/* Validate that ADDRESS and ADDRESS+OFFSET lie in a known module
+ and both within the same contiguous region for relocation purposes.
+ Returns zero for success and -1 for errors. */
+extern int dwfl_validate_address (Dwfl *dwfl,
+ Dwarf_Addr address, Dwarf_Sword offset);
+
+
+/*** ELF access functions ***/
+
+/* Fetch the module main ELF file (where the allocated sections
+ are found) for use with libelf. If successful, fills in *BIAS
+ with the difference between addresses within the loaded module
+ and those in symbol tables or Dwarf information referring to it. */
+extern Elf *dwfl_module_getelf (Dwfl_Module *, GElf_Addr *bias)
+ __nonnull_attribute__ (2);
+
+/* Return the number of symbols in the module's symbol table,
+ or -1 for errors. */
+extern int dwfl_module_getsymtab (Dwfl_Module *mod);
+
+/* Return the index of the first global symbol in the module's symbol
+ table, or -1 for errors. In each symbol table, all symbols with
+ STB_LOCAL binding precede the weak and global symbols. This
+ function returns the symbol table index one greater than the last
+ local symbol. */
+extern int dwfl_module_getsymtab_first_global (Dwfl_Module *mod);
+
+/* Fetch one entry from the module's symbol table. On errors, returns
+ NULL. If successful, fills in *SYM and returns the string for st_name.
+ This works like gelf_getsym except that st_value is always adjusted to
+ an absolute value based on the module's location, when the symbol is in
+ an SHF_ALLOC section. If SHNDXP is non-null, it's set with the section
+ index (whether from st_shndx or extended index table); in case of a
+ symbol in a non-allocated section, *SHNDXP is instead set to -1.
+ Note that since symbols can come from either the main, debug or auxiliary
+ ELF symbol file (either dynsym or symtab) the section index can only
+ be reliably used to compare against special section constants like
+ SHN_UNDEF or SHN_ABS. It is recommended to use dwfl_module_getsym_info
+ which doesn't have these deficiencies. */
+extern const char *dwfl_module_getsym (Dwfl_Module *mod, int ndx,
+ GElf_Sym *sym, GElf_Word *shndxp)
+ __nonnull_attribute__ (3);
+
+/* Fetch one entry from the module's symbol table and the associated
+ address value. On errors, returns NULL. If successful, fills in
+ *SYM, *ADDR and returns the string for st_name. This works like
+ gelf_getsym. *ADDR is set to the st_value adjusted to an absolute
+ value based on the module's location, when the symbol is in an
+ SHF_ALLOC section. For non-ET_REL files, if the arch uses function
+ descriptors, and the st_value points to one, *ADDR will be resolved
+ to the actual function entry address. The SYM->ST_VALUE itself
+ isn't adjusted in any way. Fills in ELFP, if not NULL, with the
+ ELF file the symbol originally came from. Note that symbols can
+ come from either the main, debug or auxiliary ELF symbol file
+ (either dynsym or symtab). If SHNDXP is non-null, it's set with
+ the section index (whether from st_shndx or extended index table);
+ in case of a symbol in a non-allocated section, *SHNDXP is instead
+ set to -1. Fills in BIAS, if not NULL, with the difference between
+ addresses within the loaded module and those in symbol table of the
+ ELF file. Note that the address associated with the symbol might
+ be in a different section than the returned symbol. The section in
+ the main elf file in which returned ADDR falls can be found with
+ dwfl_module_address_section. */
+extern const char *dwfl_module_getsym_info (Dwfl_Module *mod, int ndx,
+ GElf_Sym *sym, GElf_Addr *addr,
+ GElf_Word *shndxp,
+ Elf **elfp, Dwarf_Addr *bias)
+ __nonnull_attribute__ (3, 4);
+
+/* Find the symbol that ADDRESS lies inside, and return its name. */
+extern const char *dwfl_module_addrname (Dwfl_Module *mod, GElf_Addr address);
+
+/* Find the symbol associated with ADDRESS. Return its name or NULL
+ when nothing was found. If the architecture uses function
+ descriptors, and symbol st_value points to one, ADDRESS wil be
+ matched against either the adjusted st_value or the associated
+ function entry value as described in dwfl_module_getsym_info. If
+ OFFSET is not NULL it will be filled in with the difference from
+ the start of the symbol (or function entry). If SYM is not NULL it
+ is filled in with the symbol associated with the matched ADDRESS.
+ The SYM->ST_VALUE itself isn't adjusted in any way. Fills in ELFP,
+ if not NULL, with the ELF file the symbol originally came from.
+ Note that symbols can come from either the main, debug or auxiliary
+ ELF symbol file (either dynsym or symtab). If SHNDXP is non-null,
+ it's set with the section index (whether from st_shndx or extended
+ index table). Fills in BIAS, if not NULL, with the difference
+ between addresses within the loaded module and those in symbol
+ table of the ELF file. Note that the address matched against the
+ symbol might be in a different section than the returned symbol.
+ The section in the main elf file in ADDRESS falls can be found with
+ dwfl_module_address_section. */
+extern const char *dwfl_module_addrinfo (Dwfl_Module *mod, GElf_Addr address,
+ GElf_Off *offset, GElf_Sym *sym,
+ GElf_Word *shndxp, Elf **elfp,
+ Dwarf_Addr *bias)
+ __nonnull_attribute__ (3);
+
+/* Find the symbol that ADDRESS lies inside, and return detailed
+ information as for dwfl_module_getsym (above). Note that like
+ dwfl_module_getsym this function also adjusts SYM->ST_VALUE to an
+ absolute value based on the module's location. ADDRESS is only
+ matched against this adjusted SYM->ST_VALUE. This means that
+ depending on architecture this might only match symbols that
+ represent function descriptor addresses (and not function entry
+ addresses). For these reasons it is recommended to use
+ dwfl_module_addrinfo instead. */
+extern const char *dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr address,
+ GElf_Sym *sym, GElf_Word *shndxp)
+ __nonnull_attribute__ (3);
+
+/* Find the ELF section that *ADDRESS lies inside and return it.
+ On success, adjusts *ADDRESS to be relative to the section,
+ and sets *BIAS to the difference between addresses used in
+ the returned section's headers and run-time addresses. */
+extern Elf_Scn *dwfl_module_address_section (Dwfl_Module *mod,
+ Dwarf_Addr *address,
+ Dwarf_Addr *bias)
+ __nonnull_attribute__ (2, 3);
+
+
+/*** Dwarf access functions ***/
+
+/* Fetch the module's debug information for use with libdw.
+ If successful, fills in *BIAS with the difference between
+ addresses within the loaded module and those to use with libdw. */
+extern Dwarf *dwfl_module_getdwarf (Dwfl_Module *, Dwarf_Addr *bias)
+ __nonnull_attribute__ (2);
+
+/* Get the libdw handle for each module. */
+extern ptrdiff_t dwfl_getdwarf (Dwfl *,
+ int (*callback) (Dwfl_Module *, void **,
+ const char *, Dwarf_Addr,
+ Dwarf *, Dwarf_Addr, void *),
+ void *arg, ptrdiff_t offset);
+
+/* Look up the module containing ADDR and return its debugging information,
+ loading it if necessary. */
+extern Dwarf *dwfl_addrdwarf (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Addr *bias)
+ __nonnull_attribute__ (3);
+
+
+/* Find the CU containing ADDR and return its DIE. */
+extern Dwarf_Die *dwfl_addrdie (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Addr *bias)
+ __nonnull_attribute__ (3);
+extern Dwarf_Die *dwfl_module_addrdie (Dwfl_Module *mod,
+ Dwarf_Addr addr, Dwarf_Addr *bias)
+ __nonnull_attribute__ (3);
+
+/* Iterate through the CUs, start with null for LASTCU. */
+extern Dwarf_Die *dwfl_nextcu (Dwfl *dwfl, Dwarf_Die *lastcu, Dwarf_Addr *bias)
+ __nonnull_attribute__ (3);
+extern Dwarf_Die *dwfl_module_nextcu (Dwfl_Module *mod,
+ Dwarf_Die *lastcu, Dwarf_Addr *bias)
+ __nonnull_attribute__ (3);
+
+/* Return the module containing the CU DIE. */
+extern Dwfl_Module *dwfl_cumodule (Dwarf_Die *cudie);
+
+
+/* Cache the source line information fo the CU and return the
+ number of Dwfl_Line entries it has. */
+extern int dwfl_getsrclines (Dwarf_Die *cudie, size_t *nlines);
+
+/* Access one line number entry within the CU. */
+extern Dwfl_Line *dwfl_onesrcline (Dwarf_Die *cudie, size_t idx);
+
+/* Get source for address. */
+extern Dwfl_Line *dwfl_module_getsrc (Dwfl_Module *mod, Dwarf_Addr addr);
+extern Dwfl_Line *dwfl_getsrc (Dwfl *dwfl, Dwarf_Addr addr);
+
+/* Get address for source. */
+extern int dwfl_module_getsrc_file (Dwfl_Module *mod,
+ const char *fname, int lineno, int column,
+ Dwfl_Line ***srcsp, size_t *nsrcs);
+
+/* Return the module containing this line record. */
+extern Dwfl_Module *dwfl_linemodule (Dwfl_Line *line);
+
+/* Return the CU containing this line record. */
+extern Dwarf_Die *dwfl_linecu (Dwfl_Line *line);
+
+/* Return the source file name and fill in other information.
+ Arguments may be null for unneeded fields. */
+extern const char *dwfl_lineinfo (Dwfl_Line *line, Dwarf_Addr *addr,
+ int *linep, int *colp,
+ Dwarf_Word *mtime, Dwarf_Word *length);
+
+ /* Return the equivalent Dwarf_Line and the bias to apply to its address. */
+extern Dwarf_Line *dwfl_dwarf_line (Dwfl_Line *line, Dwarf_Addr *bias);
+
+/* Return the compilation directory (AT_comp_dir) from this line's CU. */
+extern const char *dwfl_line_comp_dir (Dwfl_Line *line);
+
+
+/*** Machine backend access functions ***/
+
+/* Return location expression to find return value given a
+ DW_TAG_subprogram, DW_TAG_subroutine_type, or similar DIE describing
+ function itself (whose DW_AT_type attribute describes its return type).
+ The given DIE must come from the given module. Returns -1 for errors.
+ Returns zero if the function has no return value (e.g. "void" in C).
+ Otherwise, *LOCOPS gets a location expression to find the return value,
+ and returns the number of operations in the expression. The pointer is
+ permanently allocated at least as long as the module is live. */
+extern int dwfl_module_return_value_location (Dwfl_Module *mod,
+ Dwarf_Die *functypedie,
+ const Dwarf_Op **locops);
+
+/* Enumerate the DWARF register numbers and their names.
+ For each register, CALLBACK gets its DWARF number, a string describing
+ the register set (such as "integer" or "FPU"), a prefix used in
+ assembler syntax (such as "%" or "$", may be ""), and the name for the
+ register (contains identifier characters only, possibly all digits).
+ The REGNAME string is valid only during the callback. */
+extern int dwfl_module_register_names (Dwfl_Module *mod,
+ int (*callback) (void *arg,
+ int regno,
+ const char *setname,
+ const char *prefix,
+ const char *regname,
+ int bits, int type),
+ void *arg);
+
+
+/* Find the CFI for this module. Returns NULL if there is no CFI.
+ On success, fills in *BIAS with the difference between addresses
+ within the loaded module and those in the CFI referring to it.
+ The pointer returned can be used until the module is cleaned up.
+ Calling these more than once returns the same pointers.
+
+ dwfl_module_dwarf_cfi gets the '.debug_frame' information found with the
+ rest of the DWARF information. dwfl_module_eh_cfi gets the '.eh_frame'
+ information found linked into the text. A module might have either or
+ both. */
+extern Dwarf_CFI *dwfl_module_dwarf_cfi (Dwfl_Module *mod, Dwarf_Addr *bias);
+extern Dwarf_CFI *dwfl_module_eh_cfi (Dwfl_Module *mod, Dwarf_Addr *bias);
+
+
+typedef struct
+{
+ /* Called to iterate through threads. Returns next TID (thread ID) on
+ success, a negative number on failure and zero if there are no more
+ threads. dwfl_errno () should be set if negative number has been
+ returned. *THREAD_ARGP is NULL on first call, and may be optionally
+ set by the implementation. The value set by the implementation will
+ be passed in on the next call to NEXT_THREAD. THREAD_ARGP is never
+ NULL. *THREAD_ARGP will be passed to set_initial_registers or
+ thread_detach callbacks together with Dwfl_Thread *thread. This
+ method must not be NULL. */
+ pid_t (*next_thread) (Dwfl *dwfl, void *dwfl_arg, void **thread_argp)
+ __nonnull_attribute__ (1);
+
+ /* Called to get a specific thread. Returns true if there is a
+ thread with the given thread id number, returns false if no such
+ thread exists and will set dwfl_errno in that case. THREAD_ARGP
+ is never NULL. *THREAD_ARGP will be passed to
+ set_initial_registers or thread_detach callbacks together with
+ Dwfl_Thread *thread. This method may be NULL and will then be
+ emulated using the next_thread callback. */
+ bool (*get_thread) (Dwfl *dwfl, pid_t tid, void *dwfl_arg,
+ void **thread_argp)
+ __nonnull_attribute__ (1);
+
+ /* Called during unwinding to access memory (stack) state. Returns true for
+ successfully read *RESULT or false and sets dwfl_errno () on failure.
+ This method may be NULL - in such case dwfl_thread_getframes will return
+ only the initial frame. */
+ bool (*memory_read) (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result,
+ void *dwfl_arg)
+ __nonnull_attribute__ (1, 3);
+
+ /* Called on initial unwind to get the initial register state of the first
+ frame. Should call dwfl_thread_state_registers, possibly multiple times
+ for different ranges and possibly also dwfl_thread_state_register_pc, to
+ fill in initial (DWARF) register values. After this call, till at least
+ thread_detach is called, the thread is assumed to be frozen, so that it is
+ safe to unwind. Returns true on success or false and sets dwfl_errno ()
+ on failure. In the case of a failure thread_detach will not be called.
+ This method must not be NULL. */
+ bool (*set_initial_registers) (Dwfl_Thread *thread, void *thread_arg)
+ __nonnull_attribute__ (1);
+
+ /* Called by dwfl_end. All thread_detach method calls have been already
+ done. This method may be NULL. */
+ void (*detach) (Dwfl *dwfl, void *dwfl_arg)
+ __nonnull_attribute__ (1);
+
+ /* Called when unwinding is done. No callback will be called after
+ this method has been called. Iff set_initial_registers was called for
+ a TID and it returned success thread_detach will be called before the
+ detach method above. This method may be NULL. */
+ void (*thread_detach) (Dwfl_Thread *thread, void *thread_arg)
+ __nonnull_attribute__ (1);
+} Dwfl_Thread_Callbacks;
+
+/* PID is the process id associated with the DWFL state. Architecture of DWFL
+ modules is specified by ELF, ELF must remain valid during DWFL lifetime.
+ Use NULL ELF to detect architecture from DWFL, the function will then detect
+ it from arbitrary Dwfl_Module of DWFL. DWFL_ARG is the callback backend
+ state. DWFL_ARG will be provided to the callbacks. *THREAD_CALLBACKS
+ function pointers must remain valid during lifetime of DWFL. Function
+ returns true on success, false otherwise. */
+bool dwfl_attach_state (Dwfl *dwfl, Elf *elf, pid_t pid,
+ const Dwfl_Thread_Callbacks *thread_callbacks,
+ void *dwfl_arg)
+ __nonnull_attribute__ (1, 4);
+
+/* Calls dwfl_attach_state with Dwfl_Thread_Callbacks setup for extracting
+ thread state from the ELF core file. Returns the pid number extracted
+ from the core file, or -1 for errors. */
+extern int dwfl_core_file_attach (Dwfl *dwfl, Elf *elf);
+
+/* Calls dwfl_attach_state with Dwfl_Thread_Callbacks setup for extracting
+ thread state from the proc file system. Uses ptrace to attach and stop
+ the thread under inspection and detaches when thread_detach is called
+ and unwinding for the thread is done, unless ASSUME_PTRACE_STOPPED is
+ true. If ASSUME_PTRACE_STOPPED is true the caller should make sure that
+ the thread is ptrace attached and stopped before unwinding by calling
+ either dwfl_thread_getframes or dwfl_getthread_frames. Returns zero on
+ success, -1 if dwfl_attach_state failed, or an errno code if opening the
+ proc files failed. */
+extern int dwfl_linux_proc_attach (Dwfl *dwfl, pid_t pid,
+ bool assume_ptrace_stopped);
+
+/* Return PID for the process associated with DWFL. Function returns -1 if
+ dwfl_attach_state was not called for DWFL. */
+pid_t dwfl_pid (Dwfl *dwfl)
+ __nonnull_attribute__ (1);
+
+/* Return DWFL from which THREAD was created using dwfl_getthreads. */
+Dwfl *dwfl_thread_dwfl (Dwfl_Thread *thread)
+ __nonnull_attribute__ (1);
+
+/* Return positive TID (thread ID) for THREAD. This function never fails. */
+pid_t dwfl_thread_tid (Dwfl_Thread *thread)
+ __nonnull_attribute__ (1);
+
+/* Return thread for frame STATE. This function never fails. */
+Dwfl_Thread *dwfl_frame_thread (Dwfl_Frame *state)
+ __nonnull_attribute__ (1);
+
+/* Called by Dwfl_Thread_Callbacks.set_initial_registers implementation.
+ For every known continuous block of registers <FIRSTREG..FIRSTREG+NREGS)
+ (inclusive..exclusive) set their content to REGS (array of NREGS items).
+ Function returns false if any of the registers has invalid number. */
+bool dwfl_thread_state_registers (Dwfl_Thread *thread, int firstreg,
+ unsigned nregs, const Dwarf_Word *regs)
+ __nonnull_attribute__ (1, 4);
+
+/* Called by Dwfl_Thread_Callbacks.set_initial_registers implementation.
+ If PC is not contained among DWARF registers passed by
+ dwfl_thread_state_registers on the target architecture pass the PC value
+ here. */
+void dwfl_thread_state_register_pc (Dwfl_Thread *thread, Dwarf_Word pc)
+ __nonnull_attribute__ (1);
+
+/* Iterate through the threads for a process. Returns zero if all threads have
+ been processed by the callback, returns -1 on error, or the value of the
+ callback when not DWARF_CB_OK. -1 returned on error will set dwfl_errno ().
+ Keeps calling the callback with the next thread while the callback returns
+ DWARF_CB_OK, till there are no more threads. */
+int dwfl_getthreads (Dwfl *dwfl,
+ int (*callback) (Dwfl_Thread *thread, void *arg),
+ void *arg)
+ __nonnull_attribute__ (1, 2);
+
+/* Iterate through the frames for a thread. Returns zero if all frames
+ have been processed by the callback, returns -1 on error, or the value of
+ the callback when not DWARF_CB_OK. -1 returned on error will
+ set dwfl_errno (). Some systems return error instead of zero on end of the
+ backtrace, for cross-platform compatibility callers should consider error as
+ a zero. Keeps calling the callback with the next frame while the callback
+ returns DWARF_CB_OK, till there are no more frames. On start will call the
+ set_initial_registers callback and on return will call the detach_thread
+ callback of the Dwfl_Thread. */
+int dwfl_thread_getframes (Dwfl_Thread *thread,
+ int (*callback) (Dwfl_Frame *state, void *arg),
+ void *arg)
+ __nonnull_attribute__ (1, 2);
+
+/* Like dwfl_thread_getframes, but specifying the thread by its unique
+ identifier number. Returns zero if all frames have been processed
+ by the callback, returns -1 on error (and when no thread with
+ the given thread id number exists), or the value of the callback
+ when not DWARF_CB_OK. -1 returned on error will set dwfl_errno (). */
+int dwfl_getthread_frames (Dwfl *dwfl, pid_t tid,
+ int (*callback) (Dwfl_Frame *thread, void *arg),
+ void *arg)
+ __nonnull_attribute__ (1, 3);
+
+/* Return *PC (program counter) for thread-specific frame STATE.
+ Set *ISACTIVATION according to DWARF frame "activation" definition.
+ Typically you need to substract 1 from *PC if *ACTIVATION is false to safely
+ find function of the caller. ACTIVATION may be NULL. PC must not be NULL.
+ Function returns false if it failed to find *PC. */
+bool dwfl_frame_pc (Dwfl_Frame *state, Dwarf_Addr *pc, bool *isactivation)
+ __nonnull_attribute__ (1, 2);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* libdwfl.h */
diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h
new file mode 100644
index 0000000..7d5f795
--- /dev/null
+++ b/libdwfl/libdwflP.h
@@ -0,0 +1,772 @@
+/* Internal definitions for libdwfl.
+ Copyright (C) 2005-2015 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _LIBDWFLP_H
+#define _LIBDWFLP_H 1
+
+#include <libdwfl.h>
+#include <libebl.h>
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../libdw/libdwP.h" /* We need its INTDECLs. */
+#include "../libdwelf/libdwelfP.h"
+
+typedef struct Dwfl_Process Dwfl_Process;
+
+/* gettext helper macros. */
+#define _(Str) dgettext ("elfutils", Str)
+
+#define DWFL_ERRORS \
+ DWFL_ERROR (NOERROR, N_("no error")) \
+ DWFL_ERROR (UNKNOWN_ERROR, N_("unknown error")) \
+ DWFL_ERROR (NOMEM, N_("out of memory")) \
+ DWFL_ERROR (ERRNO, N_("See errno")) \
+ DWFL_ERROR (LIBELF, N_("See elf_errno")) \
+ DWFL_ERROR (LIBDW, N_("See dwarf_errno")) \
+ DWFL_ERROR (LIBEBL, N_("See ebl_errno (XXX missing)")) \
+ DWFL_ERROR (ZLIB, N_("gzip decompression failed")) \
+ DWFL_ERROR (BZLIB, N_("bzip2 decompression failed")) \
+ DWFL_ERROR (LZMA, N_("LZMA decompression failed")) \
+ DWFL_ERROR (UNKNOWN_MACHINE, N_("no support library found for machine")) \
+ DWFL_ERROR (NOREL, N_("Callbacks missing for ET_REL file")) \
+ DWFL_ERROR (BADRELTYPE, N_("Unsupported relocation type")) \
+ DWFL_ERROR (BADRELOFF, N_("r_offset is bogus")) \
+ DWFL_ERROR (BADSTROFF, N_("offset out of range")) \
+ DWFL_ERROR (RELUNDEF, N_("relocation refers to undefined symbol")) \
+ DWFL_ERROR (CB, N_("Callback returned failure")) \
+ DWFL_ERROR (NO_DWARF, N_("No DWARF information found")) \
+ DWFL_ERROR (NO_SYMTAB, N_("No symbol table found")) \
+ DWFL_ERROR (NO_PHDR, N_("No ELF program headers")) \
+ DWFL_ERROR (OVERLAP, N_("address range overlaps an existing module")) \
+ DWFL_ERROR (ADDR_OUTOFRANGE, N_("address out of range")) \
+ DWFL_ERROR (NO_MATCH, N_("no matching address range")) \
+ DWFL_ERROR (TRUNCATED, N_("image truncated")) \
+ DWFL_ERROR (ALREADY_ELF, N_("ELF file opened")) \
+ DWFL_ERROR (BADELF, N_("not a valid ELF file")) \
+ DWFL_ERROR (WEIRD_TYPE, N_("cannot handle DWARF type description")) \
+ DWFL_ERROR (WRONG_ID_ELF, N_("ELF file does not match build ID")) \
+ DWFL_ERROR (BAD_PRELINK, N_("corrupt .gnu.prelink_undo section data")) \
+ DWFL_ERROR (LIBEBL_BAD, N_("Internal error due to ebl")) \
+ DWFL_ERROR (CORE_MISSING, N_("Missing data in core file")) \
+ DWFL_ERROR (INVALID_REGISTER, N_("Invalid register")) \
+ DWFL_ERROR (PROCESS_MEMORY_READ, N_("Error reading process memory")) \
+ DWFL_ERROR (PROCESS_NO_ARCH, N_("Couldn't find architecture of any ELF")) \
+ DWFL_ERROR (PARSE_PROC, N_("Error parsing /proc filesystem")) \
+ DWFL_ERROR (INVALID_DWARF, N_("Invalid DWARF")) \
+ DWFL_ERROR (UNSUPPORTED_DWARF, N_("Unsupported DWARF")) \
+ DWFL_ERROR (NEXT_THREAD_FAIL, N_("Unable to find more threads")) \
+ DWFL_ERROR (ATTACH_STATE_CONFLICT, N_("Dwfl already has attached state")) \
+ DWFL_ERROR (NO_ATTACH_STATE, N_("Dwfl has no attached state")) \
+ DWFL_ERROR (NO_UNWIND, N_("Unwinding not supported for this architecture")) \
+ DWFL_ERROR (INVALID_ARGUMENT, N_("Invalid argument")) \
+ DWFL_ERROR (NO_CORE_FILE, N_("Not an ET_CORE ELF file"))
+
+#define DWFL_ERROR(name, text) DWFL_E_##name,
+typedef enum { DWFL_ERRORS DWFL_E_NUM } Dwfl_Error;
+#undef DWFL_ERROR
+
+#define OTHER_ERROR(name) ((unsigned int) DWFL_E_##name << 16)
+#define DWFL_E(name, errno) (OTHER_ERROR (name) | (errno))
+
+extern int __libdwfl_canon_error (Dwfl_Error) internal_function;
+extern void __libdwfl_seterrno (Dwfl_Error) internal_function;
+
+/* Resources we might keep for the user about the core file that the
+ Dwfl might have been created from. Can currently only be set
+ through std-argp. */
+struct Dwfl_User_Core
+{
+ char *executable_for_core; /* --executable if --core was specified. */
+ Elf *core; /* non-NULL if we need to free it. */
+ int fd; /* close if >= 0. */
+};
+
+struct Dwfl
+{
+ const Dwfl_Callbacks *callbacks;
+
+ Dwfl_Module *modulelist; /* List in order used by full traversals. */
+
+ Dwfl_Process *process;
+ Dwfl_Error attacherr; /* Previous error attaching process. */
+
+ GElf_Addr offline_next_address;
+
+ GElf_Addr segment_align; /* Smallest granularity of segments. */
+
+ /* Binary search table in three parallel malloc'd arrays. */
+ size_t lookup_elts; /* Elements in use. */
+ size_t lookup_alloc; /* Elements allococated. */
+ GElf_Addr *lookup_addr; /* Start address of segment. */
+ Dwfl_Module **lookup_module; /* Module associated with segment, or null. */
+ int *lookup_segndx; /* User segment index, or -1. */
+
+ /* Cache from last dwfl_report_segment call. */
+ const void *lookup_tail_ident;
+ GElf_Off lookup_tail_vaddr;
+ GElf_Off lookup_tail_offset;
+ int lookup_tail_ndx;
+
+ struct Dwfl_User_Core *user_core;
+};
+
+#define OFFLINE_REDZONE 0x10000
+
+struct dwfl_file
+{
+ char *name;
+ int fd;
+ bool valid; /* The build ID note has been matched. */
+ bool relocated; /* Partial relocation of all sections done. */
+
+ Elf *elf;
+
+ /* This is the lowest p_vaddr in this ELF file, aligned to p_align.
+ For a file without phdrs, this is zero. */
+ GElf_Addr vaddr;
+
+ /* This is an address chosen for synchronization between the main file
+ and the debug file. See dwfl_module_getdwarf.c for how it's chosen. */
+ GElf_Addr address_sync;
+};
+
+struct Dwfl_Module
+{
+ Dwfl *dwfl;
+ struct Dwfl_Module *next; /* Link on Dwfl.modulelist. */
+
+ void *userdata;
+
+ char *name; /* Iterator name for this module. */
+ GElf_Addr low_addr, high_addr;
+
+ struct dwfl_file main, debug, aux_sym;
+ GElf_Addr main_bias;
+ Ebl *ebl;
+ GElf_Half e_type; /* GElf_Ehdr.e_type cache. */
+ Dwfl_Error elferr; /* Previous failure to open main file. */
+
+ struct dwfl_relocation *reloc_info; /* Relocatable sections. */
+
+ struct dwfl_file *symfile; /* Either main or debug. */
+ Elf_Data *symdata; /* Data in the ELF symbol table section. */
+ Elf_Data *aux_symdata; /* Data in the auxiliary ELF symbol table. */
+ size_t syments; /* sh_size / sh_entsize of that section. */
+ size_t aux_syments; /* sh_size / sh_entsize of aux_sym section. */
+ int first_global; /* Index of first global symbol of table. */
+ int aux_first_global; /* Index of first global of aux_sym table. */
+ Elf_Data *symstrdata; /* Data for its string table. */
+ Elf_Data *aux_symstrdata; /* Data for aux_sym string table. */
+ Elf_Data *symxndxdata; /* Data in the extended section index table. */
+ Elf_Data *aux_symxndxdata; /* Data in the extended auxiliary table. */
+
+ Dwarf *dw; /* libdw handle for its debugging info. */
+ Dwarf *alt; /* Dwarf used for dwarf_setalt, or NULL. */
+ int alt_fd; /* descriptor, only valid when alt != NULL. */
+ Elf *alt_elf; /* Elf for alt Dwarf. */
+
+ Dwfl_Error symerr; /* Previous failure to load symbols. */
+ Dwfl_Error dwerr; /* Previous failure to load DWARF. */
+
+ /* Known CU's in this module. */
+ struct dwfl_cu *first_cu, **cu;
+
+ void *lazy_cu_root; /* Table indexed by Dwarf_Off of CU. */
+
+ struct dwfl_arange *aranges; /* Mapping of addresses in module to CUs. */
+
+ void *build_id_bits; /* malloc'd copy of build ID bits. */
+ GElf_Addr build_id_vaddr; /* Address where they reside, 0 if unknown. */
+ int build_id_len; /* -1 for prior failure, 0 if unset. */
+
+ unsigned int ncu;
+ unsigned int lazycu; /* Possible users, deleted when none left. */
+ unsigned int naranges;
+
+ Dwarf_CFI *dwarf_cfi; /* Cached DWARF CFI for this module. */
+ Dwarf_CFI *eh_cfi; /* Cached EH CFI for this module. */
+
+ int segment; /* Index of first segment table entry. */
+ bool gc; /* Mark/sweep flag. */
+ bool is_executable; /* Use Dwfl::executable_for_core? */
+};
+
+/* This holds information common for all the threads/tasks/TIDs of one process
+ for backtraces. */
+
+struct Dwfl_Process
+{
+ struct Dwfl *dwfl;
+ pid_t pid;
+ const Dwfl_Thread_Callbacks *callbacks;
+ void *callbacks_arg;
+ struct ebl *ebl;
+ bool ebl_close:1;
+};
+
+/* See its typedef in libdwfl.h. */
+
+struct Dwfl_Thread
+{
+ Dwfl_Process *process;
+ pid_t tid;
+ /* The current frame being unwound. Initially it is the bottom frame.
+ Later the processed frames get freed and this pointer is updated. */
+ Dwfl_Frame *unwound;
+ void *callbacks_arg;
+};
+
+/* See its typedef in libdwfl.h. */
+
+struct Dwfl_Frame
+{
+ Dwfl_Thread *thread;
+ /* Previous (outer) frame. */
+ Dwfl_Frame *unwound;
+ bool signal_frame : 1;
+ bool initial_frame : 1;
+ enum
+ {
+ /* This structure is still being initialized or there was an error
+ initializing it. */
+ DWFL_FRAME_STATE_ERROR,
+ /* PC field is valid. */
+ DWFL_FRAME_STATE_PC_SET,
+ /* PC field is undefined, this means the next (inner) frame was the
+ outermost frame. */
+ DWFL_FRAME_STATE_PC_UNDEFINED
+ } pc_state;
+ /* Either initialized from appropriate REGS element or on some archs
+ initialized separately as the return address has no DWARF register. */
+ Dwarf_Addr pc;
+ /* (1 << X) bitmask where 0 <= X < ebl_frame_nregs. */
+ uint64_t regs_set[3];
+ /* REGS array size is ebl_frame_nregs.
+ REGS_SET tells which of the REGS are valid. */
+ Dwarf_Addr regs[];
+};
+
+/* Fetch value from Dwfl_Frame->regs indexed by DWARF REGNO.
+ No error code is set if the function returns FALSE. */
+bool __libdwfl_frame_reg_get (Dwfl_Frame *state, unsigned regno,
+ Dwarf_Addr *val)
+ internal_function;
+
+/* Store value to Dwfl_Frame->regs indexed by DWARF REGNO.
+ No error code is set if the function returns FALSE. */
+bool __libdwfl_frame_reg_set (Dwfl_Frame *state, unsigned regno,
+ Dwarf_Addr val)
+ internal_function;
+
+/* Information cached about each CU in Dwfl_Module.dw. */
+struct dwfl_cu
+{
+ /* This caches libdw information about the CU. It's also the
+ address passed back to users, so we take advantage of the
+ fact that it's placed first to cast back. */
+ Dwarf_Die die;
+
+ Dwfl_Module *mod; /* Pointer back to containing module. */
+
+ struct dwfl_cu *next; /* CU immediately following in the file. */
+
+ struct Dwfl_Lines *lines;
+};
+
+struct Dwfl_Lines
+{
+ struct dwfl_cu *cu;
+
+ /* This is what the opaque Dwfl_Line * pointers we pass to users are.
+ We need to recover pointers to our struct dwfl_cu and a record in
+ libdw's Dwarf_Line table. To minimize the memory used in addition
+ to libdw's Dwarf_Lines buffer, we just point to our own index in
+ this table, and have one pointer back to the CU. The indices here
+ match those in libdw's Dwarf_CU.lines->info table. */
+ struct Dwfl_Line
+ {
+ unsigned int idx; /* My index in the dwfl_cu.lines table. */
+ } idx[0];
+};
+
+static inline struct dwfl_cu *
+dwfl_linecu_inline (const Dwfl_Line *line)
+{
+ const struct Dwfl_Lines *lines = ((const void *) line
+ - offsetof (struct Dwfl_Lines,
+ idx[line->idx]));
+ return lines->cu;
+}
+#define dwfl_linecu dwfl_linecu_inline
+
+static inline GElf_Addr
+dwfl_adjusted_address (Dwfl_Module *mod, GElf_Addr addr)
+{
+ return addr + mod->main_bias;
+}
+
+static inline GElf_Addr
+dwfl_deadjust_address (Dwfl_Module *mod, GElf_Addr addr)
+{
+ return addr - mod->main_bias;
+}
+
+static inline Dwarf_Addr
+dwfl_adjusted_dwarf_addr (Dwfl_Module *mod, Dwarf_Addr addr)
+{
+ return dwfl_adjusted_address (mod, (addr
+ - mod->debug.address_sync
+ + mod->main.address_sync));
+}
+
+static inline Dwarf_Addr
+dwfl_deadjust_dwarf_addr (Dwfl_Module *mod, Dwarf_Addr addr)
+{
+ return (dwfl_deadjust_address (mod, addr)
+ - mod->main.address_sync
+ + mod->debug.address_sync);
+}
+
+static inline Dwarf_Addr
+dwfl_adjusted_aux_sym_addr (Dwfl_Module *mod, Dwarf_Addr addr)
+{
+ return dwfl_adjusted_address (mod, (addr
+ - mod->aux_sym.address_sync
+ + mod->main.address_sync));
+}
+
+static inline Dwarf_Addr
+dwfl_deadjust_aux_sym_addr (Dwfl_Module *mod, Dwarf_Addr addr)
+{
+ return (dwfl_deadjust_address (mod, addr)
+ - mod->main.address_sync
+ + mod->aux_sym.address_sync);
+}
+
+static inline GElf_Addr
+dwfl_adjusted_st_value (Dwfl_Module *mod, Elf *symelf, GElf_Addr addr)
+{
+ if (symelf == mod->main.elf)
+ return dwfl_adjusted_address (mod, addr);
+ if (symelf == mod->debug.elf)
+ return dwfl_adjusted_dwarf_addr (mod, addr);
+ return dwfl_adjusted_aux_sym_addr (mod, addr);
+}
+
+static inline GElf_Addr
+dwfl_deadjust_st_value (Dwfl_Module *mod, Elf *symelf, GElf_Addr addr)
+{
+ if (symelf == mod->main.elf)
+ return dwfl_deadjust_address (mod, addr);
+ if (symelf == mod->debug.elf)
+ return dwfl_deadjust_dwarf_addr (mod, addr);
+ return dwfl_deadjust_aux_sym_addr (mod, addr);
+}
+
+/* This describes a contiguous address range that lies in a single CU.
+ We condense runs of Dwarf_Arange entries for the same CU into this. */
+struct dwfl_arange
+{
+ struct dwfl_cu *cu;
+ size_t arange; /* Index in Dwarf_Aranges. */
+};
+
+
+/* Structure used for keeping track of ptrace attaching a thread.
+ Shared by linux-pid-attach and linux-proc-maps. If it has been setup
+ then get the instance through __libdwfl_get_pid_arg. */
+struct __libdwfl_pid_arg
+{
+ /* /proc/PID/task/. */
+ DIR *dir;
+ /* Elf for /proc/PID/exe. Set to NULL if it couldn't be opened. */
+ Elf *elf;
+ /* fd for /proc/PID/exe. Set to -1 if it couldn't be opened. */
+ int elf_fd;
+ /* It is 0 if not used. */
+ pid_t tid_attached;
+ /* Valid only if TID_ATTACHED is not zero. */
+ bool tid_was_stopped;
+ /* True if threads are ptrace stopped by caller. */
+ bool assume_ptrace_stopped;
+};
+
+/* If DWfl is not NULL and a Dwfl_Process has been setup that has
+ Dwfl_Thread_Callbacks set to pid_thread_callbacks, then return the
+ callbacks_arg, which will be a struct __libdwfl_pid_arg. Otherwise
+ returns NULL. */
+extern struct __libdwfl_pid_arg *__libdwfl_get_pid_arg (Dwfl *dwfl)
+ internal_function;
+
+/* Makes sure the given tid is attached. On success returns true and
+ sets tid_was_stopped. */
+extern bool __libdwfl_ptrace_attach (pid_t tid, bool *tid_was_stoppedp)
+ internal_function;
+
+/* Detaches a tid that was attached through
+ __libdwfl_ptrace_attach. Must be given the tid_was_stopped as set
+ by __libdwfl_ptrace_attach. */
+extern void __libdwfl_ptrace_detach (pid_t tid, bool tid_was_stopped)
+ internal_function;
+
+
+/* Internal wrapper for old dwfl_module_getsym and new dwfl_module_getsym_info.
+ adjust_st_value set to true returns adjusted SYM st_value, set to false
+ it will not adjust SYM at all, but does match against resolved *ADDR. */
+extern const char *__libdwfl_getsym (Dwfl_Module *mod, int ndx, GElf_Sym *sym,
+ GElf_Addr *addr, GElf_Word *shndxp,
+ Elf **elfp, Dwarf_Addr *biasp,
+ bool *resolved, bool adjust_st_value)
+ internal_function;
+
+/* Internal wrapper for old dwfl_module_addrsym and new dwfl_module_addrinfo.
+ adjust_st_value set to true returns adjusted SYM st_value, set to false
+ it will not adjust SYM at all, but does match against resolved values. */
+extern const char *__libdwfl_addrsym (Dwfl_Module *mod, GElf_Addr addr,
+ GElf_Off *off, GElf_Sym *sym,
+ GElf_Word *shndxp, Elf **elfp,
+ Dwarf_Addr *bias,
+ bool adjust_st_value) internal_function;
+
+extern void __libdwfl_module_free (Dwfl_Module *mod) internal_function;
+
+/* Find the main ELF file, update MOD->elferr and/or MOD->main.elf. */
+extern void __libdwfl_getelf (Dwfl_Module *mod) internal_function;
+
+/* Process relocations in debugging sections in an ET_REL file.
+ FILE must be opened with ELF_C_READ_MMAP_PRIVATE or ELF_C_READ,
+ to make it possible to relocate the data in place (or ELF_C_RDWR or
+ ELF_C_RDWR_MMAP if you intend to modify the Elf file on disk). After
+ this, dwarf_begin_elf on FILE will read the relocated data.
+
+ When DEBUG is false, apply partial relocation to all sections. */
+extern Dwfl_Error __libdwfl_relocate (Dwfl_Module *mod, Elf *file, bool debug)
+ internal_function;
+
+/* Find the section index in mod->main.elf that contains the given
+ *ADDR. Adjusts *ADDR to be section relative on success, returns
+ SHN_UNDEF on failure. */
+extern size_t __libdwfl_find_section_ndx (Dwfl_Module *mod, Dwarf_Addr *addr)
+ internal_function;
+
+/* Process (simple) relocations in arbitrary section TSCN of an ET_REL file.
+ RELOCSCN is SHT_REL or SHT_RELA and TSCN is its sh_info target section. */
+extern Dwfl_Error __libdwfl_relocate_section (Dwfl_Module *mod, Elf *relocated,
+ Elf_Scn *relocscn, Elf_Scn *tscn,
+ bool partial)
+ internal_function;
+
+/* Adjust *VALUE from section-relative to absolute.
+ MOD->dwfl->callbacks->section_address is called to determine the actual
+ address of a loaded section. */
+extern Dwfl_Error __libdwfl_relocate_value (Dwfl_Module *mod, Elf *elf,
+ size_t *shstrndx_cache,
+ Elf32_Word shndx,
+ GElf_Addr *value)
+ internal_function;
+
+/* Ensure that MOD->ebl is set up. */
+extern Dwfl_Error __libdwfl_module_getebl (Dwfl_Module *mod) internal_function;
+
+/* Install a new Dwarf_CFI in *SLOT (MOD->eh_cfi or MOD->dwarf_cfi). */
+extern Dwarf_CFI *__libdwfl_set_cfi (Dwfl_Module *mod, Dwarf_CFI **slot,
+ Dwarf_CFI *cfi)
+ internal_function;
+
+/* Iterate through all the CU's in the module. Start by passing a null
+ LASTCU, and then pass the last *CU returned. Success return with null
+ *CU no more CUs. */
+extern Dwfl_Error __libdwfl_nextcu (Dwfl_Module *mod, struct dwfl_cu *lastcu,
+ struct dwfl_cu **cu) internal_function;
+
+/* Find the CU by address. */
+extern Dwfl_Error __libdwfl_addrcu (Dwfl_Module *mod, Dwarf_Addr addr,
+ struct dwfl_cu **cu) internal_function;
+
+/* Ensure that CU->lines (and CU->cu->lines) is set up. */
+extern Dwfl_Error __libdwfl_cu_getsrclines (struct dwfl_cu *cu)
+ internal_function;
+
+/* Look in ELF for an NT_GNU_BUILD_ID note. Store it to BUILD_ID_BITS,
+ its vaddr in ELF to BUILD_ID_VADDR (it is unrelocated, even if MOD is not
+ NULL) and store length to BUILD_ID_LEN. Returns -1 for errors, 1 if it was
+ stored and 0 if no note is found. MOD may be NULL, MOD must be non-NULL
+ only if ELF is ET_REL. */
+extern int __libdwfl_find_elf_build_id (Dwfl_Module *mod, Elf *elf,
+ const void **build_id_bits,
+ GElf_Addr *build_id_elfaddr,
+ int *build_id_len)
+ internal_function;
+
+/* Look in ELF for an NT_GNU_BUILD_ID note. If SET is true, store it
+ in MOD and return its length. If SET is false, instead compare it
+ to that stored in MOD and return 2 if they match, 1 if they do not.
+ Returns -1 for errors, 0 if no note is found. */
+extern int __libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf)
+ internal_function;
+
+/* Open a main or debuginfo file by its build ID, returns the fd. */
+extern int __libdwfl_open_mod_by_build_id (Dwfl_Module *mod, bool debug,
+ char **file_name) internal_function;
+
+/* Same, but takes an explicit build_id, can also be used for alt debug. */
+extern int __libdwfl_open_by_build_id (Dwfl_Module *mod, bool debug,
+ char **file_name, const size_t id_len,
+ const uint8_t *id) internal_function;
+
+extern uint32_t __libdwfl_crc32 (uint32_t crc, unsigned char *buf, size_t len)
+ attribute_hidden;
+extern int __libdwfl_crc32_file (int fd, uint32_t *resp) attribute_hidden;
+
+
+/* Given ELF and some parameters return TRUE if the *P return value parameters
+ have been successfully filled in. Any of the *P parameters can be NULL. */
+extern bool __libdwfl_elf_address_range (Elf *elf, GElf_Addr base,
+ bool add_p_vaddr, bool sanity,
+ GElf_Addr *vaddrp,
+ GElf_Addr *address_syncp,
+ GElf_Addr *startp, GElf_Addr *endp,
+ GElf_Addr *biasp, GElf_Half *e_typep)
+ internal_function;
+
+/* Meat of dwfl_report_elf, given elf_begin just called.
+ Consumes ELF on success, not on failure. */
+extern Dwfl_Module *__libdwfl_report_elf (Dwfl *dwfl, const char *name,
+ const char *file_name, int fd,
+ Elf *elf, GElf_Addr base,
+ bool add_p_vaddr, bool sanity)
+ internal_function;
+
+/* Meat of dwfl_report_offline. */
+extern Dwfl_Module *__libdwfl_report_offline (Dwfl *dwfl, const char *name,
+ const char *file_name,
+ int fd, bool closefd,
+ int (*predicate) (const char *,
+ const char *))
+ internal_function;
+
+/* Free PROCESS. Unlink and free also any structures it references. */
+extern void __libdwfl_process_free (Dwfl_Process *process)
+ internal_function;
+
+/* Update STATE->unwound for the unwound frame.
+ On error STATE->unwound == NULL
+ or STATE->unwound->pc_state == DWFL_FRAME_STATE_ERROR;
+ in such case dwfl_errno () is set.
+ If STATE->unwound->pc_state == DWFL_FRAME_STATE_PC_UNDEFINED
+ then STATE was the last valid frame. */
+extern void __libdwfl_frame_unwind (Dwfl_Frame *state)
+ internal_function;
+
+/* Align segment START downwards or END upwards addresses according to DWFL. */
+extern GElf_Addr __libdwfl_segment_start (Dwfl *dwfl, GElf_Addr start)
+ internal_function;
+extern GElf_Addr __libdwfl_segment_end (Dwfl *dwfl, GElf_Addr end)
+ internal_function;
+
+/* Decompression wrappers: decompress whole file into memory. */
+extern Dwfl_Error __libdw_gunzip (int fd, off_t start_offset,
+ void *mapped, size_t mapped_size,
+ void **whole, size_t *whole_size)
+ internal_function;
+extern Dwfl_Error __libdw_bunzip2 (int fd, off_t start_offset,
+ void *mapped, size_t mapped_size,
+ void **whole, size_t *whole_size)
+ internal_function;
+extern Dwfl_Error __libdw_unlzma (int fd, off_t start_offset,
+ void *mapped, size_t mapped_size,
+ void **whole, size_t *whole_size)
+ internal_function;
+
+/* Skip the image header before a file image: updates *START_OFFSET. */
+extern Dwfl_Error __libdw_image_header (int fd, off_t *start_offset,
+ void *mapped, size_t mapped_size)
+ internal_function;
+
+/* Open Elf handle on *FDP. This handles decompression and checks
+ elf_kind. Succeed only for ELF_K_ELF, or also ELF_K_AR if ARCHIVE_OK.
+ Returns DWFL_E_NOERROR and sets *ELFP on success, resets *FDP to -1 if
+ it's no longer used. Resets *FDP on failure too iff CLOSE_ON_FAIL. */
+extern Dwfl_Error __libdw_open_file (int *fdp, Elf **elfp,
+ bool close_on_fail, bool archive_ok)
+ internal_function;
+
+/* Fetch PT_DYNAMIC P_VADDR from ELF and store it to *VADDRP. Return success.
+ *VADDRP is not modified if the function fails. */
+extern bool __libdwfl_dynamic_vaddr_get (Elf *elf, GElf_Addr *vaddrp)
+ internal_function;
+
+/* These are working nicely for --core, but are not ready to be
+ exported interfaces quite yet. */
+
+/* Type of callback function ...
+ */
+typedef bool Dwfl_Memory_Callback (Dwfl *dwfl, int segndx,
+ void **buffer, size_t *buffer_available,
+ GElf_Addr vaddr, size_t minread, void *arg);
+
+/* Type of callback function ...
+ */
+typedef bool Dwfl_Module_Callback (Dwfl_Module *mod, void **userdata,
+ const char *name, Dwarf_Addr base,
+ void **buffer, size_t *buffer_available,
+ GElf_Off cost, GElf_Off worthwhile,
+ GElf_Off whole, GElf_Off contiguous,
+ void *arg, Elf **elfp);
+
+/* One shared library (or executable) info from DT_DEBUG link map. */
+struct r_debug_info_module
+{
+ struct r_debug_info_module *next;
+ /* FD is -1 iff ELF is NULL. */
+ int fd;
+ Elf *elf;
+ GElf_Addr l_ld;
+ /* START and END are both zero if not valid. */
+ GElf_Addr start, end;
+ bool disk_file_has_build_id;
+ char name[0];
+};
+
+/* Information gathered from DT_DEBUG by dwfl_link_map_report hinted to
+ dwfl_segment_report_module. */
+struct r_debug_info
+{
+ struct r_debug_info_module *module;
+};
+
+/* ...
+ */
+extern int dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
+ Dwfl_Memory_Callback *memory_callback,
+ void *memory_callback_arg,
+ Dwfl_Module_Callback *read_eagerly,
+ void *read_eagerly_arg,
+ const void *note_file,
+ size_t note_file_size,
+ const struct r_debug_info *r_debug_info);
+
+/* Report a module for entry in the dynamic linker's struct link_map list.
+ For each link_map entry, if an existing module resides at its address,
+ this just modifies that module's name and suggested file name. If
+ no such module exists, this calls dwfl_report_elf on the l_name string.
+
+ If AUXV is not null, it points to AUXV_SIZE bytes of auxiliary vector
+ data as contained in an NT_AUXV note or read from a /proc/pid/auxv
+ file. When this is available, it guides the search. If AUXV is null
+ or the memory it points to is not accessible, then this search can
+ only find where to begin if the correct executable file was
+ previously reported and preloaded as with dwfl_report_elf.
+
+ Fill in R_DEBUG_INFO if it is not NULL. It should be cleared by the
+ caller, this function does not touch fields it does not need to modify.
+ If R_DEBUG_INFO is not NULL then no modules get added to DWFL, caller
+ has to add them from filled in R_DEBUG_INFO.
+
+ Returns the number of modules found, or -1 for errors. */
+extern int dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
+ Dwfl_Memory_Callback *memory_callback,
+ void *memory_callback_arg,
+ struct r_debug_info *r_debug_info);
+
+
+/* Avoid PLT entries. */
+INTDECL (dwfl_begin)
+INTDECL (dwfl_errmsg)
+INTDECL (dwfl_errno)
+INTDECL (dwfl_addrmodule)
+INTDECL (dwfl_addrsegment)
+INTDECL (dwfl_addrdwarf)
+INTDECL (dwfl_addrdie)
+INTDECL (dwfl_core_file_attach)
+INTDECL (dwfl_core_file_report)
+INTDECL (dwfl_getmodules)
+INTDECL (dwfl_module_addrdie)
+INTDECL (dwfl_module_address_section)
+INTDECL (dwfl_module_addrinfo)
+INTDECL (dwfl_module_addrsym)
+INTDECL (dwfl_module_build_id)
+INTDECL (dwfl_module_getdwarf)
+INTDECL (dwfl_module_getelf)
+INTDECL (dwfl_module_getsym)
+INTDECL (dwfl_module_getsym_info)
+INTDECL (dwfl_module_getsymtab)
+INTDECL (dwfl_module_getsymtab_first_global)
+INTDECL (dwfl_module_getsrc)
+INTDECL (dwfl_module_report_build_id)
+INTDECL (dwfl_report_elf)
+INTDECL (dwfl_report_begin)
+INTDECL (dwfl_report_begin_add)
+INTDECL (dwfl_report_module)
+INTDECL (dwfl_report_segment)
+INTDECL (dwfl_report_offline)
+INTDECL (dwfl_report_end)
+INTDECL (dwfl_build_id_find_elf)
+INTDECL (dwfl_build_id_find_debuginfo)
+INTDECL (dwfl_standard_find_debuginfo)
+INTDECL (dwfl_link_map_report)
+INTDECL (dwfl_linux_kernel_find_elf)
+INTDECL (dwfl_linux_kernel_module_section_address)
+INTDECL (dwfl_linux_proc_attach)
+INTDECL (dwfl_linux_proc_report)
+INTDECL (dwfl_linux_proc_maps_report)
+INTDECL (dwfl_linux_proc_find_elf)
+INTDECL (dwfl_linux_kernel_report_kernel)
+INTDECL (dwfl_linux_kernel_report_modules)
+INTDECL (dwfl_linux_kernel_report_offline)
+INTDECL (dwfl_offline_section_address)
+INTDECL (dwfl_module_relocate_address)
+INTDECL (dwfl_module_dwarf_cfi)
+INTDECL (dwfl_module_eh_cfi)
+INTDECL (dwfl_attach_state)
+INTDECL (dwfl_pid)
+INTDECL (dwfl_thread_dwfl)
+INTDECL (dwfl_thread_tid)
+INTDECL (dwfl_frame_thread)
+INTDECL (dwfl_thread_state_registers)
+INTDECL (dwfl_thread_state_register_pc)
+INTDECL (dwfl_getthread_frames)
+INTDECL (dwfl_getthreads)
+INTDECL (dwfl_thread_getframes)
+INTDECL (dwfl_frame_pc)
+
+/* Leading arguments standard to callbacks passed a Dwfl_Module. */
+#define MODCB_ARGS(mod) (mod), &(mod)->userdata, (mod)->name, (mod)->low_addr
+#define CBFAIL (errno ? DWFL_E (ERRNO, errno) : DWFL_E_CB);
+
+
+/* The default used by dwfl_standard_find_debuginfo. */
+#define DEFAULT_DEBUGINFO_PATH ":.debug:/usr/lib/debug"
+
+
+#endif /* libdwflP.h */
diff --git a/libdwfl/libdwfl_crc32.c b/libdwfl/libdwfl_crc32.c
new file mode 100644
index 0000000..b89d0d3
--- /dev/null
+++ b/libdwfl/libdwfl_crc32.c
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002, 2005 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#define crc32 attribute_hidden __libdwfl_crc32
+#define LIB_SYSTEM_H 1
+#include <libdwflP.h>
+#include "../lib/crc32.c"
diff --git a/libdwfl/libdwfl_crc32_file.c b/libdwfl/libdwfl_crc32_file.c
new file mode 100644
index 0000000..f849128
--- /dev/null
+++ b/libdwfl/libdwfl_crc32_file.c
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002, 2005 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#define crc32_file attribute_hidden __libdwfl_crc32_file
+#define crc32 __libdwfl_crc32
+#include <libdwflP.h>
+#include "../lib/crc32_file.c"
diff --git a/libdwfl/lines.c b/libdwfl/lines.c
new file mode 100644
index 0000000..128c0c9
--- /dev/null
+++ b/libdwfl/lines.c
@@ -0,0 +1,56 @@
+/* Fetch source line info for CU.
+ Copyright (C) 2005, 2006 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include "../libdw/libdwP.h"
+
+Dwfl_Error
+internal_function
+__libdwfl_cu_getsrclines (struct dwfl_cu *cu)
+{
+ if (cu->lines == NULL)
+ {
+ Dwarf_Lines *lines;
+ size_t nlines;
+ if (INTUSE(dwarf_getsrclines) (&cu->die, &lines, &nlines) != 0)
+ return DWFL_E_LIBDW;
+
+ cu->lines = malloc (offsetof (struct Dwfl_Lines, idx[nlines]));
+ if (cu->lines == NULL)
+ return DWFL_E_NOMEM;
+ cu->lines->cu = cu;
+ for (unsigned int i = 0; i < nlines; ++i)
+ cu->lines->idx[i].idx = i;
+ }
+
+ return DWFL_E_NOERROR;
+}
diff --git a/libdwfl/link_map.c b/libdwfl/link_map.c
new file mode 100644
index 0000000..29307c7
--- /dev/null
+++ b/libdwfl/link_map.c
@@ -0,0 +1,1039 @@
+/* Report modules by examining dynamic linker data structures.
+ Copyright (C) 2008-2016 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include "libdwflP.h"
+#include "../libdw/memory-access.h"
+#include "system.h"
+
+#include <byteswap.h>
+#include <endian.h>
+#include <fcntl.h>
+
+/* This element is always provided and always has a constant value.
+ This makes it an easy thing to scan for to discern the format. */
+#define PROBE_TYPE AT_PHENT
+#define PROBE_VAL32 sizeof (Elf32_Phdr)
+#define PROBE_VAL64 sizeof (Elf64_Phdr)
+
+
+static inline bool
+do_check64 (const char *a64, uint_fast8_t *elfdata)
+{
+ /* The AUXV pointer might not even be naturally aligned for 64-bit
+ data, because note payloads in a core file are not aligned. */
+ const char *typep = a64 + offsetof (Elf64_auxv_t, a_type);
+ uint64_t type = read_8ubyte_unaligned_noncvt (typep);
+ const char *valp = a64 + offsetof (Elf64_auxv_t, a_un.a_val);
+ uint64_t val = read_8ubyte_unaligned_noncvt (valp);
+
+ if (type == BE64 (PROBE_TYPE)
+ && val == BE64 (PROBE_VAL64))
+ {
+ *elfdata = ELFDATA2MSB;
+ return true;
+ }
+
+ if (type == LE64 (PROBE_TYPE)
+ && val == LE64 (PROBE_VAL64))
+ {
+ *elfdata = ELFDATA2LSB;
+ return true;
+ }
+
+ return false;
+}
+
+static inline bool
+do_check32 (const char *a32, uint_fast8_t *elfdata)
+{
+ /* The AUXV pointer might not even be naturally aligned for 32-bit
+ data, because note payloads in a core file are not aligned. */
+ const char *typep = a32 + offsetof (Elf32_auxv_t, a_type);
+ uint32_t type = read_4ubyte_unaligned_noncvt (typep);
+ const char *valp = a32 + offsetof (Elf32_auxv_t, a_un.a_val);
+ uint32_t val = read_4ubyte_unaligned_noncvt (valp);
+
+ if (type == BE32 (PROBE_TYPE)
+ && val == BE32 (PROBE_VAL32))
+ {
+ *elfdata = ELFDATA2MSB;
+ return true;
+ }
+
+ if (type == LE32 (PROBE_TYPE)
+ && val == LE32 (PROBE_VAL32))
+ {
+ *elfdata = ELFDATA2LSB;
+ return true;
+ }
+
+ return false;
+}
+
+/* Examine an auxv data block and determine its format.
+ Return true iff we figured it out. */
+static bool
+auxv_format_probe (const void *auxv, size_t size,
+ uint_fast8_t *elfclass, uint_fast8_t *elfdata)
+{
+ for (size_t i = 0; i < size / sizeof (Elf64_auxv_t); ++i)
+ {
+ if (do_check64 (auxv + i * sizeof (Elf64_auxv_t), elfdata))
+ {
+ *elfclass = ELFCLASS64;
+ return true;
+ }
+
+ if (do_check32 (auxv + (i * 2) * sizeof (Elf32_auxv_t), elfdata)
+ || do_check32 (auxv + (i * 2 + 1) * sizeof (Elf32_auxv_t), elfdata))
+ {
+ *elfclass = ELFCLASS32;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* This is a Dwfl_Memory_Callback that wraps another memory callback.
+ If the underlying callback cannot fill the data, then this will
+ fall back to fetching data from module files. */
+
+struct integrated_memory_callback
+{
+ Dwfl_Memory_Callback *memory_callback;
+ void *memory_callback_arg;
+ void *buffer;
+};
+
+static bool
+integrated_memory_callback (Dwfl *dwfl, int ndx,
+ void **buffer, size_t *buffer_available,
+ GElf_Addr vaddr,
+ size_t minread,
+ void *arg)
+{
+ struct integrated_memory_callback *info = arg;
+
+ if (ndx == -1)
+ {
+ /* Called for cleanup. */
+ if (info->buffer != NULL)
+ {
+ /* The last probe buffer came from the underlying callback.
+ Let it do its cleanup. */
+ assert (*buffer == info->buffer); /* XXX */
+ *buffer = info->buffer;
+ info->buffer = NULL;
+ return (*info->memory_callback) (dwfl, ndx, buffer, buffer_available,
+ vaddr, minread,
+ info->memory_callback_arg);
+ }
+ *buffer = NULL;
+ *buffer_available = 0;
+ return false;
+ }
+
+ if (*buffer != NULL)
+ /* For a final-read request, we only use the underlying callback. */
+ return (*info->memory_callback) (dwfl, ndx, buffer, buffer_available,
+ vaddr, minread, info->memory_callback_arg);
+
+ /* Let the underlying callback try to fill this request. */
+ if ((*info->memory_callback) (dwfl, ndx, &info->buffer, buffer_available,
+ vaddr, minread, info->memory_callback_arg))
+ {
+ *buffer = info->buffer;
+ return true;
+ }
+
+ /* Now look for module text covering this address. */
+
+ Dwfl_Module *mod;
+ (void) INTUSE(dwfl_addrsegment) (dwfl, vaddr, &mod);
+ if (mod == NULL)
+ return false;
+
+ Dwarf_Addr bias;
+ Elf_Scn *scn = INTUSE(dwfl_module_address_section) (mod, &vaddr, &bias);
+ if (unlikely (scn == NULL))
+ {
+#if 0 // XXX would have to handle ndx=-1 cleanup calls passed down.
+ /* If we have no sections we can try to fill it from the module file
+ based on its phdr mappings. */
+ if (likely (mod->e_type != ET_REL) && mod->main.elf != NULL)
+ return INTUSE(dwfl_elf_phdr_memory_callback)
+ (dwfl, 0, buffer, buffer_available,
+ vaddr - mod->main.bias, minread, mod->main.elf);
+#endif
+ return false;
+ }
+
+ Elf_Data *data = elf_rawdata (scn, NULL);
+ if (unlikely (data == NULL))
+ // XXX throw error?
+ return false;
+
+ if (unlikely (data->d_size < vaddr))
+ return false;
+
+ /* Provide as much data as we have. */
+ void *contents = data->d_buf + vaddr;
+ size_t avail = data->d_size - vaddr;
+ if (unlikely (avail < minread))
+ return false;
+
+ /* If probing for a string, make sure it's terminated. */
+ if (minread == 0 && unlikely (memchr (contents, '\0', avail) == NULL))
+ return false;
+
+ /* We have it! */
+ *buffer = contents;
+ *buffer_available = avail;
+ return true;
+}
+
+static size_t
+addrsize (uint_fast8_t elfclass)
+{
+ return elfclass * 4;
+}
+
+/* Report a module for each struct link_map in the linked list at r_map
+ in the struct r_debug at R_DEBUG_VADDR. For r_debug_info description
+ see dwfl_link_map_report in libdwflP.h. If R_DEBUG_INFO is not NULL then no
+ modules get added to DWFL, caller has to add them from filled in
+ R_DEBUG_INFO.
+
+ For each link_map entry, if an existing module resides at its address,
+ this just modifies that module's name and suggested file name. If
+ no such module exists, this calls dwfl_report_elf on the l_name string.
+
+ Returns the number of modules found, or -1 for errors. */
+
+static int
+report_r_debug (uint_fast8_t elfclass, uint_fast8_t elfdata,
+ Dwfl *dwfl, GElf_Addr r_debug_vaddr,
+ Dwfl_Memory_Callback *memory_callback,
+ void *memory_callback_arg,
+ struct r_debug_info *r_debug_info)
+{
+ /* Skip r_version, to aligned r_map field. */
+ GElf_Addr read_vaddr = r_debug_vaddr + addrsize (elfclass);
+
+ void *buffer = NULL;
+ size_t buffer_available = 0;
+ inline int release_buffer (int result)
+ {
+ if (buffer != NULL)
+ (void) (*memory_callback) (dwfl, -1, &buffer, &buffer_available, 0, 0,
+ memory_callback_arg);
+ return result;
+ }
+
+ GElf_Addr addrs[4];
+ inline bool read_addrs (GElf_Addr vaddr, size_t n)
+ {
+ size_t nb = n * addrsize (elfclass); /* Address words -> bytes to read. */
+
+ /* Read a new buffer if the old one doesn't cover these words. */
+ if (buffer == NULL
+ || vaddr < read_vaddr
+ || vaddr - read_vaddr + nb > buffer_available)
+ {
+ release_buffer (0);
+
+ read_vaddr = vaddr;
+ int segndx = INTUSE(dwfl_addrsegment) (dwfl, vaddr, NULL);
+ if (unlikely (segndx < 0)
+ || unlikely (! (*memory_callback) (dwfl, segndx,
+ &buffer, &buffer_available,
+ vaddr, nb, memory_callback_arg)))
+ return true;
+ }
+
+ Elf32_Addr (*a32)[n] = vaddr - read_vaddr + buffer;
+ Elf64_Addr (*a64)[n] = (void *) a32;
+
+ if (elfclass == ELFCLASS32)
+ {
+ if (elfdata == ELFDATA2MSB)
+ for (size_t i = 0; i < n; ++i)
+ addrs[i] = BE32 (read_4ubyte_unaligned_noncvt (&(*a32)[i]));
+ else
+ for (size_t i = 0; i < n; ++i)
+ addrs[i] = LE32 (read_4ubyte_unaligned_noncvt (&(*a32)[i]));
+ }
+ else
+ {
+ if (elfdata == ELFDATA2MSB)
+ for (size_t i = 0; i < n; ++i)
+ addrs[i] = BE64 (read_8ubyte_unaligned_noncvt (&(*a64)[i]));
+ else
+ for (size_t i = 0; i < n; ++i)
+ addrs[i] = LE64 (read_8ubyte_unaligned_noncvt (&(*a64)[i]));
+ }
+
+ return false;
+ }
+
+ if (unlikely (read_addrs (read_vaddr, 1)))
+ return release_buffer (-1);
+
+ GElf_Addr next = addrs[0];
+
+ Dwfl_Module **lastmodp = &dwfl->modulelist;
+ int result = 0;
+
+ /* There can't be more elements in the link_map list than there are
+ segments. DWFL->lookup_elts is probably twice that number, so it
+ is certainly above the upper bound. If we iterate too many times,
+ there must be a loop in the pointers due to link_map clobberation. */
+ size_t iterations = 0;
+ while (next != 0 && ++iterations < dwfl->lookup_elts)
+ {
+ if (read_addrs (next, 4))
+ return release_buffer (-1);
+
+ /* Unused: l_addr is the difference between the address in memory
+ and the ELF file when the core was created. We need to
+ recalculate the difference below because the ELF file we use
+ might be differently pre-linked. */
+ // GElf_Addr l_addr = addrs[0];
+ GElf_Addr l_name = addrs[1];
+ GElf_Addr l_ld = addrs[2];
+ next = addrs[3];
+
+ /* If a clobbered or truncated memory image has no useful pointer,
+ just skip this element. */
+ if (l_ld == 0)
+ continue;
+
+ /* Fetch the string at the l_name address. */
+ const char *name = NULL;
+ if (buffer != NULL
+ && read_vaddr <= l_name
+ && l_name + 1 - read_vaddr < buffer_available
+ && memchr (l_name - read_vaddr + buffer, '\0',
+ buffer_available - (l_name - read_vaddr)) != NULL)
+ name = l_name - read_vaddr + buffer;
+ else
+ {
+ release_buffer (0);
+ read_vaddr = l_name;
+ int segndx = INTUSE(dwfl_addrsegment) (dwfl, l_name, NULL);
+ if (likely (segndx >= 0)
+ && (*memory_callback) (dwfl, segndx,
+ &buffer, &buffer_available,
+ l_name, 0, memory_callback_arg))
+ name = buffer;
+ }
+
+ if (name != NULL && name[0] == '\0')
+ name = NULL;
+
+ if (iterations == 1
+ && dwfl->user_core != NULL
+ && dwfl->user_core->executable_for_core != NULL)
+ name = dwfl->user_core->executable_for_core;
+
+ struct r_debug_info_module *r_debug_info_module = NULL;
+ if (r_debug_info != NULL)
+ {
+ /* Save link map information about valid shared library (or
+ executable) which has not been found on disk. */
+ const char *name1 = name == NULL ? "" : name;
+ r_debug_info_module = malloc (sizeof (*r_debug_info_module)
+ + strlen (name1) + 1);
+ if (unlikely (r_debug_info_module == NULL))
+ return release_buffer (result);
+ r_debug_info_module->fd = -1;
+ r_debug_info_module->elf = NULL;
+ r_debug_info_module->l_ld = l_ld;
+ r_debug_info_module->start = 0;
+ r_debug_info_module->end = 0;
+ r_debug_info_module->disk_file_has_build_id = false;
+ strcpy (r_debug_info_module->name, name1);
+ r_debug_info_module->next = r_debug_info->module;
+ r_debug_info->module = r_debug_info_module;
+ }
+
+ Dwfl_Module *mod = NULL;
+ if (name != NULL)
+ {
+ /* This code is mostly inlined dwfl_report_elf. */
+ // XXX hook for sysroot
+ int fd = open (name, O_RDONLY);
+ if (fd >= 0)
+ {
+ Elf *elf;
+ Dwfl_Error error = __libdw_open_file (&fd, &elf, true, false);
+ GElf_Addr elf_dynamic_vaddr;
+ if (error == DWFL_E_NOERROR
+ && __libdwfl_dynamic_vaddr_get (elf, &elf_dynamic_vaddr))
+ {
+ const void *build_id_bits;
+ GElf_Addr build_id_elfaddr;
+ int build_id_len;
+ bool valid = true;
+
+ if (__libdwfl_find_elf_build_id (NULL, elf, &build_id_bits,
+ &build_id_elfaddr,
+ &build_id_len) > 0
+ && build_id_elfaddr != 0)
+ {
+ if (r_debug_info_module != NULL)
+ r_debug_info_module->disk_file_has_build_id = true;
+ GElf_Addr build_id_vaddr = (build_id_elfaddr
+ - elf_dynamic_vaddr + l_ld);
+
+ release_buffer (0);
+ int segndx = INTUSE(dwfl_addrsegment) (dwfl,
+ build_id_vaddr,
+ NULL);
+ if (! (*memory_callback) (dwfl, segndx,
+ &buffer, &buffer_available,
+ build_id_vaddr, build_id_len,
+ memory_callback_arg))
+ {
+ /* File has valid build-id which cannot be read from
+ memory. This happens for core files without bit 4
+ (0x10) set in Linux /proc/PID/coredump_filter. */
+ }
+ else
+ {
+ if (memcmp (build_id_bits, buffer, build_id_len) != 0)
+ /* File has valid build-id which does not match
+ the one in memory. */
+ valid = false;
+ release_buffer (0);
+ }
+ }
+
+ if (valid)
+ {
+ // It is like l_addr but it handles differently prelinked
+ // files at core dumping vs. core loading time.
+ GElf_Addr base = l_ld - elf_dynamic_vaddr;
+ if (r_debug_info_module == NULL)
+ {
+ // XXX hook for sysroot
+ mod = __libdwfl_report_elf (dwfl, basename (name),
+ name, fd, elf, base,
+ true, true);
+ if (mod != NULL)
+ {
+ elf = NULL;
+ fd = -1;
+ }
+ }
+ else if (__libdwfl_elf_address_range (elf, base, true,
+ true, NULL, NULL,
+ &r_debug_info_module->start,
+ &r_debug_info_module->end,
+ NULL, NULL))
+ {
+ r_debug_info_module->elf = elf;
+ r_debug_info_module->fd = fd;
+ elf = NULL;
+ fd = -1;
+ }
+ }
+ if (elf != NULL)
+ elf_end (elf);
+ if (fd != -1)
+ close (fd);
+ }
+ }
+ }
+
+ if (mod != NULL)
+ {
+ ++result;
+
+ /* Move this module to the end of the list, so that we end
+ up with a list in the same order as the link_map chain. */
+ if (mod->next != NULL)
+ {
+ if (*lastmodp != mod)
+ {
+ lastmodp = &dwfl->modulelist;
+ while (*lastmodp != mod)
+ lastmodp = &(*lastmodp)->next;
+ }
+ *lastmodp = mod->next;
+ mod->next = NULL;
+ while (*lastmodp != NULL)
+ lastmodp = &(*lastmodp)->next;
+ *lastmodp = mod;
+ }
+
+ lastmodp = &mod->next;
+ }
+ }
+
+ return release_buffer (result);
+}
+
+static GElf_Addr
+consider_executable (Dwfl_Module *mod, GElf_Addr at_phdr, GElf_Addr at_entry,
+ uint_fast8_t *elfclass, uint_fast8_t *elfdata,
+ Dwfl_Memory_Callback *memory_callback,
+ void *memory_callback_arg)
+{
+ GElf_Ehdr ehdr;
+ if (unlikely (gelf_getehdr (mod->main.elf, &ehdr) == NULL))
+ return 0;
+
+ if (at_entry != 0)
+ {
+ /* If we have an AT_ENTRY value, reject this executable if
+ its entry point address could not have supplied that. */
+
+ if (ehdr.e_entry == 0)
+ return 0;
+
+ if (mod->e_type == ET_EXEC)
+ {
+ if (ehdr.e_entry != at_entry)
+ return 0;
+ }
+ else
+ {
+ /* It could be a PIE. */
+ }
+ }
+
+ // XXX this could be saved in the file cache: phdr vaddr, DT_DEBUG d_val vaddr
+ /* Find the vaddr of the DT_DEBUG's d_ptr. This is the memory
+ address where &r_debug was written at runtime. */
+ GElf_Xword align = mod->dwfl->segment_align;
+ GElf_Addr d_val_vaddr = 0;
+ size_t phnum;
+ if (elf_getphdrnum (mod->main.elf, &phnum) != 0)
+ return 0;
+
+ for (size_t i = 0; i < phnum; ++i)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (mod->main.elf, i, &phdr_mem);
+ if (phdr == NULL)
+ break;
+
+ if (phdr->p_align > 1 && (align == 0 || phdr->p_align < align))
+ align = phdr->p_align;
+
+ if (at_phdr != 0
+ && phdr->p_type == PT_LOAD
+ && (phdr->p_offset & -align) == (ehdr.e_phoff & -align))
+ {
+ /* This is the segment that would map the phdrs.
+ If we have an AT_PHDR value, reject this executable
+ if its phdr mapping could not have supplied that. */
+ if (mod->e_type == ET_EXEC)
+ {
+ if (ehdr.e_phoff - phdr->p_offset + phdr->p_vaddr != at_phdr)
+ return 0;
+ }
+ else
+ {
+ /* It could be a PIE. If the AT_PHDR value and our
+ phdr address don't match modulo ALIGN, then this
+ could not have been the right PIE. */
+ if (((ehdr.e_phoff - phdr->p_offset + phdr->p_vaddr) & -align)
+ != (at_phdr & -align))
+ return 0;
+
+ /* Calculate the bias applied to the PIE's p_vaddr values. */
+ GElf_Addr bias = (at_phdr - (ehdr.e_phoff - phdr->p_offset
+ + phdr->p_vaddr));
+
+ /* Final sanity check: if we have an AT_ENTRY value,
+ reject this PIE unless its biased e_entry matches. */
+ if (at_entry != 0 && at_entry != ehdr.e_entry + bias)
+ return 0;
+
+ /* If we're changing the module's address range,
+ we've just invalidated the module lookup table. */
+ GElf_Addr mod_bias = dwfl_adjusted_address (mod, 0);
+ if (bias != mod_bias)
+ {
+ mod->low_addr -= mod_bias;
+ mod->high_addr -= mod_bias;
+ mod->low_addr += bias;
+ mod->high_addr += bias;
+
+ free (mod->dwfl->lookup_module);
+ mod->dwfl->lookup_module = NULL;
+ }
+ }
+ }
+
+ if (phdr->p_type == PT_DYNAMIC)
+ {
+ Elf_Data *data = elf_getdata_rawchunk (mod->main.elf, phdr->p_offset,
+ phdr->p_filesz, ELF_T_DYN);
+ if (data == NULL)
+ continue;
+ const size_t entsize = gelf_fsize (mod->main.elf,
+ ELF_T_DYN, 1, EV_CURRENT);
+ const size_t n = data->d_size / entsize;
+ for (size_t j = 0; j < n; ++j)
+ {
+ GElf_Dyn dyn_mem;
+ GElf_Dyn *dyn = gelf_getdyn (data, j, &dyn_mem);
+ if (dyn != NULL && dyn->d_tag == DT_DEBUG)
+ {
+ d_val_vaddr = phdr->p_vaddr + entsize * j + entsize / 2;
+ break;
+ }
+ }
+ }
+ }
+
+ if (d_val_vaddr != 0)
+ {
+ /* Now we have the final address from which to read &r_debug. */
+ d_val_vaddr = dwfl_adjusted_address (mod, d_val_vaddr);
+
+ void *buffer = NULL;
+ size_t buffer_available = addrsize (ehdr.e_ident[EI_CLASS]);
+
+ int segndx = INTUSE(dwfl_addrsegment) (mod->dwfl, d_val_vaddr, NULL);
+
+ if ((*memory_callback) (mod->dwfl, segndx,
+ &buffer, &buffer_available,
+ d_val_vaddr, buffer_available,
+ memory_callback_arg))
+ {
+ const union
+ {
+ Elf32_Addr a32;
+ Elf64_Addr a64;
+ } *u = buffer;
+
+ GElf_Addr vaddr;
+ if (ehdr.e_ident[EI_CLASS] == ELFCLASS32)
+ vaddr = (ehdr.e_ident[EI_DATA] == ELFDATA2MSB
+ ? BE32 (u->a32) : LE32 (u->a32));
+ else
+ vaddr = (ehdr.e_ident[EI_DATA] == ELFDATA2MSB
+ ? BE64 (u->a64) : LE64 (u->a64));
+
+ (*memory_callback) (mod->dwfl, -1, &buffer, &buffer_available, 0, 0,
+ memory_callback_arg);
+
+ if (*elfclass == ELFCLASSNONE)
+ *elfclass = ehdr.e_ident[EI_CLASS];
+ else if (*elfclass != ehdr.e_ident[EI_CLASS])
+ return 0;
+
+ if (*elfdata == ELFDATANONE)
+ *elfdata = ehdr.e_ident[EI_DATA];
+ else if (*elfdata != ehdr.e_ident[EI_DATA])
+ return 0;
+
+ return vaddr;
+ }
+ }
+
+ return 0;
+}
+
+/* Try to find an existing executable module with a DT_DEBUG. */
+static GElf_Addr
+find_executable (Dwfl *dwfl, GElf_Addr at_phdr, GElf_Addr at_entry,
+ uint_fast8_t *elfclass, uint_fast8_t *elfdata,
+ Dwfl_Memory_Callback *memory_callback,
+ void *memory_callback_arg)
+{
+ for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next)
+ if (mod->main.elf != NULL)
+ {
+ GElf_Addr r_debug_vaddr = consider_executable (mod, at_phdr, at_entry,
+ elfclass, elfdata,
+ memory_callback,
+ memory_callback_arg);
+ if (r_debug_vaddr != 0)
+ return r_debug_vaddr;
+ }
+
+ return 0;
+}
+
+
+int
+dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
+ Dwfl_Memory_Callback *memory_callback,
+ void *memory_callback_arg,
+ struct r_debug_info *r_debug_info)
+{
+ GElf_Addr r_debug_vaddr = 0;
+
+ uint_fast8_t elfclass = ELFCLASSNONE;
+ uint_fast8_t elfdata = ELFDATANONE;
+ if (likely (auxv != NULL)
+ && likely (auxv_format_probe (auxv, auxv_size, &elfclass, &elfdata)))
+ {
+ GElf_Addr entry = 0;
+ GElf_Addr phdr = 0;
+ GElf_Xword phent = 0;
+ GElf_Xword phnum = 0;
+
+#define READ_AUXV32(ptr) read_4ubyte_unaligned_noncvt (ptr)
+#define READ_AUXV64(ptr) read_8ubyte_unaligned_noncvt (ptr)
+#define AUXV_SCAN(NN, BL) do \
+ { \
+ const Elf##NN##_auxv_t *av = auxv; \
+ for (size_t i = 0; i < auxv_size / sizeof av[0]; ++i) \
+ { \
+ const char *typep = auxv + i * sizeof (Elf##NN##_auxv_t); \
+ typep += offsetof (Elf##NN##_auxv_t, a_type); \
+ uint##NN##_t type = READ_AUXV##NN (typep); \
+ const char *valp = auxv + i * sizeof (Elf##NN##_auxv_t); \
+ valp += offsetof (Elf##NN##_auxv_t, a_un.a_val); \
+ uint##NN##_t val = BL##NN (READ_AUXV##NN (valp)); \
+ if (type == BL##NN (AT_ENTRY)) \
+ entry = val; \
+ else if (type == BL##NN (AT_PHDR)) \
+ phdr = val; \
+ else if (type == BL##NN (AT_PHNUM)) \
+ phnum = val; \
+ else if (type == BL##NN (AT_PHENT)) \
+ phent = val; \
+ else if (type == BL##NN (AT_PAGESZ)) \
+ { \
+ if (val > 1 \
+ && (dwfl->segment_align == 0 \
+ || val < dwfl->segment_align)) \
+ dwfl->segment_align = val; \
+ } \
+ } \
+ } \
+ while (0)
+
+ if (elfclass == ELFCLASS32)
+ {
+ if (elfdata == ELFDATA2MSB)
+ AUXV_SCAN (32, BE);
+ else
+ AUXV_SCAN (32, LE);
+ }
+ else
+ {
+ if (elfdata == ELFDATA2MSB)
+ AUXV_SCAN (64, BE);
+ else
+ AUXV_SCAN (64, LE);
+ }
+
+ /* If we found the phdr dimensions, search phdrs for PT_DYNAMIC. */
+ GElf_Addr dyn_vaddr = 0;
+ GElf_Xword dyn_filesz = 0;
+ GElf_Addr dyn_bias = (GElf_Addr) -1;
+
+ inline bool consider_phdr (GElf_Word type,
+ GElf_Addr vaddr, GElf_Xword filesz)
+ {
+ switch (type)
+ {
+ case PT_PHDR:
+ if (dyn_bias == (GElf_Addr) -1
+ /* Do a sanity check on the putative address. */
+ && ((vaddr & (dwfl->segment_align - 1))
+ == (phdr & (dwfl->segment_align - 1))))
+ {
+ dyn_bias = phdr - vaddr;
+ return dyn_vaddr != 0;
+ }
+ break;
+
+ case PT_DYNAMIC:
+ dyn_vaddr = vaddr;
+ dyn_filesz = filesz;
+ return dyn_bias != (GElf_Addr) -1;
+ }
+
+ return false;
+ }
+
+ if (phdr != 0 && phnum != 0)
+ {
+ Dwfl_Module *phdr_mod;
+ int phdr_segndx = INTUSE(dwfl_addrsegment) (dwfl, phdr, &phdr_mod);
+ Elf_Data in =
+ {
+ .d_type = ELF_T_PHDR,
+ .d_version = EV_CURRENT,
+ .d_size = phnum * phent,
+ .d_buf = NULL
+ };
+ bool in_ok = (*memory_callback) (dwfl, phdr_segndx, &in.d_buf,
+ &in.d_size, phdr, phnum * phent,
+ memory_callback_arg);
+ bool in_from_exec = false;
+ if (! in_ok
+ && dwfl->user_core != NULL
+ && dwfl->user_core->executable_for_core != NULL)
+ {
+ /* AUXV -> PHDR -> DYNAMIC
+ Both AUXV and DYNAMIC should be always present in a core file.
+ PHDR may be missing in core file, try to read it from
+ EXECUTABLE_FOR_CORE to find where DYNAMIC is located in the
+ core file. */
+
+ int fd = open (dwfl->user_core->executable_for_core, O_RDONLY);
+ Elf *elf;
+ Dwfl_Error error = DWFL_E_ERRNO;
+ if (fd != -1)
+ error = __libdw_open_file (&fd, &elf, true, false);
+ if (error != DWFL_E_NOERROR)
+ {
+ __libdwfl_seterrno (error);
+ return false;
+ }
+ GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
+ if (ehdr == NULL)
+ {
+ elf_end (elf);
+ close (fd);
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ return false;
+ }
+ size_t e_phnum;
+ if (elf_getphdrnum (elf, &e_phnum) != 0)
+ {
+ elf_end (elf);
+ close (fd);
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ return false;
+ }
+ if (e_phnum != phnum || ehdr->e_phentsize != phent)
+ {
+ elf_end (elf);
+ close (fd);
+ __libdwfl_seterrno (DWFL_E_BADELF);
+ return false;
+ }
+ off_t off = ehdr->e_phoff;
+ assert (in.d_buf == NULL);
+ /* Note this in the !in_ok path. That means memory_callback
+ failed. But the callback might still have reset the d_size
+ value (to zero). So explicitly set it here again. */
+ in.d_size = phnum * phent;
+ in.d_buf = malloc (in.d_size);
+ if (unlikely (in.d_buf == NULL))
+ {
+ elf_end (elf);
+ close (fd);
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return false;
+ }
+ ssize_t nread = pread_retry (fd, in.d_buf, in.d_size, off);
+ elf_end (elf);
+ close (fd);
+ if (nread != (ssize_t) in.d_size)
+ {
+ free (in.d_buf);
+ __libdwfl_seterrno (DWFL_E_ERRNO);
+ return false;
+ }
+ in_ok = true;
+ in_from_exec = true;
+ }
+ if (in_ok)
+ {
+ if (unlikely (phnum > SIZE_MAX / phent))
+ {
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return false;
+ }
+ size_t nbytes = phnum * phent;
+ void *buf = malloc (nbytes);
+ Elf32_Phdr (*p32)[phnum] = buf;
+ Elf64_Phdr (*p64)[phnum] = buf;
+ if (unlikely (buf == NULL))
+ {
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return false;
+ }
+ Elf_Data out =
+ {
+ .d_type = ELF_T_PHDR,
+ .d_version = EV_CURRENT,
+ .d_size = phnum * phent,
+ .d_buf = buf
+ };
+ in.d_size = out.d_size;
+ if (likely ((elfclass == ELFCLASS32
+ ? elf32_xlatetom : elf64_xlatetom)
+ (&out, &in, elfdata) != NULL))
+ {
+ /* We are looking for PT_DYNAMIC. */
+ if (elfclass == ELFCLASS32)
+ {
+ for (size_t i = 0; i < phnum; ++i)
+ if (consider_phdr ((*p32)[i].p_type,
+ (*p32)[i].p_vaddr,
+ (*p32)[i].p_filesz))
+ break;
+ }
+ else
+ {
+ for (size_t i = 0; i < phnum; ++i)
+ if (consider_phdr ((*p64)[i].p_type,
+ (*p64)[i].p_vaddr,
+ (*p64)[i].p_filesz))
+ break;
+ }
+ }
+
+ if (in_from_exec)
+ free (in.d_buf);
+ else
+ (*memory_callback) (dwfl, -1, &in.d_buf, &in.d_size, 0, 0,
+ memory_callback_arg);
+ free (buf);
+ }
+ else
+ /* We could not read the executable's phdrs from the
+ memory image. If we have a presupplied executable,
+ we can still use the AT_PHDR and AT_ENTRY values to
+ verify it, and to adjust its bias if it's a PIE.
+
+ If there was an ET_EXEC module presupplied that contains
+ the AT_PHDR address, then we only consider that one.
+ We'll either accept it if its phdr location and e_entry
+ make sense or reject it if they don't. If there is no
+ presupplied ET_EXEC, then look for a presupplied module,
+ which might be a PIE (ET_DYN) that needs its bias adjusted. */
+ r_debug_vaddr = ((phdr_mod == NULL
+ || phdr_mod->main.elf == NULL
+ || phdr_mod->e_type != ET_EXEC)
+ ? find_executable (dwfl, phdr, entry,
+ &elfclass, &elfdata,
+ memory_callback,
+ memory_callback_arg)
+ : consider_executable (phdr_mod, phdr, entry,
+ &elfclass, &elfdata,
+ memory_callback,
+ memory_callback_arg));
+ }
+
+ /* If we found PT_DYNAMIC, search it for DT_DEBUG. */
+ if (dyn_filesz != 0)
+ {
+ if (dyn_bias != (GElf_Addr) -1)
+ dyn_vaddr += dyn_bias;
+
+ Elf_Data in =
+ {
+ .d_type = ELF_T_DYN,
+ .d_version = EV_CURRENT,
+ .d_size = dyn_filesz,
+ .d_buf = NULL
+ };
+ int dyn_segndx = dwfl_addrsegment (dwfl, dyn_vaddr, NULL);
+ if ((*memory_callback) (dwfl, dyn_segndx, &in.d_buf, &in.d_size,
+ dyn_vaddr, dyn_filesz, memory_callback_arg))
+ {
+ void *buf = malloc (dyn_filesz);
+ Elf32_Dyn (*d32)[dyn_filesz / sizeof (Elf32_Dyn)] = buf;
+ Elf64_Dyn (*d64)[dyn_filesz / sizeof (Elf64_Dyn)] = buf;
+ if (unlikely (buf == NULL))
+ {
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return false;
+ }
+ Elf_Data out =
+ {
+ .d_type = ELF_T_DYN,
+ .d_version = EV_CURRENT,
+ .d_size = dyn_filesz,
+ .d_buf = buf
+ };
+ in.d_size = out.d_size;
+ if (likely ((elfclass == ELFCLASS32
+ ? elf32_xlatetom : elf64_xlatetom)
+ (&out, &in, elfdata) != NULL))
+ {
+ /* We are looking for DT_DEBUG. */
+ if (elfclass == ELFCLASS32)
+ {
+ size_t n = dyn_filesz / sizeof (Elf32_Dyn);
+ for (size_t i = 0; i < n; ++i)
+ if ((*d32)[i].d_tag == DT_DEBUG)
+ {
+ r_debug_vaddr = (*d32)[i].d_un.d_val;
+ break;
+ }
+ }
+ else
+ {
+ size_t n = dyn_filesz / sizeof (Elf64_Dyn);
+ for (size_t i = 0; i < n; ++i)
+ if ((*d64)[i].d_tag == DT_DEBUG)
+ {
+ r_debug_vaddr = (*d64)[i].d_un.d_val;
+ break;
+ }
+ }
+ }
+
+ (*memory_callback) (dwfl, -1, &in.d_buf, &in.d_size, 0, 0,
+ memory_callback_arg);
+ free (buf);
+ }
+ }
+ }
+ else
+ /* We have to look for a presupplied executable file to determine
+ the vaddr of its dynamic section and DT_DEBUG therein. */
+ r_debug_vaddr = find_executable (dwfl, 0, 0, &elfclass, &elfdata,
+ memory_callback, memory_callback_arg);
+
+ if (r_debug_vaddr == 0)
+ return 0;
+
+ /* For following pointers from struct link_map, we will use an
+ integrated memory access callback that can consult module text
+ elided from the core file. This is necessary when the l_name
+ pointer for the dynamic linker's own entry is a pointer into the
+ executable's .interp section. */
+ struct integrated_memory_callback mcb =
+ {
+ .memory_callback = memory_callback,
+ .memory_callback_arg = memory_callback_arg
+ };
+
+ /* Now we can follow the dynamic linker's library list. */
+ return report_r_debug (elfclass, elfdata, dwfl, r_debug_vaddr,
+ &integrated_memory_callback, &mcb, r_debug_info);
+}
+INTDEF (dwfl_link_map_report)
diff --git a/libdwfl/linux-core-attach.c b/libdwfl/linux-core-attach.c
new file mode 100644
index 0000000..9f05f72
--- /dev/null
+++ b/libdwfl/linux-core-attach.c
@@ -0,0 +1,430 @@
+/* Get Dwarf Frame state for target core file.
+ Copyright (C) 2013, 2014 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include <fcntl.h>
+#include "system.h"
+
+#include "../libdw/memory-access.h"
+
+struct core_arg
+{
+ Elf *core;
+ Elf_Data *note_data;
+ size_t thread_note_offset;
+ Ebl *ebl;
+};
+
+struct thread_arg
+{
+ struct core_arg *core_arg;
+ size_t note_offset;
+};
+
+static bool
+core_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result,
+ void *dwfl_arg)
+{
+ Dwfl_Process *process = dwfl->process;
+ struct core_arg *core_arg = dwfl_arg;
+ Elf *core = core_arg->core;
+ assert (core != NULL);
+ static size_t phnum;
+ if (elf_getphdrnum (core, &phnum) < 0)
+ {
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ return false;
+ }
+ for (size_t cnt = 0; cnt < phnum; ++cnt)
+ {
+ GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem);
+ if (phdr == NULL || phdr->p_type != PT_LOAD)
+ continue;
+ /* Bias is zero here, a core file itself has no bias. */
+ GElf_Addr start = __libdwfl_segment_start (dwfl, phdr->p_vaddr);
+ GElf_Addr end = __libdwfl_segment_end (dwfl,
+ phdr->p_vaddr + phdr->p_memsz);
+ unsigned bytes = ebl_get_elfclass (process->ebl) == ELFCLASS64 ? 8 : 4;
+ if (addr < start || addr + bytes > end)
+ continue;
+ Elf_Data *data;
+ data = elf_getdata_rawchunk (core, phdr->p_offset + addr - start,
+ bytes, ELF_T_ADDR);
+ if (data == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ return false;
+ }
+ assert (data->d_size == bytes);
+ if (bytes == 8)
+ *result = read_8ubyte_unaligned_noncvt (data->d_buf);
+ else
+ *result = read_4ubyte_unaligned_noncvt (data->d_buf);
+ return true;
+ }
+ __libdwfl_seterrno (DWFL_E_ADDR_OUTOFRANGE);
+ return false;
+}
+
+static pid_t
+core_next_thread (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg,
+ void **thread_argp)
+{
+ struct core_arg *core_arg = dwfl_arg;
+ Elf *core = core_arg->core;
+ GElf_Nhdr nhdr;
+ size_t name_offset;
+ size_t desc_offset;
+ Elf_Data *note_data = core_arg->note_data;
+ size_t offset;
+
+ struct thread_arg *thread_arg;
+ if (*thread_argp == NULL)
+ {
+ core_arg->thread_note_offset = 0;
+ thread_arg = malloc (sizeof (*thread_arg));
+ if (thread_arg == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return -1;
+ }
+ thread_arg->core_arg = core_arg;
+ *thread_argp = thread_arg;
+ }
+ else
+ thread_arg = (struct thread_arg *) *thread_argp;
+
+ while (offset = core_arg->thread_note_offset, offset < note_data->d_size
+ && (core_arg->thread_note_offset = gelf_getnote (note_data, offset,
+ &nhdr, &name_offset,
+ &desc_offset)) > 0)
+ {
+ /* Do not check NAME for now, help broken Linux kernels. */
+ const char *name = (nhdr.n_namesz == 0
+ ? "" : note_data->d_buf + name_offset);
+ const char *desc = note_data->d_buf + desc_offset;
+ GElf_Word regs_offset;
+ size_t nregloc;
+ const Ebl_Register_Location *reglocs;
+ size_t nitems;
+ const Ebl_Core_Item *items;
+ if (! ebl_core_note (core_arg->ebl, &nhdr, name,
+ ®s_offset, &nregloc, ®locs, &nitems, &items))
+ {
+ /* This note may be just not recognized, skip it. */
+ continue;
+ }
+ if (nhdr.n_type != NT_PRSTATUS)
+ continue;
+ const Ebl_Core_Item *item;
+ for (item = items; item < items + nitems; item++)
+ if (strcmp (item->name, "pid") == 0)
+ break;
+ if (item == items + nitems)
+ continue;
+ uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
+ val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
+ ? be32toh (val32) : le32toh (val32));
+ pid_t tid = (int32_t) val32;
+ eu_static_assert (sizeof val32 <= sizeof tid);
+ thread_arg->note_offset = offset;
+ return tid;
+ }
+
+ free (thread_arg);
+ return 0;
+}
+
+static bool
+core_set_initial_registers (Dwfl_Thread *thread, void *thread_arg_voidp)
+{
+ struct thread_arg *thread_arg = thread_arg_voidp;
+ struct core_arg *core_arg = thread_arg->core_arg;
+ Elf *core = core_arg->core;
+ size_t offset = thread_arg->note_offset;
+ GElf_Nhdr nhdr;
+ size_t name_offset;
+ size_t desc_offset;
+ Elf_Data *note_data = core_arg->note_data;
+ size_t nregs = ebl_frame_nregs (core_arg->ebl);
+ assert (nregs > 0);
+ assert (offset < note_data->d_size);
+ size_t getnote_err = gelf_getnote (note_data, offset, &nhdr, &name_offset,
+ &desc_offset);
+ /* __libdwfl_attach_state_for_core already verified the note is there. */
+ assert (getnote_err != 0);
+ /* Do not check NAME for now, help broken Linux kernels. */
+ const char *name = (nhdr.n_namesz == 0
+ ? "" : note_data->d_buf + name_offset);
+ const char *desc = note_data->d_buf + desc_offset;
+ GElf_Word regs_offset;
+ size_t nregloc;
+ const Ebl_Register_Location *reglocs;
+ size_t nitems;
+ const Ebl_Core_Item *items;
+ int core_note_err = ebl_core_note (core_arg->ebl, &nhdr, name, ®s_offset,
+ &nregloc, ®locs, &nitems, &items);
+ /* __libdwfl_attach_state_for_core already verified the note is there. */
+ assert (core_note_err != 0);
+ assert (nhdr.n_type == NT_PRSTATUS);
+ const Ebl_Core_Item *item;
+ for (item = items; item < items + nitems; item++)
+ if (strcmp (item->name, "pid") == 0)
+ break;
+ assert (item < items + nitems);
+ pid_t tid;
+ {
+ uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
+ val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
+ ? be32toh (val32) : le32toh (val32));
+ tid = (int32_t) val32;
+ eu_static_assert (sizeof val32 <= sizeof tid);
+ }
+ /* core_next_thread already found this TID there. */
+ assert (tid == INTUSE(dwfl_thread_tid) (thread));
+ for (item = items; item < items + nitems; item++)
+ if (item->pc_register)
+ break;
+ if (item < items + nitems)
+ {
+ Dwarf_Word pc;
+ switch (gelf_getclass (core) == ELFCLASS32 ? 32 : 64)
+ {
+ case 32:;
+ uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
+ val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
+ ? be32toh (val32) : le32toh (val32));
+ /* Do a host width conversion. */
+ pc = val32;
+ break;
+ case 64:;
+ uint64_t val64 = read_8ubyte_unaligned_noncvt (desc + item->offset);
+ val64 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
+ ? be64toh (val64) : le64toh (val64));
+ pc = val64;
+ break;
+ default:
+ abort ();
+ }
+ INTUSE(dwfl_thread_state_register_pc) (thread, pc);
+ }
+ desc += regs_offset;
+ for (size_t regloci = 0; regloci < nregloc; regloci++)
+ {
+ const Ebl_Register_Location *regloc = reglocs + regloci;
+ // Iterate even regs out of NREGS range so that we can find pc_register.
+ if (regloc->bits != 32 && regloc->bits != 64)
+ continue;
+ const char *reg_desc = desc + regloc->offset;
+ for (unsigned regno = regloc->regno;
+ regno < regloc->regno + (regloc->count ?: 1U);
+ regno++)
+ {
+ /* PPC provides DWARF register 65 irrelevant for
+ CFI which clashes with register 108 (LR) we need.
+ LR (108) is provided earlier (in NT_PRSTATUS) than the # 65.
+ FIXME: It depends now on their order in core notes.
+ FIXME: It uses private function. */
+ if (regno < nregs
+ && __libdwfl_frame_reg_get (thread->unwound, regno, NULL))
+ continue;
+ Dwarf_Word val;
+ switch (regloc->bits)
+ {
+ case 32:;
+ uint32_t val32 = read_4ubyte_unaligned_noncvt (reg_desc);
+ reg_desc += sizeof val32;
+ val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
+ ? be32toh (val32) : le32toh (val32));
+ /* Do a host width conversion. */
+ val = val32;
+ break;
+ case 64:;
+ uint64_t val64 = read_8ubyte_unaligned_noncvt (reg_desc);
+ reg_desc += sizeof val64;
+ val64 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
+ ? be64toh (val64) : le64toh (val64));
+ assert (sizeof (*thread->unwound->regs) == sizeof val64);
+ val = val64;
+ break;
+ default:
+ abort ();
+ }
+ /* Registers not valid for CFI are just ignored. */
+ if (regno < nregs)
+ INTUSE(dwfl_thread_state_registers) (thread, regno, 1, &val);
+ if (regloc->pc_register)
+ INTUSE(dwfl_thread_state_register_pc) (thread, val);
+ reg_desc += regloc->pad;
+ }
+ }
+ return true;
+}
+
+static void
+core_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg)
+{
+ struct core_arg *core_arg = dwfl_arg;
+ ebl_closebackend (core_arg->ebl);
+ free (core_arg);
+}
+
+static const Dwfl_Thread_Callbacks core_thread_callbacks =
+{
+ core_next_thread,
+ NULL, /* get_thread */
+ core_memory_read,
+ core_set_initial_registers,
+ core_detach,
+ NULL, /* core_thread_detach */
+};
+
+int
+dwfl_core_file_attach (Dwfl *dwfl, Elf *core)
+{
+ Dwfl_Error err = DWFL_E_NOERROR;
+ Ebl *ebl = ebl_openbackend (core);
+ if (ebl == NULL)
+ {
+ err = DWFL_E_LIBEBL;
+ fail_err:
+ if (dwfl->process == NULL && dwfl->attacherr == DWFL_E_NOERROR)
+ dwfl->attacherr = __libdwfl_canon_error (err);
+ __libdwfl_seterrno (err);
+ return -1;
+ }
+ size_t nregs = ebl_frame_nregs (ebl);
+ if (nregs == 0)
+ {
+ err = DWFL_E_NO_UNWIND;
+ fail:
+ ebl_closebackend (ebl);
+ goto fail_err;
+ }
+ GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (core, &ehdr_mem);
+ if (ehdr == NULL)
+ {
+ err = DWFL_E_LIBELF;
+ goto fail;
+ }
+ if (ehdr->e_type != ET_CORE)
+ {
+ err = DWFL_E_NO_CORE_FILE;
+ goto fail;
+ }
+ size_t phnum;
+ if (elf_getphdrnum (core, &phnum) < 0)
+ {
+ err = DWFL_E_LIBELF;
+ goto fail;
+ }
+ pid_t pid = -1;
+ Elf_Data *note_data = NULL;
+ for (size_t cnt = 0; cnt < phnum; ++cnt)
+ {
+ GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem);
+ if (phdr != NULL && phdr->p_type == PT_NOTE)
+ {
+ note_data = elf_getdata_rawchunk (core, phdr->p_offset,
+ phdr->p_filesz, ELF_T_NHDR);
+ break;
+ }
+ }
+ if (note_data == NULL)
+ {
+ err = DWFL_E_LIBELF;
+ goto fail;
+ }
+ size_t offset = 0;
+ GElf_Nhdr nhdr;
+ size_t name_offset;
+ size_t desc_offset;
+ while (offset < note_data->d_size
+ && (offset = gelf_getnote (note_data, offset,
+ &nhdr, &name_offset, &desc_offset)) > 0)
+ {
+ /* Do not check NAME for now, help broken Linux kernels. */
+ const char *name = (nhdr.n_namesz == 0
+ ? "" : note_data->d_buf + name_offset);
+ const char *desc = note_data->d_buf + desc_offset;
+ GElf_Word regs_offset;
+ size_t nregloc;
+ const Ebl_Register_Location *reglocs;
+ size_t nitems;
+ const Ebl_Core_Item *items;
+ if (! ebl_core_note (ebl, &nhdr, name,
+ ®s_offset, &nregloc, ®locs, &nitems, &items))
+ {
+ /* This note may be just not recognized, skip it. */
+ continue;
+ }
+ if (nhdr.n_type != NT_PRPSINFO)
+ continue;
+ const Ebl_Core_Item *item;
+ for (item = items; item < items + nitems; item++)
+ if (strcmp (item->name, "pid") == 0)
+ break;
+ if (item == items + nitems)
+ continue;
+ uint32_t val32 = read_4ubyte_unaligned_noncvt (desc + item->offset);
+ val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
+ ? be32toh (val32) : le32toh (val32));
+ pid = (int32_t) val32;
+ eu_static_assert (sizeof val32 <= sizeof pid);
+ break;
+ }
+ if (pid == -1)
+ {
+ /* No valid NT_PRPSINFO recognized in this CORE. */
+ err = DWFL_E_BADELF;
+ goto fail;
+ }
+ struct core_arg *core_arg = malloc (sizeof *core_arg);
+ if (core_arg == NULL)
+ {
+ err = DWFL_E_NOMEM;
+ goto fail;
+ }
+ core_arg->core = core;
+ core_arg->note_data = note_data;
+ core_arg->thread_note_offset = 0;
+ core_arg->ebl = ebl;
+ if (! INTUSE(dwfl_attach_state) (dwfl, core, pid, &core_thread_callbacks,
+ core_arg))
+ {
+ free (core_arg);
+ ebl_closebackend (ebl);
+ return -1;
+ }
+ return pid;
+}
+INTDEF (dwfl_core_file_attach)
diff --git a/libdwfl/linux-kernel-modules.c b/libdwfl/linux-kernel-modules.c
new file mode 100644
index 0000000..9d0fef2
--- /dev/null
+++ b/libdwfl/linux-kernel-modules.c
@@ -0,0 +1,979 @@
+/* Standard libdwfl callbacks for debugging the running Linux kernel.
+ Copyright (C) 2005-2011, 2013, 2014, 2015 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+/* In case we have a bad fts we include this before config.h because it
+ can't handle _FILE_OFFSET_BITS.
+ Everything we need here is fine if its declarations just come first.
+ Also, include sys/types.h before fts. On some systems fts.h is not self
+ contained. */
+#ifdef BAD_FTS
+ #include <sys/types.h>
+ #include <fts.h>
+#endif
+
+#include <config.h>
+#include <system.h>
+
+#include "libdwflP.h"
+#include <inttypes.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/utsname.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+/* If fts.h is included before config.h, its indirect inclusions may not
+ give us the right LFS aliases of these functions, so map them manually. */
+#ifdef BAD_FTS
+ #ifdef _FILE_OFFSET_BITS
+ #define open open64
+ #define fopen fopen64
+ #endif
+#else
+ #include <sys/types.h>
+ #include <fts.h>
+#endif
+
+
+#define KERNEL_MODNAME "kernel"
+
+#define MODULEDIRFMT "/lib/modules/%s"
+
+#define KNOTESFILE "/sys/kernel/notes"
+#define MODNOTESFMT "/sys/module/%s/notes"
+#define KSYMSFILE "/proc/kallsyms"
+#define MODULELIST "/proc/modules"
+#define SECADDRDIRFMT "/sys/module/%s/sections/"
+#define MODULE_SECT_NAME_LEN 32 /* Minimum any linux/module.h has had. */
+
+
+static const char *vmlinux_suffixes[] =
+ {
+ ".gz",
+#ifdef USE_BZLIB
+ ".bz2",
+#endif
+#ifdef USE_LZMA
+ ".xz",
+#endif
+ };
+
+/* Try to open the given file as it is or under the debuginfo directory. */
+static int
+try_kernel_name (Dwfl *dwfl, char **fname, bool try_debug)
+{
+ if (*fname == NULL)
+ return -1;
+
+ /* Don't bother trying *FNAME itself here if the path will cause it to be
+ tried because we give its own basename as DEBUGLINK_FILE. */
+ int fd = ((((dwfl->callbacks->debuginfo_path
+ ? *dwfl->callbacks->debuginfo_path : NULL)
+ ?: DEFAULT_DEBUGINFO_PATH)[0] == ':') ? -1
+ : TEMP_FAILURE_RETRY (open (*fname, O_RDONLY)));
+
+ if (fd < 0)
+ {
+ Dwfl_Module fakemod = { .dwfl = dwfl };
+
+ if (try_debug)
+ /* Passing NULL for DEBUGLINK_FILE searches for both the basenamer
+ "vmlinux" and the default of basename + ".debug", to look for
+ "vmlinux.debug" files. */
+ fd = INTUSE(dwfl_standard_find_debuginfo) (&fakemod, NULL, NULL, 0,
+ *fname, NULL, 0,
+ &fakemod.debug.name);
+ else
+ /* Try the file's unadorned basename as DEBUGLINK_FILE,
+ to look only for "vmlinux" files. */
+ fd = INTUSE(dwfl_standard_find_debuginfo) (&fakemod, NULL, NULL, 0,
+ *fname, basename (*fname),
+ 0, &fakemod.debug.name);
+
+ if (fakemod.debug.name != NULL)
+ {
+ free (*fname);
+ *fname = fakemod.debug.name;
+ }
+ }
+
+ if (fd < 0)
+ for (size_t i = 0;
+ i < sizeof vmlinux_suffixes / sizeof vmlinux_suffixes[0];
+ ++i)
+ {
+ char *zname;
+ if (asprintf (&zname, "%s%s", *fname, vmlinux_suffixes[i]) > 0)
+ {
+ fd = TEMP_FAILURE_RETRY (open (zname, O_RDONLY));
+ if (fd < 0)
+ free (zname);
+ else
+ {
+ free (*fname);
+ *fname = zname;
+ }
+ }
+ }
+
+ if (fd < 0)
+ {
+ free (*fname);
+ *fname = NULL;
+ }
+
+ return fd;
+}
+
+static inline const char *
+kernel_release (void)
+{
+#ifdef __linux__
+ /* Cache the `uname -r` string we'll use. */
+ static struct utsname utsname;
+ if (utsname.release[0] == '\0' && uname (&utsname) != 0)
+ return NULL;
+ return utsname.release;
+#else
+ /* Used for finding the running linux kernel, which isn't supported
+ on non-linux kernel systems. */
+ errno = ENOTSUP;
+ return NULL;
+#endif
+}
+
+static int
+find_kernel_elf (Dwfl *dwfl, const char *release, char **fname)
+{
+ if ((release[0] == '/'
+ ? asprintf (fname, "%s/vmlinux", release)
+ : asprintf (fname, "/boot/vmlinux-%s", release)) < 0)
+ return -1;
+
+ int fd = try_kernel_name (dwfl, fname, true);
+ if (fd < 0 && release[0] != '/')
+ {
+ free (*fname);
+ if (asprintf (fname, MODULEDIRFMT "/vmlinux", release) < 0)
+ return -1;
+ fd = try_kernel_name (dwfl, fname, true);
+ }
+
+ return fd;
+}
+
+static int
+get_release (Dwfl *dwfl, const char **release)
+{
+ if (dwfl == NULL)
+ return -1;
+
+ const char *release_string = release == NULL ? NULL : *release;
+ if (release_string == NULL)
+ {
+ release_string = kernel_release ();
+ if (release_string == NULL)
+ return errno;
+ if (release != NULL)
+ *release = release_string;
+ }
+
+ return 0;
+}
+
+static int
+report_kernel (Dwfl *dwfl, const char **release,
+ int (*predicate) (const char *module, const char *file))
+{
+ int result = get_release (dwfl, release);
+ if (unlikely (result != 0))
+ return result;
+
+ char *fname;
+ int fd = find_kernel_elf (dwfl, *release, &fname);
+
+ if (fd < 0)
+ result = ((predicate != NULL && !(*predicate) (KERNEL_MODNAME, NULL))
+ ? 0 : errno ?: ENOENT);
+ else
+ {
+ bool report = true;
+
+ if (predicate != NULL)
+ {
+ /* Let the predicate decide whether to use this one. */
+ int want = (*predicate) (KERNEL_MODNAME, fname);
+ if (want < 0)
+ result = errno;
+ report = want > 0;
+ }
+
+ if (report)
+ {
+ /* Note that on some architectures (e.g. x86_64) the vmlinux
+ is ET_EXEC, while on others (e.g. ppc64) it is ET_DYN.
+ In both cases the phdr p_vaddr load address will be non-zero.
+ We want the image to be placed as if it was ET_DYN, so
+ pass true for add_p_vaddr which will do the right thing
+ (in combination with a zero base) in either case. */
+ Dwfl_Module *mod = INTUSE(dwfl_report_elf) (dwfl, KERNEL_MODNAME,
+ fname, fd, 0, true);
+ if (mod == NULL)
+ result = -1;
+ else
+ /* The kernel is ET_EXEC, but always treat it as relocatable. */
+ mod->e_type = ET_DYN;
+ }
+
+ free (fname);
+
+ if (!report || result < 0)
+ close (fd);
+ }
+
+ return result;
+}
+
+/* Look for a kernel debug archive. If we find one, report all its modules.
+ If not, return ENOENT. */
+static int
+report_kernel_archive (Dwfl *dwfl, const char **release,
+ int (*predicate) (const char *module, const char *file))
+{
+ int result = get_release (dwfl, release);
+ if (unlikely (result != 0))
+ return result;
+
+ char *archive;
+ int res = (((*release)[0] == '/')
+ ? asprintf (&archive, "%s/debug.a", *release)
+ : asprintf (&archive, MODULEDIRFMT "/debug.a", *release));
+ if (unlikely (res < 0))
+ return ENOMEM;
+
+ int fd = try_kernel_name (dwfl, &archive, false);
+ if (fd < 0)
+ result = errno ?: ENOENT;
+ else
+ {
+ /* We have the archive file open! */
+ Dwfl_Module *last = __libdwfl_report_offline (dwfl, NULL, archive, fd,
+ true, predicate);
+ if (unlikely (last == NULL))
+ result = -1;
+ else
+ {
+ /* Find the kernel and move it to the head of the list. */
+ Dwfl_Module **tailp = &dwfl->modulelist, **prevp = tailp;
+ for (Dwfl_Module *m = *prevp; m != NULL; m = *(prevp = &m->next))
+ if (!m->gc && m->e_type != ET_REL && !strcmp (m->name, "kernel"))
+ {
+ *prevp = m->next;
+ m->next = *tailp;
+ *tailp = m;
+ break;
+ }
+ }
+ }
+
+ free (archive);
+ return result;
+}
+
+static size_t
+check_suffix (const FTSENT *f, size_t namelen)
+{
+#define TRY(sfx) \
+ if ((namelen ? f->fts_namelen == namelen + sizeof sfx - 1 \
+ : f->fts_namelen >= sizeof sfx) \
+ && !memcmp (f->fts_name + f->fts_namelen - (sizeof sfx - 1), \
+ sfx, sizeof sfx)) \
+ return sizeof sfx - 1
+
+ TRY (".ko");
+ TRY (".ko.gz");
+#if USE_BZLIB
+ TRY (".ko.bz2");
+#endif
+#if USE_LZMA
+ TRY (".ko.xz");
+#endif
+
+ return 0;
+
+#undef TRY
+}
+
+/* Report a kernel and all its modules found on disk, for offline use.
+ If RELEASE starts with '/', it names a directory to look in;
+ if not, it names a directory to find under /lib/modules/;
+ if null, /lib/modules/`uname -r` is used.
+ Returns zero on success, -1 if dwfl_report_module failed,
+ or an errno code if finding the files on disk failed. */
+int
+dwfl_linux_kernel_report_offline (Dwfl *dwfl, const char *release,
+ int (*predicate) (const char *module,
+ const char *file))
+{
+ int result = report_kernel_archive (dwfl, &release, predicate);
+ if (result != ENOENT)
+ return result;
+
+ /* First report the kernel. */
+ result = report_kernel (dwfl, &release, predicate);
+ if (result == 0)
+ {
+ /* Do "find /lib/modules/RELEASE -name *.ko". */
+
+ char *modulesdir[] = { NULL, NULL };
+ if (release[0] == '/')
+ modulesdir[0] = (char *) release;
+ else
+ {
+ if (asprintf (&modulesdir[0], MODULEDIRFMT, release) < 0)
+ return errno;
+ }
+
+ FTS *fts = fts_open (modulesdir, FTS_NOSTAT | FTS_LOGICAL, NULL);
+ if (modulesdir[0] == (char *) release)
+ modulesdir[0] = NULL;
+ if (fts == NULL)
+ {
+ free (modulesdir[0]);
+ return errno;
+ }
+
+ FTSENT *f;
+ while ((f = fts_read (fts)) != NULL)
+ {
+ /* Skip a "source" subtree, which tends to be large.
+ This insane hard-coding of names is what depmod does too. */
+ if (f->fts_namelen == sizeof "source" - 1
+ && !strcmp (f->fts_name, "source"))
+ {
+ fts_set (fts, f, FTS_SKIP);
+ continue;
+ }
+
+ switch (f->fts_info)
+ {
+ case FTS_F:
+ case FTS_SL:
+ case FTS_NSOK:;
+ /* See if this file name matches "*.ko". */
+ const size_t suffix = check_suffix (f, 0);
+ if (suffix)
+ {
+ /* We have a .ko file to report. Following the algorithm
+ by which the kernel makefiles set KBUILD_MODNAME, we
+ replace all ',' or '-' with '_' in the file name and
+ call that the module name. Modules could well be
+ built using different embedded names than their file
+ names. To handle that, we would have to look at the
+ __this_module.name contents in the module's text. */
+
+ char *name = strndup (f->fts_name, f->fts_namelen - suffix);
+ if (unlikely (name == NULL))
+ {
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ result = -1;
+ break;
+ }
+ for (size_t i = 0; i < f->fts_namelen - suffix; ++i)
+ if (name[i] == '-' || name[i] == ',')
+ name[i] = '_';
+
+ if (predicate != NULL)
+ {
+ /* Let the predicate decide whether to use this one. */
+ int want = (*predicate) (name, f->fts_path);
+ if (want < 0)
+ {
+ result = -1;
+ free (name);
+ break;
+ }
+ if (!want)
+ {
+ free (name);
+ continue;
+ }
+ }
+
+ if (dwfl_report_offline (dwfl, name, f->fts_path, -1) == NULL)
+ {
+ free (name);
+ result = -1;
+ break;
+ }
+ free (name);
+ }
+ continue;
+
+ case FTS_ERR:
+ case FTS_DNR:
+ case FTS_NS:
+ result = f->fts_errno;
+ break;
+
+ case FTS_SLNONE:
+ default:
+ continue;
+ }
+
+ /* We only get here in error cases. */
+ break;
+ }
+ fts_close (fts);
+ free (modulesdir[0]);
+ }
+
+ return result;
+}
+INTDEF (dwfl_linux_kernel_report_offline)
+
+
+/* State of read_address used by intuit_kernel_bounds. */
+struct read_address_state {
+ FILE *f;
+ char *line;
+ size_t linesz;
+ size_t n;
+ char *p;
+ const char *type;
+};
+
+static inline bool
+read_address (struct read_address_state *state, Dwarf_Addr *addr)
+{
+ if ((state->n = getline (&state->line, &state->linesz, state->f)) < 1 ||
+ state->line[state->n - 2] == ']')
+ return false;
+ *addr = strtoull (state->line, &state->p, 16);
+ state->p += strspn (state->p, " \t");
+ state->type = strsep (&state->p, " \t\n");
+ if (state->type == NULL)
+ return false;
+ return state->p != NULL && state->p != state->line;
+}
+
+
+/* Grovel around to guess the bounds of the runtime kernel image. */
+static int
+intuit_kernel_bounds (Dwarf_Addr *start, Dwarf_Addr *end, Dwarf_Addr *notes)
+{
+ struct read_address_state state = { NULL, NULL, 0, 0, NULL, NULL };
+
+ state.f = fopen (KSYMSFILE, "r");
+ if (state.f == NULL)
+ return errno;
+
+ (void) __fsetlocking (state.f, FSETLOCKING_BYCALLER);
+
+ *notes = 0;
+
+ int result;
+ do
+ result = read_address (&state, start) ? 0 : -1;
+ while (result == 0 && strchr ("TtRr", *state.type) == NULL);
+
+ if (result == 0)
+ {
+ *end = *start;
+ while (read_address (&state, end))
+ if (*notes == 0 && !strcmp (state.p, "__start_notes\n"))
+ *notes = *end;
+
+ Dwarf_Addr round_kernel = sysconf (_SC_PAGESIZE);
+ *start &= -(Dwarf_Addr) round_kernel;
+ *end += round_kernel - 1;
+ *end &= -(Dwarf_Addr) round_kernel;
+ if (*start >= *end || *end - *start < round_kernel)
+ result = -1;
+ }
+ free (state.line);
+
+ if (result == -1)
+ result = ferror_unlocked (state.f) ? errno : ENOEXEC;
+
+ fclose (state.f);
+
+ return result;
+}
+
+
+/* Look for a build ID note in NOTESFILE and associate the ID with MOD. */
+static int
+check_notes (Dwfl_Module *mod, const char *notesfile,
+ Dwarf_Addr vaddr, const char *secname)
+{
+ int fd = open (notesfile, O_RDONLY);
+ if (fd < 0)
+ return 1;
+
+ assert (sizeof (Elf32_Nhdr) == sizeof (GElf_Nhdr));
+ assert (sizeof (Elf64_Nhdr) == sizeof (GElf_Nhdr));
+ union
+ {
+ GElf_Nhdr nhdr;
+ unsigned char data[8192];
+ } buf;
+
+ ssize_t n = read (fd, buf.data, sizeof buf);
+ close (fd);
+
+ if (n <= 0)
+ return 1;
+
+ unsigned char *p = buf.data;
+ while (p < &buf.data[n])
+ {
+ /* No translation required since we are reading the native kernel. */
+ GElf_Nhdr *nhdr = (void *) p;
+ p += sizeof *nhdr;
+ unsigned char *name = p;
+ p += (nhdr->n_namesz + 3) & -4U;
+ unsigned char *bits = p;
+ p += (nhdr->n_descsz + 3) & -4U;
+
+ if (p <= &buf.data[n]
+ && nhdr->n_type == NT_GNU_BUILD_ID
+ && nhdr->n_namesz == sizeof "GNU"
+ && !memcmp (name, "GNU", sizeof "GNU"))
+ {
+ /* Found it. For a module we must figure out its VADDR now. */
+
+ if (secname != NULL
+ && (INTUSE(dwfl_linux_kernel_module_section_address)
+ (mod, NULL, mod->name, 0, secname, 0, NULL, &vaddr) != 0
+ || vaddr == (GElf_Addr) -1l))
+ vaddr = 0;
+
+ if (vaddr != 0)
+ vaddr += bits - buf.data;
+ return INTUSE(dwfl_module_report_build_id) (mod, bits,
+ nhdr->n_descsz, vaddr);
+ }
+ }
+
+ return 0;
+}
+
+/* Look for a build ID for the kernel. */
+static int
+check_kernel_notes (Dwfl_Module *kernelmod, GElf_Addr vaddr)
+{
+ return check_notes (kernelmod, KNOTESFILE, vaddr, NULL) < 0 ? -1 : 0;
+}
+
+/* Look for a build ID for a loaded kernel module. */
+static int
+check_module_notes (Dwfl_Module *mod)
+{
+ char *dirs[2] = { NULL, NULL };
+ if (asprintf (&dirs[0], MODNOTESFMT, mod->name) < 0)
+ return ENOMEM;
+
+ FTS *fts = fts_open (dirs, FTS_NOSTAT | FTS_LOGICAL, NULL);
+ if (fts == NULL)
+ {
+ free (dirs[0]);
+ return 0;
+ }
+
+ int result = 0;
+ FTSENT *f;
+ while ((f = fts_read (fts)) != NULL)
+ {
+ switch (f->fts_info)
+ {
+ case FTS_F:
+ case FTS_SL:
+ case FTS_NSOK:
+ result = check_notes (mod, f->fts_accpath, 0, f->fts_name);
+ if (result > 0) /* Nothing found. */
+ {
+ result = 0;
+ continue;
+ }
+ break;
+
+ case FTS_ERR:
+ case FTS_DNR:
+ result = f->fts_errno;
+ break;
+
+ case FTS_NS:
+ case FTS_SLNONE:
+ default:
+ continue;
+ }
+
+ /* We only get here when finished or in error cases. */
+ break;
+ }
+ fts_close (fts);
+ free (dirs[0]);
+
+ return result;
+}
+
+int
+dwfl_linux_kernel_report_kernel (Dwfl *dwfl)
+{
+ Dwarf_Addr start = 0;
+ Dwarf_Addr end = 0;
+
+ #define report() \
+ (INTUSE(dwfl_report_module) (dwfl, KERNEL_MODNAME, start, end))
+
+ /* This is a bit of a kludge. If we already reported the kernel,
+ don't bother figuring it out again--it never changes. */
+ for (Dwfl_Module *m = dwfl->modulelist; m != NULL; m = m->next)
+ if (!strcmp (m->name, KERNEL_MODNAME))
+ {
+ start = m->low_addr;
+ end = m->high_addr;
+ return report () == NULL ? -1 : 0;
+ }
+
+ /* Try to figure out the bounds of the kernel image without
+ looking for any vmlinux file. */
+ Dwarf_Addr notes;
+ /* The compiler cannot deduce that if intuit_kernel_bounds returns
+ zero NOTES will be initialized. Fake the initialization. */
+ asm ("" : "=m" (notes));
+ int result = intuit_kernel_bounds (&start, &end, ¬es);
+ if (result == 0)
+ {
+ Dwfl_Module *mod = report ();
+ return unlikely (mod == NULL) ? -1 : check_kernel_notes (mod, notes);
+ }
+ if (result != ENOENT)
+ return result;
+
+ /* Find the ELF file for the running kernel and dwfl_report_elf it. */
+ return report_kernel (dwfl, NULL, NULL);
+}
+INTDEF (dwfl_linux_kernel_report_kernel)
+
+
+static inline bool
+subst_name (char from, char to,
+ const char * const module_name,
+ char * const alternate_name,
+ const size_t namelen)
+{
+ const char *n = memchr (module_name, from, namelen);
+ if (n == NULL)
+ return false;
+ char *a = mempcpy (alternate_name, module_name, n - module_name);
+ *a++ = to;
+ ++n;
+ const char *p;
+ while ((p = memchr (n, from, namelen - (n - module_name))) != NULL)
+ {
+ a = mempcpy (a, n, p - n);
+ *a++ = to;
+ n = p + 1;
+ }
+ memcpy (a, n, namelen - (n - module_name) + 1);
+ return true;
+}
+
+/* Dwfl_Callbacks.find_elf for the running Linux kernel and its modules. */
+
+int
+dwfl_linux_kernel_find_elf (Dwfl_Module *mod,
+ void **userdata __attribute__ ((unused)),
+ const char *module_name,
+ Dwarf_Addr base __attribute__ ((unused)),
+ char **file_name, Elf **elfp)
+{
+ if (mod->build_id_len > 0)
+ {
+ int fd = INTUSE(dwfl_build_id_find_elf) (mod, NULL, NULL, 0,
+ file_name, elfp);
+ if (fd >= 0 || mod->main.elf != NULL || errno != 0)
+ return fd;
+ }
+
+ const char *release = kernel_release ();
+ if (release == NULL)
+ return errno;
+
+ if (!strcmp (module_name, KERNEL_MODNAME))
+ return find_kernel_elf (mod->dwfl, release, file_name);
+
+ /* Do "find /lib/modules/`uname -r` -name MODULE_NAME.ko". */
+
+ char *modulesdir[] = { NULL, NULL };
+ if (asprintf (&modulesdir[0], MODULEDIRFMT, release) < 0)
+ return -1;
+
+ FTS *fts = fts_open (modulesdir, FTS_NOSTAT | FTS_LOGICAL, NULL);
+ if (fts == NULL)
+ {
+ free (modulesdir[0]);
+ return -1;
+ }
+
+ size_t namelen = strlen (module_name);
+
+ /* This is a kludge. There is no actual necessary relationship between
+ the name of the .ko file installed and the module name the kernel
+ knows it by when it's loaded. The kernel's only idea of the module
+ name comes from the name embedded in the object's magic
+ .gnu.linkonce.this_module section.
+
+ In practice, these module names match the .ko file names except for
+ some using '_' and some using '-'. So our cheap kludge is to look for
+ two files when either a '_' or '-' appears in a module name, one using
+ only '_' and one only using '-'. */
+
+ char *alternate_name = malloc (namelen + 1);
+ if (unlikely (alternate_name == NULL))
+ {
+ free (modulesdir[0]);
+ return ENOMEM;
+ }
+ if (!subst_name ('-', '_', module_name, alternate_name, namelen) &&
+ !subst_name ('_', '-', module_name, alternate_name, namelen))
+ alternate_name[0] = '\0';
+
+ FTSENT *f;
+ int error = ENOENT;
+ while ((f = fts_read (fts)) != NULL)
+ {
+ /* Skip a "source" subtree, which tends to be large.
+ This insane hard-coding of names is what depmod does too. */
+ if (f->fts_namelen == sizeof "source" - 1
+ && !strcmp (f->fts_name, "source"))
+ {
+ fts_set (fts, f, FTS_SKIP);
+ continue;
+ }
+
+ error = ENOENT;
+ switch (f->fts_info)
+ {
+ case FTS_F:
+ case FTS_SL:
+ case FTS_NSOK:
+ /* See if this file name is "MODULE_NAME.ko". */
+ if (check_suffix (f, namelen)
+ && (!memcmp (f->fts_name, module_name, namelen)
+ || !memcmp (f->fts_name, alternate_name, namelen)))
+ {
+ int fd = open (f->fts_accpath, O_RDONLY);
+ *file_name = strdup (f->fts_path);
+ fts_close (fts);
+ free (modulesdir[0]);
+ free (alternate_name);
+ if (fd < 0)
+ free (*file_name);
+ else if (*file_name == NULL)
+ {
+ close (fd);
+ fd = -1;
+ }
+ return fd;
+ }
+ break;
+
+ case FTS_ERR:
+ case FTS_DNR:
+ case FTS_NS:
+ error = f->fts_errno;
+ break;
+
+ case FTS_SLNONE:
+ default:
+ break;
+ }
+ }
+
+ fts_close (fts);
+ free (modulesdir[0]);
+ free (alternate_name);
+ errno = error;
+ return -1;
+}
+INTDEF (dwfl_linux_kernel_find_elf)
+
+
+/* Dwfl_Callbacks.section_address for kernel modules in the running Linux.
+ We read the information from /sys/module directly. */
+
+int
+dwfl_linux_kernel_module_section_address
+(Dwfl_Module *mod __attribute__ ((unused)),
+ void **userdata __attribute__ ((unused)),
+ const char *modname, Dwarf_Addr base __attribute__ ((unused)),
+ const char *secname, Elf32_Word shndx __attribute__ ((unused)),
+ const GElf_Shdr *shdr __attribute__ ((unused)),
+ Dwarf_Addr *addr)
+{
+ char *sysfile;
+ if (asprintf (&sysfile, SECADDRDIRFMT "%s", modname, secname) < 0)
+ return DWARF_CB_ABORT;
+
+ FILE *f = fopen (sysfile, "r");
+ free (sysfile);
+
+ if (f == NULL)
+ {
+ if (errno == ENOENT)
+ {
+ /* The .modinfo and .data.percpu sections are never kept
+ loaded in the kernel. If the kernel was compiled without
+ CONFIG_MODULE_UNLOAD, the .exit.* sections are not
+ actually loaded at all.
+
+ Setting *ADDR to -1 tells the caller this section is
+ actually absent from memory. */
+
+ if (!strcmp (secname, ".modinfo")
+ || !strcmp (secname, ".data.percpu")
+ || !strncmp (secname, ".exit", 5))
+ {
+ *addr = (Dwarf_Addr) -1l;
+ return DWARF_CB_OK;
+ }
+
+ /* The goofy PPC64 module_frob_arch_sections function tweaks
+ the section names as a way to control other kernel code's
+ behavior, and this cruft leaks out into the /sys information.
+ The file name for ".init*" may actually look like "_init*". */
+
+ const bool is_init = !strncmp (secname, ".init", 5);
+ if (is_init)
+ {
+ if (asprintf (&sysfile, SECADDRDIRFMT "_%s",
+ modname, &secname[1]) < 0)
+ return ENOMEM;
+ f = fopen (sysfile, "r");
+ free (sysfile);
+ if (f != NULL)
+ goto ok;
+ }
+
+ /* The kernel truncates section names to MODULE_SECT_NAME_LEN - 1.
+ In case that size increases in the future, look for longer
+ truncated names first. */
+ size_t namelen = strlen (secname);
+ if (namelen >= MODULE_SECT_NAME_LEN)
+ {
+ int len = asprintf (&sysfile, SECADDRDIRFMT "%s",
+ modname, secname);
+ if (len < 0)
+ return DWARF_CB_ABORT;
+ char *end = sysfile + len;
+ do
+ {
+ *--end = '\0';
+ f = fopen (sysfile, "r");
+ if (is_init && f == NULL && errno == ENOENT)
+ {
+ sysfile[len - namelen] = '_';
+ f = fopen (sysfile, "r");
+ sysfile[len - namelen] = '.';
+ }
+ }
+ while (f == NULL && errno == ENOENT
+ && end - &sysfile[len - namelen] >= MODULE_SECT_NAME_LEN);
+ free (sysfile);
+
+ if (f != NULL)
+ goto ok;
+ }
+ }
+
+ return DWARF_CB_ABORT;
+ }
+
+ ok:
+ (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
+
+ int result = (fscanf (f, "%" PRIx64 "\n", addr) == 1 ? 0
+ : ferror_unlocked (f) ? errno : ENOEXEC);
+ fclose (f);
+
+ if (result == 0)
+ return DWARF_CB_OK;
+
+ errno = result;
+ return DWARF_CB_ABORT;
+}
+INTDEF (dwfl_linux_kernel_module_section_address)
+
+int
+dwfl_linux_kernel_report_modules (Dwfl *dwfl)
+{
+ FILE *f = fopen (MODULELIST, "r");
+ if (f == NULL)
+ return errno;
+
+ (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
+
+ int result = 0;
+ Dwarf_Addr modaddr;
+ unsigned long int modsz;
+ char modname[128];
+ char *line = NULL;
+ size_t linesz = 0;
+ /* We can't just use fscanf here because it's not easy to distinguish \n
+ from other whitespace so as to take the optional word following the
+ address but always stop at the end of the line. */
+ while (getline (&line, &linesz, f) > 0
+ && sscanf (line, "%128s %lu %*s %*s %*s %" PRIx64 " %*s\n",
+ modname, &modsz, &modaddr) == 3)
+ {
+ Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, modname,
+ modaddr, modaddr + modsz);
+ if (mod == NULL)
+ {
+ result = -1;
+ break;
+ }
+
+ result = check_module_notes (mod);
+ }
+ free (line);
+
+ if (result == 0)
+ result = ferror_unlocked (f) ? errno : feof_unlocked (f) ? 0 : ENOEXEC;
+
+ fclose (f);
+
+ return result;
+}
+INTDEF (dwfl_linux_kernel_report_modules)
diff --git a/libdwfl/linux-pid-attach.c b/libdwfl/linux-pid-attach.c
new file mode 100644
index 0000000..e6a5c41
--- /dev/null
+++ b/libdwfl/linux-pid-attach.c
@@ -0,0 +1,444 @@
+/* Get Dwarf Frame state for target live PID process.
+ Copyright (C) 2013, 2014, 2015 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libelfP.h"
+#include "libdwflP.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <unistd.h>
+
+#ifdef __linux__
+
+#include <sys/ptrace.h>
+#include <sys/syscall.h>
+
+static bool
+linux_proc_pid_is_stopped (pid_t pid)
+{
+ char buffer[64];
+ FILE *procfile;
+ bool retval, have_state;
+
+ snprintf (buffer, sizeof (buffer), "/proc/%ld/status", (long) pid);
+ procfile = fopen (buffer, "r");
+ if (procfile == NULL)
+ return false;
+
+ have_state = false;
+ while (fgets (buffer, sizeof (buffer), procfile) != NULL)
+ if (strncmp (buffer, "State:", 6) == 0)
+ {
+ have_state = true;
+ break;
+ }
+ retval = (have_state && strstr (buffer, "T (stopped)") != NULL);
+ fclose (procfile);
+ return retval;
+}
+
+bool
+internal_function
+__libdwfl_ptrace_attach (pid_t tid, bool *tid_was_stoppedp)
+{
+ if (ptrace (PTRACE_ATTACH, tid, NULL, NULL) != 0)
+ {
+ __libdwfl_seterrno (DWFL_E_ERRNO);
+ return false;
+ }
+ *tid_was_stoppedp = linux_proc_pid_is_stopped (tid);
+ if (*tid_was_stoppedp)
+ {
+ /* Make sure there is a SIGSTOP signal pending even when the process is
+ already State: T (stopped). Older kernels might fail to generate
+ a SIGSTOP notification in that case in response to our PTRACE_ATTACH
+ above. Which would make the waitpid below wait forever. So emulate
+ it. Since there can only be one SIGSTOP notification pending this is
+ safe. See also gdb/linux-nat.c linux_nat_post_attach_wait. */
+ syscall (__NR_tkill, tid, SIGSTOP);
+ ptrace (PTRACE_CONT, tid, NULL, NULL);
+ }
+ for (;;)
+ {
+ int status;
+ if (waitpid (tid, &status, __WALL) != tid || !WIFSTOPPED (status))
+ {
+ int saved_errno = errno;
+ ptrace (PTRACE_DETACH, tid, NULL, NULL);
+ errno = saved_errno;
+ __libdwfl_seterrno (DWFL_E_ERRNO);
+ return false;
+ }
+ if (WSTOPSIG (status) == SIGSTOP)
+ break;
+ if (ptrace (PTRACE_CONT, tid, NULL,
+ (void *) (uintptr_t) WSTOPSIG (status)) != 0)
+ {
+ int saved_errno = errno;
+ ptrace (PTRACE_DETACH, tid, NULL, NULL);
+ errno = saved_errno;
+ __libdwfl_seterrno (DWFL_E_ERRNO);
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool
+pid_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result, void *arg)
+{
+ struct __libdwfl_pid_arg *pid_arg = arg;
+ pid_t tid = pid_arg->tid_attached;
+ assert (tid > 0);
+ Dwfl_Process *process = dwfl->process;
+ if (ebl_get_elfclass (process->ebl) == ELFCLASS64)
+ {
+#if SIZEOF_LONG == 8
+ errno = 0;
+ *result = ptrace (PTRACE_PEEKDATA, tid, (void *) (uintptr_t) addr, NULL);
+ return errno == 0;
+#else /* SIZEOF_LONG != 8 */
+ /* This should not happen. */
+ return false;
+#endif /* SIZEOF_LONG != 8 */
+ }
+#if SIZEOF_LONG == 8
+ /* We do not care about reads unaliged to 4 bytes boundary.
+ But 0x...ffc read of 8 bytes could overrun a page. */
+ bool lowered = (addr & 4) != 0;
+ if (lowered)
+ addr -= 4;
+#endif /* SIZEOF_LONG == 8 */
+ errno = 0;
+ *result = ptrace (PTRACE_PEEKDATA, tid, (void *) (uintptr_t) addr, NULL);
+ if (errno != 0)
+ return false;
+#if SIZEOF_LONG == 8
+# if BYTE_ORDER == BIG_ENDIAN
+ if (! lowered)
+ *result >>= 32;
+# else
+ if (lowered)
+ *result >>= 32;
+# endif
+#endif /* SIZEOF_LONG == 8 */
+ *result &= 0xffffffff;
+ return true;
+}
+
+static pid_t
+pid_next_thread (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg,
+ void **thread_argp)
+{
+ struct __libdwfl_pid_arg *pid_arg = dwfl_arg;
+ struct dirent *dirent;
+ /* Start fresh on first traversal. */
+ if (*thread_argp == NULL)
+ rewinddir (pid_arg->dir);
+ do
+ {
+ errno = 0;
+ dirent = readdir (pid_arg->dir);
+ if (dirent == NULL)
+ {
+ if (errno != 0)
+ {
+ __libdwfl_seterrno (DWFL_E_ERRNO);
+ return -1;
+ }
+ return 0;
+ }
+ }
+ while (strcmp (dirent->d_name, ".") == 0
+ || strcmp (dirent->d_name, "..") == 0);
+ char *end;
+ errno = 0;
+ long tidl = strtol (dirent->d_name, &end, 10);
+ if (errno != 0)
+ {
+ __libdwfl_seterrno (DWFL_E_ERRNO);
+ return -1;
+ }
+ pid_t tid = tidl;
+ if (tidl <= 0 || (end && *end) || tid != tidl)
+ {
+ __libdwfl_seterrno (DWFL_E_PARSE_PROC);
+ return -1;
+ }
+ *thread_argp = dwfl_arg;
+ return tid;
+}
+
+/* Just checks that the thread id exists. */
+static bool
+pid_getthread (Dwfl *dwfl __attribute__ ((unused)), pid_t tid,
+ void *dwfl_arg, void **thread_argp)
+{
+ *thread_argp = dwfl_arg;
+ if (kill (tid, 0) < 0)
+ {
+ __libdwfl_seterrno (DWFL_E_ERRNO);
+ return false;
+ }
+ return true;
+}
+
+/* Implement the ebl_set_initial_registers_tid setfunc callback. */
+
+static bool
+pid_thread_state_registers_cb (int firstreg, unsigned nregs,
+ const Dwarf_Word *regs, void *arg)
+{
+ Dwfl_Thread *thread = (Dwfl_Thread *) arg;
+ if (firstreg < 0)
+ {
+ assert (firstreg == -1);
+ assert (nregs == 1);
+ INTUSE(dwfl_thread_state_register_pc) (thread, *regs);
+ return true;
+ }
+ assert (nregs > 0);
+ return INTUSE(dwfl_thread_state_registers) (thread, firstreg, nregs, regs);
+}
+
+static bool
+pid_set_initial_registers (Dwfl_Thread *thread, void *thread_arg)
+{
+ struct __libdwfl_pid_arg *pid_arg = thread_arg;
+ assert (pid_arg->tid_attached == 0);
+ pid_t tid = INTUSE(dwfl_thread_tid) (thread);
+ if (! pid_arg->assume_ptrace_stopped
+ && ! __libdwfl_ptrace_attach (tid, &pid_arg->tid_was_stopped))
+ return false;
+ pid_arg->tid_attached = tid;
+ Dwfl_Process *process = thread->process;
+ Ebl *ebl = process->ebl;
+ return ebl_set_initial_registers_tid (ebl, tid,
+ pid_thread_state_registers_cb, thread);
+}
+
+static void
+pid_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg)
+{
+ struct __libdwfl_pid_arg *pid_arg = dwfl_arg;
+ elf_end (pid_arg->elf);
+ close (pid_arg->elf_fd);
+ closedir (pid_arg->dir);
+ free (pid_arg);
+}
+
+void
+internal_function
+__libdwfl_ptrace_detach (pid_t tid, bool tid_was_stopped)
+{
+ /* This handling is needed only on older Linux kernels such as
+ 2.6.32-358.23.2.el6.ppc64. Later kernels such as
+ 3.11.7-200.fc19.x86_64 remember the T (stopped) state
+ themselves and no longer need to pass SIGSTOP during
+ PTRACE_DETACH. */
+ ptrace (PTRACE_DETACH, tid, NULL,
+ (void *) (intptr_t) (tid_was_stopped ? SIGSTOP : 0));
+}
+
+static void
+pid_thread_detach (Dwfl_Thread *thread, void *thread_arg)
+{
+ struct __libdwfl_pid_arg *pid_arg = thread_arg;
+ pid_t tid = INTUSE(dwfl_thread_tid) (thread);
+ assert (pid_arg->tid_attached == tid);
+ pid_arg->tid_attached = 0;
+ if (! pid_arg->assume_ptrace_stopped)
+ __libdwfl_ptrace_detach (tid, pid_arg->tid_was_stopped);
+}
+
+static const Dwfl_Thread_Callbacks pid_thread_callbacks =
+{
+ pid_next_thread,
+ pid_getthread,
+ pid_memory_read,
+ pid_set_initial_registers,
+ pid_detach,
+ pid_thread_detach,
+};
+
+int
+dwfl_linux_proc_attach (Dwfl *dwfl, pid_t pid, bool assume_ptrace_stopped)
+{
+ char buffer[36];
+ FILE *procfile;
+ int err = 0; /* The errno to return and set for dwfl->attcherr. */
+
+ /* Make sure to report the actual PID (thread group leader) to
+ dwfl_attach_state. */
+ snprintf (buffer, sizeof (buffer), "/proc/%ld/status", (long) pid);
+ procfile = fopen (buffer, "r");
+ if (procfile == NULL)
+ {
+ err = errno;
+ fail:
+ if (dwfl->process == NULL && dwfl->attacherr == DWFL_E_NOERROR)
+ {
+ errno = err;
+ dwfl->attacherr = __libdwfl_canon_error (DWFL_E_ERRNO);
+ }
+ return err;
+ }
+
+ char *line = NULL;
+ size_t linelen = 0;
+ while (getline (&line, &linelen, procfile) >= 0)
+ if (strncmp (line, "Tgid:", 5) == 0)
+ {
+ errno = 0;
+ char *endptr;
+ long val = strtol (&line[5], &endptr, 10);
+ if ((errno == ERANGE && val == LONG_MAX)
+ || *endptr != '\n' || val < 0 || val != (pid_t) val)
+ pid = 0;
+ else
+ pid = (pid_t) val;
+ break;
+ }
+ free (line);
+ fclose (procfile);
+
+ if (pid == 0)
+ {
+ err = ESRCH;
+ goto fail;
+ }
+
+ char name[64];
+ int i = snprintf (name, sizeof (name), "/proc/%ld/task", (long) pid);
+ assert (i > 0 && i < (ssize_t) sizeof (name) - 1);
+ DIR *dir = opendir (name);
+ if (dir == NULL)
+ {
+ err = errno;
+ goto fail;
+ }
+
+ Elf *elf;
+ i = snprintf (name, sizeof (name), "/proc/%ld/exe", (long) pid);
+ assert (i > 0 && i < (ssize_t) sizeof (name) - 1);
+ int elf_fd = open (name, O_RDONLY);
+ if (elf_fd >= 0)
+ {
+ elf = elf_begin (elf_fd, ELF_C_READ_MMAP, NULL);
+ if (elf == NULL)
+ {
+ /* Just ignore, dwfl_attach_state will fall back to trying
+ to associate the Dwfl with one of the existing DWfl_Module
+ ELF images (to know the machine/class backend to use). */
+ close (elf_fd);
+ elf_fd = -1;
+ }
+ }
+ else
+ elf = NULL;
+ struct __libdwfl_pid_arg *pid_arg = malloc (sizeof *pid_arg);
+ if (pid_arg == NULL)
+ {
+ elf_end (elf);
+ close (elf_fd);
+ closedir (dir);
+ err = ENOMEM;
+ goto fail;
+ }
+ pid_arg->dir = dir;
+ pid_arg->elf = elf;
+ pid_arg->elf_fd = elf_fd;
+ pid_arg->tid_attached = 0;
+ pid_arg->assume_ptrace_stopped = assume_ptrace_stopped;
+ if (! INTUSE(dwfl_attach_state) (dwfl, elf, pid, &pid_thread_callbacks,
+ pid_arg))
+ {
+ elf_end (elf);
+ close (elf_fd);
+ closedir (dir);
+ free (pid_arg);
+ return -1;
+ }
+ return 0;
+}
+INTDEF (dwfl_linux_proc_attach)
+
+struct __libdwfl_pid_arg *
+internal_function
+__libdwfl_get_pid_arg (Dwfl *dwfl)
+{
+ if (dwfl != NULL && dwfl->process != NULL
+ && dwfl->process->callbacks == &pid_thread_callbacks)
+ return (struct __libdwfl_pid_arg *) dwfl->process->callbacks_arg;
+
+ return NULL;
+}
+
+#else /* __linux__ */
+
+bool
+internal_function
+__libdwfl_ptrace_attach (pid_t tid __attribute__ ((unused)),
+ bool *tid_was_stoppedp __attribute__ ((unused)))
+{
+ errno = ENOSYS;
+ __libdwfl_seterrno (DWFL_E_ERRNO);
+ return false;
+}
+
+void
+internal_function
+__libdwfl_ptrace_detach (pid_t tid __attribute__ ((unused)),
+ bool tid_was_stopped __attribute__ ((unused)))
+{
+}
+
+int
+dwfl_linux_proc_attach (Dwfl *dwfl __attribute__ ((unused)),
+ pid_t pid __attribute__ ((unused)),
+ bool assume_ptrace_stopped __attribute__ ((unused)))
+{
+ return ENOSYS;
+}
+INTDEF (dwfl_linux_proc_attach)
+
+struct __libdwfl_pid_arg *
+internal_function
+__libdwfl_get_pid_arg (Dwfl *dwfl __attribute__ ((unused)))
+{
+ return NULL;
+}
+
+#endif /* ! __linux __ */
+
diff --git a/libdwfl/linux-proc-maps.c b/libdwfl/linux-proc-maps.c
new file mode 100644
index 0000000..c4438c0
--- /dev/null
+++ b/libdwfl/linux-proc-maps.c
@@ -0,0 +1,440 @@
+/* Standard libdwfl callbacks for debugging a live Linux process.
+ Copyright (C) 2005-2010, 2013, 2014, 2016 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+#include <endian.h>
+#include "system.h"
+
+
+#define PROCMAPSFMT "/proc/%d/maps"
+#define PROCMEMFMT "/proc/%d/mem"
+#define PROCAUXVFMT "/proc/%d/auxv"
+#define PROCEXEFMT "/proc/%d/exe"
+
+
+/* Return ELFCLASS64 or ELFCLASS32 for the main ELF executable. Return
+ ELFCLASSNONE for an error. */
+
+static unsigned char
+get_pid_class (pid_t pid)
+{
+ char *fname;
+ if (asprintf (&fname, PROCEXEFMT, pid) < 0)
+ return ELFCLASSNONE;
+
+ int fd = open (fname, O_RDONLY);
+ free (fname);
+ if (fd < 0)
+ return ELFCLASSNONE;
+
+ unsigned char buf[EI_CLASS + 1];
+ ssize_t nread = pread_retry (fd, &buf, sizeof buf, 0);
+ close (fd);
+ if (nread != sizeof buf || buf[EI_MAG0] != ELFMAG0
+ || buf[EI_MAG1] != ELFMAG1 || buf[EI_MAG2] != ELFMAG2
+ || buf[EI_MAG3] != ELFMAG3
+ || (buf[EI_CLASS] != ELFCLASS64 && buf[EI_CLASS] != ELFCLASS32))
+ return ELFCLASSNONE;
+
+ return buf[EI_CLASS];
+}
+
+/* Search /proc/PID/auxv for the AT_SYSINFO_EHDR tag.
+
+ It would be easiest to call get_pid_class and parse everything according to
+ the 32-bit or 64-bit class. But this would bring the overhead of syscalls
+ to open and read the "/proc/%d/exe" file.
+
+ Therefore this function tries to parse the "/proc/%d/auxv" content both
+ ways, as if it were the 32-bit format and also if it were the 64-bit format.
+ Only if it gives some valid data in both cases get_pid_class gets called.
+ In most cases only one of the format bit sizes gives valid data and the
+ get_pid_class call overhead can be saved. */
+
+static int
+grovel_auxv (pid_t pid, Dwfl *dwfl, GElf_Addr *sysinfo_ehdr)
+{
+ char *fname;
+ if (asprintf (&fname, PROCAUXVFMT, pid) < 0)
+ return ENOMEM;
+
+ int fd = open (fname, O_RDONLY);
+ free (fname);
+ if (fd < 0)
+ return errno == ENOENT ? 0 : errno;
+
+ GElf_Addr sysinfo_ehdr64 = 0;
+ GElf_Addr sysinfo_ehdr32 = 0;
+ GElf_Addr segment_align64 = dwfl->segment_align;
+ GElf_Addr segment_align32 = dwfl->segment_align;
+ off_t offset = 0;
+ ssize_t nread;
+ union
+ {
+ Elf64_auxv_t a64[64];
+ Elf32_auxv_t a32[128];
+ } d;
+ do
+ {
+ eu_static_assert (sizeof d.a64 == sizeof d.a32);
+ nread = pread_retry (fd, d.a64, sizeof d.a64, offset);
+ if (nread < 0)
+ {
+ int ret = errno;
+ close (fd);
+ return ret;
+ }
+ for (size_t a32i = 0; a32i < nread / sizeof d.a32[0]; a32i++)
+ {
+ const Elf32_auxv_t *a32 = d.a32 + a32i;
+ switch (a32->a_type)
+ {
+ case AT_SYSINFO_EHDR:
+ sysinfo_ehdr32 = a32->a_un.a_val;
+ break;
+ case AT_PAGESZ:
+ segment_align32 = a32->a_un.a_val;
+ break;
+ }
+ }
+ for (size_t a64i = 0; a64i < nread / sizeof d.a64[0]; a64i++)
+ {
+ const Elf64_auxv_t *a64 = d.a64 + a64i;
+ switch (a64->a_type)
+ {
+ case AT_SYSINFO_EHDR:
+ sysinfo_ehdr64 = a64->a_un.a_val;
+ break;
+ case AT_PAGESZ:
+ segment_align64 = a64->a_un.a_val;
+ break;
+ }
+ }
+ offset += nread;
+ }
+ while (nread == sizeof d.a64);
+
+ close (fd);
+
+ bool valid64 = sysinfo_ehdr64 != 0 || segment_align64 != dwfl->segment_align;
+ bool valid32 = sysinfo_ehdr32 != 0 || segment_align32 != dwfl->segment_align;
+
+ unsigned char pid_class = ELFCLASSNONE;
+ if (valid64 && valid32)
+ pid_class = get_pid_class (pid);
+
+ if (pid_class == ELFCLASS64 || (valid64 && ! valid32))
+ {
+ *sysinfo_ehdr = sysinfo_ehdr64;
+ dwfl->segment_align = segment_align64;
+ return 0;
+ }
+ if (pid_class == ELFCLASS32 || (! valid64 && valid32))
+ {
+ *sysinfo_ehdr = sysinfo_ehdr32;
+ dwfl->segment_align = segment_align32;
+ return 0;
+ }
+ return ENOEXEC;
+}
+
+static inline bool
+do_report (Dwfl *dwfl, char **plast_file, Dwarf_Addr low, Dwarf_Addr high)
+{
+ if (*plast_file != NULL)
+ {
+ Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, *plast_file,
+ low, high);
+ free (*plast_file);
+ *plast_file = NULL;
+ if (unlikely (mod == NULL))
+ return true;
+ }
+ return false;
+}
+
+#define report() do_report(dwfl, &last_file, low, high)
+
+static int
+proc_maps_report (Dwfl *dwfl, FILE *f, GElf_Addr sysinfo_ehdr, pid_t pid)
+{
+ unsigned int last_dmajor = -1, last_dminor = -1;
+ uint64_t last_ino = -1;
+ char *last_file = NULL;
+ Dwarf_Addr low = 0, high = 0;
+
+ char *line = NULL;
+ size_t linesz;
+ ssize_t len;
+ while ((len = getline (&line, &linesz, f)) > 0)
+ {
+ if (line[len - 1] == '\n')
+ line[len - 1] = '\0';
+
+ Dwarf_Addr start, end, offset;
+ unsigned int dmajor, dminor;
+ uint64_t ino;
+ int nread = -1;
+ if (sscanf (line, "%" PRIx64 "-%" PRIx64 " %*s %" PRIx64
+ " %x:%x %" PRIi64 " %n",
+ &start, &end, &offset, &dmajor, &dminor, &ino, &nread) < 6
+ || nread <= 0)
+ {
+ free (line);
+ free (last_file);
+ return ENOEXEC;
+ }
+
+ /* If this is the special mapping AT_SYSINFO_EHDR pointed us at,
+ report the last one and then this special one. */
+ if (start == sysinfo_ehdr && start != 0)
+ {
+ if (report ())
+ {
+ bad_report:
+ free (line);
+ return -1;
+ }
+
+ low = start;
+ high = end;
+ if (asprintf (&last_file, "[vdso: %d]", (int) pid) < 0
+ || report ())
+ goto bad_report;
+ }
+
+ char *file = line + nread + strspn (line + nread, " \t");
+ if (file[0] != '/' || (ino == 0 && dmajor == 0 && dminor == 0))
+ /* This line doesn't indicate a file mapping. */
+ continue;
+
+ if (last_file != NULL
+ && ino == last_ino && dmajor == last_dmajor && dminor == last_dminor)
+ {
+ /* This is another portion of the same file's mapping. */
+ if (strcmp (last_file, file) != 0)
+ {
+ free (last_file);
+ goto bad_report;
+ }
+ high = end;
+ }
+ else
+ {
+ /* This is a different file mapping. Report the last one. */
+ if (report ())
+ goto bad_report;
+ low = start;
+ high = end;
+ last_file = strdup (file);
+ last_ino = ino;
+ last_dmajor = dmajor;
+ last_dminor = dminor;
+ }
+ }
+ free (line);
+
+ int result = ferror_unlocked (f) ? errno : feof_unlocked (f) ? 0 : ENOEXEC;
+
+ /* Report the final one. */
+ bool lose = report ();
+
+ return result != 0 ? result : lose ? -1 : 0;
+}
+
+int
+dwfl_linux_proc_maps_report (Dwfl *dwfl, FILE *f)
+{
+ return proc_maps_report (dwfl, f, 0, 0);
+}
+INTDEF (dwfl_linux_proc_maps_report)
+
+int
+dwfl_linux_proc_report (Dwfl *dwfl, pid_t pid)
+{
+ if (dwfl == NULL)
+ return -1;
+
+ /* We'll notice the AT_SYSINFO_EHDR address specially when we hit it. */
+ GElf_Addr sysinfo_ehdr = 0;
+ int result = grovel_auxv (pid, dwfl, &sysinfo_ehdr);
+ if (result != 0)
+ return result;
+
+ char *fname;
+ if (asprintf (&fname, PROCMAPSFMT, pid) < 0)
+ return ENOMEM;
+
+ FILE *f = fopen (fname, "r");
+ free (fname);
+ if (f == NULL)
+ return errno;
+
+ (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
+
+ result = proc_maps_report (dwfl, f, sysinfo_ehdr, pid);
+
+ fclose (f);
+
+ return result;
+}
+INTDEF (dwfl_linux_proc_report)
+
+static ssize_t
+read_proc_memory (void *arg, void *data, GElf_Addr address,
+ size_t minread, size_t maxread)
+{
+ const int fd = *(const int *) arg;
+
+ /* This code relies on the fact the Linux kernel accepts negative
+ offsets when seeking /dev/$$/mem files, as a special case. In
+ particular pread cannot be used here, because it will always
+ return EINVAL when passed a negative offset. */
+
+ if (lseek (fd, (off_t) address, SEEK_SET) == -1)
+ return -1;
+
+ ssize_t nread = read (fd, data, maxread);
+
+ if (nread > 0 && (size_t) nread < minread)
+ nread = 0;
+ return nread;
+}
+
+extern Elf *elf_from_remote_memory (GElf_Addr ehdr_vma,
+ GElf_Xword pagesize,
+ GElf_Addr *loadbasep,
+ ssize_t (*read_memory) (void *arg,
+ void *data,
+ GElf_Addr address,
+ size_t minread,
+ size_t maxread),
+ void *arg);
+
+
+/* Dwfl_Callbacks.find_elf */
+
+int
+dwfl_linux_proc_find_elf (Dwfl_Module *mod __attribute__ ((unused)),
+ void **userdata __attribute__ ((unused)),
+ const char *module_name, Dwarf_Addr base,
+ char **file_name, Elf **elfp)
+{
+ int pid = -1;
+ if (module_name[0] == '/')
+ {
+ /* When this callback is used together with dwfl_linux_proc_report
+ then we might see mappings of special character devices. Make
+ sure we only open and return regular files. Special devices
+ might hang on open or read. (deleted) files are super special.
+ The image might come from memory if we are attached. */
+ struct stat sb;
+ if (stat (module_name, &sb) == -1 || (sb.st_mode & S_IFMT) != S_IFREG)
+ {
+ if (strcmp (strrchr (module_name, ' ') ?: "", " (deleted)") == 0)
+ pid = INTUSE(dwfl_pid) (mod->dwfl);
+ else
+ return -1;
+ }
+
+ if (pid == -1)
+ {
+ int fd = open (module_name, O_RDONLY);
+ if (fd >= 0)
+ {
+ *file_name = strdup (module_name);
+ if (*file_name == NULL)
+ {
+ close (fd);
+ return ENOMEM;
+ }
+ }
+ return fd;
+ }
+ }
+
+ if (pid != -1 || sscanf (module_name, "[vdso: %d]", &pid) == 1)
+ {
+ /* Special case for in-memory ELF image. */
+
+ bool detach = false;
+ bool tid_was_stopped = false;
+ struct __libdwfl_pid_arg *pid_arg = __libdwfl_get_pid_arg (mod->dwfl);
+ if (pid_arg != NULL && ! pid_arg->assume_ptrace_stopped)
+ {
+ /* If any thread is already attached we are fine. Read
+ through that thread. It doesn't have to be the main
+ thread pid. */
+ pid_t tid = pid_arg->tid_attached;
+ if (tid != 0)
+ pid = tid;
+ else
+ detach = __libdwfl_ptrace_attach (pid, &tid_was_stopped);
+ }
+
+ char *fname;
+ if (asprintf (&fname, PROCMEMFMT, pid) < 0)
+ goto detach;
+
+ int fd = open (fname, O_RDONLY);
+ free (fname);
+ if (fd < 0)
+ goto detach;
+
+ *elfp = elf_from_remote_memory (base, sysconf (_SC_PAGESIZE), NULL,
+ &read_proc_memory, &fd);
+
+ close (fd);
+
+ *file_name = NULL;
+
+ detach:
+ if (detach)
+ __libdwfl_ptrace_detach (pid, tid_was_stopped);
+ return -1;
+ }
+
+ return -1;
+}
+INTDEF (dwfl_linux_proc_find_elf)
diff --git a/libdwfl/lzma.c b/libdwfl/lzma.c
new file mode 100644
index 0000000..3edfdc2
--- /dev/null
+++ b/libdwfl/lzma.c
@@ -0,0 +1,4 @@
+/* liblzma is pretty close to zlib and bzlib. */
+
+#define LZMA
+#include "gzip.c"
diff --git a/libdwfl/offline.c b/libdwfl/offline.c
new file mode 100644
index 0000000..80c80a1
--- /dev/null
+++ b/libdwfl/offline.c
@@ -0,0 +1,315 @@
+/* Recover relocatibility for addresses computed from debug information.
+ Copyright (C) 2005-2009, 2012 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include <fcntl.h>
+#include <unistd.h>
+
+/* Since dwfl_report_elf lays out the sections already, this will only be
+ called when the section headers of the debuginfo file are being
+ consulted instead, or for the section placed at 0. With binutils
+ strip-to-debug, the symbol table is in the debuginfo file and relocation
+ looks there. */
+int
+dwfl_offline_section_address (Dwfl_Module *mod,
+ void **userdata __attribute__ ((unused)),
+ const char *modname __attribute__ ((unused)),
+ Dwarf_Addr base __attribute__ ((unused)),
+ const char *secname __attribute__ ((unused)),
+ Elf32_Word shndx,
+ const GElf_Shdr *shdr __attribute__ ((unused)),
+ Dwarf_Addr *addr)
+{
+ assert (mod->e_type == ET_REL);
+ assert (shdr->sh_addr == 0);
+ assert (shdr->sh_flags & SHF_ALLOC);
+ assert (shndx != 0);
+
+ if (mod->debug.elf == NULL)
+ /* We are only here because sh_addr is zero even though layout is complete.
+ The first section in the first file under -e is placed at 0. */
+ return 0;
+
+ /* The section numbers might not match between the two files.
+ The best we can rely on is the order of SHF_ALLOC sections. */
+
+ Elf_Scn *ourscn = elf_getscn (mod->debug.elf, shndx);
+ Elf_Scn *scn = NULL;
+ uint_fast32_t skip_alloc = 0;
+ while ((scn = elf_nextscn (mod->debug.elf, scn)) != ourscn)
+ {
+ assert (scn != NULL);
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *sh = gelf_getshdr (scn, &shdr_mem);
+ if (unlikely (sh == NULL))
+ return -1;
+ if (sh->sh_flags & SHF_ALLOC)
+ ++skip_alloc;
+ }
+
+ scn = NULL;
+ while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *main_shdr = gelf_getshdr (scn, &shdr_mem);
+ if (unlikely (main_shdr == NULL))
+ return -1;
+ if ((main_shdr->sh_flags & SHF_ALLOC) && skip_alloc-- == 0)
+ {
+ assert (main_shdr->sh_flags == shdr->sh_flags);
+ *addr = main_shdr->sh_addr;
+ return 0;
+ }
+ }
+
+ /* This should never happen. */
+ return -1;
+}
+INTDEF (dwfl_offline_section_address)
+
+/* Forward declarations. */
+static Dwfl_Module *process_elf (Dwfl *dwfl, const char *name,
+ const char *file_name, int fd, Elf *elf);
+static Dwfl_Module *process_archive (Dwfl *dwfl, const char *name,
+ const char *file_name, int fd, Elf *elf,
+ int (*predicate) (const char *module,
+ const char *file));
+
+/* Report one module for an ELF file, or many for an archive.
+ Always consumes ELF and FD. */
+static Dwfl_Module *
+process_file (Dwfl *dwfl, const char *name, const char *file_name, int fd,
+ Elf *elf, int (*predicate) (const char *module,
+ const char *file))
+{
+ switch (elf_kind (elf))
+ {
+ default:
+ case ELF_K_NONE:
+ __libdwfl_seterrno (elf == NULL ? DWFL_E_LIBELF : DWFL_E_BADELF);
+ return NULL;
+
+ case ELF_K_ELF:
+ return process_elf (dwfl, name, file_name, fd, elf);
+
+ case ELF_K_AR:
+ return process_archive (dwfl, name, file_name, fd, elf, predicate);
+ }
+}
+
+/* Report the open ELF file as a module. Always consumes ELF and FD. */
+static Dwfl_Module *
+process_elf (Dwfl *dwfl, const char *name, const char *file_name, int fd,
+ Elf *elf)
+{
+ Dwfl_Module *mod = __libdwfl_report_elf (dwfl, name, file_name, fd, elf,
+ dwfl->offline_next_address, true,
+ false);
+ if (mod != NULL)
+ {
+ /* If this is an ET_EXEC file with fixed addresses, the address range
+ it consumed may or may not intersect with the arbitrary range we
+ will use for relocatable modules. Make sure we always use a free
+ range for the offline allocations. If this module did use
+ offline_next_address, it may have rounded it up for the module's
+ alignment requirements. */
+ if ((dwfl->offline_next_address >= mod->low_addr
+ || mod->low_addr - dwfl->offline_next_address < OFFLINE_REDZONE)
+ && dwfl->offline_next_address < mod->high_addr + OFFLINE_REDZONE)
+ dwfl->offline_next_address = mod->high_addr + OFFLINE_REDZONE;
+
+ /* Don't keep the file descriptor around. */
+ if (mod->main.fd != -1 && elf_cntl (mod->main.elf, ELF_C_FDREAD) == 0)
+ {
+ close (mod->main.fd);
+ mod->main.fd = -1;
+ }
+ }
+
+ return mod;
+}
+
+/* Always consumes MEMBER. Returns elf_next result on success.
+ For errors returns ELF_C_NULL with *MOD set to null. */
+static Elf_Cmd
+process_archive_member (Dwfl *dwfl, const char *name, const char *file_name,
+ int (*predicate) (const char *module, const char *file),
+ int fd, Elf *member, Dwfl_Module **mod)
+{
+ const Elf_Arhdr *h = elf_getarhdr (member);
+ if (unlikely (h == NULL))
+ {
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ fail:
+ elf_end (member);
+ *mod = NULL;
+ return ELF_C_NULL;
+ }
+
+ if (!strcmp (h->ar_name, "/") || !strcmp (h->ar_name, "//")
+ || !strcmp (h->ar_name, "/SYM64/"))
+ {
+ skip:;
+ /* Skip this and go to the next. */
+ Elf_Cmd result = elf_next (member);
+ elf_end (member);
+ return result;
+ }
+
+ char *member_name;
+ if (unlikely (asprintf (&member_name, "%s(%s)", file_name, h->ar_name) < 0))
+ {
+ nomem:
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ elf_end (member);
+ *mod = NULL;
+ return ELF_C_NULL;
+ }
+
+ char *module_name = NULL;
+ if (name == NULL || name[0] == '\0')
+ name = h->ar_name;
+ else if (unlikely (asprintf (&module_name, "%s:%s", name, h->ar_name) < 0))
+ {
+ free (member_name);
+ goto nomem;
+ }
+ else
+ name = module_name;
+
+ if (predicate != NULL)
+ {
+ /* Let the predicate decide whether to use this one. */
+ int want = (*predicate) (name, member_name);
+ if (want <= 0)
+ {
+ free (member_name);
+ free (module_name);
+ if (unlikely (want < 0))
+ {
+ __libdwfl_seterrno (DWFL_E_CB);
+ goto fail;
+ }
+ goto skip;
+ }
+ }
+
+ /* We let __libdwfl_report_elf cache the fd in mod->main.fd,
+ though it's the same fd for all the members.
+ On module teardown we will close it only on the last Elf reference. */
+ *mod = process_file (dwfl, name, member_name, fd, member, predicate);
+ free (member_name);
+ free (module_name);
+
+ if (*mod == NULL) /* process_file called elf_end. */
+ return ELF_C_NULL;
+
+ /* Advance the archive-reading offset for the next iteration. */
+ return elf_next (member);
+}
+
+/* Report each member of the archive as its own module. */
+static Dwfl_Module *
+process_archive (Dwfl *dwfl, const char *name, const char *file_name, int fd,
+ Elf *archive,
+ int (*predicate) (const char *module, const char *file))
+
+{
+ Dwfl_Module *mod = NULL;
+ Elf *member = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, archive);
+ if (unlikely (member == NULL)) /* Empty archive. */
+ {
+ __libdwfl_seterrno (DWFL_E_BADELF);
+ return NULL;
+ }
+
+ while (process_archive_member (dwfl, name, file_name, predicate,
+ fd, member, &mod) != ELF_C_NULL)
+ member = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, archive);
+
+ /* We can drop the archive Elf handle even if we're still using members
+ in live modules. When the last module's elf_end on a member returns
+ zero, that module will close FD. If no modules survived the predicate,
+ we are all done with the file right here. */
+ if (mod != NULL /* If no modules, caller will clean up. */
+ && elf_end (archive) == 0)
+ close (fd);
+
+ return mod;
+}
+
+Dwfl_Module *
+internal_function
+__libdwfl_report_offline (Dwfl *dwfl, const char *name,
+ const char *file_name, int fd, bool closefd,
+ int (*predicate) (const char *module,
+ const char *file))
+{
+ Elf *elf;
+ Dwfl_Error error = __libdw_open_file (&fd, &elf, closefd, true);
+ if (error != DWFL_E_NOERROR)
+ {
+ __libdwfl_seterrno (error);
+ return NULL;
+ }
+ Dwfl_Module *mod = process_file (dwfl, name, file_name, fd, elf, predicate);
+ if (mod == NULL)
+ {
+ elf_end (elf);
+ if (closefd)
+ close (fd);
+ }
+ return mod;
+}
+
+Dwfl_Module *
+dwfl_report_offline (Dwfl *dwfl, const char *name,
+ const char *file_name, int fd)
+{
+ if (dwfl == NULL)
+ return NULL;
+
+ bool closefd = false;
+ if (fd < 0)
+ {
+ closefd = true;
+ fd = open (file_name, O_RDONLY);
+ if (fd < 0)
+ {
+ __libdwfl_seterrno (DWFL_E_ERRNO);
+ return NULL;
+ }
+ }
+
+ return __libdwfl_report_offline (dwfl, name, file_name, fd, closefd, NULL);
+}
+INTDEF (dwfl_report_offline)
diff --git a/libdwfl/open.c b/libdwfl/open.c
new file mode 100644
index 0000000..4e0461b
--- /dev/null
+++ b/libdwfl/open.c
@@ -0,0 +1,180 @@
+/* Decompression support for libdwfl: zlib (gzip), bzlib (bzip2) or lzma (xz).
+ Copyright (C) 2009, 2016 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "../libelf/libelfP.h"
+#undef _
+#include "libdwflP.h"
+
+#include <unistd.h>
+
+#if !USE_BZLIB
+# define __libdw_bunzip2(...) DWFL_E_BADELF
+#endif
+
+#if !USE_LZMA
+# define __libdw_unlzma(...) DWFL_E_BADELF
+#endif
+
+/* Consumes and replaces *ELF only on success. */
+static Dwfl_Error
+decompress (int fd __attribute__ ((unused)), Elf **elf)
+{
+ Dwfl_Error error = DWFL_E_BADELF;
+ void *buffer = NULL;
+ size_t size = 0;
+
+ const off_t offset = (*elf)->start_offset;
+ void *const mapped = ((*elf)->map_address == NULL ? NULL
+ : (*elf)->map_address + offset);
+ const size_t mapped_size = (*elf)->maximum_size;
+ if (mapped_size == 0)
+ return error;
+
+ error = __libdw_gunzip (fd, offset, mapped, mapped_size, &buffer, &size);
+ if (error == DWFL_E_BADELF)
+ error = __libdw_bunzip2 (fd, offset, mapped, mapped_size, &buffer, &size);
+ if (error == DWFL_E_BADELF)
+ error = __libdw_unlzma (fd, offset, mapped, mapped_size, &buffer, &size);
+
+ if (error == DWFL_E_NOERROR)
+ {
+ if (unlikely (size == 0))
+ {
+ error = DWFL_E_BADELF;
+ free (buffer);
+ }
+ else
+ {
+ Elf *memelf = elf_memory (buffer, size);
+ if (memelf == NULL)
+ {
+ error = DWFL_E_LIBELF;
+ free (buffer);
+ }
+ else
+ {
+ memelf->flags |= ELF_F_MALLOCED;
+ elf_end (*elf);
+ *elf = memelf;
+ }
+ }
+ }
+ else
+ free (buffer);
+
+ return error;
+}
+
+static Dwfl_Error
+what_kind (int fd, Elf **elfp, Elf_Kind *kind, bool *close_fd)
+{
+ Dwfl_Error error = DWFL_E_NOERROR;
+ *kind = elf_kind (*elfp);
+ if (unlikely (*kind == ELF_K_NONE))
+ {
+ if (unlikely (*elfp == NULL))
+ error = DWFL_E_LIBELF;
+ else
+ {
+ error = decompress (fd, elfp);
+ if (error == DWFL_E_NOERROR)
+ {
+ *close_fd = true;
+ *kind = elf_kind (*elfp);
+ }
+ }
+ }
+ return error;
+}
+
+Dwfl_Error internal_function
+__libdw_open_file (int *fdp, Elf **elfp, bool close_on_fail, bool archive_ok)
+{
+ bool close_fd = false;
+
+ Elf *elf = elf_begin (*fdp, ELF_C_READ_MMAP_PRIVATE, NULL);
+
+ Elf_Kind kind;
+ Dwfl_Error error = what_kind (*fdp, &elf, &kind, &close_fd);
+ if (error == DWFL_E_BADELF)
+ {
+ /* It's not an ELF file or a compressed file.
+ See if it's an image with a header preceding the real file. */
+
+ off_t offset = elf->start_offset;
+ error = __libdw_image_header (*fdp, &offset,
+ (elf->map_address == NULL ? NULL
+ : elf->map_address + offset),
+ elf->maximum_size);
+ if (error == DWFL_E_NOERROR)
+ {
+ /* Pure evil. libelf needs some better interfaces. */
+ elf->kind = ELF_K_AR;
+ elf->state.ar.elf_ar_hdr.ar_name = "libdwfl is faking you out";
+ elf->state.ar.elf_ar_hdr.ar_size = elf->maximum_size - offset;
+ elf->state.ar.offset = offset - sizeof (struct ar_hdr);
+ Elf *subelf = elf_begin (-1, ELF_C_READ_MMAP_PRIVATE, elf);
+ elf->kind = ELF_K_NONE;
+ if (unlikely (subelf == NULL))
+ error = DWFL_E_LIBELF;
+ else
+ {
+ subelf->parent = NULL;
+ subelf->flags |= elf->flags & (ELF_F_MMAPPED | ELF_F_MALLOCED);
+ elf->flags &= ~(ELF_F_MMAPPED | ELF_F_MALLOCED);
+ elf_end (elf);
+ elf = subelf;
+ error = what_kind (*fdp, &elf, &kind, &close_fd);
+ }
+ }
+ }
+
+ if (error == DWFL_E_NOERROR
+ && kind != ELF_K_ELF
+ && !(archive_ok && kind == ELF_K_AR))
+ error = DWFL_E_BADELF;
+
+ if (error != DWFL_E_NOERROR)
+ {
+ elf_end (elf);
+ elf = NULL;
+ }
+
+ if (error == DWFL_E_NOERROR ? close_fd : close_on_fail)
+ {
+ close (*fdp);
+ *fdp = -1;
+ }
+
+ *elfp = elf;
+ return error;
+}
diff --git a/libdwfl/relocate.c b/libdwfl/relocate.c
new file mode 100644
index 0000000..1768243
--- /dev/null
+++ b/libdwfl/relocate.c
@@ -0,0 +1,789 @@
+/* Relocate debug information.
+ Copyright (C) 2005-2011, 2014, 2016 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+typedef uint8_t GElf_Byte;
+
+/* Adjust *VALUE to add the load address of the SHNDX section.
+ We update the section header in place to cache the result. */
+
+Dwfl_Error
+internal_function
+__libdwfl_relocate_value (Dwfl_Module *mod, Elf *elf, size_t *shstrndx,
+ Elf32_Word shndx, GElf_Addr *value)
+{
+ /* No adjustment needed for section zero, it is never loaded.
+ Handle it first, just in case the ELF file has strange section
+ zero flags set. */
+ if (shndx == 0)
+ return DWFL_E_NOERROR;
+
+ Elf_Scn *refscn = elf_getscn (elf, shndx);
+ GElf_Shdr refshdr_mem, *refshdr = gelf_getshdr (refscn, &refshdr_mem);
+ if (refshdr == NULL)
+ return DWFL_E_LIBELF;
+
+ if (refshdr->sh_addr == 0 && (refshdr->sh_flags & SHF_ALLOC))
+ {
+ /* This is a loaded section. Find its actual
+ address and update the section header. */
+
+ if (*shstrndx == SHN_UNDEF
+ && unlikely (elf_getshdrstrndx (elf, shstrndx) < 0))
+ return DWFL_E_LIBELF;
+
+ const char *name = elf_strptr (elf, *shstrndx, refshdr->sh_name);
+ if (unlikely (name == NULL))
+ return DWFL_E_LIBELF;
+
+ if ((*mod->dwfl->callbacks->section_address) (MODCB_ARGS (mod),
+ name, shndx, refshdr,
+ &refshdr->sh_addr))
+ return CBFAIL;
+
+ if (refshdr->sh_addr == (Dwarf_Addr) -1l)
+ /* The callback indicated this section wasn't really loaded but we
+ don't really care. */
+ refshdr->sh_addr = 0; /* Make no adjustment below. */
+
+ /* Update the in-core file's section header to show the final
+ load address (or unloadedness). This serves as a cache,
+ so we won't get here again for the same section. */
+ if (likely (refshdr->sh_addr != 0)
+ && unlikely (! gelf_update_shdr (refscn, refshdr)))
+ return DWFL_E_LIBELF;
+ }
+
+ if (refshdr->sh_flags & SHF_ALLOC)
+ /* Apply the adjustment. */
+ *value += dwfl_adjusted_address (mod, refshdr->sh_addr);
+
+ return DWFL_E_NOERROR;
+}
+
+
+/* Cache used by relocate_getsym. */
+struct reloc_symtab_cache
+{
+ Elf *symelf;
+ Elf_Data *symdata;
+ Elf_Data *symxndxdata;
+ Elf_Data *symstrdata;
+ size_t symshstrndx;
+ size_t strtabndx;
+};
+#define RELOC_SYMTAB_CACHE(cache) \
+ struct reloc_symtab_cache cache = \
+ { NULL, NULL, NULL, NULL, SHN_UNDEF, SHN_UNDEF }
+
+/* This is just doing dwfl_module_getsym, except that we must always use
+ the symbol table in RELOCATED itself when it has one, not MOD->symfile. */
+static Dwfl_Error
+relocate_getsym (Dwfl_Module *mod,
+ Elf *relocated, struct reloc_symtab_cache *cache,
+ int symndx, GElf_Sym *sym, GElf_Word *shndx)
+{
+ if (cache->symdata == NULL)
+ {
+ if (mod->symfile == NULL || mod->symfile->elf != relocated)
+ {
+ /* We have to look up the symbol table in the file we are
+ relocating, if it has its own. These reloc sections refer to
+ the symbol table in this file, and a symbol table in the main
+ file might not match. However, some tools did produce ET_REL
+ .debug files with relocs but no symtab of their own. */
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (relocated, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem, *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr != NULL)
+ {
+ /* We need uncompressed data. */
+ if ((shdr->sh_type == SHT_SYMTAB
+ || shdr->sh_type == SHT_SYMTAB_SHNDX)
+ && (shdr->sh_flags & SHF_COMPRESSED) != 0)
+ if (elf_compress (scn, 0, 0) < 0)
+ return DWFL_E_LIBELF;
+
+ switch (shdr->sh_type)
+ {
+ default:
+ continue;
+ case SHT_SYMTAB:
+ cache->symelf = relocated;
+ cache->symdata = elf_getdata (scn, NULL);
+ cache->strtabndx = shdr->sh_link;
+ if (unlikely (cache->symdata == NULL))
+ return DWFL_E_LIBELF;
+ break;
+ case SHT_SYMTAB_SHNDX:
+ cache->symxndxdata = elf_getdata (scn, NULL);
+ if (unlikely (cache->symxndxdata == NULL))
+ return DWFL_E_LIBELF;
+ break;
+ }
+ }
+ if (cache->symdata != NULL && cache->symxndxdata != NULL)
+ break;
+ }
+ }
+ if (cache->symdata == NULL)
+ {
+ /* We might not have looked for a symbol table file yet,
+ when coming from __libdwfl_relocate_section. */
+ if (unlikely (mod->symfile == NULL)
+ && unlikely (INTUSE(dwfl_module_getsymtab) (mod) < 0))
+ return dwfl_errno ();
+
+ /* The symbol table we have already cached is the one from
+ the file being relocated, so it's what we need. Or else
+ this is an ET_REL .debug file with no .symtab of its own;
+ the symbols refer to the section indices in the main file. */
+ cache->symelf = mod->symfile->elf;
+ cache->symdata = mod->symdata;
+ cache->symxndxdata = mod->symxndxdata;
+ cache->symstrdata = mod->symstrdata;
+ }
+ }
+
+ if (unlikely (gelf_getsymshndx (cache->symdata, cache->symxndxdata,
+ symndx, sym, shndx) == NULL))
+ return DWFL_E_LIBELF;
+
+ if (sym->st_shndx != SHN_XINDEX)
+ *shndx = sym->st_shndx;
+
+ switch (sym->st_shndx)
+ {
+ case SHN_ABS:
+ case SHN_UNDEF:
+ return DWFL_E_NOERROR;
+
+ case SHN_COMMON:
+ sym->st_value = 0; /* Value is size, not helpful. */
+ return DWFL_E_NOERROR;
+ }
+
+ return __libdwfl_relocate_value (mod, cache->symelf, &cache->symshstrndx,
+ *shndx, &sym->st_value);
+}
+
+/* Handle an undefined symbol. We really only support ET_REL for Linux
+ kernel modules, and offline archives. The behavior of the Linux module
+ loader is very simple and easy to mimic. It only matches magically
+ exported symbols, and we match any defined symbols. But we get the same
+ answer except when the module's symbols are undefined and would prevent
+ it from being loaded. */
+static Dwfl_Error
+resolve_symbol (Dwfl_Module *referer, struct reloc_symtab_cache *symtab,
+ GElf_Sym *sym, GElf_Word shndx)
+{
+ /* First we need its name. */
+ if (sym->st_name != 0)
+ {
+ if (symtab->symstrdata == NULL)
+ {
+ /* Cache the strtab for this symtab. */
+ assert (referer->symfile == NULL
+ || referer->symfile->elf != symtab->symelf);
+
+ Elf_Scn *scn = elf_getscn (symtab->symelf, symtab->strtabndx);
+ if (scn == NULL)
+ return DWFL_E_LIBELF;
+
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr == NULL)
+ return DWFL_E_LIBELF;
+
+ if (symtab->symshstrndx == SHN_UNDEF
+ && elf_getshdrstrndx (symtab->symelf, &symtab->symshstrndx) < 0)
+ return DWFL_E_LIBELF;
+
+ const char *sname = elf_strptr (symtab->symelf, symtab->symshstrndx,
+ shdr->sh_name);
+ if (sname == NULL)
+ return DWFL_E_LIBELF;
+
+ /* If the section is already decompressed, that isn't an error. */
+ if (strncmp (sname, ".zdebug", strlen (".zdebug")) == 0)
+ elf_compress_gnu (scn, 0, 0);
+
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ if (elf_compress (scn, 0, 0) < 0)
+ return DWFL_E_LIBELF;
+
+ symtab->symstrdata = elf_getdata (scn, NULL);
+ if (unlikely (symtab->symstrdata == NULL
+ || symtab->symstrdata->d_buf == NULL))
+ return DWFL_E_LIBELF;
+ }
+ if (unlikely (sym->st_name >= symtab->symstrdata->d_size))
+ return DWFL_E_BADSTROFF;
+
+ const char *name = symtab->symstrdata->d_buf;
+ name += sym->st_name;
+
+ for (Dwfl_Module *m = referer->dwfl->modulelist; m != NULL; m = m->next)
+ if (m != referer)
+ {
+ /* Get this module's symtab.
+ If we got a fresh error reading the table, report it.
+ If we just have no symbols in this module, no harm done. */
+ if (m->symdata == NULL
+ && m->symerr == DWFL_E_NOERROR
+ && INTUSE(dwfl_module_getsymtab) (m) < 0
+ && m->symerr != DWFL_E_NO_SYMTAB)
+ return m->symerr;
+
+ for (size_t ndx = 1; ndx < m->syments; ++ndx)
+ {
+ sym = gelf_getsymshndx (m->symdata, m->symxndxdata,
+ ndx, sym, &shndx);
+ if (unlikely (sym == NULL))
+ return DWFL_E_LIBELF;
+ if (sym->st_shndx != SHN_XINDEX)
+ shndx = sym->st_shndx;
+
+ /* We are looking for a defined global symbol with a name. */
+ if (shndx == SHN_UNDEF || shndx == SHN_COMMON
+ || GELF_ST_BIND (sym->st_info) == STB_LOCAL
+ || sym->st_name == 0)
+ continue;
+
+ /* Get this candidate symbol's name. */
+ if (unlikely (sym->st_name >= m->symstrdata->d_size))
+ return DWFL_E_BADSTROFF;
+ const char *n = m->symstrdata->d_buf;
+ n += sym->st_name;
+
+ /* Does the name match? */
+ if (strcmp (name, n))
+ continue;
+
+ /* We found it! */
+ if (shndx == SHN_ABS) /* XXX maybe should apply bias? */
+ return DWFL_E_NOERROR;
+
+ if (m->e_type != ET_REL)
+ {
+ sym->st_value = dwfl_adjusted_st_value (m, m->symfile->elf,
+ sym->st_value);
+ return DWFL_E_NOERROR;
+ }
+
+ /* In an ET_REL file, the symbol table values are relative
+ to the section, not to the module's load base. */
+ size_t symshstrndx = SHN_UNDEF;
+ return __libdwfl_relocate_value (m, m->symfile->elf,
+ &symshstrndx,
+ shndx, &sym->st_value);
+ }
+ }
+ }
+
+ return DWFL_E_RELUNDEF;
+}
+
+/* Apply one relocation. Returns true for any invalid data. */
+static Dwfl_Error
+relocate (Dwfl_Module * const mod,
+ Elf * const relocated,
+ struct reloc_symtab_cache * const reloc_symtab,
+ Elf_Data * const tdata,
+ const GElf_Ehdr * const ehdr,
+ GElf_Addr offset,
+ const GElf_Sxword *addend,
+ int rtype,
+ int symndx)
+{
+ /* First see if this is a reloc we can handle.
+ If we are skipping it, don't bother resolving the symbol. */
+
+ if (unlikely (rtype == 0))
+ /* In some odd situations, the linker can leave R_*_NONE relocs
+ behind. This is probably bogus ld -r behavior, but the only
+ cases it's known to appear in are harmless: DWARF data
+ referring to addresses in a section that has been discarded.
+ So we just pretend it's OK without further relocation. */
+ return DWFL_E_NOERROR;
+
+ Elf_Type type = ebl_reloc_simple_type (mod->ebl, rtype);
+ if (unlikely (type == ELF_T_NUM))
+ return DWFL_E_BADRELTYPE;
+
+ /* First, resolve the symbol to an absolute value. */
+ GElf_Addr value;
+
+ if (symndx == STN_UNDEF)
+ /* When strip removes a section symbol referring to a
+ section moved into the debuginfo file, it replaces
+ that symbol index in relocs with STN_UNDEF. We
+ don't actually need the symbol, because those relocs
+ are always references relative to the nonallocated
+ debugging sections, which start at zero. */
+ value = 0;
+ else
+ {
+ GElf_Sym sym;
+ GElf_Word shndx;
+ Dwfl_Error error = relocate_getsym (mod, relocated, reloc_symtab,
+ symndx, &sym, &shndx);
+ if (unlikely (error != DWFL_E_NOERROR))
+ return error;
+
+ if (shndx == SHN_UNDEF || shndx == SHN_COMMON)
+ {
+ /* Maybe we can figure it out anyway. */
+ error = resolve_symbol (mod, reloc_symtab, &sym, shndx);
+ if (error != DWFL_E_NOERROR
+ && !(error == DWFL_E_RELUNDEF && shndx == SHN_COMMON))
+ return error;
+ }
+
+ value = sym.st_value;
+ }
+
+ /* These are the types we can relocate. */
+#define TYPES DO_TYPE (BYTE, Byte); DO_TYPE (HALF, Half); \
+ DO_TYPE (WORD, Word); DO_TYPE (SWORD, Sword); \
+ DO_TYPE (XWORD, Xword); DO_TYPE (SXWORD, Sxword)
+ size_t size;
+ switch (type)
+ {
+#define DO_TYPE(NAME, Name) \
+ case ELF_T_##NAME: \
+ size = sizeof (GElf_##Name); \
+ break
+ TYPES;
+#undef DO_TYPE
+ default:
+ return DWFL_E_BADRELTYPE;
+ }
+
+ if (offset > tdata->d_size || tdata->d_size - offset < size)
+ return DWFL_E_BADRELOFF;
+
+#define DO_TYPE(NAME, Name) GElf_##Name Name;
+ union { TYPES; } tmpbuf;
+#undef DO_TYPE
+ Elf_Data tmpdata =
+ {
+ .d_type = type,
+ .d_buf = &tmpbuf,
+ .d_size = size,
+ .d_version = EV_CURRENT,
+ };
+ Elf_Data rdata =
+ {
+ .d_type = type,
+ .d_buf = tdata->d_buf + offset,
+ .d_size = size,
+ .d_version = EV_CURRENT,
+ };
+
+ /* XXX check for overflow? */
+ if (addend)
+ {
+ /* For the addend form, we have the value already. */
+ value += *addend;
+ switch (type)
+ {
+#define DO_TYPE(NAME, Name) \
+ case ELF_T_##NAME: \
+ tmpbuf.Name = value; \
+ break
+ TYPES;
+#undef DO_TYPE
+ default:
+ abort ();
+ }
+ }
+ else
+ {
+ /* Extract the original value and apply the reloc. */
+ Elf_Data *d = gelf_xlatetom (relocated, &tmpdata, &rdata,
+ ehdr->e_ident[EI_DATA]);
+ if (d == NULL)
+ return DWFL_E_LIBELF;
+ assert (d == &tmpdata);
+ switch (type)
+ {
+#define DO_TYPE(NAME, Name) \
+ case ELF_T_##NAME: \
+ tmpbuf.Name += (GElf_##Name) value; \
+ break
+ TYPES;
+#undef DO_TYPE
+ default:
+ abort ();
+ }
+ }
+
+ /* Now convert the relocated datum back to the target
+ format. This will write into rdata.d_buf, which
+ points into the raw section data being relocated. */
+ Elf_Data *s = gelf_xlatetof (relocated, &rdata, &tmpdata,
+ ehdr->e_ident[EI_DATA]);
+ if (s == NULL)
+ return DWFL_E_LIBELF;
+ assert (s == &rdata);
+
+ /* We have applied this relocation! */
+ return DWFL_E_NOERROR;
+}
+
+static inline void
+check_badreltype (bool *first_badreltype,
+ Dwfl_Module *mod,
+ Dwfl_Error *result)
+{
+ if (*first_badreltype)
+ {
+ *first_badreltype = false;
+ if (ebl_get_elfmachine (mod->ebl) == EM_NONE)
+ /* This might be because ebl_openbackend failed to find
+ any libebl_CPU.so library. Diagnose that clearly. */
+ *result = DWFL_E_UNKNOWN_MACHINE;
+ }
+}
+
+static Dwfl_Error
+relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr,
+ size_t shstrndx, struct reloc_symtab_cache *reloc_symtab,
+ Elf_Scn *scn, GElf_Shdr *shdr,
+ Elf_Scn *tscn, bool debugscn, bool partial)
+{
+ /* First, fetch the name of the section these relocations apply to.
+ Then try to decompress both relocation and target section. */
+ GElf_Shdr tshdr_mem;
+ GElf_Shdr *tshdr = gelf_getshdr (tscn, &tshdr_mem);
+ if (tshdr == NULL)
+ return DWFL_E_LIBELF;
+
+ const char *tname = elf_strptr (relocated, shstrndx, tshdr->sh_name);
+ if (tname == NULL)
+ return DWFL_E_LIBELF;
+
+ if (debugscn && ! ebl_debugscn_p (mod->ebl, tname))
+ /* This relocation section is not for a debugging section.
+ Nothing to do here. */
+ return DWFL_E_NOERROR;
+
+ if (strncmp (tname, ".zdebug", strlen ("zdebug")) == 0)
+ elf_compress_gnu (tscn, 0, 0);
+
+ if ((tshdr->sh_flags & SHF_COMPRESSED) != 0)
+ if (elf_compress (tscn, 0, 0) < 0)
+ return DWFL_E_LIBELF;
+
+ /* Reload Shdr in case section was just decompressed. */
+ tshdr = gelf_getshdr (tscn, &tshdr_mem);
+ if (tshdr == NULL)
+ return DWFL_E_LIBELF;
+
+ if (unlikely (tshdr->sh_type == SHT_NOBITS)
+ || unlikely (tshdr->sh_size == 0))
+ /* No contents to relocate. */
+ return DWFL_E_NOERROR;
+
+ const char *sname = elf_strptr (relocated, shstrndx, shdr->sh_name);
+ if (sname == NULL)
+ return DWFL_E_LIBELF;
+
+ if (strncmp (sname, ".zdebug", strlen ("zdebug")) == 0)
+ elf_compress_gnu (scn, 0, 0);
+
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ if (elf_compress (scn, 0, 0) < 0)
+ return DWFL_E_LIBELF;
+
+ /* Reload Shdr in case section was just decompressed. */
+ GElf_Shdr shdr_mem;
+ shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr == NULL)
+ return DWFL_E_LIBELF;
+
+ /* Fetch the section data that needs the relocations applied. */
+ Elf_Data *tdata = elf_rawdata (tscn, NULL);
+ if (tdata == NULL)
+ return DWFL_E_LIBELF;
+
+ /* If either the section that needs the relocation applied, or the
+ section that the relocations come from overlap one of the ehdrs,
+ shdrs or phdrs data then we refuse to do the relocations. It
+ isn't illegal for ELF section data to overlap the header data,
+ but updating the (relocation) data might corrupt the in-memory
+ libelf headers causing strange corruptions or errors. */
+ size_t ehsize = gelf_fsize (relocated, ELF_T_EHDR, 1, EV_CURRENT);
+ if (unlikely (shdr->sh_offset < ehsize
+ || tshdr->sh_offset < ehsize))
+ return DWFL_E_BADELF;
+
+ GElf_Off shdrs_start = ehdr->e_shoff;
+ size_t shnums;
+ if (elf_getshdrnum (relocated, &shnums) < 0)
+ return DWFL_E_LIBELF;
+ /* Overflows will have been checked by elf_getshdrnum/get|rawdata. */
+ size_t shentsize = gelf_fsize (relocated, ELF_T_SHDR, 1, EV_CURRENT);
+ GElf_Off shdrs_end = shdrs_start + shnums * shentsize;
+ if (unlikely ((shdrs_start < shdr->sh_offset + shdr->sh_size
+ && shdr->sh_offset < shdrs_end)
+ || (shdrs_start < tshdr->sh_offset + tshdr->sh_size
+ && tshdr->sh_offset < shdrs_end)))
+ return DWFL_E_BADELF;
+
+ GElf_Off phdrs_start = ehdr->e_phoff;
+ size_t phnums;
+ if (elf_getphdrnum (relocated, &phnums) < 0)
+ return DWFL_E_LIBELF;
+ if (phdrs_start != 0 && phnums != 0)
+ {
+ /* Overflows will have been checked by elf_getphdrnum/get|rawdata. */
+ size_t phentsize = gelf_fsize (relocated, ELF_T_PHDR, 1, EV_CURRENT);
+ GElf_Off phdrs_end = phdrs_start + phnums * phentsize;
+ if (unlikely ((phdrs_start < shdr->sh_offset + shdr->sh_size
+ && shdr->sh_offset < phdrs_end)
+ || (phdrs_start < tshdr->sh_offset + tshdr->sh_size
+ && tshdr->sh_offset < phdrs_end)))
+ return DWFL_E_BADELF;
+ }
+
+ /* Fetch the relocation section and apply each reloc in it. */
+ Elf_Data *reldata = elf_getdata (scn, NULL);
+ if (reldata == NULL)
+ return DWFL_E_LIBELF;
+
+ Dwfl_Error result = DWFL_E_NOERROR;
+ bool first_badreltype = true;
+
+ size_t sh_entsize
+ = gelf_fsize (relocated, shdr->sh_type == SHT_REL ? ELF_T_REL : ELF_T_RELA,
+ 1, EV_CURRENT);
+ size_t nrels = shdr->sh_size / sh_entsize;
+ size_t complete = 0;
+ if (shdr->sh_type == SHT_REL)
+ for (size_t relidx = 0; !result && relidx < nrels; ++relidx)
+ {
+ GElf_Rel rel_mem, *r = gelf_getrel (reldata, relidx, &rel_mem);
+ if (r == NULL)
+ return DWFL_E_LIBELF;
+ result = relocate (mod, relocated, reloc_symtab, tdata, ehdr,
+ r->r_offset, NULL,
+ GELF_R_TYPE (r->r_info),
+ GELF_R_SYM (r->r_info));
+ check_badreltype (&first_badreltype, mod, &result);
+ if (partial)
+ switch (result)
+ {
+ case DWFL_E_NOERROR:
+ /* We applied the relocation. Elide it. */
+ memset (&rel_mem, 0, sizeof rel_mem);
+ if (unlikely (gelf_update_rel (reldata, relidx, &rel_mem) == 0))
+ return DWFL_E_LIBELF;
+ ++complete;
+ break;
+ case DWFL_E_BADRELTYPE:
+ case DWFL_E_RELUNDEF:
+ /* We couldn't handle this relocation. Skip it. */
+ result = DWFL_E_NOERROR;
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ for (size_t relidx = 0; !result && relidx < nrels; ++relidx)
+ {
+ GElf_Rela rela_mem, *r = gelf_getrela (reldata, relidx,
+ &rela_mem);
+ if (r == NULL)
+ return DWFL_E_LIBELF;
+ result = relocate (mod, relocated, reloc_symtab, tdata, ehdr,
+ r->r_offset, &r->r_addend,
+ GELF_R_TYPE (r->r_info),
+ GELF_R_SYM (r->r_info));
+ check_badreltype (&first_badreltype, mod, &result);
+ if (partial)
+ switch (result)
+ {
+ case DWFL_E_NOERROR:
+ /* We applied the relocation. Elide it. */
+ memset (&rela_mem, 0, sizeof rela_mem);
+ if (unlikely (gelf_update_rela (reldata, relidx,
+ &rela_mem) == 0))
+ return DWFL_E_LIBELF;
+ ++complete;
+ break;
+ case DWFL_E_BADRELTYPE:
+ case DWFL_E_RELUNDEF:
+ /* We couldn't handle this relocation. Skip it. */
+ result = DWFL_E_NOERROR;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (likely (result == DWFL_E_NOERROR))
+ {
+ if (!partial || complete == nrels)
+ /* Mark this relocation section as being empty now that we have
+ done its work. This affects unstrip -R, so e.g. it emits an
+ empty .rela.debug_info along with a .debug_info that has
+ already been fully relocated. */
+ nrels = 0;
+ else if (complete != 0)
+ {
+ /* We handled some of the relocations but not all.
+ We've zeroed out the ones we processed.
+ Now remove them from the section. */
+
+ size_t next = 0;
+ if (shdr->sh_type == SHT_REL)
+ for (size_t relidx = 0; relidx < nrels; ++relidx)
+ {
+ GElf_Rel rel_mem;
+ GElf_Rel *r = gelf_getrel (reldata, relidx, &rel_mem);
+ if (unlikely (r == NULL))
+ return DWFL_E_LIBELF;
+ if (r->r_info != 0 || r->r_offset != 0)
+ {
+ if (next != relidx)
+ if (unlikely (gelf_update_rel (reldata, next, r) == 0))
+ return DWFL_E_LIBELF;
+ ++next;
+ }
+ }
+ else
+ for (size_t relidx = 0; relidx < nrels; ++relidx)
+ {
+ GElf_Rela rela_mem;
+ GElf_Rela *r = gelf_getrela (reldata, relidx, &rela_mem);
+ if (unlikely (r == NULL))
+ return DWFL_E_LIBELF;
+ if (r->r_info != 0 || r->r_offset != 0 || r->r_addend != 0)
+ {
+ if (next != relidx)
+ if (unlikely (gelf_update_rela (reldata, next, r) == 0))
+ return DWFL_E_LIBELF;
+ ++next;
+ }
+ }
+ nrels = next;
+ }
+
+ shdr->sh_size = reldata->d_size = nrels * sh_entsize;
+ if (unlikely (gelf_update_shdr (scn, shdr) == 0))
+ return DWFL_E_LIBELF;
+ }
+
+ return result;
+}
+
+Dwfl_Error
+internal_function
+__libdwfl_relocate (Dwfl_Module *mod, Elf *debugfile, bool debug)
+{
+ assert (mod->e_type == ET_REL);
+
+ GElf_Ehdr ehdr_mem;
+ const GElf_Ehdr *ehdr = gelf_getehdr (debugfile, &ehdr_mem);
+ if (ehdr == NULL)
+ return DWFL_E_LIBELF;
+
+ size_t d_shstrndx;
+ if (elf_getshdrstrndx (debugfile, &d_shstrndx) < 0)
+ return DWFL_E_LIBELF;
+
+ RELOC_SYMTAB_CACHE (reloc_symtab);
+
+ /* Look at each section in the debuginfo file, and process the
+ relocation sections for debugging sections. */
+ Dwfl_Error result = DWFL_E_NOERROR;
+ Elf_Scn *scn = NULL;
+ while (result == DWFL_E_NOERROR
+ && (scn = elf_nextscn (debugfile, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (unlikely (shdr == NULL))
+ return DWFL_E_LIBELF;
+
+ if ((shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
+ && shdr->sh_size != 0)
+ {
+ /* It's a relocation section. */
+
+ Elf_Scn *tscn = elf_getscn (debugfile, shdr->sh_info);
+ if (unlikely (tscn == NULL))
+ result = DWFL_E_LIBELF;
+ else
+ result = relocate_section (mod, debugfile, ehdr, d_shstrndx,
+ &reloc_symtab, scn, shdr, tscn,
+ debug, !debug);
+ }
+ }
+
+ return result;
+}
+
+Dwfl_Error
+internal_function
+__libdwfl_relocate_section (Dwfl_Module *mod, Elf *relocated,
+ Elf_Scn *relocscn, Elf_Scn *tscn, bool partial)
+{
+ GElf_Ehdr ehdr_mem;
+ GElf_Shdr shdr_mem;
+
+ RELOC_SYMTAB_CACHE (reloc_symtab);
+
+ size_t shstrndx;
+ if (elf_getshdrstrndx (relocated, &shstrndx) < 0)
+ return DWFL_E_LIBELF;
+
+ Dwfl_Error result = __libdwfl_module_getebl (mod);
+ if (unlikely (result != DWFL_E_NOERROR))
+ return result;
+
+ GElf_Ehdr *ehdr = gelf_getehdr (relocated, &ehdr_mem);
+ if (unlikely (ehdr == NULL))
+ return DWFL_E_LIBELF;
+
+ GElf_Shdr *shdr = gelf_getshdr (relocscn, &shdr_mem);
+ if (unlikely (shdr == NULL))
+ return DWFL_E_LIBELF;
+
+ return relocate_section (mod, relocated, ehdr, shstrndx, &reloc_symtab,
+ relocscn, shdr, tscn, false, partial);
+}
diff --git a/libdwfl/segment.c b/libdwfl/segment.c
new file mode 100644
index 0000000..d9599a7
--- /dev/null
+++ b/libdwfl/segment.c
@@ -0,0 +1,337 @@
+/* Manage address space lookup table for libdwfl.
+ Copyright (C) 2008, 2009, 2010, 2013, 2015 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+GElf_Addr
+internal_function
+__libdwfl_segment_start (Dwfl *dwfl, GElf_Addr start)
+{
+ if (dwfl->segment_align > 1)
+ start &= -dwfl->segment_align;
+ return start;
+}
+
+GElf_Addr
+internal_function
+__libdwfl_segment_end (Dwfl *dwfl, GElf_Addr end)
+{
+ if (dwfl->segment_align > 1)
+ end = (end + dwfl->segment_align - 1) & -dwfl->segment_align;
+ return end;
+}
+
+static bool
+insert (Dwfl *dwfl, size_t i, GElf_Addr start, GElf_Addr end, int segndx)
+{
+ bool need_start = (i == 0 || dwfl->lookup_addr[i - 1] != start);
+ bool need_end = (i + 1 >= dwfl->lookup_elts
+ || dwfl->lookup_addr[i + 1] != end);
+ size_t need = need_start + need_end;
+ if (need == 0)
+ return false;
+
+ if (dwfl->lookup_alloc - dwfl->lookup_elts < need)
+ {
+ size_t n = dwfl->lookup_alloc == 0 ? 16 : dwfl->lookup_alloc * 2;
+ GElf_Addr *naddr = realloc (dwfl->lookup_addr, sizeof naddr[0] * n);
+ if (unlikely (naddr == NULL))
+ return true;
+ int *nsegndx = realloc (dwfl->lookup_segndx, sizeof nsegndx[0] * n);
+ if (unlikely (nsegndx == NULL))
+ {
+ if (naddr != dwfl->lookup_addr)
+ free (naddr);
+ return true;
+ }
+ dwfl->lookup_alloc = n;
+ dwfl->lookup_addr = naddr;
+ dwfl->lookup_segndx = nsegndx;
+
+ if (dwfl->lookup_module != NULL)
+ {
+ /* Make sure this array is big enough too. */
+ Dwfl_Module **old = dwfl->lookup_module;
+ dwfl->lookup_module = realloc (dwfl->lookup_module,
+ sizeof dwfl->lookup_module[0] * n);
+ if (unlikely (dwfl->lookup_module == NULL))
+ {
+ free (old);
+ return true;
+ }
+ }
+ }
+
+ if (unlikely (i < dwfl->lookup_elts))
+ {
+ const size_t move = dwfl->lookup_elts - i;
+ memmove (&dwfl->lookup_addr[i + need], &dwfl->lookup_addr[i],
+ move * sizeof dwfl->lookup_addr[0]);
+ memmove (&dwfl->lookup_segndx[i + need], &dwfl->lookup_segndx[i],
+ move * sizeof dwfl->lookup_segndx[0]);
+ if (dwfl->lookup_module != NULL)
+ memmove (&dwfl->lookup_module[i + need], &dwfl->lookup_module[i],
+ move * sizeof dwfl->lookup_module[0]);
+ }
+
+ if (need_start)
+ {
+ dwfl->lookup_addr[i] = start;
+ dwfl->lookup_segndx[i] = segndx;
+ if (dwfl->lookup_module != NULL)
+ dwfl->lookup_module[i] = NULL;
+ ++i;
+ }
+ else
+ dwfl->lookup_segndx[i - 1] = segndx;
+
+ if (need_end)
+ {
+ dwfl->lookup_addr[i] = end;
+ dwfl->lookup_segndx[i] = -1;
+ if (dwfl->lookup_module != NULL)
+ dwfl->lookup_module[i] = NULL;
+ }
+
+ dwfl->lookup_elts += need;
+
+ return false;
+}
+
+static int
+lookup (Dwfl *dwfl, GElf_Addr address, int hint)
+{
+ if (hint >= 0
+ && address >= dwfl->lookup_addr[hint]
+ && ((size_t) hint + 1 == dwfl->lookup_elts
+ || address < dwfl->lookup_addr[hint + 1]))
+ return hint;
+
+ /* Do binary search on the array indexed by module load address. */
+ size_t l = 0, u = dwfl->lookup_elts;
+ while (l < u)
+ {
+ size_t idx = (l + u) / 2;
+ if (address < dwfl->lookup_addr[idx])
+ u = idx;
+ else
+ {
+ l = idx + 1;
+ if (l == dwfl->lookup_elts || address < dwfl->lookup_addr[l])
+ return idx;
+ }
+ }
+
+ return -1;
+}
+
+static bool
+reify_segments (Dwfl *dwfl)
+{
+ int hint = -1;
+ int highest = -1;
+ bool fixup = false;
+ for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next)
+ if (! mod->gc)
+ {
+ const GElf_Addr start = __libdwfl_segment_start (dwfl, mod->low_addr);
+ const GElf_Addr end = __libdwfl_segment_end (dwfl, mod->high_addr);
+ bool resized = false;
+
+ int idx = lookup (dwfl, start, hint);
+ if (unlikely (idx < 0))
+ {
+ /* Module starts below any segment. Insert a low one. */
+ if (unlikely (insert (dwfl, 0, start, end, -1)))
+ return true;
+ idx = 0;
+ resized = true;
+ }
+ else if (dwfl->lookup_addr[idx] > start)
+ {
+ /* The module starts in the middle of this segment. Split it. */
+ if (unlikely (insert (dwfl, idx + 1, start, end,
+ dwfl->lookup_segndx[idx])))
+ return true;
+ ++idx;
+ resized = true;
+ }
+ else if (dwfl->lookup_addr[idx] < start)
+ {
+ /* The module starts past the end of this segment.
+ Add a new one. */
+ if (unlikely (insert (dwfl, idx + 1, start, end, -1)))
+ return true;
+ ++idx;
+ resized = true;
+ }
+
+ if ((size_t) idx + 1 < dwfl->lookup_elts
+ && end < dwfl->lookup_addr[idx + 1])
+ {
+ /* The module ends in the middle of this segment. Split it. */
+ if (unlikely (insert (dwfl, idx + 1,
+ end, dwfl->lookup_addr[idx + 1], -1)))
+ return true;
+ resized = true;
+ }
+
+ if (dwfl->lookup_module == NULL)
+ {
+ dwfl->lookup_module = calloc (dwfl->lookup_alloc,
+ sizeof dwfl->lookup_module[0]);
+ if (unlikely (dwfl->lookup_module == NULL))
+ return true;
+ }
+
+ /* Cache a backpointer in the module. */
+ mod->segment = idx;
+
+ /* Put MOD in the table for each segment that's inside it. */
+ do
+ dwfl->lookup_module[idx++] = mod;
+ while ((size_t) idx < dwfl->lookup_elts
+ && dwfl->lookup_addr[idx] < end);
+ assert (dwfl->lookup_module[mod->segment] == mod);
+
+ if (resized && idx - 1 >= highest)
+ /* Expanding the lookup tables invalidated backpointers
+ we've already stored. Reset those ones. */
+ fixup = true;
+
+ highest = idx - 1;
+ hint = (size_t) idx < dwfl->lookup_elts ? idx : -1;
+ }
+
+ if (fixup)
+ /* Reset backpointer indices invalidated by table insertions. */
+ for (size_t idx = 0; idx < dwfl->lookup_elts; ++idx)
+ if (dwfl->lookup_module[idx] != NULL)
+ dwfl->lookup_module[idx]->segment = idx;
+
+ return false;
+}
+
+int
+dwfl_addrsegment (Dwfl *dwfl, Dwarf_Addr address, Dwfl_Module **mod)
+{
+ if (unlikely (dwfl == NULL))
+ return -1;
+
+ if (unlikely (dwfl->lookup_module == NULL)
+ && mod != NULL
+ && unlikely (reify_segments (dwfl)))
+ {
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return -1;
+ }
+
+ int idx = lookup (dwfl, address, -1);
+ if (likely (mod != NULL))
+ {
+ if (unlikely (idx < 0) || unlikely (dwfl->lookup_module == NULL))
+ *mod = NULL;
+ else
+ {
+ *mod = dwfl->lookup_module[idx];
+
+ /* If this segment does not have a module, but the address is
+ the upper boundary of the previous segment's module, use that. */
+ if (*mod == NULL && idx > 0 && dwfl->lookup_addr[idx] == address)
+ {
+ *mod = dwfl->lookup_module[idx - 1];
+ if (*mod != NULL && (*mod)->high_addr != address)
+ *mod = NULL;
+ }
+ }
+ }
+
+ if (likely (idx >= 0))
+ /* Translate internal segment table index to user segment index. */
+ idx = dwfl->lookup_segndx[idx];
+
+ return idx;
+}
+INTDEF (dwfl_addrsegment)
+
+int
+dwfl_report_segment (Dwfl *dwfl, int ndx, const GElf_Phdr *phdr, GElf_Addr bias,
+ const void *ident)
+{
+ if (dwfl == NULL)
+ return -1;
+
+ if (ndx < 0)
+ ndx = dwfl->lookup_tail_ndx;
+
+ if (phdr->p_align > 1 && (dwfl->segment_align <= 1 ||
+ phdr->p_align < dwfl->segment_align))
+ dwfl->segment_align = phdr->p_align;
+
+ if (unlikely (dwfl->lookup_module != NULL))
+ {
+ free (dwfl->lookup_module);
+ dwfl->lookup_module = NULL;
+ }
+
+ GElf_Addr start = __libdwfl_segment_start (dwfl, bias + phdr->p_vaddr);
+ GElf_Addr end = __libdwfl_segment_end (dwfl,
+ bias + phdr->p_vaddr + phdr->p_memsz);
+
+ /* Coalesce into the last one if contiguous and matching. */
+ if (ndx != dwfl->lookup_tail_ndx
+ || ident == NULL
+ || ident != dwfl->lookup_tail_ident
+ || start != dwfl->lookup_tail_vaddr
+ || phdr->p_offset != dwfl->lookup_tail_offset)
+ {
+ /* Normally just appending keeps us sorted. */
+
+ size_t i = dwfl->lookup_elts;
+ while (i > 0 && unlikely (start < dwfl->lookup_addr[i - 1]))
+ --i;
+
+ if (unlikely (insert (dwfl, i, start, end, ndx)))
+ {
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return -1;
+ }
+ }
+
+ dwfl->lookup_tail_ident = ident;
+ dwfl->lookup_tail_vaddr = end;
+ dwfl->lookup_tail_offset = end - bias - phdr->p_vaddr + phdr->p_offset;
+ dwfl->lookup_tail_ndx = ndx + 1;
+
+ return ndx;
+}
+INTDEF (dwfl_report_segment)