diff options
Diffstat (limited to 'src/libdwfl')
67 files changed, 15720 insertions, 0 deletions
diff --git a/src/libdwfl/Android.mk b/src/libdwfl/Android.mk new file mode 100755 index 00000000..dc6a14ad --- /dev/null +++ b/src/libdwfl/Android.mk @@ -0,0 +1,116 @@ +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH := $(call my-dir) + +LIBDWFL_SRC_FILES := \ + dwfl_addrdwarf.c \ + dwfl_addrmodule.c \ + dwfl_begin.c \ + dwfl_build_id_find_elf.c \ + dwfl_build_id_find_debuginfo.c \ + dwfl_end.c \ + dwfl_error.c \ + dwfl_module.c \ + dwfl_module_addrdie.c \ + dwfl_module_addrsym.c \ + dwfl_module_build_id.c \ + dwfl_report_elf.c \ + dwfl_module_getdwarf.c \ + dwfl_module_getsym.c \ + dwfl_module_report_build_id.c \ + find-debuginfo.c \ + image-header.c \ + libdwfl_crc32.c \ + libdwfl_crc32_file.c \ + linux-kernel-modules.c \ + offline.c \ + open.c \ + relocate.c \ + segment.c \ + +ifeq ($(HOST_OS),linux) + +# +# host libdwfl +# + +include $(CLEAR_VARS) + +# Clang has no nested functions. +LOCAL_CLANG := false + +LOCAL_SRC_FILES := $(LIBDWFL_SRC_FILES) + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/.. \ + $(LOCAL_PATH)/../lib \ + $(LOCAL_PATH)/../libdwfl \ + $(LOCAL_PATH)/../libebl \ + $(LOCAL_PATH)/../libdw \ + $(LOCAL_PATH)/../libelf + +LOCAL_CFLAGS += -DHAVE_CONFIG_H -std=gnu99 -D_GNU_SOURCE + +# to suppress the "pointer of type ‘void *’ used in arithmetic" warning +LOCAL_CFLAGS += -Wno-pointer-arith + +# Asserts are not compiled, so some debug variables appear unused. Rather than +# fix, we prefer to turn off the warning locally. +LOCAL_CFLAGS += -Wno-unused-but-set-variable + +LOCAL_MODULE:= libdwfl + +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) + +include $(BUILD_HOST_STATIC_LIBRARY) + +endif # linux + +# +# target libdwfl +# + +include $(CLEAR_VARS) + +# Clang has no nested functions. +LOCAL_CLANG := false + +LOCAL_SRC_FILES := $(LIBDWFL_SRC_FILES) + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/.. \ + $(LOCAL_PATH)/../lib \ + $(LOCAL_PATH)/../libdwfl \ + $(LOCAL_PATH)/../libebl \ + $(LOCAL_PATH)/../libdw \ + $(LOCAL_PATH)/../libelf + +LOCAL_C_INCLUDES += $(LOCAL_PATH)/../bionic-fixup + +LOCAL_CFLAGS += -include $(LOCAL_PATH)/../bionic-fixup/AndroidFixup.h + +LOCAL_CFLAGS += -DHAVE_CONFIG_H -std=gnu99 -Werror + +# to suppress the "pointer of type ‘void *’ used in arithmetic" warning +LOCAL_CFLAGS += -Wno-pointer-arith + +# See above. +LOCAL_CFLAGS += -Wno-unused-but-set-variable + +LOCAL_MODULE:= libdwfl + +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) + +include $(BUILD_STATIC_LIBRARY) diff --git a/src/libdwfl/ChangeLog b/src/libdwfl/ChangeLog new file mode 100644 index 00000000..87a05550 --- /dev/null +++ b/src/libdwfl/ChangeLog @@ -0,0 +1,1418 @@ +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/src/libdwfl/Makefile.am b/src/libdwfl/Makefile.am new file mode 100644 index 00000000..65b38965 --- /dev/null +++ b/src/libdwfl/Makefile.am @@ -0,0 +1,99 @@ +## Makefile.am for libdwfl library subdirectory in elfutils. +## +## Process this file with automake to create Makefile.in +## +## Copyright (C) 2005-2010 Red Hat, Inc. +## This file is part of Red Hat elfutils. +## +## Red Hat elfutils is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by the +## Free Software Foundation; version 2 of the License. +## +## Red Hat 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 a copy of the GNU General Public License along +## with Red Hat elfutils; if not, write to the Free Software Foundation, +## Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. +## +## Red Hat elfutils is an included package of the Open Invention Network. +## An included package of the Open Invention Network is a package for which +## Open Invention Network licensees cross-license their patents. No patent +## license is granted, either expressly or impliedly, by designation as an +## included package. Should you wish to participate in the Open Invention +## Network licensing program, please visit www.openinventionnetwork.com +## <http://www.openinventionnetwork.com>. +## +include $(top_srcdir)/config/eu.am +INCLUDES += -I$(srcdir) -I$(srcdir)/../libelf -I$(srcdir)/../libebl \ + -I$(srcdir)/../libdw +VERSION = 1 + +noinst_LIBRARIES = libdwfl.a +if !MUDFLAP +noinst_LIBRARIES += libdwfl_pic.a +endif + +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 + +if ZLIB +libdwfl_a_SOURCES += gzip.c +endif +if BZLIB +libdwfl_a_SOURCES += bzip2.c +endif +if LZMA +libdwfl_a_SOURCES += lzma.c +endif + +if MUDFLAP +libdwfl = libdwfl.a $(libdw) $(libebl) $(libelf) $(libeu) +libdw = ../libdw/libdw.a +libelf = ../libelf/libelf.a +else +libdwfl = $(libdw) +libdw = ../libdw/libdw.so +libelf = ../libelf/libelf.so +endif +libebl = ../libebl/libebl.a +libeu = ../lib/libeu.a + +if !MUDFLAP +libdwfl_pic_a_SOURCES = +am_libdwfl_pic_a_OBJECTS = $(libdwfl_a_SOURCES:.c=.os) +endif + +noinst_HEADERS = libdwflP.h + +CLEANFILES += $(am_libdwfl_pic_a_OBJECTS) diff --git a/src/libdwfl/Makefile.in b/src/libdwfl/Makefile.in new file mode 100644 index 00000000..b22e1ad1 --- /dev/null +++ b/src/libdwfl/Makefile.in @@ -0,0 +1,680 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +DIST_COMMON = $(noinst_HEADERS) $(pkginclude_HEADERS) \ + $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(top_srcdir)/config/eu.am ChangeLog +@MUDFLAP_TRUE@am__append_1 = -fmudflap +@MUDFLAP_FALSE@am__append_2 = libdwfl_pic.a +@ZLIB_TRUE@am__append_3 = gzip.c +@BZLIB_TRUE@am__append_4 = bzip2.c +@LZMA_TRUE@am__append_5 = lzma.c +@MUDFLAP_TRUE@am_libdwfl_pic_a_OBJECTS = +subdir = libdwfl +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \ + $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/zip.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +libdwfl_a_AR = $(AR) $(ARFLAGS) +libdwfl_a_LIBADD = +am__libdwfl_a_SOURCES_DIST = 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 gzip.c bzip2.c \ + lzma.c +@ZLIB_TRUE@am__objects_1 = gzip.$(OBJEXT) +@BZLIB_TRUE@am__objects_2 = bzip2.$(OBJEXT) +@LZMA_TRUE@am__objects_3 = lzma.$(OBJEXT) +am_libdwfl_a_OBJECTS = dwfl_begin.$(OBJEXT) dwfl_end.$(OBJEXT) \ + dwfl_error.$(OBJEXT) dwfl_version.$(OBJEXT) \ + dwfl_module.$(OBJEXT) dwfl_report_elf.$(OBJEXT) \ + relocate.$(OBJEXT) dwfl_module_build_id.$(OBJEXT) \ + dwfl_module_report_build_id.$(OBJEXT) derelocate.$(OBJEXT) \ + offline.$(OBJEXT) segment.$(OBJEXT) dwfl_module_info.$(OBJEXT) \ + dwfl_getmodules.$(OBJEXT) dwfl_getdwarf.$(OBJEXT) \ + dwfl_module_getdwarf.$(OBJEXT) dwfl_module_getelf.$(OBJEXT) \ + dwfl_validate_address.$(OBJEXT) argp-std.$(OBJEXT) \ + find-debuginfo.$(OBJEXT) dwfl_build_id_find_elf.$(OBJEXT) \ + dwfl_build_id_find_debuginfo.$(OBJEXT) \ + linux-kernel-modules.$(OBJEXT) linux-proc-maps.$(OBJEXT) \ + dwfl_addrmodule.$(OBJEXT) dwfl_addrdwarf.$(OBJEXT) \ + cu.$(OBJEXT) dwfl_module_nextcu.$(OBJEXT) \ + dwfl_nextcu.$(OBJEXT) dwfl_cumodule.$(OBJEXT) \ + dwfl_module_addrdie.$(OBJEXT) dwfl_addrdie.$(OBJEXT) \ + lines.$(OBJEXT) dwfl_lineinfo.$(OBJEXT) \ + dwfl_line_comp_dir.$(OBJEXT) dwfl_linemodule.$(OBJEXT) \ + dwfl_linecu.$(OBJEXT) dwfl_dwarf_line.$(OBJEXT) \ + dwfl_getsrclines.$(OBJEXT) dwfl_onesrcline.$(OBJEXT) \ + dwfl_module_getsrc.$(OBJEXT) dwfl_getsrc.$(OBJEXT) \ + dwfl_module_getsrc_file.$(OBJEXT) libdwfl_crc32.$(OBJEXT) \ + libdwfl_crc32_file.$(OBJEXT) elf-from-memory.$(OBJEXT) \ + dwfl_module_dwarf_cfi.$(OBJEXT) dwfl_module_eh_cfi.$(OBJEXT) \ + dwfl_module_getsym.$(OBJEXT) dwfl_module_addrname.$(OBJEXT) \ + dwfl_module_addrsym.$(OBJEXT) \ + dwfl_module_return_value_location.$(OBJEXT) \ + dwfl_module_register_names.$(OBJEXT) \ + dwfl_segment_report_module.$(OBJEXT) link_map.$(OBJEXT) \ + core-file.$(OBJEXT) open.$(OBJEXT) image-header.$(OBJEXT) \ + $(am__objects_1) $(am__objects_2) $(am__objects_3) +libdwfl_a_OBJECTS = $(am_libdwfl_a_OBJECTS) +libdwfl_pic_a_AR = $(AR) $(ARFLAGS) +libdwfl_pic_a_LIBADD = +libdwfl_pic_a_OBJECTS = $(am_libdwfl_pic_a_OBJECTS) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(libdwfl_a_SOURCES) $(libdwfl_pic_a_SOURCES) +DIST_SOURCES = $(am__libdwfl_a_SOURCES_DIST) $(libdwfl_pic_a_SOURCES) +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__installdirs = "$(DESTDIR)$(pkgincludedir)" +HEADERS = $(noinst_HEADERS) $(pkginclude_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEBUGPRED = @DEBUGPRED@ +DEFS = -D_GNU_SOURCE -DHAVE_CONFIG_H -DLOCALEDIR='"${localedir}"' +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EXEEXT = @EXEEXT@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBEBL_SUBDIR = @LIBEBL_SUBDIR@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MODVERSION = @MODVERSION@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USE_NLS = @USE_NLS@ +VERSION = 1 +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +base_cpu = @base_cpu@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +eu_version = @eu_version@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +zip_LIBS = @zip_LIBS@ +INCLUDES = -I. -I$(srcdir) -I$(top_srcdir)/lib -I.. -I$(srcdir) \ + -I$(srcdir)/../libelf -I$(srcdir)/../libebl \ + -I$(srcdir)/../libdw +AM_CFLAGS = -std=gnu99 -Wall -Wshadow $(if \ + $($(*F)_no_Werror),,-Werror) $(if \ + $($(*F)_no_Wunused),,-Wunused -Wextra) $(if \ + $($(*F)_no_Wformat),-Wno-format,-Wformat=2) $($(*F)_CFLAGS) \ + $(am__append_1) +@MUDFLAP_FALSE@libmudflap = +@MUDFLAP_TRUE@libmudflap = -lmudflap +COMPILE.os = $(filter-out -fprofile-arcs -ftest-coverage $(no_mudflap.os),\ + $(COMPILE)) + +CLEANFILES = *.gcno *.gcda $(am_libdwfl_pic_a_OBJECTS) +textrel_check = if readelf -d $@ | fgrep -q TEXTREL; then exit 1; fi +noinst_LIBRARIES = libdwfl.a $(am__append_2) +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 $(am__append_3) \ + $(am__append_4) $(am__append_5) +@MUDFLAP_FALSE@libdwfl = $(libdw) +@MUDFLAP_TRUE@libdwfl = libdwfl.a $(libdw) $(libebl) $(libelf) $(libeu) +@MUDFLAP_FALSE@libdw = ../libdw/libdw.so +@MUDFLAP_TRUE@libdw = ../libdw/libdw.a +@MUDFLAP_FALSE@libelf = ../libelf/libelf.so +@MUDFLAP_TRUE@libelf = ../libelf/libelf.a +libebl = ../libebl/libebl.a +libeu = ../lib/libeu.a +@MUDFLAP_FALSE@libdwfl_pic_a_SOURCES = +@MUDFLAP_FALSE@am_libdwfl_pic_a_OBJECTS = $(libdwfl_a_SOURCES:.c=.os) +noinst_HEADERS = libdwflP.h +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/config/eu.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnits libdwfl/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnits libdwfl/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libdwfl.a: $(libdwfl_a_OBJECTS) $(libdwfl_a_DEPENDENCIES) + -rm -f libdwfl.a + $(libdwfl_a_AR) libdwfl.a $(libdwfl_a_OBJECTS) $(libdwfl_a_LIBADD) + $(RANLIB) libdwfl.a +libdwfl_pic.a: $(libdwfl_pic_a_OBJECTS) $(libdwfl_pic_a_DEPENDENCIES) + -rm -f libdwfl_pic.a + $(libdwfl_pic_a_AR) libdwfl_pic.a $(libdwfl_pic_a_OBJECTS) $(libdwfl_pic_a_LIBADD) + $(RANLIB) libdwfl_pic.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/argp-std.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bzip2.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/core-file.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cu.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/derelocate.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_addrdie.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_addrdwarf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_addrmodule.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_begin.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_build_id_find_debuginfo.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_build_id_find_elf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_cumodule.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_dwarf_line.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_end.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_error.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_getdwarf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_getmodules.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_getsrc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_getsrclines.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_line_comp_dir.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_linecu.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_lineinfo.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_linemodule.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_module.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_module_addrdie.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_module_addrname.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_module_addrsym.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_module_build_id.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_module_dwarf_cfi.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_module_eh_cfi.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_module_getdwarf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_module_getelf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_module_getsrc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_module_getsrc_file.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_module_getsym.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_module_info.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_module_nextcu.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_module_register_names.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_module_report_build_id.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_module_return_value_location.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_nextcu.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_onesrcline.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_report_elf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_segment_report_module.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_validate_address.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwfl_version.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf-from-memory.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/find-debuginfo.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gzip.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/image-header.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdwfl_crc32.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdwfl_crc32_file.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lines.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/link_map.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/linux-kernel-modules.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/linux-proc-maps.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lzma.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/offline.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/open.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/relocate.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/segment.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` +install-pkgincludeHEADERS: $(pkginclude_HEADERS) + @$(NORMAL_INSTALL) + test -z "$(pkgincludedir)" || $(MKDIR_P) "$(DESTDIR)$(pkgincludedir)" + @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkgincludedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkgincludedir)" || exit $$?; \ + done + +uninstall-pkgincludeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(pkgincludedir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(pkgincludedir)" && rm -f $$files + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(pkgincludedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pkgincludeHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-pkgincludeHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES ctags distclean distclean-compile \ + distclean-generic distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-pkgincludeHEADERS install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-pkgincludeHEADERS + + +%.os: %.c %.o +@AMDEP_TRUE@ if $(COMPILE.os) -c -o $@ -fpic -DPIC -DSHARED -MT $@ -MD -MP \ +@AMDEP_TRUE@ -MF "$(DEPDIR)/$*.Tpo" `test -f '$<' || echo '$(srcdir)/'`$<; \ +@AMDEP_TRUE@ then cat "$(DEPDIR)/$*.Tpo" >> "$(DEPDIR)/$*.Po"; \ +@AMDEP_TRUE@ rm -f "$(DEPDIR)/$*.Tpo"; \ +@AMDEP_TRUE@ else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; \ +@AMDEP_TRUE@ fi +@AMDEP_FALSE@ $(COMPILE.os) -c -o $@ -fpic -DPIC -DSHARED $< + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/libdwfl/argp-std.c b/src/libdwfl/argp-std.c new file mode 100644 index 00000000..e598c6ef --- /dev/null +++ b/src/libdwfl/argp-std.c @@ -0,0 +1,334 @@ +/* Standard argp argument parsers for tools using libdwfl. + Copyright (C) 2005-2010 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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), + }; + +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + inline void failure (Dwfl *dwfl, int errnum, const char *msg) + { + 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); + } + inline error_t fail (Dwfl *dwfl, int errnum, const char *msg) + { + failure (dwfl, errnum, msg); + return errnum == -1 ? EIO : errnum; + } + + switch (key) + { + case OPT_DEBUGINFO: + debuginfo_path = arg; + break; + + case 'e': + { + Dwfl *dwfl = state->hook; + if (dwfl == NULL) + { + dwfl = INTUSE(dwfl_begin) (&offline_callbacks); + if (dwfl == NULL) + return fail (dwfl, -1, arg); + state->hook = 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) + { + if (INTUSE(dwfl_report_offline) (dwfl, "", arg, -1) == NULL) + return fail (dwfl, -1, arg); + state->hook = dwfl; + } + else + { + toomany: + argp_error (state, "%s", + _("only one of -e, -p, -k, -K, or --core allowed")); + return EINVAL; + } + } + break; + + case 'p': + if (state->hook == 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->hook = dwfl; + } + else + goto toomany; + break; + + case 'M': + if (state->hook == NULL) + { + FILE *f = fopen (arg, "r"); + if (f == NULL) + nofile: + { + 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->hook = dwfl; + } + else + goto toomany; + break; + + case OPT_COREFILE: + { + Dwfl *dwfl = state->hook; + if (dwfl == NULL) + state->hook = dwfl = INTUSE(dwfl_begin) (&offline_callbacks); + /* Permit -e and --core together. */ + else if (dwfl->callbacks != &offline_callbacks) + goto toomany; + + int fd = open64 (arg, O_RDONLY); + if (fd < 0) + goto nofile; + + 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); + if (result < 0) + { + elf_end (core); + close (fd); + return fail (dwfl, result, arg); + } + + /* From now we leak FD and CORE. */ + + if (result == 0) + { + argp_failure (state, EXIT_FAILURE, 0, + _("No modules recognized in core file")); + return ENOENT; + } + } + break; + + case 'k': + if (state->hook == 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")); + result = INTUSE(dwfl_linux_kernel_report_modules) (dwfl); + if (result != 0) + /* Non-fatal to have no modules since we do have the kernel. */ + failure (dwfl, result, _("cannot find kernel modules")); + state->hook = dwfl; + } + else + goto toomany; + break; + + case 'K': + if (state->hook == 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->hook = dwfl; + } + else + goto toomany; + break; + + case ARGP_KEY_SUCCESS: + { + Dwfl *dwfl = state->hook; + + 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->hook = dwfl; + } + + /* 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); + } + break; + + case ARGP_KEY_ERROR: + dwfl_end (state->hook); + state->hook = NULL; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + + /* Update the input all along, so a parent parser can see it. */ + *(Dwfl **) state->input = state->hook; + return 0; +} + +static const struct argp libdwfl_argp = + { .options = options, .parser = parse_opt }; + +const struct argp * +dwfl_standard_argp (void) +{ + return &libdwfl_argp; +} + +#ifdef _MUDFLAP +/* In the absence of a mudflap wrapper for argp_parse, or a libc compiled + with -fmudflap, we'll see spurious errors for using the struct argp_state + on argp_parse's stack. */ + +void __attribute__ ((constructor)) +__libdwfl_argp_mudflap_options (void) +{ + __mf_set_options ("-heur-stack-bound"); +} +#endif diff --git a/src/libdwfl/bzip2.c b/src/libdwfl/bzip2.c new file mode 100644 index 00000000..8ad4ee5a --- /dev/null +++ b/src/libdwfl/bzip2.c @@ -0,0 +1,4 @@ +/* bzlib is almost just like zlib. */ + +#define BZLIB +#include "gzip.c" diff --git a/src/libdwfl/core-file.c b/src/libdwfl/core-file.c new file mode 100644 index 00000000..1b556dde --- /dev/null +++ b/src/libdwfl/core-file.c @@ -0,0 +1,484 @@ +/* Core file handling. + Copyright (C) 2008-2010 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#include <config.h> +#include "../libelf/libelfP.h" /* For NOTE_ALIGN. */ +#undef _ +#include "libdwflP.h" +#include <gelf.h> + +#include <sys/param.h> +#include <unistd.h> +#include <endian.h> +#include <byteswap.h> +#include "system.h" + + +/* 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, loff_t offset, loff_t size, loff_t *next) +{ + if (parent == NULL) + return NULL; + + /* On failure return, we update *NEXT to point back at OFFSET. */ + inline Elf *fail (int error) + { + if (next != NULL) + *next = offset; + //__libelf_seterrno (error); + __libdwfl_seterrno (DWFL_E (LIBELF, error)); + return NULL; + } + + loff_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 >= (loff_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 ((loff_t) parent->maximum_size - offset < size)) + return fail (ELF_E_INVALID_ARCHIVE); + } + + if (unlikely ((loff_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 == (loff_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 ((loff_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 + +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; + + 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. + 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 (mod->build_id_len > 0) + /* 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; + + 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; +} + +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; + + inline void update_end () + { + end = (phdr.p_offset + phdr.p_filesz + align - 1) & -align; + end_vaddr = (phdr.p_vaddr + phdr.p_memsz + align - 1) & -align; + } + + update_end (); + + /* Use following contiguous segments to get towards SIZE. */ + inline bool more (size_t size) + { + while (end <= start || end - start < size) + { + if (phdr.p_filesz < phdr.p_memsz) + /* This segment is truncated, so no following one helps us. */ + return false; + + if (unlikely (gelf_getphdr (elf, ndx++, &phdr) == NULL)) + return false; + + if (phdr.p_type == PT_LOAD) + { + if (phdr.p_offset > end + || phdr.p_vaddr > end_vaddr) + /* It's discontiguous! */ + return false; + + update_end (); + } + } + return true; + } + + /* 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; +} + +int +dwfl_core_file_report (Dwfl *dwfl, Elf *elf) +{ + size_t phnum; + if (unlikely (elf_getphdrnum (elf, &phnum) != 0)) + { + __libdwfl_seterrno (DWFL_E_LIBELF); + return -1; + } + + /* First report each PT_LOAD segment. */ + GElf_Phdr notes_phdr; + int ndx = dwfl_report_core_segments (dwfl, elf, phnum, ¬es_phdr); + if (unlikely (ndx <= 0)) + return ndx; + + /* Now sniff segment contents for modules. */ + int sniffed = 0; + ndx = 0; + do + { + int seg = dwfl_segment_report_module (dwfl, ndx, NULL, + &dwfl_elf_phdr_memory_callback, elf, + core_file_read_eagerly, elf); + if (unlikely (seg < 0)) + return seg; + if (seg > ndx) + { + ndx = seg; + ++sniffed; + } + else + ++ndx; + } + while (ndx < (int) phnum); + + /* Next, we should follow the chain from DT_DEBUG. */ + + const void *auxv = NULL; + size_t auxv_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_type == NT_AUXV + && nhdr.n_namesz == sizeof "CORE" + && !memcmp (notes->d_buf + name_pos, "CORE", sizeof "CORE")) + { + auxv = notes->d_buf + desc_pos; + auxv_size = nhdr.n_descsz; + break; + } + } + } + + /* Now we have NT_AUXV contents. From here on this processing could be + used for a live process with auxv read from /proc. */ + + int listed = dwfl_link_map_report (dwfl, auxv, auxv_size, + dwfl_elf_phdr_memory_callback, elf); + + /* 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. If link_map handling + failed, we still have the sniffed modules. */ + return sniffed == 0 || listed > sniffed ? listed : sniffed; +} +INTDEF (dwfl_core_file_report) diff --git a/src/libdwfl/cu.c b/src/libdwfl/cu.c new file mode 100644 index 00000000..515aff3b --- /dev/null +++ b/src/libdwfl/cu.c @@ -0,0 +1,324 @@ +/* Keeping track of DWARF compilation units in libdwfl. + Copyright (C) 2005-2010 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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) +{ + return DIE_OFFSET_FROM_CU_OFFSET (cu->die.cu->start, cu->die.cu->offset_size, + cu->die.cu->type_sig8 != 0); +} + +static int +compare_cukey (const void *a, const void *b) +{ + return cudie_offset (a) - cudie_offset (b); +} + +/* Intern the CU if necessary. */ +static Dwfl_Error +intern_cu (Dwfl_Module *mod, Dwarf_Off cuoff, struct dwfl_cu **result) +{ + 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) + { + if (unlikely (cuoff + 4 >= mod->dw->sectiondata[IDX_debug_info]->d_size)) + { + /* This is the EOF marker. Now we have interned all the CUs. + One increment in MOD->lazycu counts not having hit EOF yet. */ + *found = (void *) -1l; + less_lazy (mod); + } + else + { + /* 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; + + /* XXX use non-searching lookup */ + Dwarf_Die *die = INTUSE(dwarf_offdie) (mod->dw, cuoff, &cu->die); + if (die == NULL) + return DWFL_E_LIBDW; + assert (die == &cu->die); + + 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)->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/src/libdwfl/derelocate.c b/src/libdwfl/derelocate.c new file mode 100644 index 00000000..b8c8dd73 --- /dev/null +++ b/src/libdwfl/derelocate.c @@ -0,0 +1,411 @@ +/* Recover relocatibility for addresses computed from debug information. + Copyright (C) 2005-2010 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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; + + return 0; +} + +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); + return -1; + } + + 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 = alloca (sizeof *newref); + 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 (mod->reloc_info == NULL) + { + __libdwfl_seterrno (DWFL_E_NOMEM); + return -1; + } + + struct secref **sortrefs = alloca (nrefs * sizeof sortrefs[0]); + 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; + } + + 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; + } + } + } + } + + 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 (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 < 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; +} + +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/src/libdwfl/dwfl_addrdie.c b/src/libdwfl/dwfl_addrdie.c new file mode 100644 index 00000000..20c15783 --- /dev/null +++ b/src/libdwfl/dwfl_addrdie.c @@ -0,0 +1,57 @@ +/* Fetch CU DIE from address. + Copyright (C) 2005 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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/src/libdwfl/dwfl_addrdwarf.c b/src/libdwfl/dwfl_addrdwarf.c new file mode 100644 index 00000000..365c69c5 --- /dev/null +++ b/src/libdwfl/dwfl_addrdwarf.c @@ -0,0 +1,58 @@ +/* Fetch libdw handle from address. + Copyright (C) 2005 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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/src/libdwfl/dwfl_addrmodule.c b/src/libdwfl/dwfl_addrmodule.c new file mode 100644 index 00000000..64559436 --- /dev/null +++ b/src/libdwfl/dwfl_addrmodule.c @@ -0,0 +1,59 @@ +/* Find module containing address. + Copyright (C) 2005, 2006, 2007, 2008 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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/src/libdwfl/dwfl_begin.c b/src/libdwfl/dwfl_begin.c new file mode 100644 index 00000000..d388ee00 --- /dev/null +++ b/src/libdwfl/dwfl_begin.c @@ -0,0 +1,72 @@ +/* Set up a session using libdwfl. + Copyright (C) 2005 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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/src/libdwfl/dwfl_build_id_find_debuginfo.c b/src/libdwfl/dwfl_build_id_find_debuginfo.c new file mode 100644 index 00000000..e51b65b1 --- /dev/null +++ b/src/libdwfl/dwfl_build_id_find_debuginfo.c @@ -0,0 +1,98 @@ +/* Find the debuginfo file for a module from its build ID. + Copyright (C) 2007, 2009 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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; + const unsigned char *bits; + GElf_Addr vaddr; + if (INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr) > 0) + fd = __libdwfl_open_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/src/libdwfl/dwfl_build_id_find_elf.c b/src/libdwfl/dwfl_build_id_find_elf.c new file mode 100644 index 00000000..12ce1b58 --- /dev/null +++ b/src/libdwfl/dwfl_build_id_find_elf.c @@ -0,0 +1,180 @@ +/* Find an ELF file for a module from its build ID. + Copyright (C) 2007-2010 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#include "libdwflP.h" +#include <inttypes.h> +#include <fcntl.h> +#include <unistd.h> + + +int +internal_function +__libdwfl_open_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; + + /* Search debuginfo_path directories' .build-id/ subdirectories. */ + + char id_name[sizeof "/.build-id/" + 1 + id_len * 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; +#if defined(__BIONIC__) || defined(__APPLE__) + char *path = strdup ((cb->debuginfo_path ? *cb->debuginfo_path : NULL) + ?: DEFAULT_DEBUGINFO_PATH); +#else + char *path = strdupa ((cb->debuginfo_path ? *cb->debuginfo_path : NULL) + ?: DEFAULT_DEBUGINFO_PATH); +#endif + + int fd = -1; + char *dir; + while (fd < 0 && (dir = strsep (&path, ":")) != 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 (open64 (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); + } + +#if defined(__BIONIC__) || defined(__APPLE__) + free(path); +#endif + + /* 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 +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; + int fd = __libdwfl_open_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/src/libdwfl/dwfl_cumodule.c b/src/libdwfl/dwfl_cumodule.c new file mode 100644 index 00000000..787c098f --- /dev/null +++ b/src/libdwfl/dwfl_cumodule.c @@ -0,0 +1,57 @@ +/* Find the module for a CU DIE previously returned by libdwfl. + Copyright (C) 2005 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#include "libdwflP.h" + +Dwfl_Module * +dwfl_cumodule (Dwarf_Die *cudie) +{ + struct dwfl_cu *cu = (struct dwfl_cu *) cudie; + return cu->mod; +} diff --git a/src/libdwfl/dwfl_dwarf_line.c b/src/libdwfl/dwfl_dwarf_line.c new file mode 100644 index 00000000..eb085e4a --- /dev/null +++ b/src/libdwfl/dwfl_dwarf_line.c @@ -0,0 +1,64 @@ +/* Get information from a source line record returned by libdwfl. + Copyright (C) 2010 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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/src/libdwfl/dwfl_end.c b/src/libdwfl/dwfl_end.c new file mode 100644 index 00000000..429abb13 --- /dev/null +++ b/src/libdwfl/dwfl_end.c @@ -0,0 +1,71 @@ +/* Finish a session using libdwfl. + Copyright (C) 2005, 2008 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#include "libdwflP.h" + +void +dwfl_end (Dwfl *dwfl) +{ + if (dwfl == NULL) + return; + + 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); + } + + free (dwfl); +} diff --git a/src/libdwfl/dwfl_error.c b/src/libdwfl/dwfl_error.c new file mode 100644 index 00000000..966c9901 --- /dev/null +++ b/src/libdwfl/dwfl_error.c @@ -0,0 +1,189 @@ +/* Error handling in libdwfl. + Copyright (C) 2005-2010 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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. */ +#ifdef __APPLE__ +static int global_error; +#else +static __thread int global_error; +#endif + + +int +dwfl_errno (void) +{ + int result = global_error; + global_error = DWFL_E_NOERROR; + return result; +} +INTDEF (dwfl_errno) + + +static const struct msgtable +{ +#define DWFL_ERROR(name, text) char msg_##name[sizeof text]; + DWFL_ERRORS +#undef DWFL_ERROR +} msgtable = + { +#define DWFL_ERROR(name, text) text, + DWFL_ERRORS +#undef DWFL_ERROR + }; +#define msgstr (&msgtable.msg_NOERROR[0]) + +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 (error) + 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): +#ifdef __BIONIC__ + strerror_r (error & 0xffff, "bad", 0); + return "bad"; +#else + return strerror_r (error & 0xffff, "bad", 0); +#endif + 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/src/libdwfl/dwfl_getdwarf.c b/src/libdwfl/dwfl_getdwarf.c new file mode 100644 index 00000000..0a0656f7 --- /dev/null +++ b/src/libdwfl/dwfl_getdwarf.c @@ -0,0 +1,80 @@ +/* Iterate through modules to fetch Dwarf information. + Copyright (C) 2005, 2008 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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/src/libdwfl/dwfl_getmodules.c b/src/libdwfl/dwfl_getmodules.c new file mode 100644 index 00000000..7c6ab971 --- /dev/null +++ b/src/libdwfl/dwfl_getmodules.c @@ -0,0 +1,113 @@ +/* Iterate through modules. + Copyright (C) 2005, 2008 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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/src/libdwfl/dwfl_getsrc.c b/src/libdwfl/dwfl_getsrc.c new file mode 100644 index 00000000..1cc16953 --- /dev/null +++ b/src/libdwfl/dwfl_getsrc.c @@ -0,0 +1,57 @@ +/* Find source location for PC address. + Copyright (C) 2005 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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/src/libdwfl/dwfl_getsrclines.c b/src/libdwfl/dwfl_getsrclines.c new file mode 100644 index 00000000..790481fd --- /dev/null +++ b/src/libdwfl/dwfl_getsrclines.c @@ -0,0 +1,69 @@ +/* Fetch source line information for CU. + Copyright (C) 2005 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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 -1; +} diff --git a/src/libdwfl/dwfl_line_comp_dir.c b/src/libdwfl/dwfl_line_comp_dir.c new file mode 100644 index 00000000..a755524d --- /dev/null +++ b/src/libdwfl/dwfl_line_comp_dir.c @@ -0,0 +1,64 @@ +/* Get information from a source line record returned by libdwfl. + Copyright (C) 2005, 2006 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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/src/libdwfl/dwfl_linecu.c b/src/libdwfl/dwfl_linecu.c new file mode 100644 index 00000000..34f5bb10 --- /dev/null +++ b/src/libdwfl/dwfl_linecu.c @@ -0,0 +1,62 @@ +/* Fetch the module containing a source line record returned by libdwfl. + Copyright (C) 2006 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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/src/libdwfl/dwfl_lineinfo.c b/src/libdwfl/dwfl_lineinfo.c new file mode 100644 index 00000000..6049de84 --- /dev/null +++ b/src/libdwfl/dwfl_lineinfo.c @@ -0,0 +1,76 @@ +/* Get information from a source line record returned by libdwfl. + Copyright (C) 2005-2010 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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; + + 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/src/libdwfl/dwfl_linemodule.c b/src/libdwfl/dwfl_linemodule.c new file mode 100644 index 00000000..fb98f837 --- /dev/null +++ b/src/libdwfl/dwfl_linemodule.c @@ -0,0 +1,59 @@ +/* Fetch the module containing a source line record returned by libdwfl. + Copyright (C) 2005 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#include "libdwflP.h" + +Dwfl_Module * +dwfl_linemodule (Dwfl_Line *line) +{ + if (line == NULL) + return NULL; + + return dwfl_linecu (line)->mod; +} diff --git a/src/libdwfl/dwfl_module.c b/src/libdwfl/dwfl_module.c new file mode 100644 index 00000000..d7e54138 --- /dev/null +++ b/src/libdwfl/dwfl_module.c @@ -0,0 +1,226 @@ +/* Maintenance of module list in libdwfl. + Copyright (C) 2005, 2006, 2007, 2008 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#include "libdwflP.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); + } + + if (mod->dw != NULL) + INTUSE(dwarf_end) (mod->dw); + + if (mod->ebl != NULL) + ebl_closebackend (mod->ebl); + + if (mod->debug.elf != mod->main.elf) + free_file (&mod->debug); + free_file (&mod->main); + + if (mod->build_id_bits != NULL) + free (mod->build_id_bits); + + 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) + +/* 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; + + inline Dwfl_Module *use (Dwfl_Module *mod) + { + mod->next = *tailp; + *tailp = mod; + + if (unlikely (dwfl->lookup_module != NULL)) + { + free (dwfl->lookup_module); + dwfl->lookup_module = NULL; + } + + return mod; + } + + 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); + } + + 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); +} +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/src/libdwfl/dwfl_module_addrdie.c b/src/libdwfl/dwfl_module_addrdie.c new file mode 100644 index 00000000..2733196f --- /dev/null +++ b/src/libdwfl/dwfl_module_addrdie.c @@ -0,0 +1,66 @@ +/* Fetch the CU DIE for a PC address in a given module. + Copyright (C) 2005 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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/src/libdwfl/dwfl_module_addrname.c b/src/libdwfl/dwfl_module_addrname.c new file mode 100644 index 00000000..7d365fe2 --- /dev/null +++ b/src/libdwfl/dwfl_module_addrname.c @@ -0,0 +1,57 @@ +/* Find debugging and symbol information for a module in libdwfl. + Copyright (C) 2005, 2006, 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#include "libdwflP.h" + +const char * +dwfl_module_addrname (Dwfl_Module *mod, GElf_Addr addr) +{ + GElf_Sym sym; + return INTUSE(dwfl_module_addrsym) (mod, addr, &sym, NULL); +} diff --git a/src/libdwfl/dwfl_module_addrsym.c b/src/libdwfl/dwfl_module_addrsym.c new file mode 100644 index 00000000..9ced0cfb --- /dev/null +++ b/src/libdwfl/dwfl_module_addrsym.c @@ -0,0 +1,200 @@ +/* Find debugging and symbol information for a module in libdwfl. + Copyright (C) 2005-2011 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#include "libdwflP.h" + +/* Returns the name of the symbol "closest" to ADDR. + Never returns symbols at addresses above ADDR. */ + +const char * +dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr, + GElf_Sym *closest_sym, GElf_Word *shndxp) +{ + int syments = INTUSE(dwfl_module_getsymtab) (mod); + if (syments < 0) + return NULL; + + /* Return true iff we consider ADDR to lie in the same section as SYM. */ + GElf_Word addr_shndx = SHN_UNDEF; + inline bool same_section (const GElf_Sym *sym, GElf_Word shndx) + { + /* For absolute symbols and the like, only match exactly. */ + if (shndx >= SHN_LORESERVE) + return sym->st_value == addr; + + /* Figure out what section ADDR lies in. */ + if (addr_shndx == SHN_UNDEF) + { + GElf_Addr mod_addr = dwfl_deadjust_st_value (mod, addr); + Elf_Scn *scn = NULL; + addr_shndx = SHN_ABS; + while ((scn = elf_nextscn (mod->symfile->elf, 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) + { + addr_shndx = elf_ndxscn (scn); + break; + } + } + } + + return shndx == addr_shndx; + } + + /* Keep track of the closest symbol we have seen so far. + Here we store only symbols with nonzero st_size. */ + const char *closest_name = NULL; + GElf_Word closest_shndx = SHN_UNDEF; + + /* Keep track of an eligible symbol with st_size == 0 as a fallback. */ + const char *sizeless_name = NULL; + GElf_Sym sizeless_sym = { 0, 0, 0, 0, 0, SHN_UNDEF }; + GElf_Word sizeless_shndx = SHN_UNDEF; + + /* Keep track of the lowest address a relevant sizeless symbol could have. */ + GElf_Addr min_label = 0; + + /* Look through the symbol table for a matching symbol. */ + inline void search_table (int start, int end) + { + for (int i = start; i < end; ++i) + { + GElf_Sym sym; + GElf_Word shndx; + const char *name = INTUSE(dwfl_module_getsym) (mod, i, &sym, &shndx); + if (name != NULL && name[0] != '\0' + && sym.st_shndx != SHN_UNDEF + && sym.st_value <= 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) + { + /* Even if we don't choose this symbol, its existence excludes + any sizeless symbol (assembly label) that is below its upper + bound. */ + if (sym.st_value + sym.st_size > min_label) + min_label = sym.st_value + sym.st_size; + + if (sym.st_size == 0 || addr - sym.st_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 (closest_name == NULL + || closest_sym->st_value < sym.st_value + || (GELF_ST_BIND (closest_sym->st_info) + < GELF_ST_BIND (sym.st_info))) + { + if (sym.st_size != 0) + { + *closest_sym = sym; + closest_shndx = shndx; + closest_name = name; + } + else if (closest_name == NULL + && sym.st_value >= min_label + && same_section (&sym, 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. */ + sizeless_sym = sym; + sizeless_shndx = shndx; + sizeless_name = name; + } + } + /* When the beginning of its range is no closer, + the end of its range might be. But do not + replace a global symbol with a local! */ + else if (sym.st_size != 0 + && closest_sym->st_value == sym.st_value + && closest_sym->st_size > sym.st_size + && (GELF_ST_BIND (closest_sym->st_info) + <= GELF_ST_BIND (sym.st_info))) + { + *closest_sym = sym; + closest_shndx = shndx; + closest_name = name; + } + } + } + } + } + + /* First go through global symbols. mod->first_global is setup by + dwfl_module_getsymtab to the index of the first global symbol in + the module's symbol table, or -1 when unknown. All symbols with + local binding come first in the symbol table, then all globals. */ + search_table (mod->first_global < 0 ? 1 : mod->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 (closest_name == NULL && mod->first_global > 1 + && (sizeless_name == NULL || sizeless_sym.st_value != addr)) + search_table (1, mod->first_global); + + /* If we found no proper sized symbol to use, fall back to the best + candidate sizeless symbol we found, if any. */ + if (closest_name == NULL + && sizeless_name != NULL && sizeless_sym.st_value >= min_label) + { + *closest_sym = sizeless_sym; + closest_shndx = sizeless_shndx; + closest_name = sizeless_name; + } + + if (shndxp != NULL) + *shndxp = closest_shndx; + return closest_name; +} +INTDEF (dwfl_module_addrsym) diff --git a/src/libdwfl/dwfl_module_build_id.c b/src/libdwfl/dwfl_module_build_id.c new file mode 100644 index 00000000..f9888660 --- /dev/null +++ b/src/libdwfl/dwfl_module_build_id.c @@ -0,0 +1,195 @@ +/* Return build ID information for a module. + Copyright (C) 2007-2010 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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; +} + +#define NO_VADDR ((GElf_Addr) -1l) + +static int +check_notes (Dwfl_Module *mod, bool set, Elf_Data *data, GElf_Addr data_vaddr) +{ + size_t pos = 0; + GElf_Nhdr nhdr; + size_t name_pos; + size_t desc_pos; + while ((pos = gelf_getnote (data, pos, &nhdr, &name_pos, &desc_pos)) > 0) + if (nhdr.n_type == NT_GNU_BUILD_ID + && nhdr.n_namesz == sizeof "GNU" && !memcmp (data->d_buf + name_pos, + "GNU", sizeof "GNU")) + return found_build_id (mod, set, + data->d_buf + desc_pos, nhdr.n_descsz, + data_vaddr == NO_VADDR ? 0 + : data_vaddr + desc_pos); + return 0; +} + +int +internal_function +__libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf) +{ + size_t shstrndx = SHN_UNDEF; + int result = 0; + + Elf_Scn *scn = elf_nextscn (elf, NULL); + + if (scn == NULL) + { + /* No sections, have to look for phdrs. */ + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); + size_t phnum; + if (unlikely (ehdr == NULL) + || unlikely (elf_getphdrnum (elf, &phnum) != 0)) + { + __libdwfl_seterrno (DWFL_E_LIBELF); + return -1; + } + for (size_t i = 0; result == 0 && i < phnum; ++i) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem); + if (likely (phdr != NULL) && phdr->p_type == PT_NOTE) + result = check_notes (mod, set, + elf_getdata_rawchunk (elf, + phdr->p_offset, + phdr->p_filesz, + ELF_T_NHDR), + dwfl_adjusted_address (mod, phdr->p_vaddr)); + } + } + else + do + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (likely (shdr != NULL) && shdr->sh_type == SHT_NOTE) + { + /* Determine the right sh_addr in this module. */ + GElf_Addr vaddr = 0; + if (!(shdr->sh_flags & SHF_ALLOC)) + vaddr = NO_VADDR; + else if (mod->e_type != ET_REL) + vaddr = dwfl_adjusted_address (mod, shdr->sh_addr); + else if (__libdwfl_relocate_value (mod, elf, &shstrndx, + elf_ndxscn (scn), &vaddr)) + vaddr = NO_VADDR; + result = check_notes (mod, set, elf_getdata (scn, NULL), vaddr); + } + } + while (result == 0 && (scn = elf_nextscn (elf, scn)) != NULL); + + return result; +} + +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 SHARED +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/src/libdwfl/dwfl_module_dwarf_cfi.c b/src/libdwfl/dwfl_module_dwarf_cfi.c new file mode 100644 index 00000000..96e60fbf --- /dev/null +++ b/src/libdwfl/dwfl_module_dwarf_cfi.c @@ -0,0 +1,92 @@ +/* Find DWARF CFI for a module in libdwfl. + Copyright (C) 2009-2010 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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 (mod, bias) + 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/src/libdwfl/dwfl_module_eh_cfi.c b/src/libdwfl/dwfl_module_eh_cfi.c new file mode 100644 index 00000000..79c8279c --- /dev/null +++ b/src/libdwfl/dwfl_module_eh_cfi.c @@ -0,0 +1,78 @@ +/* Find EH CFI for a module in libdwfl. + Copyright (C) 2009-2010 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#include "libdwflP.h" +#include "../libdw/cfi.h" + +Dwarf_CFI * +dwfl_module_eh_cfi (mod, bias) + 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/src/libdwfl/dwfl_module_getdwarf.c b/src/libdwfl/dwfl_module_getdwarf.c new file mode 100644 index 00000000..14fcd550 --- /dev/null +++ b/src/libdwfl/dwfl_module_getdwarf.c @@ -0,0 +1,1093 @@ +/* Find debugging and symbol information for a module in libdwfl. + Copyright (C) 2005-2011 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#include "libdwflP.h" +#include <fcntl.h> +#include <string.h> +#include <unistd.h> +#include "../libdw/libdwP.h" /* DWARF_E_* values are here. */ + + +/* 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) +{ + if (file->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 (file->fd < 0 && file->name != NULL) + file->fd = TEMP_FAILURE_RETRY (open64 (file->name, O_RDONLY)); + + if (file->fd < 0) + return CBFAIL; + + Dwfl_Error error = __libdw_open_file (&file->fd, &file->elf, true, false); + if (error != DWFL_E_NOERROR) + return error; + } + else if (unlikely (elf_kind (file->elf) != ELF_K_ELF)) + { + elf_end (file->elf); + file->elf = NULL; + close (file->fd); + file->fd = -1; + return DWFL_E_BADELF; + } + + 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 (mod->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; + } + } + } + + 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; + + return DWFL_E_NOERROR; +} + +/* 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) + { + /* We have an authoritative build ID for this module, so + don't use a file by name that doesn't match that ID. */ + + 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; + } + } + + mod->main_bias = mod->e_type == ET_REL ? 0 : mod->low_addr - mod->main.vaddr; +} + +/* Search an ELF file for a ".gnu_debuglink" section. */ +static const char * +find_debuglink (Elf *elf, GElf_Word *crc) +{ + size_t shstrndx; + if (elf_getshdrstrndx (elf, &shstrndx) < 0) + return NULL; + + 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 NULL; + + const char *name = elf_strptr (elf, shstrndx, shdr->sh_name); + if (name == NULL) + return NULL; + + if (!strcmp (name, ".gnu_debuglink")) + break; + } + + if (scn == NULL) + return NULL; + + /* Found the .gnu_debuglink section. Extract its contents. */ + Elf_Data *rawdata = elf_rawdata (scn, NULL); + if (rawdata == NULL) + return NULL; + + Elf_Data crcdata = + { + .d_type = ELF_T_WORD, + .d_buf = crc, + .d_size = sizeof *crc, + .d_version = EV_CURRENT, + }; + Elf_Data conv = + { + .d_type = ELF_T_WORD, + .d_buf = rawdata->d_buf + rawdata->d_size - sizeof *crc, + .d_size = sizeof *crc, + .d_version = EV_CURRENT, + }; + + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); + if (ehdr == NULL) + return NULL; + + Elf_Data *d = gelf_xlatetom (elf, &crcdata, &conv, ehdr->e_ident[EI_DATA]); + if (d == NULL) + return NULL; + assert (d == &crcdata); + + return rawdata->d_buf; +} + +/* 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) +{ + /* 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 (undodata->d_size != (src.d_size + + phnum * phentsize + + (shnum - 1) * shentsize))) + return DWFL_E_BAD_PRELINK; + + /* 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; + { + union + { + Elf32_Phdr p32[phnum]; + Elf64_Phdr p64[phnum]; + } phdr; + dst.d_buf = &phdr; + dst.d_size = sizeof phdr; + if (unlikely (gelf_xlatetom (mod->main.elf, &dst, &src, + ehdr.e32.e_ident[EI_DATA]) == NULL)) + return DWFL_E_LIBELF; + if (ehdr.e32.e_ident[EI_CLASS] == ELFCLASS32) + { + for (uint_fast16_t i = 0; i < phnum; ++i) + if (phdr.p32[i].p_type == PT_INTERP) + { + undo_interp = phdr.p32[i].p_vaddr; + break; + } + } + else + { + for (uint_fast16_t i = 0; i < phnum; ++i) + if (phdr.p64[i].p_type == PT_INTERP) + { + undo_interp = phdr.p64[i].p_vaddr; + break; + } + } + } + + 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 - 1, EV_CURRENT); + + union + { + Elf32_Shdr s32[shnum - 1]; + Elf64_Shdr s64[shnum - 1]; + } shdr; + dst.d_buf = &shdr; + dst.d_size = sizeof shdr; + if (unlikely (gelf_xlatetom (mod->main.elf, &dst, &src, + ehdr.e32.e_ident[EI_DATA]) == NULL)) + 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; + + inline void consider_shdr (GElf_Addr interp, + GElf_Word sh_type, + GElf_Xword sh_flags, + GElf_Addr sh_addr, + GElf_Xword sh_size) + { + 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 > highest) + highest = sh_end; + } + } + + 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)) + return DWFL_E_LIBELF; + consider_shdr (main_interp, sh->sh_type, sh->sh_flags, + sh->sh_addr, sh->sh_size); + } + if (highest > mod->main.vaddr) + { + mod->main.address_sync = highest; + + highest = 0; + if (ehdr.e32.e_ident[EI_CLASS] == ELFCLASS32) + for (size_t i = 0; i < shnum - 1; ++i) + consider_shdr (undo_interp, shdr.s32[i].sh_type, shdr.s32[i].sh_flags, + shdr.s32[i].sh_addr, shdr.s32[i].sh_size); + else + for (size_t i = 0; i < shnum - 1; ++i) + consider_shdr (undo_interp, shdr.s64[i].sh_type, shdr.s64[i].sh_flags, + shdr.s64[i].sh_addr, shdr.s64[i].sh_size); + + if (highest > mod->debug.vaddr) + mod->debug.address_sync = highest; + else + return DWFL_E_BAD_PRELINK; + } + + 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 = find_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); + return result; +} + + +/* 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: + 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. */ + *symscn = scn; + *symfile = file; + *strshndx = shdr->sh_link; + *syments = shdr->sh_size / shdr->sh_entsize; + 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, 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 + && addrs[j] - phdr->p_vaddr < phdr->p_filesz) + { + offs[j] = addrs[j] - phdr->p_vaddr + phdr->p_offset; + if (--unsolved == 0) + break; + } + } +} + +/* 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; + + enum + { + i_symtab, + i_strtab, + i_hash, + i_gnu_hash, + i_max + }; + 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; + } + + /* Translate pointers into file offsets. */ + GElf_Off offs[i_max] = { 0, }; + find_offsets (mod->main.elf, 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); + 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; + + 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)); + + 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; + } + return; + } + } + } +} + +/* 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 /* 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; + + mod->first_global = -1; /* Unknown, unless explicitly set by load_symtab. */ + + /* First see if the main ELF file has the debugging information. */ + Elf_Scn *symscn = NULL, *xndxscn = NULL; + GElf_Word strshndx; + 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: + if (symscn != NULL) + { + /* We still have the dynamic symbol table. */ + mod->symerr = DWFL_E_NOERROR; + break; + } + + /* 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->symerr = DWFL_E (LIBELF, elf_errno ()); + return; + } + + /* Cache the data; MOD->syments and MOD->first_global were set above. */ + + mod->symstrdata = elf_getdata (elf_getscn (mod->symfile->elf, strshndx), + NULL); + if (mod->symstrdata == NULL) + goto elferr; + + if (xndxscn == NULL) + mod->symxndxdata = NULL; + else + { + mod->symxndxdata = elf_getdata (xndxscn, NULL); + if (mod->symxndxdata == NULL) + goto elferr; + } + + mod->symdata = elf_getdata (symscn, NULL); + if (mod->symdata == NULL) + goto elferr; +} + + +/* 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; + 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); + 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) + return mod->syments; + + __libdwfl_seterrno (mod->symerr); + return -1; +} +INTDEF (dwfl_module_getsymtab) diff --git a/src/libdwfl/dwfl_module_getelf.c b/src/libdwfl/dwfl_module_getelf.c new file mode 100644 index 00000000..b4e4a2b7 --- /dev/null +++ b/src/libdwfl/dwfl_module_getelf.c @@ -0,0 +1,88 @@ +/* Find debugging and symbol information for a module in libdwfl. + Copyright (C) 2009-2010 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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/src/libdwfl/dwfl_module_getsrc.c b/src/libdwfl/dwfl_module_getsrc.c new file mode 100644 index 00000000..be03055e --- /dev/null +++ b/src/libdwfl/dwfl_module_getsrc.c @@ -0,0 +1,99 @@ +/* Find source location for PC address in module. + Copyright (C) 2005, 2008 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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)) + { + /* 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 = cu->die.cu->lines->nlines; + while (l < u) + { + size_t idx = (l + u) / 2; + if (addr < cu->die.cu->lines->info[idx].addr) + u = idx; + else if (addr > cu->die.cu->lines->info[idx].addr) + l = idx + 1; + else + return &cu->lines->idx[idx]; + } + + if (cu->die.cu->lines->nlines > 0) + assert (cu->die.cu->lines->info + [cu->die.cu->lines->nlines - 1].end_sequence); + + /* If none were equal, the closest one below is what we want. + We never want the last one, because it's the end-sequence + marker with an address at the high bound of the CU's code. */ + if (u > 0 && u < cu->die.cu->lines->nlines + && addr > cu->die.cu->lines->info[u - 1].addr) + return &cu->lines->idx[u - 1]; + + error = DWFL_E_ADDR_OUTOFRANGE; + } + + __libdwfl_seterrno (error); + return NULL; +} +INTDEF (dwfl_module_getsrc) diff --git a/src/libdwfl/dwfl_module_getsrc_file.c b/src/libdwfl/dwfl_module_getsrc_file.c new file mode 100644 index 00000000..9d0c786b --- /dev/null +++ b/src/libdwfl/dwfl_module_getsrc_file.c @@ -0,0 +1,188 @@ +/* Find matching source locations in a module. + Copyright (C) 2005 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#include "libdwflP.h" +#include "../libdw/libdwP.h" + + +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) + { + inline const char *INTUSE(dwarf_line_file) (const Dwarf_Line *line) + { + return line->files->info[line->file].name; + } + inline Dwarf_Line *dwfl_line (const Dwfl_Line *line) + { + return &dwfl_linecu (line)->die.cu->lines->info[line->idx]; + } + inline const char *dwfl_line_file (const Dwfl_Line *line) + { + return INTUSE(dwarf_line_file) (dwfl_line (line)); + } + + /* 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)) + { + __libdwfl_seterrno (DWFL_E (LIBDW, DWARF_E_INVALID_DWARF)); + return -1; + } + else + { + const char *file = INTUSE(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]) + == INTUSE(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/src/libdwfl/dwfl_module_getsym.c b/src/libdwfl/dwfl_module_getsym.c new file mode 100644 index 00000000..6bc063bb --- /dev/null +++ b/src/libdwfl/dwfl_module_getsym.c @@ -0,0 +1,128 @@ +/* Find debugging and symbol information for a module in libdwfl. + Copyright (C) 2006-2010 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#include "libdwflP.h" + +const char * +dwfl_module_getsym (Dwfl_Module *mod, int ndx, + GElf_Sym *sym, GElf_Word *shndxp) +{ + if (unlikely (mod == NULL)) + return NULL; + + if (unlikely (mod->symdata == NULL)) + { + int result = INTUSE(dwfl_module_getsymtab) (mod); + if (result < 0) + return NULL; + } + + GElf_Word shndx; + sym = gelf_getsymshndx (mod->symdata, mod->symxndxdata, ndx, 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 (mod->symfile->elf, shndx), + &shdr_mem); + alloc = unlikely (shdr == NULL) || (shdr->sh_flags & SHF_ALLOC); + } + + 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, mod->symfile->elf, + &symshstrndx, + shndx, &sym->st_value); + if (unlikely (result != DWFL_E_NOERROR)) + { + __libdwfl_seterrno (result); + return NULL; + } + } + else if (alloc) + /* Apply the bias to the symbol value. */ + sym->st_value = dwfl_adjusted_st_value (mod, sym->st_value); + break; + } + + if (unlikely (sym->st_name >= mod->symstrdata->d_size)) + { + __libdwfl_seterrno (DWFL_E_BADSTROFF); + return NULL; + } + return (const char *) mod->symstrdata->d_buf + sym->st_name; +} +INTDEF (dwfl_module_getsym) diff --git a/src/libdwfl/dwfl_module_info.c b/src/libdwfl/dwfl_module_info.c new file mode 100644 index 00000000..bfde6fc1 --- /dev/null +++ b/src/libdwfl/dwfl_module_info.c @@ -0,0 +1,82 @@ +/* Return information about a module. + Copyright (C) 2005-2010 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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, 0)); + + if (mainfile) + *mainfile = mod->main.name; + + if (debugfile) + *debugfile = mod->debug.name; + + return mod->name; +} diff --git a/src/libdwfl/dwfl_module_nextcu.c b/src/libdwfl/dwfl_module_nextcu.c new file mode 100644 index 00000000..1b37d299 --- /dev/null +++ b/src/libdwfl/dwfl_module_nextcu.c @@ -0,0 +1,65 @@ +/* Iterate through DWARF compilation units in a module. + Copyright (C) 2005 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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/src/libdwfl/dwfl_module_register_names.c b/src/libdwfl/dwfl_module_register_names.c new file mode 100644 index 00000000..79a874a8 --- /dev/null +++ b/src/libdwfl/dwfl_module_register_names.c @@ -0,0 +1,100 @@ +/* Enumerate DWARF register numbers and their names. + Copyright (C) 2005, 2006 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#include "libdwflP.h" + + +int +dwfl_module_register_names (mod, func, arg) + Dwfl_Module *mod; + int (*func) (void *, int regno, const char *setname, + const char *prefix, const char *regname, + int bits, int type); + 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/src/libdwfl/dwfl_module_report_build_id.c b/src/libdwfl/dwfl_module_report_build_id.c new file mode 100644 index 00000000..9a1b14f7 --- /dev/null +++ b/src/libdwfl/dwfl_module_report_build_id.c @@ -0,0 +1,101 @@ +/* Report build ID information for a module. + Copyright (C) 2007, 2008 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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/src/libdwfl/dwfl_module_return_value_location.c b/src/libdwfl/dwfl_module_return_value_location.c new file mode 100644 index 00000000..3d5154e2 --- /dev/null +++ b/src/libdwfl/dwfl_module_return_value_location.c @@ -0,0 +1,85 @@ +/* Return location expression to find return value given a function type DIE. + Copyright (C) 2005, 2006 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#include "libdwflP.h" + + +int +dwfl_module_return_value_location (mod, functypedie, locops) + 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/src/libdwfl/dwfl_nextcu.c b/src/libdwfl/dwfl_nextcu.c new file mode 100644 index 00000000..90862d20 --- /dev/null +++ b/src/libdwfl/dwfl_nextcu.c @@ -0,0 +1,103 @@ +/* Iterate through DWARF compilation units across all modules. + Copyright (C) 2005-2010 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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/src/libdwfl/dwfl_onesrcline.c b/src/libdwfl/dwfl_onesrcline.c new file mode 100644 index 00000000..558d2408 --- /dev/null +++ b/src/libdwfl/dwfl_onesrcline.c @@ -0,0 +1,77 @@ +/* Return one of the sources lines of a CU. + Copyright (C) 2005 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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/src/libdwfl/dwfl_report_elf.c b/src/libdwfl/dwfl_report_elf.c new file mode 100644 index 00000000..4c4132b1 --- /dev/null +++ b/src/libdwfl/dwfl_report_elf.c @@ -0,0 +1,306 @@ +/* Report a module to libdwfl based on ELF program headers. + Copyright (C) 2005-2010 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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) + +Dwfl_Module * +internal_function +__libdwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name, + int fd, Elf *elf, GElf_Addr base, bool sanity) +{ + GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem); + if (ehdr == NULL) + { + elf_error: + __libdwfl_seterrno (DWFL_E_LIBELF); + return NULL; + } + + 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; + + 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; + if ((base & (ph->p_align - 1)) != 0) + base = (base + ph->p_align - 1) & -ph->p_align; + start = base + (ph->p_vaddr & -ph->p_align); + break; + } + } + bias = start - 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 = base + (ph->p_vaddr + ph->p_memsz); + break; + } + } + + if (end == 0 && sanity) + { + __libdwfl_seterrno (DWFL_E_NO_PHDR); + return NULL; + } + break; + } + + 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 = ehdr->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 closefd = false; + if (fd < 0) + { + closefd = true; + fd = open64 (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, true); + if (mod == NULL) + { + elf_end (elf); + if (closefd) + close (fd); + } + + return mod; +} +INTDEF (dwfl_report_elf) diff --git a/src/libdwfl/dwfl_segment_report_module.c b/src/libdwfl/dwfl_segment_report_module.c new file mode 100644 index 00000000..012a0fde --- /dev/null +++ b/src/libdwfl/dwfl_segment_report_module.c @@ -0,0 +1,673 @@ +/* Sniff out modules from ELF headers visible in memory segments. + Copyright (C) 2008-2010 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#include <config.h> +#include "../libelf/libelfP.h" /* For NOTE_ALIGN. */ +#undef _ +#include "libdwflP.h" + +#include <elf.h> +#include <gelf.h> +#include <inttypes.h> +#include <sys/param.h> +#include <alloca.h> +#include <endian.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; +} + +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) +{ + 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; + + inline int finish (void) + { + release_buffer (&buffer, &buffer_available); + 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) + { + *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. */ + 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, + }; + switch (((const unsigned char *) buffer)[EI_CLASS]) + { + case ELFCLASS32: + xlatefrom.d_size = sizeof (Elf32_Ehdr); + if (elf32_xlatetom (&xlateto, &xlatefrom, + ((const unsigned char *) buffer)[EI_DATA]) == NULL) + return finish (); + 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, + ((const unsigned char *) buffer)[EI_DATA]) == NULL) + return finish (); + 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; + + union + { + Elf32_Phdr p32[phnum]; + Elf64_Phdr p64[phnum]; + } phdrs; + + xlateto.d_buf = &phdrs; + xlateto.d_size = sizeof phdrs; + + /* 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 (ehdr.e32.e_ident[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; + } + } + if (ehdr.e32.e_ident[EI_CLASS] == ELFCLASS32) + { + if (elf32_xlatetom (&xlateto, &xlatefrom, + ehdr.e32.e_ident[EI_DATA]) == NULL) + found_bias = false; /* Trigger error check. */ + else + for (uint_fast16_t i = 0; i < phnum; ++i) + consider_phdr (phdrs.p32[i].p_type, + phdrs.p32[i].p_vaddr, phdrs.p32[i].p_memsz, + phdrs.p32[i].p_offset, phdrs.p32[i].p_filesz, + phdrs.p32[i].p_align); + } + else + { + if (elf64_xlatetom (&xlateto, &xlatefrom, + ehdr.e32.e_ident[EI_DATA]) == NULL) + found_bias = false; /* Trigger error check. */ + else + for (uint_fast16_t i = 0; i < phnum; ++i) + consider_phdr (phdrs.p64[i].p_type, + phdrs.p64[i].p_vaddr, phdrs.p64[i].p_memsz, + phdrs.p64[i].p_offset, phdrs.p64[i].p_filesz, + phdrs.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)) + return finish (); + + /* Now we know enough to report a module for sure: its bounds. */ + module_start += bias; + module_end += bias; + + dyn_vaddr += bias; + + /* 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 = (ehdr.e32.e_ident[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)) + { + union + { + Elf32_Dyn d32[dyn_filesz / sizeof (Elf32_Dyn)]; + Elf64_Dyn d64[dyn_filesz / sizeof (Elf64_Dyn)]; + } dyn; + + xlatefrom.d_type = xlateto.d_type = ELF_T_DYN; + xlatefrom.d_buf = (void *) dyn_data; + xlatefrom.d_size = dyn_filesz; + xlateto.d_buf = &dyn; + xlateto.d_size = sizeof dyn; + + if (ehdr.e32.e_ident[EI_CLASS] == ELFCLASS32) + { + if (elf32_xlatetom (&xlateto, &xlatefrom, + ehdr.e32.e_ident[EI_DATA]) != NULL) + for (size_t i = 0; i < dyn_filesz / sizeof dyn.d32[0]; ++i) + if (consider_dyn (dyn.d32[i].d_tag, dyn.d32[i].d_un.d_val)) + break; + } + else + { + if (elf64_xlatetom (&xlateto, &xlatefrom, + ehdr.e32.e_ident[EI_DATA]) != NULL) + for (size_t i = 0; i < dyn_filesz / sizeof dyn.d64[0]; ++i) + if (consider_dyn (dyn.d64[i].d_tag, dyn.d64[i].d_un.d_val)) + break; + } + } + 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 = ehdr.e32.e_type == ET_EXEC ? "[exe]" : execlike ? "[pie]" : "[dso]"; + + void *soname = NULL; + size_t soname_size = 0; + if (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); + 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); + + Elf *elf = NULL; + if ((*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 (ehdr.e32.e_ident[EI_CLASS] == ELFCLASS32) + for (uint_fast16_t i = 0; i < phnum; ++i) + read_phdr (phdrs.p32[i].p_type, phdrs.p32[i].p_vaddr, + phdrs.p32[i].p_offset, phdrs.p32[i].p_filesz); + else + for (uint_fast16_t i = 0; i < phnum; ++i) + read_phdr (phdrs.p64[i].p_type, phdrs.p64[i].p_vaddr, + phdrs.p64[i].p_offset, phdrs.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; + mod->main.vaddr = module_start - bias; + mod->main.address_sync = module_address_sync; + } + + return finish (); +} diff --git a/src/libdwfl/dwfl_validate_address.c b/src/libdwfl/dwfl_validate_address.c new file mode 100644 index 00000000..81073762 --- /dev/null +++ b/src/libdwfl/dwfl_validate_address.c @@ -0,0 +1,82 @@ +/* Validate an address and the relocatability of an offset from it. + Copyright (C) 2005 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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/src/libdwfl/dwfl_version.c b/src/libdwfl/dwfl_version.c new file mode 100644 index 00000000..9c7074c5 --- /dev/null +++ b/src/libdwfl/dwfl_version.c @@ -0,0 +1,57 @@ +/* Return implementation's version string suitable for printing. + Copyright (C) 2006, 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#include "libdwflP.h" + +const char * +dwfl_version (dwfl) + Dwfl *dwfl __attribute__ ((unused)); +{ + return PACKAGE_VERSION; +} diff --git a/src/libdwfl/elf-from-memory.c b/src/libdwfl/elf-from-memory.c new file mode 100644 index 00000000..0fb5f8a3 --- /dev/null +++ b/src/libdwfl/elf-from-memory.c @@ -0,0 +1,368 @@ +/* Reconstruct an ELF file by reading the segments out of remote memory. + Copyright (C) 2005-2011 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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. */ + +Elf * +elf_from_remote_memory (GElf_Addr ehdr_vma, + GElf_Addr *loadbasep, + ssize_t (*read_memory) (void *arg, void *data, + GElf_Addr address, + size_t minread, + size_t maxread), + void *arg) +{ + /* First read in the file header and check its sanity. */ + + const size_t initial_bufsize = 256; + unsigned char *buffer = malloc (initial_bufsize); + if (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); + __libdwfl_seterrno (nread < 0 ? DWFL_E_ERRNO : DWFL_E_TRUNCATED); + return NULL; + } + + if (memcmp (buffer, ELFMAG, SELFMAG) != 0) + { + bad_elf: + __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 < phnum * phentsize) + { + unsigned char *newbuf = realloc (buffer, phnum * phentsize); + if (newbuf == NULL) + { + free (buffer); + 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; + } + + union + { + Elf32_Phdr p32[phnum]; + Elf64_Phdr p64[phnum]; + } phdrs; + + xlateto.d_buf = &phdrs; + xlateto.d_size = sizeof phdrs; + + /* 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_Addr loadbase = ehdr_vma; + bool found_base = false; + switch (ehdr.e32.e_ident[EI_CLASS]) + { + inline void handle_segment (GElf_Addr vaddr, GElf_Off offset, + GElf_Xword filesz, GElf_Xword align) + { + GElf_Off segment_end = ((offset + filesz + align - 1) & -align); + + if (segment_end > (GElf_Off) contents_size) + contents_size = segment_end; + + if (!found_base && (offset & -align) == 0) + { + loadbase = ehdr_vma - (vaddr & -align); + found_base = true; + } + + segments_end = offset + filesz; + } + + 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 (phdrs.p32[i].p_type == PT_LOAD) + handle_segment (phdrs.p32[i].p_vaddr, phdrs.p32[i].p_offset, + phdrs.p32[i].p_filesz, phdrs.p32[i].p_align); + 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 (phdrs.p64[i].p_type == PT_LOAD) + handle_segment (phdrs.p64[i].p_vaddr, phdrs.p64[i].p_offset, + phdrs.p64[i].p_filesz, phdrs.p64[i].p_align); + 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, keep them. */ + if ((GElf_Off) contents_size > segments_end + && (GElf_Off) contents_size >= shdrs_end) + { + 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) + goto no_memory; + + switch (ehdr.e32.e_ident[EI_CLASS]) + { + inline bool handle_segment (GElf_Addr vaddr, GElf_Off offset, + GElf_Xword filesz, GElf_Xword align) + { + GElf_Off start = offset & -align; + GElf_Off end = (offset + filesz + align - 1) & -align; + if (end > (GElf_Off) contents_size) + end = contents_size; + nread = (*read_memory) (arg, buffer + start, + (loadbase + vaddr) & -align, + end - start, end - start); + return nread <= 0; + } + + case ELFCLASS32: + for (uint_fast16_t i = 0; i < phnum; ++i) + if (phdrs.p32[i].p_type == PT_LOAD) + if (handle_segment (phdrs.p32[i].p_vaddr, phdrs.p32[i].p_offset, + phdrs.p32[i].p_filesz, phdrs.p32[i].p_align)) + 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 (phdrs.p32[i].p_type == PT_LOAD) + if (handle_segment (phdrs.p64[i].p_vaddr, phdrs.p64[i].p_offset, + phdrs.p64[i].p_filesz, phdrs.p64[i].p_align)) + 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; + } + + /* 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/src/libdwfl/find-debuginfo.c b/src/libdwfl/find-debuginfo.c new file mode 100644 index 00000000..12cfe636 --- /dev/null +++ b/src/libdwfl/find-debuginfo.c @@ -0,0 +1,315 @@ +/* Standard find_debuginfo callback for libdwfl. + Copyright (C) 2005-2010 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#include "libdwflP.h" +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/stat.h> +#include "system.h" + + +/* Try to open64 [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 stat64 *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 (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 stat64 st; + int fd = TEMP_FAILURE_RETRY (open64 (fname, O_RDONLY)); + if (fd < 0) + free (fname); + else if (fstat64 (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. */ + 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) +{ + /* 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); + if (debuglink_file == NULL) + { + if (file_basename == NULL) + { + errno = 0; + return -1; + } + + size_t len = strlen (file_basename); + char *localname = alloca (len + sizeof ".debug"); + 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; +#if defined(__BIONIC__) || defined(__APPLE__) + char *path = strdup ((cb->debuginfo_path ? *cb->debuginfo_path : NULL) + ?: DEFAULT_DEBUGINFO_PATH); +#else + char *path = strdupa ((cb->debuginfo_path ? *cb->debuginfo_path : NULL) + ?: DEFAULT_DEBUGINFO_PATH); +#endif + + /* A leading - or + in the whole path sets whether to check file CRCs. */ + bool defcheck = true; + if (path[0] == '-' || path[0] == '+') + { + defcheck = path[0] == '+'; + ++path; + } + + /* XXX dev/ino should be cached in struct dwfl_file. */ + struct stat64 main_stat; + if (unlikely ((mod->main.fd != -1 ? fstat64 (mod->main.fd, &main_stat) + : file_name != NULL ? stat64 (file_name, &main_stat) + : -1) < 0)) + { + main_stat.st_dev = 0; + main_stat.st_ino = 0; + } + +#if defined(__BIONIC__) || defined(__APPLE__) + char *file_dirname = (file_basename == file_name ? NULL + : strndup (file_name, file_basename - 1 - file_name)); +#else + char *file_dirname = (file_basename == file_name ? NULL + : strndupa (file_name, file_basename - 1 - file_name)); +#endif + + 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; + + const char *dir, *subdir; + switch (p[0]) + { + case '\0': + /* An empty entry says to try the main file's directory. */ + dir = file_dirname; + subdir = NULL; + 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. */ + if (file_dirname == NULL || file_dirname[0] != '/') + continue; + dir = p; + subdir = file_dirname + 1; + break; + default: + /* A relative path says to try a subdirectory of that name + in the main file's directory. */ + dir = file_dirname; + subdir = p; + break; + } + + char *fname = NULL; + int fd = try_open (&main_stat, dir, subdir, debuglink_file, &fname); + if (fd < 0) + switch (errno) + { + case ENOENT: + case ENOTDIR: + continue; + default: +#if defined(__BIONIC__) || defined(__APPLE__) + free(path); + free(file_dirname); +#endif + return -1; + } + if (validate (mod, fd, check, debuglink_crc)) + { + *debuginfo_file_name = fname; +#if defined(__BIONIC__) || defined(__APPLE__) + free(path); + free(file_dirname); +#endif + return fd; + } + free (fname); + close (fd); + } + + /* No dice. */ + errno = 0; +#if defined(__BIONIC__) || defined(__APPLE__) + free(path); + free(file_dirname); +#endif + 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) + { + int fd = INTUSE(dwfl_build_id_find_debuginfo) (mod, + NULL, NULL, 0, + NULL, NULL, 0, + debuginfo_file_name); + if (fd >= 0 || mod->debug.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) + { + /* 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/src/libdwfl/gzip.c b/src/libdwfl/gzip.c new file mode 100644 index 00000000..5604d490 --- /dev/null +++ b/src/libdwfl/gzip.c @@ -0,0 +1,316 @@ +/* Decompression support for libdwfl: zlib (gzip) and/or bzlib (bzip2). + Copyright (C) 2009 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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) + +/* 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, off64_t start_offset, + void *mapped, size_t mapped_size, + void **whole, size_t *whole_size) +{ + void *buffer = NULL; + size_t size = 0; + inline bool bigger_buffer (size_t start) + { + size_t more = size ? size * 2 : start; + char *b = realloc (buffer, more); + while (unlikely (b == NULL) && more >= size + 1024) + b = realloc (buffer, more -= 1024); + if (unlikely (b == NULL)) + return false; + buffer = b; + size = more; + return true; + } + inline void smaller_buffer (size_t end) + { + buffer = realloc (buffer, end) ?: end == 0 ? NULL : buffer; + size = end; + } + + void *input_buffer = NULL; + off_t input_pos = 0; + + inline Dwfl_Error fail (Dwfl_Error failure) + { + if (input_pos == (off_t) mapped_size) + *whole = input_buffer; + else + { + free (input_buffer); + *whole = NULL; + } + free (buffer); + return failure; + } + + inline Dwfl_Error zlib_fail (int result) + { + switch (result) + { + case Z (MEM_ERROR): + return fail (DWFL_E_NOMEM); + case Z (ERRNO): + return fail (DWFL_E_ERRNO); + default: + return fail (DWFL_E_ZLIB); + } + } + + if (mapped == NULL) + { + if (*whole == NULL) + { + input_buffer = malloc (READ_SIZE); + if (unlikely (input_buffer == NULL)) + return DWFL_E_NOMEM; + + ssize_t n = pread_retry (fd, input_buffer, READ_SIZE, start_offset); + if (unlikely (n < 0)) + return zlib_fail (Z (ERRNO)); + + input_pos = n; + mapped = input_buffer; + mapped_size = n; + } + else + { + input_buffer = *whole; + input_pos = mapped_size = *whole_size; + } + } + +#define NOMAGIC(magic) \ + (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 = mapped_size }; + int result = inflateInit (&z); + if (result != Z (OK)) + { + inflateEnd (&z); + return zlib_fail (result); + } + + do + { + if (z.avail_in == 0 && input_buffer != NULL) + { + ssize_t n = pread_retry (fd, input_buffer, READ_SIZE, + start_offset + input_pos); + if (unlikely (n < 0)) + { + inflateEnd (&z); + return zlib_fail (Z (ERRNO)); + } + z.next_in = input_buffer; + z.avail_in = n; + input_pos += n; + } + if (z.avail_out == 0) + { + ptrdiff_t pos = (void *) z.next_out - buffer; + if (!bigger_buffer (z.avail_in)) + { + result = Z (MEM_ERROR); + break; + } + z.next_out = buffer + pos; + z.avail_out = 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 (total_out); +#else + smaller_buffer (z.total_out); +#endif + + inflateEnd (&z); + + if (result != Z (STREAM_END)) + return zlib_fail (result); + +#else /* gzip only. */ + + /* Let the decompression library read the file directly. */ + + gzFile zf; + Dwfl_Error open_stream (void) + { + int d = dup (fd); + if (unlikely (d < 0)) + return DWFL_E_BADELF; + if (start_offset != 0) + { + off64_t off = lseek (d, start_offset, SEEK_SET); + if (off != start_offset) + { + close (d); + return DWFL_E_BADELF; + } + } + zf = gzdopen (d, "r"); + if (unlikely (zf == NULL)) + { + close (d); + return zlib_fail (Z (MEM_ERROR)); + } + + /* From here on, zlib will close D. */ + + return DWFL_E_NOERROR; + } + + Dwfl_Error result = open_stream (); + + if (result == DWFL_E_NOERROR && gzdirect (zf)) + { + gzclose (zf); + return fail (DWFL_E_BADELF); + } + + if (result != DWFL_E_NOERROR) + return fail (result); + + ptrdiff_t pos = 0; + while (1) + { + if (!bigger_buffer (1024)) + { + gzclose (zf); + return zlib_fail (Z (MEM_ERROR)); + } + int n = gzread (zf, buffer + pos, size - pos); + if (n < 0) + { + int code; + gzerror (zf, &code); + gzclose (zf); + return zlib_fail (code); + } + if (n == 0) + break; + pos += n; + } + + gzclose (zf); + smaller_buffer (pos); +#endif + + free (input_buffer); + + *whole = buffer; + *whole_size = size; + + return DWFL_E_NOERROR; +} diff --git a/src/libdwfl/image-header.c b/src/libdwfl/image-header.c new file mode 100644 index 00000000..c36d10c1 --- /dev/null +++ b/src/libdwfl/image-header.c @@ -0,0 +1,122 @@ +/* Linux kernel image support for libdwfl. + Copyright (C) 2009-2011 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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, off64_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/src/libdwfl/libdwfl.h b/src/libdwfl/libdwfl.h new file mode 100644 index 00000000..4ea2796c --- /dev/null +++ b/src/libdwfl/libdwfl.h @@ -0,0 +1,585 @@ +/* Interfaces for libdwfl. + Copyright (C) 2005-2010 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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; + +/* 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 with start and end addresses computed from the ELF + program headers in the given file, plus BASE. For an ET_REL file, + does a simple absolute section layout starting at BASE. + 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. */ +extern Dwfl_Module *dwfl_report_elf (Dwfl *dwfl, const char *name, + const char *file_name, int fd, + GElf_Addr base); + +/* 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 + 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 /bin/ls, the default string + ":.debug:/usr/lib/debug" says to look in /bin, then /bin/.debug, + then /usr/lib/debug/bin, 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. 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); + +/* 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 kernel binary 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) __attribute__ ((const)); + + +/*** 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); + +/* Return the number of symbols in the module's symbol table, + or -1 for errors. */ +extern int dwfl_module_getsymtab (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. */ +extern const char *dwfl_module_getsym (Dwfl_Module *mod, int ndx, + GElf_Sym *sym, GElf_Word *shndxp) + __nonnull_attribute__ (3); + +/* 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 that ADDRESS lies inside, and return detailed + information as for dwfl_module_getsym (above). */ +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); + + +#ifdef __cplusplus +} +#endif + +#endif /* libdwfl.h */ diff --git a/src/libdwfl/libdwflP.h b/src/libdwfl/libdwflP.h new file mode 100644 index 00000000..bca82d2e --- /dev/null +++ b/src/libdwfl/libdwflP.h @@ -0,0 +1,512 @@ +/* Internal definitions for libdwfl. + Copyright (C) 2005-2011 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#ifndef _LIBDWFLP_H +#define _LIBDWFLP_H 1 + +#ifndef PACKAGE_NAME +# include <config.h> +#endif +#include <libdwfl.h> +#include <libebl.h> +#include <assert.h> +#include <errno.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +#include "../libdw/libdwP.h" /* We need its INTDECLs. */ + +/* 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")) + +#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; + +struct Dwfl +{ + const Dwfl_Callbacks *callbacks; + + Dwfl_Module *modulelist; /* List in order used by full traversals. */ + + 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; +}; + +#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; + 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. */ + size_t syments; /* sh_size / sh_entsize of that section. */ + int first_global; /* Index of first global symbol of table. */ + Elf_Data *symstrdata; /* Data for its string table. */ + Elf_Data *symxndxdata; /* Data in the extended section index table. */ + + Dwarf *dw; /* libdw handle for its debugging info. */ + + 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. */ +}; + + + +/* 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 GElf_Addr +dwfl_adjusted_st_value (Dwfl_Module *mod, GElf_Addr addr) +{ + if (mod->symfile == &mod->main) + return dwfl_adjusted_address (mod, addr); + return dwfl_adjusted_dwarf_addr (mod, addr); +} + +static inline GElf_Addr +dwfl_deadjust_st_value (Dwfl_Module *mod, GElf_Addr addr) +{ + if (mod->symfile == &mod->main) + return dwfl_deadjust_address (mod, addr); + return dwfl_deadjust_dwarf_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. */ +}; + + + +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; + +/* 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. 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_by_build_id (Dwfl_Module *mod, bool debug, + char **file_name) 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; + + +/* 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 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; + +/* Decompression wrappers: decompress whole file into memory. */ +extern Dwfl_Error __libdw_gunzip (int fd, off64_t start_offset, + void *mapped, size_t mapped_size, + void **whole, size_t *whole_size) + internal_function; +extern Dwfl_Error __libdw_bunzip2 (int fd, off64_t start_offset, + void *mapped, size_t mapped_size, + void **whole, size_t *whole_size) + internal_function; +extern Dwfl_Error __libdw_unlzma (int fd, off64_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, off64_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; + +/* 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); + +/* ... + */ +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); + +/* 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. + + 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); + + +/* 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_report) +INTDECL (dwfl_getmodules) +INTDECL (dwfl_module_addrdie) +INTDECL (dwfl_module_address_section) +INTDECL (dwfl_module_addrsym) +INTDECL (dwfl_module_build_id) +INTDECL (dwfl_module_getdwarf) +INTDECL (dwfl_module_getelf) +INTDECL (dwfl_module_getsym) +INTDECL (dwfl_module_getsymtab) +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_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) + +/* 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/src/libdwfl/libdwfl_crc32.c b/src/libdwfl/libdwfl_crc32.c new file mode 100644 index 00000000..0fa23789 --- /dev/null +++ b/src/libdwfl/libdwfl_crc32.c @@ -0,0 +1,56 @@ +/* Copyright (C) 2002, 2005 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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/src/libdwfl/libdwfl_crc32_file.c b/src/libdwfl/libdwfl_crc32_file.c new file mode 100644 index 00000000..ca154be3 --- /dev/null +++ b/src/libdwfl/libdwfl_crc32_file.c @@ -0,0 +1,57 @@ +/* Copyright (C) 2002, 2005 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#define crc32_file attribute_hidden __libdwfl_crc32_file +#define crc32 __libdwfl_crc32 +#define LIB_SYSTEM_H 1 +#include <libdwflP.h> +#include "../lib/crc32_file.c" diff --git a/src/libdwfl/lines.c b/src/libdwfl/lines.c new file mode 100644 index 00000000..ed4267fc --- /dev/null +++ b/src/libdwfl/lines.c @@ -0,0 +1,73 @@ +/* Fetch source line info for CU. + Copyright (C) 2005, 2006 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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/src/libdwfl/link_map.c b/src/libdwfl/link_map.c new file mode 100644 index 00000000..05839b3a --- /dev/null +++ b/src/libdwfl/link_map.c @@ -0,0 +1,888 @@ +/* Report modules by examining dynamic linker data structures. + Copyright (C) 2008-2010 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#include <config.h> +#include "libdwflP.h" + +#include <byteswap.h> +#include <endian.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) + +#if BYTE_ORDER == BIG_ENDIAN +# define BE32(x) (x) +# define BE64(x) (x) +# define LE32(x) bswap_32 (x) +# define LE64(x) bswap_64 (x) +#else +# define LE32(x) (x) +# define LE64(x) (x) +# define BE32(x) bswap_32 (x) +# define BE64(x) bswap_64 (x) +#endif + + +/* 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) +{ + const union + { + char buf[size]; + Elf32_auxv_t a32[size / sizeof (Elf32_auxv_t)]; + Elf64_auxv_t a64[size / sizeof (Elf64_auxv_t)]; + } *u = auxv; + + inline bool check64 (size_t i) + { + if (u->a64[i].a_type == BE64 (PROBE_TYPE) + && u->a64[i].a_un.a_val == BE64 (PROBE_VAL64)) + { + *elfdata = ELFDATA2MSB; + return true; + } + + if (u->a64[i].a_type == LE64 (PROBE_TYPE) + && u->a64[i].a_un.a_val == LE64 (PROBE_VAL64)) + { + *elfdata = ELFDATA2LSB; + return true; + } + + return false; + } + + inline bool check32 (size_t i) + { + if (u->a32[i].a_type == BE32 (PROBE_TYPE) + && u->a32[i].a_un.a_val == BE32 (PROBE_VAL32)) + { + *elfdata = ELFDATA2MSB; + return true; + } + + if (u->a32[i].a_type == LE32 (PROBE_TYPE) + && u->a32[i].a_un.a_val == LE32 (PROBE_VAL32)) + { + *elfdata = ELFDATA2LSB; + return true; + } + + return false; + } + + for (size_t i = 0; i < size / sizeof (Elf64_auxv_t); ++i) + { + if (check64 (i)) + { + *elfclass = ELFCLASS64; + return true; + } + + if (check32 (i * 2) || check32 (i * 2 + 1)) + { + *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 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) +{ + /* 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; + } + + const union + { + Elf32_Addr a32[n]; + Elf64_Addr a64[n]; + } *in = vaddr - read_vaddr + buffer; + + if (elfclass == ELFCLASS32) + { + if (elfdata == ELFDATA2MSB) + for (size_t i = 0; i < n; ++i) + addrs[i] = BE32 (in->a32[i]); + else + for (size_t i = 0; i < n; ++i) + addrs[i] = LE32 (in->a32[i]); + } + else + { + if (elfdata == ELFDATA2MSB) + for (size_t i = 0; i < n; ++i) + addrs[i] = BE64 (in->a64[i]); + else + for (size_t i = 0; i < n; ++i) + addrs[i] = LE64 (in->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); + + 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 content-sniffing already reported a module covering + the same area, find that existing module to adjust. + The l_ld address is the only one we know for sure + to be within the module's own segments (its .dynamic). */ + Dwfl_Module *mod = INTUSE(dwfl_addrmodule) (dwfl, l_ld); + if (mod != NULL) + { + /* We have a module. We can give it a better name from l_name. */ + if (name != NULL && mod->name[0] == '[') + { + char *newname = strdup (basename (name)); + if (newname != NULL) + { + free (mod->name); + mod->name = newname; + } + } + + if (name == NULL && mod->name[0] == '/') + name = mod->name; + + /* If we don't have a file for it already, we can pre-install + the full file name from l_name. Opening the file by this + name will be the fallback when no build ID match is found. + XXX hook for sysroot */ + if (name != NULL && mod->main.name == NULL) + mod->main.name = strdup (name); + } + else if (name != NULL) + { + /* We have to find the file's phdrs to compute along with l_addr + what its runtime address boundaries are. */ + + // XXX hook for sysroot + mod = INTUSE(dwfl_report_elf) (dwfl, basename (name), + name, -1, l_addr); + } + + 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; + for (uint_fast16_t i = 0; i < ehdr.e_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) +{ + 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 AUXV_SCAN(NN, BL) do \ + { \ + const Elf##NN##_auxv_t *av = auxv; \ + for (size_t i = 0; i < auxv_size / sizeof av[0]; ++i) \ + { \ + Elf##NN##_Addr val = BL##NN (av[i].a_un.a_val); \ + if (av[i].a_type == BL##NN (AT_ENTRY)) \ + entry = val; \ + else if (av[i].a_type == BL##NN (AT_PHDR)) \ + phdr = val; \ + else if (av[i].a_type == BL##NN (AT_PHNUM)) \ + phnum = val; \ + else if (av[i].a_type == BL##NN (AT_PHENT)) \ + phent = val; \ + else if (av[i].a_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 + }; + if ((*memory_callback) (dwfl, phdr_segndx, &in.d_buf, &in.d_size, + phdr, phnum * phent, memory_callback_arg)) + { + union + { + Elf32_Phdr p32; + Elf64_Phdr p64; + char data[phnum * phent]; + } buf; + 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. */ + const union + { + Elf32_Phdr p32[phnum]; + Elf64_Phdr p64[phnum]; + } *u = (void *) &buf; + if (elfclass == ELFCLASS32) + { + for (size_t i = 0; i < phnum; ++i) + if (consider_phdr (u->p32[i].p_type, + u->p32[i].p_vaddr, + u->p32[i].p_filesz)) + break; + } + else + { + for (size_t i = 0; i < phnum; ++i) + if (consider_phdr (u->p64[i].p_type, + u->p64[i].p_vaddr, + u->p64[i].p_filesz)) + break; + } + } + + (*memory_callback) (dwfl, -1, &in.d_buf, &in.d_size, 0, 0, + memory_callback_arg); + } + 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)) + { + union + { + Elf32_Dyn d32; + Elf64_Dyn d64; + char data[dyn_filesz]; + } buf; + 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. */ + const union + { + Elf32_Dyn d32[dyn_filesz / sizeof (Elf32_Dyn)]; + Elf64_Dyn d64[dyn_filesz / sizeof (Elf64_Dyn)]; + } *u = (void *) &buf; + if (elfclass == ELFCLASS32) + { + size_t n = dyn_filesz / sizeof (Elf32_Dyn); + for (size_t i = 0; i < n; ++i) + if (u->d32[i].d_tag == DT_DEBUG) + { + r_debug_vaddr = u->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 (u->d64[i].d_tag == DT_DEBUG) + { + r_debug_vaddr = u->d64[i].d_un.d_val; + break; + } + } + } + + (*memory_callback) (dwfl, -1, &in.d_buf, &in.d_size, 0, 0, + memory_callback_arg); + } + } + } + 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); +} +INTDEF (dwfl_link_map_report) diff --git a/src/libdwfl/linux-kernel-modules.c b/src/libdwfl/linux-kernel-modules.c new file mode 100644 index 00000000..e2392722 --- /dev/null +++ b/src/libdwfl/linux-kernel-modules.c @@ -0,0 +1,943 @@ +/* Standard libdwfl callbacks for debugging the running Linux kernel. + Copyright (C) 2005-2011 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +/* 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. */ + +/* TODO ANDROID - defined in AndroidConfig.h. */ +#undef _FILE_OFFSET_BITS + +#include <fts.h> + +#include <config.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> + + +#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. */ + + +#if defined (USE_ZLIB) || defined (USE_BZLIB) || defined (USE_LZMA) +static const char *vmlinux_suffixes[] = + { +#ifdef USE_ZLIB + ".gz", +#endif +#ifdef USE_BZLIB + ".bz2", +#endif +#ifdef USE_LZMA + ".xz", +#endif + }; +#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 (open64 (*fname, O_RDONLY))); + + if (fd < 0) + { + char *debugfname = NULL; + Dwfl_Module fakemod = { .dwfl = dwfl }; + /* First try the file's unadorned basename as DEBUGLINK_FILE, + to look for "vmlinux" files. */ + fd = INTUSE(dwfl_standard_find_debuginfo) (&fakemod, NULL, NULL, 0, + *fname, basename (*fname), 0, + &debugfname); + if (fd < 0 && try_debug) + /* Next, let the call use the default of basename + ".debug", + to look for "vmlinux.debug" files. */ + fd = INTUSE(dwfl_standard_find_debuginfo) (&fakemod, NULL, NULL, 0, + *fname, NULL, 0, + &debugfname); + if (debugfname != NULL) + { + free (*fname); + *fname = debugfname; + } + } + +#if defined (USE_ZLIB) || defined (USE_BZLIB) || defined (USE_LZMA) + 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 (open64 (zname, O_RDONLY)); + if (fd < 0) + free (zname); + else + { + free (*fname); + *fname = zname; + } + } + } +#endif + + if (fd < 0) + { + free (*fname); + *fname = NULL; + } + + return fd; +} + +static inline const char * +kernel_release (void) +{ + /* 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; +} + +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) + { + Dwfl_Module *mod = INTUSE(dwfl_report_elf) (dwfl, KERNEL_MODNAME, + fname, fd, 0); + if (mod == NULL) + result = -1; + else + /* The kernel is ET_EXEC, but always treat it as relocatable. */ + mod->e_type = ET_DYN; + } + + if (!report || result < 0) + close (fd); + } + + free (fname); + + 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; + if (unlikely ((*release)[0] == '/' + ? asprintf (&archive, "%s/debug.a", *release) + : asprintf (&archive, MODULEDIRFMT "/debug.a", *release)) < 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"); +#if USE_ZLIB + TRY (".ko.gz"); +#endif +#if USE_BZLIB + TRY (".ko.bz2"); +#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[f->fts_namelen - suffix + 1]; + for (size_t i = 0; i < f->fts_namelen - 3U; ++i) + if (f->fts_name[i] == '-' || f->fts_name[i] == ',') + name[i] = '_'; + else + name[i] = f->fts_name[i]; + name[f->fts_namelen - suffix] = '\0'; + + if (predicate != NULL) + { + /* Let the predicate decide whether to use this one. */ + int want = (*predicate) (name, f->fts_path); + if (want < 0) + { + result = -1; + break; + } + if (!want) + continue; + } + + if (dwfl_report_offline (dwfl, name, f->fts_path, -1) == NULL) + { + result = -1; + break; + } + } + 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) + + +/* 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) +{ + FILE *f = fopen (KSYMSFILE, "r"); + if (f == NULL) + return errno; + + (void) __fsetlocking (f, FSETLOCKING_BYCALLER); + + *notes = 0; + + char *line = NULL; + size_t linesz = 0; + size_t n; + char *p = NULL; + const char *type; + + inline bool read_address (Dwarf_Addr *addr) + { + if ((n = getline (&line, &linesz, f)) < 1 || line[n - 2] == ']') + return false; + *addr = strtoull (line, &p, 16); + p += strspn (p, " \t"); + type = strsep (&p, " \t\n"); + if (type == NULL) + return false; + return p != NULL && p != line; + } + + int result; + do + result = read_address (start) ? 0 : -1; + while (result == 0 && strchr ("TtRr", *type) == NULL); + + if (result == 0) + { + *end = *start; + while (read_address (end)) + if (*notes == 0 && !strcmp (p, "__start_notes\n")) + *notes = *end; + + Dwarf_Addr round_kernel = sysconf (_SC_PAGE_SIZE); + *start &= -(Dwarf_Addr) round_kernel; + *end += round_kernel - 1; + *end &= -(Dwarf_Addr) round_kernel; + if (*start >= *end || *end - *start < round_kernel) + result = -1; + } + free (line); + + if (result == -1) + result = ferror_unlocked (f) ? errno : ENOEXEC; + + fclose (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 = open64 (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; + Dwarf_Addr end; + inline Dwfl_Module *report (void) + { + return INTUSE(dwfl_report_module) (dwfl, KERNEL_MODNAME, start, end); + } + + /* This is a bit of a kludge. If we already reported the kernel, + don't bother figuring it out again--it never changes. */ + for (Dwfl_Module *m = dwfl->modulelist; m != NULL; m = m->next) + if (!strcmp (m->name, KERNEL_MODNAME)) + { + start = m->low_addr; + end = m->high_addr; + return report () == NULL ? -1 : 0; + } + + /* Try to figure out the bounds of the kernel image without + looking for any vmlinux file. */ + Dwarf_Addr notes; + /* The compiler cannot deduce that if intuit_kernel_bounds returns + zero NOTES will be initialized. Fake the initialization. */ + asm ("" : "=m" (notes)); + int result = intuit_kernel_bounds (&start, &end, ¬es); + if (result == 0) + { + Dwfl_Module *mod = report (); + return unlikely (mod == NULL) ? -1 : check_kernel_notes (mod, notes); + } + if (result != ENOENT) + return result; + + /* Find the ELF file for the running kernel and dwfl_report_elf it. */ + return report_kernel (dwfl, NULL, NULL); +} +INTDEF (dwfl_linux_kernel_report_kernel) + + +/* 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[namelen + 1]; + inline bool subst_name (char from, char to) + { + 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; + } + if (!subst_name ('-', '_') && !subst_name ('_', '-')) + 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 = open64 (f->fts_accpath, O_RDONLY); + *file_name = strdup (f->fts_path); + fts_close (fts); + free (modulesdir[0]); + 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]); + 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/src/libdwfl/linux-proc-maps.c b/src/libdwfl/linux-proc-maps.c new file mode 100644 index 00000000..8504a5f3 --- /dev/null +++ b/src/libdwfl/linux-proc-maps.c @@ -0,0 +1,348 @@ +/* Standard libdwfl callbacks for debugging a live Linux process. + Copyright (C) 2005-2010 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#include "libdwflP.h" +#include <inttypes.h> +#include <sys/types.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> + + +#define PROCMAPSFMT "/proc/%d/maps" +#define PROCMEMFMT "/proc/%d/mem" +#define PROCAUXVFMT "/proc/%d/auxv" + + +/* Search /proc/PID/auxv for the AT_SYSINFO_EHDR tag. */ + +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 = open64 (fname, O_RDONLY); + free (fname); + if (fd < 0) + return errno == ENOENT ? 0 : errno; + + ssize_t nread; + do + { + union + { + char buffer[sizeof (long int) * 2 * 64]; + Elf64_auxv_t a64[sizeof (long int) * 2 * 64 / sizeof (Elf64_auxv_t)]; + Elf32_auxv_t a32[sizeof (long int) * 2 * 32 / sizeof (Elf32_auxv_t)]; + } d; + nread = read (fd, &d, sizeof d); + if (nread > 0) + { + switch (sizeof (long int)) + { + case 4: + for (size_t i = 0; (char *) &d.a32[i] < &d.buffer[nread]; ++i) + if (d.a32[i].a_type == AT_SYSINFO_EHDR) + { + *sysinfo_ehdr = d.a32[i].a_un.a_val; + if (dwfl->segment_align > 1) + { + nread = 0; + break; + } + } + else if (d.a32[i].a_type == AT_PAGESZ + && dwfl->segment_align <= 1) + dwfl->segment_align = d.a32[i].a_un.a_val; + break; + case 8: + for (size_t i = 0; (char *) &d.a64[i] < &d.buffer[nread]; ++i) + if (d.a64[i].a_type == AT_SYSINFO_EHDR) + { + *sysinfo_ehdr = d.a64[i].a_un.a_val; + if (dwfl->segment_align > 1) + { + nread = 0; + break; + } + } + else if (d.a64[i].a_type == AT_PAGESZ + && dwfl->segment_align <= 1) + dwfl->segment_align = d.a64[i].a_un.a_val; + break; + default: + abort (); + break; + } + } + } + while (nread > 0); + + close (fd); + + return nread < 0 ? errno : 0; +} + +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; + + inline bool report (void) + { + if (last_file != NULL) + { + Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, last_file, + low, high); + free (last_file); + last_file = NULL; + if (unlikely (mod == NULL)) + return true; + } + return false; + } + + 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); + 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); + fclose (f); + 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] == '\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. */ + assert (!strcmp (last_file, file)); + 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; + ssize_t nread = pread64 (fd, data, maxread, (off64_t) address); + /* Some kernels don't actually let us do this read, ignore those errors. */ + if (nread < 0 && (errno == EINVAL || errno == EPERM)) + return 0; + if (nread > 0 && (size_t) nread < minread) + nread = 0; + return nread; +} + +extern Elf *elf_from_remote_memory (GElf_Addr ehdr_vma, + 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) +{ + if (module_name[0] == '/') + { + int fd = open64 (module_name, O_RDONLY); + if (fd >= 0) + { + *file_name = strdup (module_name); + if (*file_name == NULL) + { + close (fd); + return ENOMEM; + } + } + return fd; + } + + int pid; + if (sscanf (module_name, "[vdso: %d]", &pid) == 1) + { + /* Special case for in-memory ELF image. */ + + char *fname; + if (asprintf (&fname, PROCMEMFMT, pid) < 0) + return -1; + + int fd = open64 (fname, O_RDONLY); + free (fname); + if (fd < 0) + return -1; + + *elfp = elf_from_remote_memory (base, NULL, &read_proc_memory, &fd); + + close (fd); + + *file_name = NULL; + return -1; + } + + abort (); + return -1; +} +INTDEF (dwfl_linux_proc_find_elf) diff --git a/src/libdwfl/lzma.c b/src/libdwfl/lzma.c new file mode 100644 index 00000000..3edfdc22 --- /dev/null +++ b/src/libdwfl/lzma.c @@ -0,0 +1,4 @@ +/* liblzma is pretty close to zlib and bzlib. */ + +#define LZMA +#include "gzip.c" diff --git a/src/libdwfl/offline.c b/src/libdwfl/offline.c new file mode 100644 index 00000000..34aa9f84 --- /dev/null +++ b/src/libdwfl/offline.c @@ -0,0 +1,329 @@ +/* Recover relocatibility for addresses computed from debug information. + Copyright (C) 2005, 2006, 2007, 2008, 2009 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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); + + 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, 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, "//")) + { + 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 = open64 (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/src/libdwfl/open.c b/src/libdwfl/open.c new file mode 100644 index 00000000..397af358 --- /dev/null +++ b/src/libdwfl/open.c @@ -0,0 +1,203 @@ +/* Decompression support for libdwfl: zlib (gzip) and/or bzlib (bzip2). + Copyright (C) 2009 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#include "../libelf/libelfP.h" +#undef _ +#include "libdwflP.h" + +#include <unistd.h> + +#if !USE_ZLIB +# define __libdw_gunzip(...) false +#endif + +#if !USE_BZLIB +# define __libdw_bunzip2(...) false +#endif + +#if !USE_LZMA +# define __libdw_unlzma(...) false +#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; + +#if USE_ZLIB || USE_BZLIB || USE_LZMA + const off64_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); +#endif + + 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. */ + + off64_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/src/libdwfl/relocate.c b/src/libdwfl/relocate.c new file mode 100644 index 00000000..95206f47 --- /dev/null +++ b/src/libdwfl/relocate.c @@ -0,0 +1,655 @@ +/* Relocate debug information. + Copyright (C) 2005-2010 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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) +{ + assert (mod->e_type == ET_REL); + + 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) + 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); + symtab->symstrdata = elf_getdata (elf_getscn (symtab->symelf, + symtab->strtabndx), + NULL); + if (unlikely (symtab->symstrdata == 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, 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; +} + +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. */ + GElf_Shdr tshdr_mem; + GElf_Shdr *tshdr = gelf_getshdr (tscn, &tshdr_mem); + const char *tname = elf_strptr (relocated, shstrndx, tshdr->sh_name); + if (tname == 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; + + 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; + + /* Fetch the section data that needs the relocations applied. */ + Elf_Data *tdata = elf_rawdata (tscn, NULL); + if (tdata == NULL) + return DWFL_E_LIBELF; + + /* Apply one relocation. Returns true for any invalid data. */ + Dwfl_Error relocate (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 + size > tdata->d_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; + } + + /* 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; + inline void check_badreltype (void) + { + 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; + } + } + + size_t nrels = shdr->sh_size / shdr->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 (r->r_offset, NULL, + GELF_R_TYPE (r->r_info), + GELF_R_SYM (r->r_info)); + check_badreltype (); + if (partial) + switch (result) + { + case DWFL_E_NOERROR: + /* We applied the relocation. Elide it. */ + memset (&rel_mem, 0, sizeof rel_mem); + gelf_update_rel (reldata, relidx, &rel_mem); + ++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 (r->r_offset, &r->r_addend, + GELF_R_TYPE (r->r_info), + GELF_R_SYM (r->r_info)); + check_badreltype (); + if (partial) + switch (result) + { + case DWFL_E_NOERROR: + /* We applied the relocation. Elide it. */ + memset (&rela_mem, 0, sizeof rela_mem); + gelf_update_rela (reldata, relidx, &rela_mem); + ++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 (r->r_info != 0 || r->r_offset != 0) + { + if (next != relidx) + gelf_update_rel (reldata, next, r); + ++next; + } + } + else + for (size_t relidx = 0; relidx < nrels; ++relidx) + { + GElf_Rela rela_mem; + GElf_Rela *r = gelf_getrela (reldata, relidx, &rela_mem); + if (r->r_info != 0 || r->r_offset != 0 || r->r_addend != 0) + { + if (next != relidx) + gelf_update_rela (reldata, next, r); + ++next; + } + } + nrels = next; + } + + shdr->sh_size = reldata->d_size = nrels * shdr->sh_entsize; + gelf_update_shdr (scn, shdr); + } + + 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 ((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; + + return (__libdwfl_module_getebl (mod) + ?: relocate_section (mod, relocated, + gelf_getehdr (relocated, &ehdr_mem), shstrndx, + &reloc_symtab, + relocscn, gelf_getshdr (relocscn, &shdr_mem), + tscn, false, partial)); +} diff --git a/src/libdwfl/segment.c b/src/libdwfl/segment.c new file mode 100644 index 00000000..9d78c87f --- /dev/null +++ b/src/libdwfl/segment.c @@ -0,0 +1,350 @@ +/* Manage address space lookup table for libdwfl. + Copyright (C) 2008, 2009, 2010 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat 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 a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#include "libdwflP.h" + +static GElf_Addr +segment_start (Dwfl *dwfl, GElf_Addr start) +{ + if (dwfl->segment_align > 1) + start &= -dwfl->segment_align; + return start; +} + +static GElf_Addr +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 >= 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 = segment_start (dwfl, mod->low_addr); + const GElf_Addr end = 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 = segment_start (dwfl, bias + phdr->p_vaddr); + GElf_Addr end = 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) |