Hi Mark, patch updated to address the issues/regressions.
On Sun, 19 May 2013 18:38:56 +0200, Jan Kratochvil wrote: > New dwfl_link_map_report does not report modules for which it cannot find its > file on disk. This is correct as one does not know the module boundaries just > from a link map entry. The module boundaries were found in such case by > dwfl_segment_report_module and names filled in later by dwfl_link_map_report. > > Recent Linux kernels now produce also core note NT_FILE but in such case one > usually has also build-id headers already so this patch is not needed. > > My current plan is: > (1) Save filenames for non-existing files during initial dwfl_link_map_report. > (2) Run dwfl_segment_report_module even if dwfl_link_map_report found > something. > (2b) dwfl_segment_report_module should omit conflicting modules. > (3) Update found anonymous "[dso]" modules by the saved filenames. Implemented. This patch addresses the issues and use case (2) discussed below: > On Sat, 18 May 2013 00:00:58 +0200, Mark Wielaard wrote: > > So you don't sniff at all if dwfl_link_map_report () found > > something. Would it hurt at this point to sniff? > > That's the question. Sniffing will always bring in possibly incorrect data. > I believe it would be useful only in the case link_map (r_debug->r_map) is > corrupted in the middle and some of the libraries do not get reported > (as I have seen in real world core files from ABRT). Maybe report_r_debug > could report corrupted link_map - it currently internally checks > ++iterations < dwfl->lookup_elts > but it should also check l_prev does match the previous entry like GDB does. > > Still core files with corrupted link_map I find more a corner case and it > could be added as an add-on patch IMO. On Fri, 26 Apr 2013 20:57:39 +0200, Jan Kratochvil wrote: > Not sure if one should try to map address-non-conflicting files from ELF > headers, this patch does not do it. (That could better fit case (2) below.) [...] > (2) I want to know all build-ids: eu-unstrip -n --core=xxx > Modules placement does not matter, excessive output does not matter, > I need to only install needed debuginfos. > > This change will behave better in (1) but it may regress (2), for example in > cases where link_map list is overwritten/corrupted in its middle. Thanks, Jan commit 457b771b842a0f6d23118e4b39d93f6581fb9b41 Author: Jan Kratochvil <[email protected]> Date: Thu May 23 20:19:16 2013 +0200 Use DT_DEBUG library search first. libdwfl/ 2013-05-23 Jan Kratochvil <[email protected]> * argp-std.c (parse_opt) <ARGP_KEY_SUCCESS> <opt->core> <opt->e>: Set executable_for_core before calling dwfl_core_file_report. * core-file.c (free_r_debug_info): New function. (dwfl_core_file_report): Move raw segments reporting lower. New variable r_debug_info. (dwfl_core_file_report) (report_module): New function. (dwfl_core_file_report): Call also report_module for EXEC_VADDR and VDSO_L_LD. Call free_r_debug_info in the end. Return sum of LISTED and SNIFFED. * dwfl_module_build_id.c (check_notes): Move into __libdwfl_find_elf_build_id. (__libdwfl_find_build_id): Rename to ... (__libdwfl_find_elf_build_id): ... here. Add parameters build_id_bits, build_id_elfaddr and build_id_len. Verify MOD vs. ELF. (__libdwfl_find_elf_build_id) (check_notes): Remove parameters mod and set, rename data_vaddr to data_elfaddr. Do not call found_build_id. (__libdwfl_find_elf_build_id): Update the check_notes caller, do not adjust its data_elfaddr parameter. (__libdwfl_find_build_id): New wrapper of __libdwfl_find_elf_build_id. * dwfl_segment_report_module.c (dwfl_segment_report_module): New parameter r_debug_info. New variable name_is_final. Adjust addresses according to R_DEBUG_INFO->MODULE. Check conflicts against DWFL. Do not overwise NAME by SONAME if NAME_IS_FINAL. * libdwflP.h (__libdwfl_find_elf_build_id): New declaration. (struct r_debug_info_module, struct r_debug_info): New definitions. (dwfl_segment_report_module, dwfl_link_map_report): Add parameter r_debug_info. * link_map.c: Include fcntl.h. (report_r_debug): Add parameter r_debug_info, describe them in the function comment. Delete dwfl_addrmodule call and its dependent code. Verify build-id before calling dwfl_report_elf, also supply executable_for_core to it and skip vDSO. Report vdso_bias and vdso_l_ld when found. Clear exec_vaddr when found. Store r_debug_info->module info when appropriate. (dwfl_link_map_report): Add parameter r_debug_info. (dwfl_link_map_report) (consider_phdr): Add parameter offset. Set r_debug_info.exec_vaddr when found. (dwfl_link_map_report): New variable in_ok. Try to read IN from EXECUTABLE_FOR_CORE. Update consider_phdr caller parameters. Update report_r_debug caller parameters. tests/ 2013-05-23 Jan Kratochvil <[email protected]> * Makefile.am (EXTRA_DIST): Add test-core-lib.so.bz2, test-core.core.bz2 and test-core.exec.bz2. * run-addrname-test.sh: New test for these files. * run-unstrip-n.sh: Update expected output. New test for these files. * test-core-lib.so.bz2: New file. * test-core.core.bz2: New file. * test-core.exec.bz2: New file. Signed-off-by: Jan Kratochvil <[email protected]> diff --git a/libdwfl/argp-std.c b/libdwfl/argp-std.c index e54f720..c884390 100644 --- a/libdwfl/argp-std.c +++ b/libdwfl/argp-std.c @@ -295,6 +295,9 @@ parse_opt (int key, char *arg, struct argp_state *state) if (opt->core) { + if (opt->e) + dwfl->executable_for_core = strdup (opt->e); + int fd = open64 (opt->core, O_RDONLY); if (fd < 0) { @@ -330,9 +333,6 @@ parse_opt (int key, char *arg, struct argp_state *state) _("No modules recognized in core file")); return ENOENT; } - - if (opt->e) - dwfl->executable_for_core = strdup (opt->e); } else if (opt->e) { diff --git a/libdwfl/core-file.c b/libdwfl/core-file.c index ac2aa8c..324bc5c 100644 --- a/libdwfl/core-file.c +++ b/libdwfl/core-file.c @@ -381,6 +381,19 @@ dwfl_elf_phdr_memory_callback (Dwfl *dwfl, int ndx, return true; } +/* Free the contents of R_DEBUG_INFO without the R_DEBUG_INFO memory itself. */ + +static void +free_r_debug_info (struct r_debug_info *r_debug_info) +{ + while (r_debug_info->module != NULL) + { + struct r_debug_info_module *module = r_debug_info->module; + r_debug_info->module = module->next; + free (module); + } +} + int dwfl_core_file_report (Dwfl *dwfl, Elf *elf) { @@ -431,33 +444,85 @@ dwfl_core_file_report (Dwfl *dwfl, Elf *elf) /* Now we have NT_AUXV contents. From here on this processing could be used for a live process with auxv read from /proc. */ + struct r_debug_info r_debug_info; + memset (&r_debug_info, 0, sizeof r_debug_info); int listed = dwfl_link_map_report (dwfl, auxv, auxv_size, - dwfl_elf_phdr_memory_callback, elf); + dwfl_elf_phdr_memory_callback, elf, + &r_debug_info); + + /* Call dwfl_segment_report_module, update *NDXP when appropriate. Return + true for a successfully reported module. Otherwise return false and also + call free_r_debug_info. */ + + inline bool report_module (int *ndxp, int *count, const char *name) + { + int seg = dwfl_segment_report_module (dwfl, *ndxp, name, + &dwfl_elf_phdr_memory_callback, elf, + core_file_read_eagerly, elf, + &r_debug_info); + if (unlikely (seg < 0)) + { + free_r_debug_info (&r_debug_info); + return false; + } + if (seg > *ndxp) + { + *ndxp = seg; + ++*count; + } + else + ++*ndxp; + return true; + } + + if (r_debug_info.vdso_l_ld != 0 || r_debug_info.exec_vaddr != 0) + for (ndx = 0; ndx < (int) phnum;) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (elf, ndx, &phdr_mem); + if (unlikely (phdr == NULL)) + { + free_r_debug_info (&r_debug_info); + __libdwfl_seterrno (DWFL_E_LIBELF); + return -1; + } + if (phdr->p_type == PT_LOAD && r_debug_info.exec_vaddr != 0 + && phdr->p_vaddr == r_debug_info.exec_vaddr) + { + if (unlikely (! report_module (&ndx, &listed, "[exe]"))) + return -1; + } + else if (phdr->p_type == PT_LOAD && r_debug_info.vdso_l_ld != 0 + && phdr->p_filesz == phdr->p_memsz + && phdr->p_vaddr <= r_debug_info.vdso_l_ld + && r_debug_info.vdso_l_ld < phdr->p_vaddr + phdr->p_memsz) + { + /* FIXME: Use vdso_bias. Currently dwfl_segment_report_module + detects the bias on its own. */ + if (unlikely (! report_module (&ndx, &listed, "[vdso]"))) + return -1; + } + else + ++ndx; + } + + /* Now sniff segment contents for modules hinted by information gathered + from DT_DEBUG. */ - /* 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); + ndx = 0; + do + { + if (unlikely (! report_module (&ndx, &sniffed, NULL) )) + return -1; + } + while (ndx < (int) phnum); + + free_r_debug_info (&r_debug_info); /* We return the number of modules we found if we found any. If we found none, we return -1 instead of 0 if there was an - error rather than just nothing found. If link_map handling - failed, we still have the sniffed modules. */ - return sniffed == 0 || listed > sniffed ? listed : sniffed; + error rather than just nothing found. */ + return sniffed || listed >= 0 ? listed + sniffed : listed; } INTDEF (dwfl_core_file_report) diff --git a/libdwfl/dwfl_module_build_id.c b/libdwfl/dwfl_module_build_id.c index 98cb9f4..cae007b 100644 --- a/libdwfl/dwfl_module_build_id.c +++ b/libdwfl/dwfl_module_build_id.c @@ -56,10 +56,20 @@ found_build_id (Dwfl_Module *mod, bool set, int internal_function -__libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf) +__libdwfl_find_elf_build_id (Dwfl_Module *mod, Elf *elf, + const void **build_id_bits, + GElf_Addr *build_id_elfaddr, int *build_id_len) { - int - check_notes (Dwfl_Module *mod, bool set, Elf_Data *data, GElf_Addr data_vaddr) + GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem); + if (unlikely (ehdr == NULL)) + { + __libdwfl_seterrno (DWFL_E_LIBELF); + return -1; + } + // MOD->E_TYPE is zero here. + assert (ehdr->e_type != ET_REL || mod != NULL); + + int check_notes (Elf_Data *data, GElf_Addr data_elfaddr) { size_t pos = 0; GElf_Nhdr nhdr; @@ -69,10 +79,13 @@ __libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf) 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); + { + *build_id_bits = data->d_buf + desc_pos; + *build_id_elfaddr = (data_elfaddr == NO_VADDR + ? 0 : data_elfaddr + desc_pos); + *build_id_len = nhdr.n_descsz; + return 1; + } return 0; } @@ -84,11 +97,8 @@ __libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf) 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)) + if (unlikely (elf_getphdrnum (elf, &phnum) != 0)) { __libdwfl_seterrno (DWFL_E_LIBELF); return -1; @@ -98,12 +108,11 @@ __libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf) 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, + result = check_notes (elf_getdata_rawchunk (elf, phdr->p_offset, phdr->p_filesz, ELF_T_NHDR), - dwfl_adjusted_address (mod, phdr->p_vaddr)); + phdr->p_vaddr); } } else @@ -117,12 +126,12 @@ __libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf) 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 (mod == NULL || ehdr->e_type != ET_REL) + vaddr = 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); + result = check_notes (elf_getdata (scn, NULL), vaddr); } } while (result == 0 && (scn = elf_nextscn (elf, scn)) != NULL); @@ -131,6 +140,24 @@ __libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf) } int +internal_function +__libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf) +{ + const void *build_id_bits; + GElf_Addr build_id_elfaddr; + int build_id_len; + + int result = __libdwfl_find_elf_build_id (mod, elf, &build_id_bits, + &build_id_elfaddr, &build_id_len); + if (result <= 0) + return result; + + GElf_Addr build_id_vaddr = build_id_elfaddr + (build_id_elfaddr != 0 + ? mod->main_bias : 0); + return found_build_id (mod, set, build_id_bits, build_id_len, build_id_vaddr); +} + +int dwfl_module_build_id (Dwfl_Module *mod, const unsigned char **bits, GElf_Addr *vaddr) { diff --git a/libdwfl/dwfl_segment_report_module.c b/libdwfl/dwfl_segment_report_module.c index 7cf7499..e2d0337 100644 --- a/libdwfl/dwfl_segment_report_module.c +++ b/libdwfl/dwfl_segment_report_module.c @@ -83,7 +83,8 @@ 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) + void *read_eagerly_arg, + const struct r_debug_info *r_debug_info) { size_t segment = ndx; @@ -433,6 +434,43 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name, dyn_vaddr += bias; + /* NAME found from link map has precedence over DT_SONAME possibly read + below. */ + bool name_is_final = false; + + /* Try to match up DYN_VADDR against L_LD as found in link map. + Segments sniffing may guess invalid address as the first read-only memory + mapping may not be dumped to the core file (if ELF headers are not dumped) + and the ELF header is dumped first with the read/write mapping of the same + file at higher addresses. */ + if (r_debug_info != NULL) + for (const struct r_debug_info_module *module = r_debug_info->module; + module != NULL; module = module->next) + if (module_start <= module->l_ld && module->l_ld < module_end) + { + GElf_Addr fixup = module->l_ld - dyn_vaddr; + if (module_start + fixup <= module->l_ld + && module->l_ld < module_end + fixup) + { + module_start += fixup; + module_end += fixup; + dyn_vaddr += fixup; + bias += fixup; + if (module->name[0] != '\0') + { + name = module->name; + name_is_final = true; + } + break; + } + } + + /* Ignore this found module if it would conflict in address space with any + already existing module of DWFL. */ + for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next) + if (module_end > mod->low_addr && module_start < mod->high_addr) + return finish (); + /* Our return value now says to skip the segments contained within the module. */ ndx = addr_segndx (dwfl, segment, module_end, true); @@ -518,7 +556,7 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name, void *soname = NULL; size_t soname_size = 0; - if (dynstrsz != 0 && dynstr_vaddr != 0) + if (! name_is_final && dynstrsz != 0 && dynstr_vaddr != 0) { /* We know the bounds of the .dynstr section. diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h index 37a9dff..1307bdb 100644 --- a/libdwfl/libdwflP.h +++ b/libdwfl/libdwflP.h @@ -362,6 +362,16 @@ extern Dwfl_Error __libdwfl_addrcu (Dwfl_Module *mod, Dwarf_Addr addr, extern Dwfl_Error __libdwfl_cu_getsrclines (struct dwfl_cu *cu) internal_function; +/* Look in ELF for an NT_GNU_BUILD_ID note. Store it to BUILD_ID_BITS, + its vaddr in ELF to BUILD_ID_VADDR (it is unrelocated, even if MOD is not + NULL) and store length to BUILD_ID_LEN. Returns -1 for errors, 1 if it was + stored and 0 if no note is found. MOD may be NULL, MOD must be non-NULL + only if ELF is ET_REL. */ +extern int __libdwfl_find_elf_build_id (Dwfl_Module *mod, Elf *elf, + const void **build_id_bits, + GElf_Addr *build_id_elfaddr, + int *build_id_len); + /* 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. @@ -439,13 +449,35 @@ typedef bool Dwfl_Module_Callback (Dwfl_Module *mod, void **userdata, GElf_Off whole, GElf_Off contiguous, void *arg, Elf **elfp); +/* One shared library (or executable) info from DT_DEBUG link map. */ +struct r_debug_info_module +{ + struct r_debug_info_module *next; + GElf_Addr l_ld; + char name[0]; +}; + +/* Information gathered from DT_DEBUG by dwfl_link_map_report hinted to + dwfl_segment_report_module. */ +struct r_debug_info +{ + /* Found l_addr and l_ld fields for vDSO. */ + GElf_Addr vdso_bias, vdso_l_ld; + + /* Found memory address of start of the executable. */ + GElf_Addr exec_vaddr; + + struct r_debug_info_module *module; +}; + /* ... */ extern int dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name, Dwfl_Memory_Callback *memory_callback, void *memory_callback_arg, Dwfl_Module_Callback *read_eagerly, - void *read_eagerly_arg); + void *read_eagerly_arg, + const struct r_debug_info *r_debug_info); /* Report a module for entry in the dynamic linker's struct link_map list. For each link_map entry, if an existing module resides at its address, @@ -459,10 +491,14 @@ extern int dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name, only find where to begin if the correct executable file was previously reported and preloaded as with dwfl_report_elf. + Fill in R_DEBUG_INFO if it is not NULL. It should be cleared by the + caller, this function does not touch fields it does not need to modify. + 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); + void *memory_callback_arg, + struct r_debug_info *r_debug_info); /* Avoid PLT entries. */ diff --git a/libdwfl/link_map.c b/libdwfl/link_map.c index 5dbf17e..c1bd60a 100644 --- a/libdwfl/link_map.c +++ b/libdwfl/link_map.c @@ -33,6 +33,7 @@ #include <byteswap.h> #include <endian.h> +#include <fcntl.h> /* This element is always provided and always has a constant value. This makes it an easy thing to scan for to discern the format. */ @@ -222,7 +223,8 @@ addrsize (uint_fast8_t elfclass) } /* Report a module for each struct link_map in the linked list at r_map - in the struct r_debug at R_DEBUG_VADDR. + in the struct r_debug at R_DEBUG_VADDR. For r_debug_info description + see dwfl_link_map_report in libdwflP.h. 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 @@ -234,7 +236,8 @@ 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) + void *memory_callback_arg, + struct r_debug_info *r_debug_info) { /* Skip r_version, to aligned r_map field. */ GElf_Addr read_vaddr = r_debug_vaddr + addrsize (elfclass); @@ -352,9 +355,108 @@ report_r_debug (uint_fast8_t elfclass, uint_fast8_t elfdata, /* 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, true); + Dwfl_Module *mod = NULL; + if (iterations == 1 && dwfl->executable_for_core != NULL) + { + /* Find the main executable. + FIXME: Try to also find it via build-id. */ + name = dwfl->executable_for_core; + } + if (iterations != 2 && name != NULL) + { + /* Find a shared library or main executable. Do not try to + find a file for vDSO (where ITERATIONS equals 2). */ + + /* This code is mostly inlined dwfl_report_elf. */ + // XXX hook for sysroot + int fd = open64 (name, O_RDONLY); + if (fd >= 0) + { + Elf *elf; + Dwfl_Error error = __libdw_open_file (&fd, &elf, true, false); + if (error == DWFL_E_NOERROR) + { + const void *build_id_bits; + GElf_Addr build_id_elfaddr; + int build_id_len; + bool valid = true; + + /* FIXME: Bias L_ADDR should be computed from the prelink + state in memory (when the file got loaded), not against + the current on-disk file state as is computed below. + + This verification gives false positive if in-core ELF had + build-id but on-disk ELF does not have any. But we cannot + reliably find ELF header and/or the ELF build id just from + the link map (and checking core segments is also not + reliable). */ + + if (__libdwfl_find_elf_build_id (NULL, elf, &build_id_bits, + &build_id_elfaddr, + &build_id_len) > 0 + && build_id_elfaddr != 0) + { + GElf_Addr build_id_vaddr = build_id_elfaddr + l_addr; + release_buffer (0); + int segndx = INTUSE(dwfl_addrsegment) (dwfl, + build_id_vaddr, + NULL); + if (! (*memory_callback) (dwfl, segndx, + &buffer, &buffer_available, + build_id_vaddr, build_id_len, + memory_callback_arg) + || memcmp (build_id_bits, buffer, build_id_len) != 0) + { + /* File has valid build-id which cannot be verified + in memory. */ + valid = false; + } + } + + if (valid) + // XXX hook for sysroot + mod = __libdwfl_report_elf (dwfl, basename (name), name, + fd, elf, l_addr, true, true); + if (mod == NULL) + { + elf_end (elf); + close (fd); + } + } + } + } + if (mod != NULL && iterations == 1) + { + /* Cancel reporting of raw segments for the main executable as + its file has been found now. */ + if (r_debug_info != NULL) + r_debug_info->exec_vaddr = 0; + } + if (iterations == 2) + { + /* vDSO is only reported to the caller, it is never searched + for on the disk. */ + assert (mod == NULL); + if (r_debug_info != NULL) + { + r_debug_info->vdso_bias = l_addr; + r_debug_info->vdso_l_ld = l_ld; + } + } + if (r_debug_info != NULL && mod == NULL) + { + /* Save link map information about valid shared library (or + executable) which has not been found on disk. */ + const char *base = name == NULL ? "" : basename (name); + struct r_debug_info_module *module; + module = malloc (sizeof (*module) + strlen (base) + 1); + if (module == NULL) + return release_buffer (result); + module->l_ld = l_ld; + strcpy (module->name, base); + module->next = r_debug_info->module; + r_debug_info->module = module; + } if (mod != NULL) { @@ -570,7 +672,8 @@ find_executable (Dwfl *dwfl, GElf_Addr at_phdr, GElf_Addr at_entry, int dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size, Dwfl_Memory_Callback *memory_callback, - void *memory_callback_arg) + void *memory_callback_arg, + struct r_debug_info *r_debug_info) { GElf_Addr r_debug_vaddr = 0; @@ -632,8 +735,8 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size, 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) + inline bool consider_phdr (GElf_Word type, GElf_Addr vaddr, + GElf_Xword filesz, Elf32_Off offset) { switch (type) { @@ -644,6 +747,8 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size, == (phdr & (dwfl->segment_align - 1)))) { dyn_bias = phdr - vaddr; + if (r_debug_info != NULL) + r_debug_info->exec_vaddr = phdr - offset; return dyn_vaddr != 0; } break; @@ -668,8 +773,65 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size, .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)) + bool in_ok = (*memory_callback) (dwfl, phdr_segndx, &in.d_buf, + &in.d_size, phdr, phnum * phent, + memory_callback_arg); + if (! in_ok && dwfl->executable_for_core != NULL) + { + /* AUXV -> PHDR -> DYNAMIC + Both AUXV and DYNAMIC should be always present in a core file. + PHDR may be missing in core file, try to read it from + EXECUTABLE_FOR_CORE to find where DYNAMIC is located in the + core file. */ + + int fd = open (dwfl->executable_for_core, O_RDONLY); + Elf *elf; + Dwfl_Error error = DWFL_E_ERRNO; + if (fd != -1) + error = __libdw_open_file (&fd, &elf, true, false); + if (error != DWFL_E_NOERROR) + { + __libdwfl_seterrno (error); + return false; + } + GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem); + if (ehdr == NULL) + { + elf_end (elf); + close (fd); + __libdwfl_seterrno (DWFL_E_LIBELF); + return false; + } + if (ehdr->e_phnum != phnum || ehdr->e_phentsize != phent) + { + elf_end (elf); + close (fd); + __libdwfl_seterrno (DWFL_E_BADELF); + return false; + } + off_t off = ehdr->e_phoff; + assert (in.d_buf == NULL); + assert (in.d_size == phnum * phent); + in.d_buf = malloc (in.d_size); + if (unlikely (in.d_buf == NULL)) + { + elf_end (elf); + close (fd); + __libdwfl_seterrno (DWFL_E_NOMEM); + return false; + } + ssize_t nread = pread_retry (fd, in.d_buf, in.d_size, off); + elf_end (elf); + close (fd); + if (nread != (ssize_t) in.d_size) + { + free (in.d_buf); + __libdwfl_seterrno (DWFL_E_ERRNO); + return false; + } + in_ok = true; + } + if (in_ok) { union { @@ -700,7 +862,8 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size, 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)) + u->p32[i].p_filesz, + u->p32[i].p_offset)) break; } else @@ -708,7 +871,8 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size, 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)) + u->p64[i].p_filesz, + u->p64[i].p_offset)) break; } } @@ -831,6 +995,6 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size, /* Now we can follow the dynamic linker's library list. */ return report_r_debug (elfclass, elfdata, dwfl, r_debug_vaddr, - &integrated_memory_callback, &mcb); + &integrated_memory_callback, &mcb, r_debug_info); } INTDEF (dwfl_link_map_report) diff --git a/tests/Makefile.am b/tests/Makefile.am index 6327edb..2d819c5 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -199,7 +199,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \ testfile70.core.bz2 testfile70.exec.bz2 \ run-dwfllines.sh run-dwfl-report-elf-align.sh \ testfile-dwfl-report-elf-align-shlib.so.bz2 \ - testfilenolines + testfilenolines test-core-lib.so.bz2 test-core.core.bz2 \ + test-core.exec.bz2 if USE_VALGRIND valgrind_cmd='valgrind -q --trace-children=yes --error-exitcode=1 --run-libc-freeres=no' diff --git a/tests/run-addrname-test.sh b/tests/run-addrname-test.sh index 9351fb2..8624074 100755 --- a/tests/run-addrname-test.sh +++ b/tests/run-addrname-test.sh @@ -316,4 +316,10 @@ main+0x9 ??:0 EOF +testfiles test-core-lib.so test-core.core test-core.exec +testrun_compare ${abs_top_builddir}/src/addr2line -S -e test-core.exec --core=test-core.core 0x7f67f2aaf619 <<\EOF +libfunc+0x9 +??:0 +EOF + exit 0 diff --git a/tests/run-unstrip-n.sh b/tests/run-unstrip-n.sh index 9c2b43b..b35d064 100755 --- a/tests/run-unstrip-n.sh +++ b/tests/run-unstrip-n.sh @@ -36,19 +36,34 @@ testfiles testcore-rtlib testcore-rtlib-ppc testrun_compare ${abs_top_builddir}/src/unstrip -n --core=testcore-rtlib <<\EOF 0x8048000+0x2000 f1c600bc36cb91bf01f9a63a634ecb79aa4c3199@0x8048178 . - [exe] 0xf77d6000+0x1000 676560b1b765cde9c2e53f134f4ee354ea894747@0xf77d6210 . - linux-gate.so.1 -0xf77b3000+0x9000 c6c5b5e35ab9589d4762ac85b4bd56b1b2720e37@0xf77b3164 /lib/librt.so.1 - librt.so.1 -0xf7603000+0x1b0000 0b9bf374699e141e5dfc14757ff42b8c2373b4de@0xf7603184 /lib/libc.so.6 - libc.so.6 -0xf75e9000+0x1a000 29a103420abe341e92072fb14274e250e4072148@0xf75e9164 /lib/libpthread.so.0 - libpthread.so.0 -0xf77d7000+0x21000 6d2cb32650054f1c176d01d48713a4a5e5e84c1a@0xf77d7124 /lib/ld-linux.so.2 - ld-linux.so.2 +0xf75e9000+0x1a000 29a103420abe341e92072fb14274e250e4072148@0xf75e9164 - - libpthread.so.0 +0xf7603000+0x1b0000 0b9bf374699e141e5dfc14757ff42b8c2373b4de@0xf7603184 - - libc.so.6 +0xf77b3000+0x9000 c6c5b5e35ab9589d4762ac85b4bd56b1b2720e37@0xf77b3164 - - librt.so.1 +0xf77d7000+0x21000 6d2cb32650054f1c176d01d48713a4a5e5e84c1a@0xf77d7124 - - ld-linux.so.2 EOF testrun_compare ${abs_top_builddir}/src/unstrip -n --core=testcore-rtlib-ppc <<\EOF -0x10000000+0x20000 979b7a26747cc09bd84a42b311b5288c704baea5@0x10000174 . - [exe] 0x100000+0x10000 708b900b05176964512a6b0fe90c2a0c9d73d726@0x100334 . - linux-vdso32.so.1 -0xfd50000+0x30000 3f7d21508470322d2f47acddc20ab10516edba99@0xfd50164 /lib/librt.so.1 - librt.so.1 -0xfdf0000+0x1c0000 edf3dd232e09d01b90683889bd16b9406c52d4de@0xfdf0184 /lib/libc.so.6 - libc.so.6 -0xfdb0000+0x40000 f6ee91d4c629bc7dacc10534cb30056914e7e0b5@0xfdb0164 /lib/libpthread.so.0 - libpthread.so.0 -0xffb0000+0x50000 edec437a85026a1cf8cda94003706202733130c1@0xffb0124 /lib/ld.so.1 - ld.so.1 +0x10000000+0x20000 979b7a26747cc09bd84a42b311b5288c704baea5@0x10000174 . - [exe] +0xfd50000+0x30000 3f7d21508470322d2f47acddc20ab10516edba99@0xfd50164 . - librt.so.1 +0xfdb0000+0x40000 f6ee91d4c629bc7dacc10534cb30056914e7e0b5@0xfdb0164 - - libpthread.so.0 +0xfdf0000+0x1c0000 edf3dd232e09d01b90683889bd16b9406c52d4de@0xfdf0184 - - libc.so.6 +0xffb0000+0x50000 edec437a85026a1cf8cda94003706202733130c1@0xffb0124 - - ld.so.1 +EOF + +# FAIL was 0x7f67f2caf000 for test-core-lib.so . +# /lib64/libc.so.6 and /lib64/ld-linux-x86-64.so.2 from link map +# do not have ELF header stored in the core file. +# ELF headers in the core file: +# Offset VirtAddr +# 0x014000 0x00007f67f2caf000 ./test-core-lib.so +# 0x03a000 0x00007fff1596c000 linux-vdso.so.1 +testfiles test-core.core test-core.exec +rm -f test-core-lib.so +testrun_compare ${abs_top_builddir}/src/unstrip -n -e test-core.exec --core=test-core.core <<\EOF +0x400000+0x202038 - test-core.exec - test-core.exec +0x7fff1596c000+0x1000 a9cf37f53897b5468ee018655760be61b8633d3c@0x7fff1596c340 . - linux-vdso.so.1 +0x7f67f2aaf000+0x202000 - . - test-core-lib.so EOF test_cleanup diff --git a/tests/test-core-lib.so.bz2 b/tests/test-core-lib.so.bz2 new file mode 100755 index 0000000..bb2da88 Binary files /dev/null and b/tests/test-core-lib.so.bz2 differ diff --git a/tests/test-core.core.bz2 b/tests/test-core.core.bz2 new file mode 100644 index 0000000..4d4346b Binary files /dev/null and b/tests/test-core.core.bz2 differ diff --git a/tests/test-core.exec.bz2 b/tests/test-core.exec.bz2 new file mode 100755 index 0000000..49ce551 Binary files /dev/null and b/tests/test-core.exec.bz2 differ _______________________________________________ elfutils-devel mailing list [email protected] https://lists.fedorahosted.org/mailman/listinfo/elfutils-devel
