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, &notes_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, &regno))
+    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, &regno))
+    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, &reg_ops,
+				&reg_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, &regval))
+		continue;
+	    }
+	  else
+	    {
+	      __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+	      continue;
+	    }
+	}
+      else if (! expr_eval (state, frame, reg_ops, reg_nops, &regval, 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,
+			   &regs_offset, &nregloc, &reglocs, &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, &regs_offset,
+				     &nregloc, &reglocs, &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,
+			   &regs_offset, &nregloc, &reglocs, &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, &notes);
+  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)