From: Jan Kratochvil Date: Thu, 30 May 2013 11:21:20 +0000 (+0200) Subject: Use DT_DEBUG library search first. X-Git-Tag: elfutils-0.156~15 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=8ff862960efb648cdff647d7fad1be5acffe9b11;p=thirdparty%2Felfutils.git Use DT_DEBUG library search first. libdwfl/ 2013-05-30 Jan Kratochvil * argp-std.c (parse_opt) core> e>: Set executable_for_core before calling dwfl_core_file_report. * core-file.c (clear_r_debug_info): New function. (dwfl_core_file_report): Move raw segments reporting lower. New variable r_debug_info, pass it to dwfl_segment_report_module. Call clear_r_debug_info in the end. Return sum of LISTED and SNIFFED. * dwfl_module_build_id.c (check_notes): Move into __libdwfl_find_elf_build_id. (__libdwfl_find_build_id): Rename to ... (__libdwfl_find_elf_build_id): ... here. Add parameters build_id_bits, build_id_elfaddr and build_id_len. Verify MOD vs. ELF. (__libdwfl_find_elf_build_id) (check_notes): Remove parameters mod and set, rename data_vaddr to data_elfaddr. Do not call found_build_id. (__libdwfl_find_elf_build_id): Update the check_notes caller, do not adjust its data_elfaddr parameter. (__libdwfl_find_build_id): New wrapper of __libdwfl_find_elf_build_id. * dwfl_segment_report_module.c (dwfl_segment_report_module): New parameter r_debug_info. New variable name_is_final. Adjust addresses according to R_DEBUG_INFO->MODULE. Check conflicts against DWFL. Do not overwrite NAME by SONAME if NAME_IS_FINAL. * libdwflP.h (__libdwfl_find_elf_build_id): New declaration. (struct r_debug_info_module, struct r_debug_info): New definitions. (dwfl_segment_report_module, dwfl_link_map_report): Add parameter r_debug_info. * link_map.c: Include fcntl.h. (report_r_debug): Add parameter r_debug_info, describe it in the function comment. Delete dwfl_addrmodule call and its dependent code. Verify build-id before calling dwfl_report_elf, also supply executable_for_core to it. Store r_debug_info->module info when appropriate. (dwfl_link_map_report): Add parameter r_debug_info. New variable in_ok. Try to read IN from EXECUTABLE_FOR_CORE. Update report_r_debug caller parameters. tests/ 2013-05-30 Jan Kratochvil * 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 --- diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog index 74ede01dc..9ee0ab16d 100644 --- a/libdwfl/ChangeLog +++ b/libdwfl/ChangeLog @@ -1,3 +1,39 @@ +2013-05-30 Jan Kratochvil + + * argp-std.c (parse_opt) core> e>: Set + executable_for_core before calling dwfl_core_file_report. + * core-file.c (clear_r_debug_info): New function. + (dwfl_core_file_report): Move raw segments reporting lower. New + variable r_debug_info, pass it to dwfl_segment_report_module. Call + clear_r_debug_info in the end. Return sum of LISTED and SNIFFED. + * dwfl_module_build_id.c (check_notes): Move into + __libdwfl_find_elf_build_id. + (__libdwfl_find_build_id): Rename to ... + (__libdwfl_find_elf_build_id): ... here. Add parameters build_id_bits, + build_id_elfaddr and build_id_len. Verify MOD vs. ELF. + (__libdwfl_find_elf_build_id) (check_notes): Remove parameters mod and + set, rename data_vaddr to data_elfaddr. Do not call found_build_id. + (__libdwfl_find_elf_build_id): Update the check_notes caller, do not + adjust its data_elfaddr parameter. + (__libdwfl_find_build_id): New wrapper of __libdwfl_find_elf_build_id. + * dwfl_segment_report_module.c (dwfl_segment_report_module): New + parameter r_debug_info. New variable name_is_final. Adjust addresses + according to R_DEBUG_INFO->MODULE. Check conflicts against DWFL. + Do not overwrite NAME by SONAME if NAME_IS_FINAL. + * libdwflP.h (__libdwfl_find_elf_build_id): New declaration. + (struct r_debug_info_module, struct r_debug_info): New definitions. + (dwfl_segment_report_module, dwfl_link_map_report): Add parameter + r_debug_info. + * link_map.c: Include fcntl.h. + (report_r_debug): Add parameter r_debug_info, describe it in the + function comment. Delete dwfl_addrmodule call and its dependent code. + Verify build-id before calling dwfl_report_elf, also supply + executable_for_core to it. Store r_debug_info->module info when + appropriate. + (dwfl_link_map_report): Add parameter r_debug_info. New variable + in_ok. Try to read IN from EXECUTABLE_FOR_CORE. Update report_r_debug + caller parameters. + 2013-04-30 Jan Kratochvil * dwfl_report_elf.c (__libdwfl_report_elf): Add parameter add_p_vaddr. diff --git a/libdwfl/argp-std.c b/libdwfl/argp-std.c index e54f720c4..c8843909a 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 1545ca869..d5e340cbe 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 +clear_r_debug_info (struct r_debug_info *r_debug_info) +{ + while (r_debug_info->module != NULL) + { + struct r_debug_info_module *module = r_debug_info->module; + r_debug_info->module = module->next; + free (module); + } +} + int dwfl_core_file_report (Dwfl *dwfl, Elf *elf) { @@ -397,26 +410,6 @@ dwfl_core_file_report (Dwfl *dwfl, Elf *elf) 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; @@ -451,13 +444,43 @@ 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); + + /* Now sniff segment contents for modules hinted by information gathered + from DT_DEBUG. */ + + 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, + &r_debug_info); + if (unlikely (seg < 0)) + { + clear_r_debug_info (&r_debug_info); + return seg; + } + if (seg > ndx) + { + ndx = seg; + ++sniffed; + } + else + ++ndx; + } + while (ndx < (int) phnum); + + clear_r_debug_info (&r_debug_info); /* We return the number of modules we found if we found any. If we found none, we return -1 instead of 0 if there was an - error rather than just nothing found. 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 660c733f0..cae007b48 100644 --- a/libdwfl/dwfl_module_build_id.c +++ b/libdwfl/dwfl_module_build_id.c @@ -54,28 +54,41 @@ found_build_id (Dwfl_Module *mod, bool set, #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) +__libdwfl_find_elf_build_id (Dwfl_Module *mod, Elf *elf, + const void **build_id_bits, + GElf_Addr *build_id_elfaddr, int *build_id_len) { + 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; + 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")) + { + *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; + } + size_t shstrndx = SHN_UNDEF; int result = 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); @@ -130,6 +139,24 @@ __libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf) return result; } +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 7cf74994e..d454ccb50 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,47 @@ 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) + { + /* L_LD read from link map must be right while DYN_VADDR is unsafe. + Therefore subtract DYN_VADDR and add L_LD to get a possibly + corrective displacement for all addresses computed so far. */ + GElf_Addr fixup = module->l_ld - dyn_vaddr; + if ((fixup & (dwfl->segment_align - 1)) == 0 + && module_start + fixup <= module->l_ld + && module->l_ld < module_end + fixup) + { + module_start += fixup; + module_end += fixup; + dyn_vaddr += fixup; + bias += fixup; + if (module->name[0] != '\0') + { + name = 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 +560,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 37a9dff4e..69e6e12a5 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,29 @@ 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 +{ + 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 +485,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 9f1b867e3..e752a5dbf 100644 --- a/libdwfl/link_map.c +++ b/libdwfl/link_map.c @@ -33,6 +33,7 @@ #include #include +#include /* 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); @@ -349,42 +352,83 @@ report_r_debug (uint_fast8_t elfclass, uint_fast8_t elfdata, 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) + if (iterations == 1 && dwfl->executable_for_core != NULL) + name = dwfl->executable_for_core; + + Dwfl_Module *mod = NULL; + if (name != NULL) { - /* We have a module. We can give it a better name from l_name. */ - if (name != NULL && mod->name[0] == '[') + /* This code is mostly inlined dwfl_report_elf. */ + // XXX hook for sysroot + int fd = open64 (name, O_RDONLY); + if (fd >= 0) { - char *newname = strdup (basename (name)); - if (newname != NULL) + Elf *elf; + Dwfl_Error error = __libdw_open_file (&fd, &elf, true, false); + if (error == DWFL_E_NOERROR) { - free (mod->name); - mod->name = newname; + 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 (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) + if (r_debug_info != NULL && mod == 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, true); + /* 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) @@ -601,7 +645,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; @@ -699,8 +744,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 { @@ -862,6 +964,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/ChangeLog b/tests/ChangeLog index 6df580a7f..eca60c39e 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,13 @@ +2013-05-30 Jan Kratochvil + + * 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. + 2013-05-03 Mark Wielaard * testfilenolines.bz2: New test file. diff --git a/tests/Makefile.am b/tests/Makefile.am index 6327edbb1..2d819c5b3 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 9351fb2a0..8624074fa 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 9c2b43b3c..6ede4c7e6 100755 --- a/tests/run-unstrip-n.sh +++ b/tests/run-unstrip-n.sh @@ -35,20 +35,35 @@ testfiles testcore-rtlib testcore-rtlib-ppc testrun_compare ${abs_top_builddir}/src/unstrip -n --core=testcore-rtlib <<\EOF 0x8048000+0x2000 f1c600bc36cb91bf01f9a63a634ecb79aa4c3199@0x8048178 . - [exe] +0xf75e9000+0x1a000 29a103420abe341e92072fb14274e250e4072148@0xf75e9164 - - libpthread.so.0 +0xf7603000+0x1b0000 0b9bf374699e141e5dfc14757ff42b8c2373b4de@0xf7603184 - - libc.so.6 +0xf77b3000+0x9000 c6c5b5e35ab9589d4762ac85b4bd56b1b2720e37@0xf77b3164 - - librt.so.1 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 +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 +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 +0x10000000+0x20000 979b7a26747cc09bd84a42b311b5288c704baea5@0x10000174 . - [exe] +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 +0x7f67f2aaf000+0x202000 - . - test-core-lib.so +0x7fff1596c000+0x1000 a9cf37f53897b5468ee018655760be61b8633d3c@0x7fff1596c340 . - linux-vdso.so.1 EOF test_cleanup diff --git a/tests/test-core-lib.so.bz2 b/tests/test-core-lib.so.bz2 new file mode 100755 index 000000000..bb2da888c 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 000000000..4d4346b9c 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 000000000..49ce5519c Binary files /dev/null and b/tests/test-core.exec.bz2 differ