diff options
author | Petr Machata <pmachata@redhat.com> | 2008-12-15 14:17:39 +0100 |
---|---|---|
committer | Petr Machata <pmachata@redhat.com> | 2008-12-15 14:17:39 +0100 |
commit | 19932ff89b678877a45c25cd7c555db255cacabb (patch) | |
tree | a539e43d1a5ad24a2a870695791484348f3f9da3 | |
parent | 468fe4d81a3e92157f4c0446675487dc230b2ec6 (diff) |
Dump com.redhat.elfutils.pmachata.sharing from monotone.upstream/pmachata/sharing
37 files changed, 2079 insertions, 933 deletions
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog index 4f03855f..1b4a0463 100644 --- a/libdwfl/ChangeLog +++ b/libdwfl/ChangeLog @@ -125,11 +125,91 @@ * linux-kernel-modules.c (check_module_notes): Use FTS_LOGICAL so we accept symlinks. +2008-04-30 Petr Machata <pmachata@redhat.com> + + * dwfl_module_getdwarf.c + (find_debuginfo): Use mod->debug.cberr to record failure. + 2008-04-27 Roland McGrath <roland@redhat.com> * linux-kernel-modules.c (report_kernel): Fix crash when dwfl_report_elf fails. +2008-04-22 Petr Machata <pmachata@redhat.com> + + * libdwflP.h: Move Module.ebl to dwfl_shared_file. + * dwfl_module_getdwarf.c: Adjust to change above. Module.main's + ebl is always used. + * dwfl_module_register_names.c: Likewise. + * dwfl_module_return_value_location.c: Likewise. + * relocate.c: Likewise. + * dwfl_module.c: Move ebl_closebackend... + * dwfl_file.c: ... here. + +2008-04-22 Petr Machata <pmachata@redhat.com> + + * libdwflP.h: Add dwerr to dwfl_shared_file. Module.dwerr was + retained to record errors in establishing shared debug file. + * dwfl_nextcu.c: Adjust to above change. + * dwfl_module_getdwarf.c: Likewise. + (find_dw): Now returns Dwfl_Error. + +2008-04-22 Petr Machata <pmachata@redhat.com> + + * libdwflP.h: Move Module.dw to struct dwfl_shared_file + * cu.c: Adjust to above change. + * derelocate.c: Likewise. + * dwfl_module_getdwarf.c: Likewise. + * dwfl_module_getsrc_file.c: Likewise. + * dwfl_nextcu.c: Likewise. + * dwfl_module.c: Move dwarf_ending of dw... + * dwfl_file.c: ... here. + +2008-04-17 Petr Machata <pmachata@redhat.com> + + * dwfl_file.c (__libdwfl_open_file): Handle synthetic + ELF (non-null ELF handle, but fd==-1). Key it with all zeroes. + +2008-04-16 Petr Machata <pmachata@redhat.com> + + * dwfl_build_id_find_elf.c (open_and_check): New function, opens a + file and checks its build ID. + (__libdwfl_open_by_build_id): Change prototype. + (dwfl_build_id_find_elf): Only call __libdwfl_open_by_build_id. + * dwfl_module_build_id.c (__libdwfl_find_build_id): Pass build_id + pointer instead of dwfl Module argument. + (dwfl_module_build_id): Adjust to above: pass NULL instead of true. + * libdwflP.h: Adjust to changes above. + * dwfl_build_id_find_debuginfo.c + (dwfl_build_id_find_debuginfo): Call __libdwfl_open_file_build_id + to open file and check build ID. + * dwfl_module_getdwarf.c (find_file): Only open file if callback + didn't open it via __libdwfl_open_file_build_id already. + (find_debuginfo): Likewise. + +2008-04-14 Petr Machata <pmachata@redhat.com> + + * dwfl_build_id_find_elf.c (dwfl_build_id_find_elf): + Cache build_id if it is found to be valid. + +2008-04-11 Petr Machata <pmachata@redhat.com> + + * dwfl_module_build_id.c (found_build_id): Renamed to + __libdwfl_found_build_id, exported. + Now allocates struct build_id on the heap. + * libdwflP.h: Export above. + (BUILD_ID_NOT_FOUND): New macro holds value of cached + build-id-not-found pointer. + (BUILD_ID_PTR): Predicate of validity of build ID pointer. + (struct dwfl_shared_file, struct Dwfl_Module): Make struct + dwfl_build_id heap-allocated. + * dwfl_module_build_id.c (dwfl_module_report_build_id): Call + __libdwfl_found_build_id instead of duplicating the work. + Adjust to above changes. + * dwfl_build_id_find_elf.c, dwfl_file.c, dwfl_module.c, + dwfl_module_getdwarf.c, find-debuginfo.c, linux-kernel-modules.c: + Adjust to above changes. + 2008-04-05 Roland McGrath <roland@redhat.com> * linux-proc-maps.c (proc_maps_report): Don't leak LAST_FILE. @@ -157,6 +237,84 @@ prototype to avoid older compiler's complaint about reuse of the name. (__libdwfl_canon_error): Likewise. +2008-03-14 Petr Machata <pmachata@redhat.com> + + * libdwflP.h (Dwfl_Module.symerr): Delete field. + * dwfl_module_getdwarf.c: Adjust to above. + * relocate.c: Likewise. + +2008-03-14 Petr Machata <pmachata@redhat.com> + + * libdwflP.h (Dwfl_Module.elferr): Delete field. + (dwfl_file.cberr): New field. + * dwfl_module_getdwarf.c: Adjust to above. + +2008-03-13 Petr Machata <pmachata@redhat.com> + + * libdwflP.h (dwfl_shared_file.elferr): New field. + * dwfl_file.c: When reading of the Elf file failed, keep the cache + entry around to cache the failure. + * derelocate.c: Adjust to above. + * dwfl_module_getdwarf.c: Likewise. + * dwfl_module_info.c: Likewise. + * offline.c: Likewise. + +2008-03-07 Petr Machata <pmachata@redhat.com> + + * libdwflP.h + (struct dwfl_build_id): New structure. + (dwfl_shared_file.valid): Dropped. + (dwfl_shared_file.build_id): New field. + (Dwfl_Module.build_id_*): Dropped. + (Dwfl_Module.build_id): New field. + * dwfl_file.c + (__libdwfl_close_file): Always free filename. Also free build id. + * dwfl_module_report_build_id.c: Adjust to above. + * linux-kernel-modules.c: Likewise. + * dwfl_build_id_find_debuginfo.c: Drop validation. + * dwfl_build_id_find_elf.c: Adjust to above, drop validation. + * dwfl_module_build_id.c + (found_build_id): Take dwfl_build_id arg instead of Dwfl_Module. + (check_notes): Likewise. + (__libdwfl_find_build_id): Use appropriate build_id cache + Adjust the rest to above changes. + (dwfl_module_build_id): Likewise. + * find-debuginfo.c: Adjust to above. + (validate): Drop ELF backdooring for now. + +2008-03-07 Petr Machata <pmachata@redhat.com> + + * dwfl_file.c (__libdwfl_open_file): Call elf_cntl ELF_C_FDREAD if + possible, and consume fd right away. + * dwfl_module_getdwarf.c (load_dw): Remove ELF_C_FDREAD hack. + * offline.c (process_elf): Likewise. + * dwfl_report_elf.c (__libdwfl_report_elf): Return error instead + of asserting. + +2008-03-05 Petr Machata <pmachata@redhat.com> + + * libdwflP.h + (dwfl_shared_file): Renamed from dwfl_file. + (dwfl_file): New structure. + (DWBIAS, SYMBIAS): Adjust code to above change. + * delrelocate.c: Likewise. + * dwfl_build_id_find_debuginfo.c: Likewise. + * dwfl_build_id_find_elf.c: Likewise. + * dwfl_module.c: Likewise. + * dwfl_module_addrsym.c: Likewise. + * dwfl_module_build_id.c: Likewise. + * dwfl_module_getdwarf.c: Likewise. + * dwfl_module_getsym.c: Likewise. + * dwfl_module_info.c: Likewise. + * dwfl_module_report_build_id.c: Likewise. + * dwfl_report_elf.c: Likewise. + * dwfl_symtab.c: Likewise. + * find-debuginfo.c: Likewise. + * offline.c: Likewise. + * relocate.c: Likewise. + * dwfl_file.c: Likewise. + (__libdwfl_open_file): Consume passed-in Elf on cache hit. + 2008-02-19 Roland McGrath <roland@redhat.com> * relocate.c (relocate_section): Check for an unhandled relocation @@ -173,11 +331,122 @@ * dwfl_build_id_find_elf.c (__libdwfl_open_by_build_id): Don't clear incoming *FILE_NAME at the start. +2008-01-23 Petr Machata <pmachata@redhat.com> + + * relocate.c (find_relocation_symfile): New function. + (relocate_section): Take additional argument SYMFILE. + (__libdwfl_relocate, __libdwfl_relocate_section): Use above. + +2008-01-22 Petr Machata <pmachata@redhat.com> + + * dwfl_module_getdwarf.c (load_dw): Remove useless call to + find_symfile. + +2008-01-18 Petr Machata <pmachata@redhat.com> + + * relocate.c: + (struct reloc_symtab_cache): Drop. + (#define RELOC_SYMTAB_CACHE): Drop. + (relocate_getsym): Drop most of the code, inline the rest. + (resolve_symbol): Drop loading of symstrdata, that's already done + in __libdwfl_find_symtab. + (relocate_section): Inlined the rest of relocate_getsym. + (__libdwfl_relocate): Take dwfl_file instead of Elf. + (__libdwfl_relocate_section): Likewise. + * derelocate.c: Adjust to above. + * dwfl_module_getdwarf.c: Likewise. + * libdwflP.h (__libdwfl_relocate): Adjust prototype. + (__libdwfl_relocate_section): Likewise. + +2008-01-18 Petr Machata <pmachata@redhat.com> + + * dwfl_symtab.c: New file. + (__libdwfl_find_symtab): New function. + * Makefile.am: Add dwfl_symtab.c to the build process. + * dwfl_module_getdwarf.c + (load_symtab, find_offsets, find_dynsym): Move to dwfl_symtab.c + * libdwflP.h (struct dwfl_file.is_symtab): New field. + (struct dwfl_file.symerr): Likekwise. + +2008-01-14 Petr Machata <pmachata@redhat.com> + + * dwfl_file.c (__libdwfl_open_file): close the file descriptor + when reusing the node, and even when it was passed in by the caller. + (__libdwfl_close_file): Unconditionally close fd and call elf_end. + * dwfl_module_build_id.c: Formatting change. + * dwfl_module_getdwarf.c + (find_file): Return Dwfl_Error instead of void; adjust to changes + in __libdwfl_open_file. + (find_symtab): Take advantage from find_file returning Dwfl_Error. + (__libdwfl_module_getebl, find_dw, dwfl_module_getelf): Likewise. + 2008-01-08 Roland McGrath <roland@redhat.com> * Makefile.am (euinclude): Variable removed. (pkginclude_HEADERS): Set this instead of euinclude_HEADERS. +2007-12-11 Petr Machata <pmachata@redhat.com> + + * libdwflP.h + (struct dwfl_file.bias): Moved to struct Dwfl_Module. + (struct dwfl_file.align): New field. + (struct dwfl_file.start): New field. + (#define DWBIAS): New macro. + (#define SYMBIAS): New macro. + * cu.c: Use SYMBIAS/DWBIAS instead of main/debug->bias. + * derelocate.c: Likewise. + * dwfl_lineinfo.c: Likewise. + * dwfl_module_addrsym.c: Likewise. + * dwfl_module_build_id.c: Likewise. + * dwfl_module_getsym.c: Likewise. + * dwfl_module_info.c: Likewise. + * dwfl_nextcu.c: Likewise. + * dwfl_report_elf.c: Likewise. + * dwfl_module_getdwarf.c: Likewise. + +2007-12-10 Petr Machata <pmachata@redhat.com> + + * dwfl_file.c: Use likely/unlikely where possible. + * libdwflP.h (struct Dwfl_Module): Move symdata, syments, + symstrdata, symxndxdata, to struct dwfl_file. + * dwfl_module_getdwarf.c: Adjust to above changes. + (load_symtab): Drop the parameter `syments'. + (find_symtab): Use module->main or module->debug to initialize + module->symfile when possible. + * dwfl_module_getsym.c: Adjust to above changes. + * dwfl_report_elf.c: Likewise. + * relocate.c: Likewise. + +2007-12-05 Petr Machata <pmachata@redhat.com> + + * dwfl_file.c: New file. + * Makefile.am (libdwfl_a_SOURCES): Add dwfl_file.c. + * libdwflP.h + (__libdwfl_open_file): New internal function. + (__libdwfl_close_file): New internal function. + * cu.c: Adjust to the above change. + * derelocate.c: Likewise. + * dwfl_build_id_find_debuginfo.c: Likewise. + * dwfl_build_id_find_elf.c: Likewise. + * dwfl_lineinfo.c: Likewise. + * dwfl_module.c: Likewise. + * dwfl_module_build_id.c: Likewise. + * dwfl_module_getdwarf.c: Likewise. + * dwfl_module_info.c: Likewise. + * dwfl_module_report_build_id.c: Likewise. + * dwfl_nextcu.c: Likewise. + * dwfl_report_elf.c: Likewise. + * find-debuginfo.c: Likewise. + * offline.c: Likewise. + + * dwfl_module.c (free_file): Code moved to dwfl_file.c. + (__libdwfl_module_free): Call __libdwfl_release_file. + * dwfl_module_getdwarf.c (open_elf): Code moved to dwfl_file.c. + (find_file): Call __libdwfl_open_file. + (find_debuginfo): Likewise. + (find_dw): Call __libdwfl_retain_file instead of copying members. + * dwfl_report_elf.c (__libdwfl_report_elf): Call __libdwfl_open_file. + 2007-10-23 Roland McGrath <roland@redhat.com> * linux-kernel-modules.c (report_kernel_archive): Reorder the kernel diff --git a/libdwfl/Makefile.am b/libdwfl/Makefile.am index db14db2a..a7d6b2b1 100644 --- a/libdwfl/Makefile.am +++ b/libdwfl/Makefile.am @@ -73,7 +73,8 @@ libdwfl_a_SOURCES = dwfl_begin.c dwfl_end.c dwfl_error.c dwfl_version.c \ dwfl_module_return_value_location.c \ dwfl_module_register_names.c \ dwfl_segment_report_module.c \ - link_map.c core-file.c + link_map.c core-file.c \ + dwfl_file.c dwfl_symtab.c if MUDFLAP diff --git a/libdwfl/core-file.c b/libdwfl/core-file.c index bc881eb9..d34fe0ed 100644 --- a/libdwfl/core-file.c +++ b/libdwfl/core-file.c @@ -240,7 +240,8 @@ core_file_read_eagerly (Dwfl_Module *mod, requires find_elf hook re-doing the magic to fall back if no file found */ - if (mod->build_id_len > 0) + if (mod->build_id != NULL + || (mod->main.shared != NULL && mod->main.shared->build_id != NULL)) /* 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. */ diff --git a/libdwfl/cu.c b/libdwfl/cu.c index 8f01ea6b..fe6ef1a6 100644 --- a/libdwfl/cu.c +++ b/libdwfl/cu.c @@ -56,7 +56,7 @@ static inline Dwarf_Arange * dwar (Dwfl_Module *mod, unsigned int idx) { - return &mod->dw->aranges->info[mod->aranges[idx].arange]; + return &mod->debug.shared->dw->aranges->info[mod->aranges[idx].arange]; } @@ -68,7 +68,7 @@ addrarange (Dwfl_Module *mod, Dwarf_Addr addr, struct dwfl_arange **arange) struct dwfl_arange *aranges = NULL; Dwarf_Aranges *dwaranges = NULL; size_t naranges; - if (INTUSE(dwarf_getaranges) (mod->dw, &dwaranges, &naranges) != 0) + if (INTUSE(dwarf_getaranges) (mod->debug.shared->dw, &dwaranges, &naranges) != 0) return DWFL_E_LIBDW; /* If the module has no aranges (when no code is included) we @@ -106,7 +106,7 @@ addrarange (Dwfl_Module *mod, Dwarf_Addr addr, struct dwfl_arange **arange) } /* The address must be inside the module to begin with. */ - addr -= mod->debug.bias; + addr -= DWBIAS (mod); /* The ranges are sorted by address, so we can use binary search. */ size_t l = 0, u = mod->naranges; @@ -132,8 +132,9 @@ addrarange (Dwfl_Module *mod, Dwarf_Addr addr, struct dwfl_arange **arange) else { /* It might be in the last range. */ + Dwarf *dw = mod->debug.shared->dw; const Dwarf_Arange *last - = &mod->dw->aranges->info[mod->dw->aranges->naranges - 1]; + = &dw->aranges->info[dw->aranges->naranges - 1]; if (addr > last->addr + last->length) break; } @@ -196,7 +197,8 @@ intern_cu (Dwfl_Module *mod, Dwarf_Off cuoff, struct dwfl_cu **result) if (*found == &key || *found == NULL) { - if (unlikely (cuoff + 4 >= mod->dw->sectiondata[IDX_debug_info]->d_size)) + Dwarf * dw = mod->debug.shared->dw; + if (unlikely (cuoff + 4 >= 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. */ @@ -218,7 +220,7 @@ intern_cu (Dwfl_Module *mod, Dwarf_Off cuoff, struct dwfl_cu **result) cu->lines = NULL; /* XXX use non-searching lookup */ - Dwarf_Die *die = INTUSE(dwarf_offdie) (mod->dw, cuoff, &cu->die); + Dwarf_Die *die = INTUSE(dwarf_offdie) (dw, cuoff, &cu->die); if (die == NULL) return DWFL_E_LIBDW; assert (die == &cu->die); @@ -272,8 +274,9 @@ __libdwfl_nextcu (Dwfl_Module *mod, struct dwfl_cu *lastcu, { size_t cuhdrsz; Dwarf_Off nextoff; - int end = INTUSE(dwarf_nextcu) (mod->dw, cuoff, &nextoff, &cuhdrsz, - NULL, NULL, NULL); + int end = INTUSE(dwarf_nextcu) (mod->debug.shared->dw, cuoff, + &nextoff, &cuhdrsz, + NULL, NULL, NULL); if (end < 0) return DWFL_E_LIBDW; if (end > 0) @@ -302,7 +305,8 @@ 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]; + Dwarf *dw = mod->debug.shared->dw; + const Dwarf_Arange *dwarange = &dw->aranges->info[arange->arange]; Dwfl_Error result = intern_cu (mod, dwarange->offset, &arange->cu); if (result != DWFL_E_NOERROR) return result; diff --git a/libdwfl/derelocate.c b/libdwfl/derelocate.c index 402bc06f..2e62f6af 100644 --- a/libdwfl/derelocate.c +++ b/libdwfl/derelocate.c @@ -93,8 +93,12 @@ cache_sections (Dwfl_Module *mod) struct secref *refs = NULL; size_t nrefs = 0; + assert (mod->main.shared != NULL + && mod->main.shared->elf != NULL); + Elf *main_elf = mod->main.shared->elf; + size_t shstrndx; - if (unlikely (elf_getshstrndx (mod->main.elf, &shstrndx) < 0)) + if (unlikely (elf_getshstrndx (main_elf, &shstrndx) < 0)) { elf_error: __libdwfl_seterrno (DWFL_E_LIBELF); @@ -103,7 +107,7 @@ cache_sections (Dwfl_Module *mod) bool check_reloc_sections = false; Elf_Scn *scn = NULL; - while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL) + while ((scn = elf_nextscn (main_elf, scn)) != NULL) { GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); @@ -113,7 +117,7 @@ cache_sections (Dwfl_Module *mod) if ((shdr->sh_flags & SHF_ALLOC) && shdr->sh_addr == 0) { /* This section might not yet have been looked at. */ - if (__libdwfl_relocate_value (mod, mod->main.elf, &shstrndx, + if (__libdwfl_relocate_value (mod, main_elf, &shstrndx, elf_ndxscn (scn), &shdr->sh_addr) != DWFL_E_NOERROR) continue; @@ -124,7 +128,7 @@ cache_sections (Dwfl_Module *mod) if (shdr->sh_flags & SHF_ALLOC) { - const char *name = elf_strptr (mod->main.elf, shstrndx, + const char *name = elf_strptr (main_elf, shstrndx, shdr->sh_name); if (unlikely (name == NULL)) goto elf_error; @@ -133,7 +137,7 @@ cache_sections (Dwfl_Module *mod) newref->scn = scn; newref->relocs = NULL; newref->name = name; - newref->start = shdr->sh_addr + mod->main.bias; + newref->start = shdr->sh_addr + mod->bias; newref->end = newref->start + shdr->sh_size; newref->next = refs; refs = newref; @@ -148,7 +152,7 @@ cache_sections (Dwfl_Module *mod) 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); + Elf_Scn *tscn = elf_getscn (main_elf, shdr->sh_info); if (likely (tscn != NULL)) for (struct secref *sec = refs; sec != NULL; sec = sec->next) if (sec->scn == tscn) @@ -194,7 +198,7 @@ cache_sections (Dwfl_Module *mod) possible target sections we care about. */ scn = NULL; - while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL) + while ((scn = elf_nextscn (main_elf, scn)) != NULL) { GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); @@ -204,7 +208,7 @@ cache_sections (Dwfl_Module *mod) 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); + Elf_Scn *tscn = elf_getscn (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) @@ -238,7 +242,9 @@ dwfl_module_relocations (Dwfl_Module *mod) return 1; case ET_EXEC: - assert (mod->debug.bias == 0); + assert (mod->debug.shared != NULL + && mod->debug.shared->elf != NULL); + assert (DWBIAS (mod) == 0); break; } @@ -296,7 +302,8 @@ check_module (Dwfl_Module *mod) } } - if (mod->dw == NULL) + if (mod->debug.shared == NULL + || mod->debug.shared->dw == NULL) { Dwarf_Addr bias; if (INTUSE(dwfl_module_getdwarf) (mod, &bias) == NULL) @@ -358,7 +365,7 @@ dwfl_module_relocate_address (Dwfl_Module *mod, Dwarf_Addr *addr) if (mod->e_type != ET_REL) { - *addr -= mod->debug.bias; + *addr -= DWBIAS (mod); return 0; } @@ -383,7 +390,7 @@ dwfl_module_address_section (Dwfl_Module *mod, Dwarf_Addr *address, 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, + Dwfl_Error result = __libdwfl_relocate_section (mod, mod->main.shared, relocscn, tscn, true); if (likely (result == DWFL_E_NOERROR)) mod->reloc_info->refs[idx].relocs = NULL; @@ -394,7 +401,7 @@ dwfl_module_address_section (Dwfl_Module *mod, Dwarf_Addr *address, } } - *bias = mod->main.bias; + *bias = mod->bias; return mod->reloc_info->refs[idx].scn; } INTDEF (dwfl_module_address_section) diff --git a/libdwfl/dwfl_build_id_find_debuginfo.c b/libdwfl/dwfl_build_id_find_debuginfo.c index 97def072..e7096723 100644 --- a/libdwfl/dwfl_build_id_find_debuginfo.c +++ b/libdwfl/dwfl_build_id_find_debuginfo.c @@ -61,32 +61,15 @@ dwfl_build_id_find_debuginfo (Dwfl_Module *mod, 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. */ - mod->debug.elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL); - 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; - free (*debuginfo_file_name); - *debuginfo_file_name = NULL; - errno = 0; - } - } - return fd; + + if (INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr) > 0 + && !__libdwfl_open_by_build_id (mod->dwfl->callbacks, + &mod->debug, mod->main.shared->build_id, + mod->bias, true, debuginfo_file_name)) + errno = 0; + + return -1; } INTDEF (dwfl_build_id_find_debuginfo) diff --git a/libdwfl/dwfl_build_id_find_elf.c b/libdwfl/dwfl_build_id_find_elf.c index 1a226dfd..381203d8 100644 --- a/libdwfl/dwfl_build_id_find_elf.c +++ b/libdwfl/dwfl_build_id_find_elf.c @@ -53,18 +53,44 @@ #include <unistd.h> -int +static bool +open_and_check (struct dwfl_file *file, + const struct dwfl_build_id *build_id, + GElf_Addr bias, + char *file_name, int fd) +{ + if (__libdwfl_open_file (file, file_name, + fd, NULL) != DWFL_E_NOERROR) + return false; + + /* For the "check" (set==false) call, we can safely cast away const + and take stack pointer. */ + if (__libdwfl_find_build_id ((struct dwfl_build_id **)&build_id, + bias, false, file->shared->elf) != 2) + { + __libdwfl_close_file (file); + return false; + } + + return true; +} + +bool internal_function -__libdwfl_open_by_build_id (Dwfl_Module *mod, bool debug, char **file_name) +__libdwfl_open_by_build_id (const Dwfl_Callbacks *const cb, + struct dwfl_file *file, + const struct dwfl_build_id *build_id, + GElf_Addr bias, + 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) + if (!BUILD_ID_PTR (build_id)) return -1; - const size_t id_len = mod->build_id_len; - const uint8_t *id = mod->build_id_bits; + const size_t id_len = build_id->len; + const uint8_t *id = build_id->bits; /* Search debuginfo_path directories' .build-id/ subdirectories. */ @@ -83,7 +109,6 @@ __libdwfl_open_by_build_id (Dwfl_Module *mod, bool debug, char **file_name) strcpy (&id_name[sizeof "/.build-id/" - 1 + 3 + (id_len - 1) * 2], ".debug"); - const Dwfl_Callbacks *const cb = mod->dwfl->callbacks; char *path = strdupa ((cb->debuginfo_path ? *cb->debuginfo_path : NULL) ?: DEFAULT_DEBUGINFO_PATH); @@ -126,7 +151,17 @@ __libdwfl_open_by_build_id (Dwfl_Module *mod, bool debug, char **file_name) if (fd < 0 && errno == ENOENT) errno = 0; - return fd; + if (fd >= 0) + { + char *fn = *file_name; + *file_name = open_and_check (file, build_id, bias, fn, fd) + ? file->name : NULL; + + if (fn != *file_name) + free (fn); /* Failure, or strdup in __libdwfl_open_file. */ + } + + return *file_name != NULL; } int @@ -137,24 +172,17 @@ dwfl_build_id_find_elf (Dwfl_Module *mod, char **file_name, Elf **elfp) { *elfp = NULL; - int fd = __libdwfl_open_by_build_id (mod, false, file_name); - if (fd >= 0) + if (__libdwfl_open_by_build_id (mod->dwfl->callbacks, + &mod->main, mod->build_id, + mod->bias, false, file_name) + && mod->main.shared->build_id == NULL) { - *elfp = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL); - if (__libdwfl_find_build_id (mod, false, *elfp) == 2) - /* This is a backdoor signal to short-circuit the ID refresh. */ - mod->main.valid = true; - 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; - } + /* Move build ID bits into the cache. */ + mod->build_id->vaddr -= mod->bias; + mod->main.shared->build_id = mod->build_id; + mod->build_id = NULL; } - return fd; + + return -1; } INTDEF (dwfl_build_id_find_elf) diff --git a/libdwfl/dwfl_file.c b/libdwfl/dwfl_file.c new file mode 100644 index 00000000..c7927d02 --- /dev/null +++ b/libdwfl/dwfl_file.c @@ -0,0 +1,281 @@ +/* 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 <config.h> +#include "../libelf/libelfP.h" /* For elf->map_address. */ +#undef _ + +#include "libdwflP.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> + +struct cache_key +{ + dev_t dev; + ino64_t ino; + struct timespec ctim; + size_t refcount; +}; + +struct cache_entry +{ + struct dwfl_shared_file shared; + struct cache_key key; + + struct cache_entry *next; +}; + +static struct cache_entry *cache = NULL; + +static struct cache_entry * +lookup_entry (struct stat64 *s) +{ + for (struct cache_entry *entry = cache; entry != NULL; entry = entry->next) + if (entry->key.dev == s->st_dev + && entry->key.ino == s->st_ino + && entry->key.ctim.tv_nsec == s->st_ctim.tv_nsec + && entry->key.ctim.tv_sec == s->st_ctim.tv_sec) + return entry; + return NULL; +} + +Dwfl_Error +internal_function +__libdwfl_open_file (struct dwfl_file *tgt, + const char *file_name, + int fd, Elf *elf) +{ + if (tgt->shared != NULL) + return tgt->shared->elferr; + + Dwfl_Error error; + if (fd < 0 + && elf == NULL + && unlikely ((fd = open64 (file_name, O_RDONLY)) < 0)) + { + tgt->shared = NULL; + return DWFL_E (ERRNO, errno); + } + + struct cache_entry *entry = NULL; + bool synthetic = false; + struct stat64 s; + + /* If we get there and fd < 0, it's ELF without file backing. */ + if (fd >= 0) + { + if (unlikely (fstat64 (fd, &s) < 0)) + { + tgt->shared = NULL; + return DWFL_E (ERRNO, errno); + } + + /* Reuse cache entry if possible. */ + entry = lookup_entry (&s); + if (entry != NULL) + { + /* Consume elf. */ + if (elf != NULL) + elf_end (elf); + + /* Consume the fd. We don't need it when reusing. */ + if (fd >= 0 && unlikely (close (fd) < 0)) + { + tgt->shared = NULL; + return DWFL_E (ERRNO, errno); + } + + ++entry->key.refcount; + tgt->shared = &entry->shared; + return entry->shared.elferr; + } + } + else + synthetic = true; + + /* Failing that, enlist a new entry. */ + entry = calloc (1, sizeof *entry); + if (unlikely (entry == NULL)) + { + error = DWFL_E_NOMEM; + goto fail; + } + + entry->next = cache; + cache = entry; + + /* For synthetic files, the key is left initialized to zero. */ + if (!synthetic) + { + entry->key.dev = s.st_dev; + entry->key.ino = s.st_ino; + entry->key.ctim = s.st_ctim; + } + entry->key.refcount = 1; + + tgt->shared = &entry->shared; + tgt->name = file_name ? strdup (file_name) : NULL; + + if (elf == NULL) + { + elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL); + if (unlikely (elf == NULL)) + { + error = DWFL_E_LIBELF; + goto fail; + } + } + if (!synthetic + && elf->map_address != NULL + && elf_cntl (elf, ELF_C_FDREAD) == 0) + { + close (fd); + fd = -1; + } + entry->shared.elf = elf; + entry->shared.fd = fd; + + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (entry->shared.elf, &ehdr_mem); + if (unlikely (ehdr == NULL)) + { + error = DWFL_E_LIBELF; + goto fail; + } + + for (uint_fast16_t i = 0; i < ehdr->e_phnum; ++i) + { + GElf_Phdr ph_mem; + GElf_Phdr *ph = gelf_getphdr (entry->shared.elf, i, &ph_mem); + if (ph == NULL) + { + error = DWFL_E_LIBELF; + goto fail; + } + if (ph->p_type == PT_LOAD) + { + entry->shared.start = ph->p_vaddr; + entry->shared.align = ph->p_align; + break; + } + } + + return entry->shared.elferr = DWFL_E_NOERROR; + + + /* Upon failure, keep the shared file open, but cache error. */ + fail: + if (fd != -1) + { + /* Consume the FD, even if the caller opened it. We don't check + for success, because if it failed, which error should we pass up? */ + close (fd); + entry->shared.fd = -1; + } + + if (elf != NULL) + { + elf_end (elf); + entry->shared.elf = NULL; + } + + error = __libdwfl_canon_error (error); + if (entry != NULL) + entry->shared.elferr = error; + return error; +} + +void +internal_function +__libdwfl_close_file (struct dwfl_file *tgt) +{ + if (likely (tgt->shared != NULL)) + { + /* Look up the file in cache. With singly linked list, we can't + simply cast file to entry, we need the prev pointer. */ + struct cache_entry *entry; + struct cache_entry **prevp = &cache; + for (entry = cache; entry != NULL; + entry = *(prevp = &entry->next)) + if (&entry->shared == tgt->shared) + break; + assert (entry != NULL); + + if (--entry->key.refcount == 0) + { + if (entry->shared.elf != NULL) + elf_end (entry->shared.elf); + if (entry->shared.fd != -1) + close (entry->shared.fd); + + if (tgt->shared->dw != NULL) + INTUSE(dwarf_end) (tgt->shared->dw); + + if (tgt->shared->ebl != NULL) + ebl_closebackend (tgt->shared->ebl); + + *prevp = entry->next; + free (entry); + + if (BUILD_ID_PTR (entry->shared.build_id)) + free (entry->shared.build_id); + } + + tgt->shared = NULL; + } + + if (tgt->name != NULL) + { + free (tgt->name); + tgt->name = NULL; + } +} diff --git a/libdwfl/dwfl_lineinfo.c b/libdwfl/dwfl_lineinfo.c index 0d8a6887..86bae138 100644 --- a/libdwfl/dwfl_lineinfo.c +++ b/libdwfl/dwfl_lineinfo.c @@ -61,7 +61,7 @@ dwfl_lineinfo (Dwfl_Line *line, Dwarf_Addr *addr, int *linep, int *colp, const Dwarf_Line *info = &cu->die.cu->lines->info[line->idx]; if (addr != NULL) - *addr = info->addr + cu->mod->debug.bias; + *addr = info->addr + DWBIAS (cu->mod); if (linep != NULL) *linep = info->line; if (colp != NULL) diff --git a/libdwfl/dwfl_module.c b/libdwfl/dwfl_module.c index d7e54138..908982a5 100644 --- a/libdwfl/dwfl_module.c +++ b/libdwfl/dwfl_module.c @@ -64,16 +64,6 @@ 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) @@ -91,18 +81,15 @@ __libdwfl_module_free (Dwfl_Module *mod) free (mod->cu); } - if (mod->dw != NULL) - INTUSE(dwarf_end) (mod->dw); - - if (mod->ebl != NULL) - ebl_closebackend (mod->ebl); + if (mod->debug.shared != NULL && mod->debug.shared != mod->main.shared) + __libdwfl_close_file (&mod->debug); + mod->debug.name = NULL; - if (mod->debug.elf != mod->main.elf) - free_file (&mod->debug); - free_file (&mod->main); + __libdwfl_close_file (&mod->main); + mod->main.name = NULL; - if (mod->build_id_bits != NULL) - free (mod->build_id_bits); + if (BUILD_ID_PTR (mod->build_id)) + free (mod->build_id); free (mod->name); free (mod); diff --git a/libdwfl/dwfl_module_addrsym.c b/libdwfl/dwfl_module_addrsym.c index 72280d11..6607b274 100644 --- a/libdwfl/dwfl_module_addrsym.c +++ b/libdwfl/dwfl_module_addrsym.c @@ -71,10 +71,10 @@ dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr, /* Figure out what section ADDR lies in. */ if (addr_shndx == SHN_UNDEF) { - GElf_Addr mod_addr = addr - mod->symfile->bias; + GElf_Addr mod_addr = addr - SYMBIAS (mod); Elf_Scn *scn = NULL; addr_shndx = SHN_ABS; - while ((scn = elf_nextscn (mod->symfile->elf, scn)) != NULL) + while ((scn = elf_nextscn (mod->symfile->shared->elf, scn)) != NULL) { GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); diff --git a/libdwfl/dwfl_module_build_id.c b/libdwfl/dwfl_module_build_id.c index 8725afbc..d02c89f2 100644 --- a/libdwfl/dwfl_module_build_id.c +++ b/libdwfl/dwfl_module_build_id.c @@ -49,34 +49,41 @@ #include "libdwflP.h" -static int -found_build_id (Dwfl_Module *mod, bool set, - const void *bits, int len, GElf_Addr vaddr) +int +internal_function +__libdwfl_found_build_id (struct dwfl_build_id **build_idp, bool set, + const void *bits, int len, GElf_Addr vaddr) { + struct dwfl_build_id *build_id = *build_idp; + 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)); + return 1 + (build_id != NULL + && build_id->len == len + && !memcmp (bits, build_id->bits, len)); - void *copy = malloc (len); - if (unlikely (copy == NULL)) + build_id = malloc (offsetof (struct dwfl_build_id, bits[len])); + if (unlikely (build_id == 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; + memcpy (build_id->bits, bits, len); + build_id->vaddr = vaddr; + build_id->len = len; + + *build_idp = build_id; return len; } #define NO_VADDR ((GElf_Addr) -1l) static int -check_notes (Dwfl_Module *mod, bool set, Elf_Data *data, GElf_Addr data_vaddr) +check_notes (struct dwfl_build_id **build_idp, bool set, + Elf_Data *data, GElf_Addr data_vaddr) { size_t pos = 0; GElf_Nhdr nhdr; @@ -84,21 +91,25 @@ check_notes (Dwfl_Module *mod, bool set, Elf_Data *data, GElf_Addr data_vaddr) 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); + && nhdr.n_namesz == sizeof "GNU" + && !memcmp (data->d_buf + name_pos, "GNU", sizeof "GNU")) + return __libdwfl_found_build_id (build_idp, 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_build_id (struct dwfl_build_id **build_idp, GElf_Addr bias, + bool set, Elf *elf) { - int result = 0; + if (*build_idp == BUILD_ID_NOT_FOUND) /* Cached failure. */ + return -1; + int result = 0; Elf_Scn *scn = elf_nextscn (elf, NULL); if (scn == NULL) @@ -116,12 +127,12 @@ __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, + result = check_notes (build_idp, set, elf_getdata_rawchunk (elf, phdr->p_offset, phdr->p_filesz, ELF_T_NHDR), - phdr->p_vaddr + mod->main.bias); + phdr->p_vaddr + bias); } } else @@ -130,12 +141,17 @@ __libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf) GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); if (likely (shdr != NULL) && shdr->sh_type == SHT_NOTE) - result = check_notes (mod, set, elf_getdata (scn, NULL), + result = check_notes (build_idp, set, + elf_getdata (scn, NULL), (shdr->sh_flags & SHF_ALLOC) - ? shdr->sh_addr + mod->main.bias : NO_VADDR); + ? shdr->sh_addr + bias : NO_VADDR); } while (result == 0 && (scn = elf_nextscn (elf, scn)) != NULL); + /* Cache negative result. */ + if (result <= 0) + *build_idp = BUILD_ID_NOT_FOUND; + return result; } @@ -146,22 +162,28 @@ dwfl_module_build_id (Dwfl_Module *mod, if (mod == NULL) return -1; - if (mod->build_id_len == 0 && mod->main.elf != NULL) + struct dwfl_build_id *build_id; + if (mod->main.shared == NULL) + build_id = mod->build_id; + else if (mod->main.shared->build_id != NULL) + build_id = mod->main.shared->build_id; + else if (mod->main.shared->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; - } + __libdwfl_find_build_id (&mod->main.shared->build_id, mod->bias, + true, mod->main.shared->elf); + build_id = mod->main.shared->build_id; } + else + return -1; - if (mod->build_id_len <= 0) + if (build_id == NULL) return 0; + else if (build_id == BUILD_ID_NOT_FOUND) + return -1; - *bits = mod->build_id_bits; - *vaddr = mod->build_id_vaddr; - return mod->build_id_len; + *bits = build_id->bits; + *vaddr = build_id->vaddr; + return build_id->len; } INTDEF (dwfl_module_build_id) diff --git a/libdwfl/dwfl_module_getdwarf.c b/libdwfl/dwfl_module_getdwarf.c index 652383be..d8313663 100644 --- a/libdwfl/dwfl_module_getdwarf.c +++ b/libdwfl/dwfl_module_getdwarf.c @@ -53,92 +53,76 @@ #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->bias are set up. */ -static inline Dwfl_Error -open_elf (Dwfl_Module *mod, struct dwfl_file *file) +/* Find the main ELF file for this module and open libelf on it. + When we return success, MOD->main is set up. MOD->elferr is + set up in any case. */ +static Dwfl_Error +find_file (Dwfl_Module *mod) { - if (file->elf == NULL) + if (mod->main.cberr != DWFL_E_NOERROR) + return mod->main.cberr; + if (mod->main.shared != NULL) /* Already done. */ + return mod->main.shared->elferr; + + Elf *elf = NULL; + mod->main.name = NULL; + int fd = (*mod->dwfl->callbacks->find_elf) (MODCB_ARGS (mod), + &mod->main.name, + &elf); + + Dwfl_Error err = DWFL_E_NOERROR; + + /* If the callback didn't open the dwfl_file for us, but gave us at + least indices, we will do it ourselves. */ + if (mod->main.shared == NULL) { - /* 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; + if (unlikely (fd < 0 && mod->main.name == NULL && elf == NULL)) + /* The callback didn't give us anything... */ + return mod->main.cberr = DWFL_E_CB; - file->elf = elf_begin (file->fd, ELF_C_READ_MMAP_PRIVATE, NULL); + err = __libdwfl_open_file (&mod->main, mod->main.name, fd, elf); } - if (unlikely (elf_kind (file->elf) != ELF_K_ELF)) - { - close (file->fd); - file->fd = -1; - return DWFL_E_BADELF; - } - - GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (file->elf, &ehdr_mem); - if (ehdr == NULL) + if (likely (err == DWFL_E_NOERROR)) { - elf_error: - close (file->fd); - file->fd = -1; - return DWFL_E (LIBELF, elf_errno ()); - } - - /* The addresses in an ET_EXEC file are absolute. The lowest p_vaddr of - the main file can differ from that of the debug file due to prelink. - But that doesn't not change addresses that symbols, debuginfo, or - sh_addr of any program sections refer to. */ - file->bias = 0; - if (mod->e_type != ET_EXEC) - for (uint_fast16_t i = 0; i < ehdr->e_phnum; ++i) - { - GElf_Phdr ph_mem; - GElf_Phdr *ph = gelf_getphdr (file->elf, i, &ph_mem); - if (ph == NULL) - goto elf_error; - if (ph->p_type == PT_LOAD) - { - file->bias = ((mod->low_addr & -ph->p_align) - - (ph->p_vaddr & -ph->p_align)); - break; - } - } - - mod->e_type = ehdr->e_type; + if (unlikely (elf_kind (mod->main.shared->elf) != ELF_K_ELF)) + { + err = DWFL_E_BADELF; + elf_error: + __libdwfl_close_file (&mod->main); + mod->main.name = NULL; + return err; + } - /* Relocatable Linux kernels are ET_EXEC but act like ET_DYN. */ - if (mod->e_type == ET_EXEC && file->bias != 0) - mod->e_type = ET_DYN; + /* 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); + mod->build_id = NULL; - return DWFL_E_NOERROR; -} + /* The following code duplicate to dwfl_report_elf.c. */ + mod->bias = ((mod->low_addr & -mod->main.shared->align) + - (mod->main.shared->start & -mod->main.shared->align)); -/* 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. */ -static void -find_file (Dwfl_Module *mod) -{ - if (mod->main.elf != NULL /* Already done. */ - || mod->elferr != DWFL_E_NOERROR) /* Cached failure. */ - return; + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (mod->main.shared->elf, &ehdr_mem); + if (ehdr == NULL) + { + /* XXX This shouldn't happen, we already got ehdr in + __libdwfl_open_file. Maybe put the the ehdr in shared + struct, since we extract it anyway? */ + err = DWFL_E (LIBELF, elf_errno ()); + goto elf_error; + } - mod->main.fd = (*mod->dwfl->callbacks->find_elf) (MODCB_ARGS (mod), - &mod->main.name, - &mod->main.elf); - mod->elferr = open_elf (mod, &mod->main); + mod->e_type = ehdr->e_type; - if (mod->elferr == DWFL_E_NOERROR && !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; + /* Relocatable Linux kernels are ET_EXEC but act like ET_DYN. */ + if (mod->e_type == ET_EXEC + && mod->bias != 0) + mod->e_type = ET_DYN; } + + return mod->main.cberr = err; } /* Search an ELF file for a ".gnu_debuglink" section. */ @@ -207,405 +191,91 @@ find_debuglink (Elf *elf, GElf_Word *crc) static Dwfl_Error find_debuginfo (Dwfl_Module *mod) { - if (mod->debug.elf != NULL) - return DWFL_E_NOERROR; + if (mod->debug.cberr != DWFL_E_NOERROR) + return mod->debug.cberr; + if (mod->debug.shared != NULL) /* Already done. */ + return mod->debug.shared->elferr; 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); - return open_elf (mod, &mod->debug); -} - - -/* 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, 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; - 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. */ + const char *debuglink_file = find_debuglink (mod->main.shared->elf, &debuglink_crc); + + int fd = (*mod->dwfl->callbacks->find_debuginfo) (MODCB_ARGS (mod), + mod->main.name, + debuglink_file, + debuglink_crc, + &mod->debug.name); + if (mod->debug.shared != NULL) 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; -} + if (fd < 0) + return mod->debug.cberr = DWFL_E_CB; - -/* Translate addresses into file offsets. - OFFS[*] start out zero and remain zero if unresolved. */ -static void -find_offsets (Elf *elf, const GElf_Ehdr *ehdr, size_t n, - GElf_Addr addrs[n], GElf_Off offs[n]) -{ - size_t unsolved = n; - for (uint_fast16_t i = 0; i < ehdr->e_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; - } - } + return mod->debug.cberr = __libdwfl_open_file (&mod->debug, mod->debug.name, + fd, NULL); } -/* 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); - for (uint_fast16_t i = 0; i < ehdr->e_phnum; ++i) +/* Try to open a libebl backend for MOD. */ +Dwfl_Error +internal_function +__libdwfl_module_getebl (Dwfl_Module *mod) +{ + if (mod->main.shared == NULL + || mod->main.shared->ebl == NULL) { - GElf_Phdr phdr_mem; - GElf_Phdr *phdr = gelf_getphdr (mod->main.elf, i, &phdr_mem); - if (phdr == NULL) - break; + if (find_file (mod) != DWFL_E_NOERROR) + return mod->main.cberr; - 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, ehdr, 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; - } - } + mod->main.shared->ebl = ebl_openbackend (mod->main.shared->elf); + if (mod->main.shared->ebl == NULL) + return DWFL_E_LIBEBL; } + return DWFL_E_NOERROR; } -/* Try to find a symbol table in either MOD->main.elf or MOD->debug.elf. */ -static void -find_symtab (Dwfl_Module *mod) +static Dwfl_Error +find_symfile (Dwfl_Module *mod) { - if (mod->symdata != NULL /* Already done. */ - || mod->symerr != DWFL_E_NOERROR) /* Cached previous failure. */ - return; + if (mod->symfile != NULL) + return DWFL_E_NOERROR; - find_file (mod); - mod->symerr = mod->elferr; - if (mod->symerr != DWFL_E_NOERROR) - return; + Dwfl_Error error = find_file (mod); + if (error != DWFL_E_NOERROR) + return error; - /* 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, &strshndx); - switch (mod->symerr) + error = __libdwfl_find_symtab (mod->main.shared); + if (error == DWFL_E_NO_SYMTAB + || (error == DWFL_E_NOERROR && !mod->main.shared->is_symtab)) { - 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) + error = find_debuginfo (mod); + if (error == DWFL_E_NOERROR) { - default: - return; - - case DWFL_E_NOERROR: - mod->symerr = load_symtab (&mod->debug, &mod->symfile, &symscn, - &xndxscn, &mod->syments, &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) + error = __libdwfl_find_symtab (mod->debug.shared); + if (error == DWFL_E_NOERROR) { - /* We still have the dynamic symbol table. */ - mod->symerr = DWFL_E_NOERROR; - break; + /* .dynsym in debuginfo makes no sense, so if there is a + symtab, it's the proper one. */ + mod->symfile = &mod->debug; + return error; } - - /* 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 was 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) - { - find_file (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; + else if (mod->main.shared->symerr != DWFL_E_NOERROR) + /* No .dynsym in main, so report failure to find debuginfo. */ + return error; } - return DWFL_E_NOERROR; + if (error == DWFL_E_NOERROR) + mod->symfile = &mod->main; + return error; } /* Try to start up libdw on DEBUGFILE. */ static Dwfl_Error -load_dw (Dwfl_Module *mod, struct dwfl_file *debugfile) +load_dw (Dwfl_Module *mod, struct dwfl_shared_file *debugfile) { + if (debugfile->dw != NULL /* Already done. */ + || debugfile->dwerr != DWFL_E_NOERROR) /* Cached previous failure. */ + return debugfile->dwerr; + if (mod->e_type == ET_REL && !debugfile->relocated) { const Dwfl_Callbacks *const cb = mod->dwfl->callbacks; @@ -618,28 +288,13 @@ load_dw (Dwfl_Module *mod, struct dwfl_file *debugfile) 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; - } + error = __libdwfl_relocate (mod, debugfile, true); + if (error != DWFL_E_NOERROR) + return error; } - mod->dw = INTUSE(dwarf_begin_elf) (debugfile->elf, DWARF_C_READ, NULL); - if (mod->dw == NULL) + debugfile->dw = INTUSE(dwarf_begin_elf) (debugfile->elf, DWARF_C_READ, NULL); + if (debugfile->dw == NULL) { int err = INTUSE(dwarf_errno) (); return err == DWARF_E_NO_DWARF ? DWFL_E_NO_DWARF : DWFL_E (LIBDW, err); @@ -652,32 +307,35 @@ load_dw (Dwfl_Module *mod, struct dwfl_file *debugfile) } /* Try to start up libdw on either the main file or the debuginfo file. */ -static void +static Dwfl_Error find_dw (Dwfl_Module *mod) { - if (mod->dw != NULL /* Already done. */ - || mod->dwerr != DWFL_E_NOERROR) /* Cached previous failure. */ - return; + if (mod->dwerr != DWFL_E_NOERROR) /* Cached failure to setup debug file. */ + return mod->dwerr; - find_file (mod); - mod->dwerr = mod->elferr; + if (mod->debug.shared != NULL + && (mod->debug.shared->dw != NULL /* Already done. */ + || mod->debug.shared->dwerr != DWFL_E_NOERROR)) /* Cached failure. */ + return mod->debug.shared->dwerr; + + mod->dwerr = find_file (mod); if (mod->dwerr != DWFL_E_NOERROR) - return; + return mod->dwerr; /* First see if the main ELF file has the debugging information. */ - mod->dwerr = load_dw (mod, &mod->main); - switch (mod->dwerr) + mod->main.shared->dwerr = load_dw (mod, mod->main.shared); + switch (mod->main.shared->dwerr) { case DWFL_E_NOERROR: - mod->debug.elf = mod->main.elf; - mod->debug.bias = mod->main.bias; - return; + mod->debug.shared = mod->main.shared; + return DWFL_E_NOERROR; case DWFL_E_NO_DWARF: break; default: - goto canonicalize; + return (mod->main.shared->dwerr + = __libdwfl_canon_error (mod->main.shared->dwerr)); } /* Now we have to look for a separate debuginfo file. */ @@ -685,19 +343,15 @@ find_dw (Dwfl_Module *mod) switch (mod->dwerr) { case DWFL_E_NOERROR: - mod->dwerr = load_dw (mod, &mod->debug); - break; + return (mod->debug.shared->dwerr + = __libdwfl_canon_error (load_dw (mod, mod->debug.shared))); case DWFL_E_CB: /* The find_debuginfo hook failed. */ - mod->dwerr = DWFL_E_NO_DWARF; - return; + return mod->dwerr = DWFL_E_NO_DWARF; default: - break; + return mod->dwerr = __libdwfl_canon_error (mod->dwerr); } - - canonicalize: - mod->dwerr = __libdwfl_canon_error (mod->dwerr); } @@ -707,34 +361,33 @@ dwfl_module_getelf (Dwfl_Module *mod, GElf_Addr *loadbase) if (mod == NULL) return NULL; - find_file (mod); - if (mod->elferr == DWFL_E_NOERROR) + if (find_file (mod) == DWFL_E_NOERROR) { - if (mod->e_type == ET_REL && ! mod->main.relocated) + if (mod->e_type == ET_REL && ! mod->main.shared->relocated) { /* Before letting them get at the Elf handle, apply all the relocations we know how to. */ - mod->main.relocated = true; + mod->main.shared->relocated = true; if (likely (__libdwfl_module_getebl (mod) == DWFL_E_NOERROR)) { - (void) __libdwfl_relocate (mod, mod->main.elf, false); + (void) __libdwfl_relocate (mod, mod->main.shared, false); - if (mod->debug.elf == mod->main.elf) - mod->debug.relocated = true; - else if (mod->debug.elf != NULL && ! mod->debug.relocated) + if (mod->debug.shared == mod->main.shared) + mod->debug.shared->relocated = true; + else if (mod->debug.shared != NULL && ! mod->debug.shared->relocated) { - mod->debug.relocated = true; - (void) __libdwfl_relocate (mod, mod->debug.elf, false); + mod->debug.shared->relocated = true; + (void) __libdwfl_relocate (mod, mod->debug.shared, false); } } } - *loadbase = mod->main.bias; - return mod->main.elf; + *loadbase = mod->bias; + return mod->main.shared->elf; } - __libdwfl_seterrno (mod->elferr); + __libdwfl_seterrno (mod->main.cberr); return NULL; } INTDEF (dwfl_module_getelf) @@ -746,24 +399,24 @@ dwfl_module_getdwarf (Dwfl_Module *mod, Dwarf_Addr *bias) if (mod == NULL) return NULL; - find_dw (mod); - if (mod->dwerr == DWFL_E_NOERROR) + Dwfl_Error err = find_dw (mod); + if (err == 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->main.shared->relocated && ! mod->debug.shared->relocated) { - mod->debug.relocated = true; - if (mod->debug.elf != mod->main.elf) - (void) __libdwfl_relocate (mod, mod->debug.elf, false); + mod->debug.shared->relocated = true; + if (mod->debug.shared->elf != mod->main.shared->elf) + (void) __libdwfl_relocate (mod, mod->debug.shared, false); } - *bias = mod->debug.bias; - return mod->dw; + *bias = DWBIAS (mod); + return mod->debug.shared->dw; } - __libdwfl_seterrno (mod->dwerr); + __libdwfl_seterrno (err); return NULL; } INTDEF (dwfl_module_getdwarf) @@ -771,14 +424,18 @@ INTDEF (dwfl_module_getdwarf) int dwfl_module_getsymtab (Dwfl_Module *mod) { - if (mod == NULL) + if (mod == NULL + || mod->main.cberr != DWFL_E_NOERROR) + /* Don't mind error in debug.cberr, symtab may be in main. */ return -1; - find_symtab (mod); - if (mod->symerr == DWFL_E_NOERROR) - return mod->syments; + Dwfl_Error err = find_symfile(mod); + if (mod->symfile == NULL) + { + __libdwfl_seterrno (err); + return -1; + } - __libdwfl_seterrno (mod->symerr); - return -1; + return mod->symfile->shared->syments; } INTDEF (dwfl_module_getsymtab) diff --git a/libdwfl/dwfl_module_getsrc_file.c b/libdwfl/dwfl_module_getsrc_file.c index 9d0c786b..6b0dae6f 100644 --- a/libdwfl/dwfl_module_getsrc_file.c +++ b/libdwfl/dwfl_module_getsrc_file.c @@ -59,7 +59,7 @@ dwfl_module_getsrc_file (Dwfl_Module *mod, if (mod == NULL) return -1; - if (mod->dw == NULL) + if (mod->debug.shared->dw == NULL) { Dwarf_Addr bias; if (INTUSE(dwfl_module_getdwarf) (mod, &bias) == NULL) diff --git a/libdwfl/dwfl_module_getsym.c b/libdwfl/dwfl_module_getsym.c index 5f289ccb..f81f555f 100644 --- a/libdwfl/dwfl_module_getsym.c +++ b/libdwfl/dwfl_module_getsym.c @@ -56,7 +56,7 @@ dwfl_module_getsym (Dwfl_Module *mod, int ndx, if (unlikely (mod == NULL)) return NULL; - if (unlikely (mod->symdata == NULL)) + if (unlikely (mod->symfile == NULL)) { int result = INTUSE(dwfl_module_getsymtab) (mod); if (result < 0) @@ -64,7 +64,9 @@ dwfl_module_getsym (Dwfl_Module *mod, int ndx, } GElf_Word shndx; - sym = gelf_getsymshndx (mod->symdata, mod->symxndxdata, ndx, sym, &shndx); + sym = gelf_getsymshndx (mod->symfile->shared->symdata, + mod->symfile->shared->symxndxdata, + ndx, sym, &shndx); if (unlikely (sym == NULL)) { __libdwfl_seterrno (DWFL_E_LIBELF); @@ -90,9 +92,9 @@ dwfl_module_getsym (Dwfl_Module *mod, int ndx, /* 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); + Dwfl_Error result + = __libdwfl_relocate_value (mod, mod->symfile->shared->elf, + &symshstrndx, shndx, &sym->st_value); if (unlikely (result != DWFL_E_NOERROR)) { __libdwfl_seterrno (result); @@ -100,15 +102,15 @@ dwfl_module_getsym (Dwfl_Module *mod, int ndx, } } /* Apply the bias to the symbol value. */ - sym->st_value += mod->symfile->bias; + sym->st_value += SYMBIAS (mod); break; } - if (unlikely (sym->st_name >= mod->symstrdata->d_size)) + if (unlikely (sym->st_name >= mod->symfile->shared->symstrdata->d_size)) { __libdwfl_seterrno (DWFL_E_BADSTROFF); return NULL; } - return (const char *) mod->symstrdata->d_buf + sym->st_name; + return (const char *) mod->symfile->shared->symstrdata->d_buf + sym->st_name; } INTDEF (dwfl_module_getsym) diff --git a/libdwfl/dwfl_module_info.c b/libdwfl/dwfl_module_info.c index 759cb621..f2cf3848 100644 --- a/libdwfl/dwfl_module_info.c +++ b/libdwfl/dwfl_module_info.c @@ -66,9 +66,12 @@ dwfl_module_info (Dwfl_Module *mod, void ***userdata, *end = mod->high_addr; if (dwbias) - *dwbias = mod->debug.elf == NULL ? (Dwarf_Addr) -1 : mod->debug.bias; + *dwbias = (mod->debug.shared == NULL + || mod->debug.shared->elf == NULL) + ? (Dwarf_Addr) -1 : DWBIAS (mod); + if (symbias) - *symbias = mod->symfile == NULL ? (Dwarf_Addr) -1 : mod->symfile->bias; + *symbias = mod->symfile == NULL ? (Dwarf_Addr) -1 : SYMBIAS (mod); if (mainfile) *mainfile = mod->main.name; diff --git a/libdwfl/dwfl_module_register_names.c b/libdwfl/dwfl_module_register_names.c index 79a874a8..b9a2d8f3 100644 --- a/libdwfl/dwfl_module_register_names.c +++ b/libdwfl/dwfl_module_register_names.c @@ -61,7 +61,8 @@ dwfl_module_register_names (mod, func, arg) if (unlikely (mod == NULL)) return -1; - if (unlikely (mod->ebl == NULL)) + if (unlikely (mod->main.shared == NULL + || mod->main.shared->ebl == NULL)) { Dwfl_Error error = __libdwfl_module_getebl (mod); if (error != DWFL_E_NOERROR) @@ -71,7 +72,8 @@ dwfl_module_register_names (mod, func, arg) } } - int nregs = ebl_register_info (mod->ebl, -1, NULL, 0, + int nregs = ebl_register_info (mod->main.shared->ebl, -1, + NULL, 0, NULL, NULL, NULL, NULL); int result = 0; for (int regno = 0; regno < nregs && likely (result == 0); ++regno) @@ -81,7 +83,8 @@ dwfl_module_register_names (mod, func, arg) const char *prefix = NULL; int bits = -1; int type = -1; - ssize_t len = ebl_register_info (mod->ebl, regno, name, sizeof name, + ssize_t len = ebl_register_info (mod->main.shared->ebl, regno, + name, sizeof name, &prefix, &setname, &bits, &type); if (unlikely (len < 0)) { diff --git a/libdwfl/dwfl_module_report_build_id.c b/libdwfl/dwfl_module_report_build_id.c index 9a1b14f7..beec9f8b 100644 --- a/libdwfl/dwfl_module_report_build_id.c +++ b/libdwfl/dwfl_module_report_build_id.c @@ -58,14 +58,16 @@ dwfl_module_report_build_id (Dwfl_Module *mod, if (mod == NULL) return -1; - if (mod->main.elf != NULL) + if (mod->main.shared != 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)) + struct dwfl_build_id *build_id = mod->main.shared->build_id; + if (BUILD_ID_PTR (build_id) + && (size_t) build_id->len == len + && (build_id->vaddr == vaddr || vaddr == 0) + && !memcmp (bits, build_id->bits, len)) return 0; __libdwfl_seterrno (DWFL_E_ALREADY_ELF); @@ -78,24 +80,12 @@ dwfl_module_report_build_id (Dwfl_Module *mod, 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); - } + struct dwfl_build_id *orig_build_id = mod->build_id; - free (mod->build_id_bits); - - mod->build_id_bits = copy; - mod->build_id_len = len; - mod->build_id_vaddr = vaddr; + if (__libdwfl_found_build_id (&mod->build_id, true, bits, len, vaddr) < 0) + return -1; + free (orig_build_id); return 0; } INTDEF (dwfl_module_report_build_id) diff --git a/libdwfl/dwfl_module_return_value_location.c b/libdwfl/dwfl_module_return_value_location.c index 3d5154e2..35612d6d 100644 --- a/libdwfl/dwfl_module_return_value_location.c +++ b/libdwfl/dwfl_module_return_value_location.c @@ -59,7 +59,8 @@ dwfl_module_return_value_location (mod, functypedie, locops) if (mod == NULL) return -1; - if (mod->ebl == NULL) + if (mod->main.shared == NULL + || mod->main.shared->ebl == NULL) { Dwfl_Error error = __libdwfl_module_getebl (mod); if (error != DWFL_E_NOERROR) @@ -69,7 +70,8 @@ dwfl_module_return_value_location (mod, functypedie, locops) } } - int nops = ebl_return_value_location (mod->ebl, functypedie, locops); + int nops = ebl_return_value_location (mod->main.shared->ebl, + functypedie, locops); if (unlikely (nops < 0)) { if (nops == -1) diff --git a/libdwfl/dwfl_nextcu.c b/libdwfl/dwfl_nextcu.c index 6db3e0f1..db7ec121 100644 --- a/libdwfl/dwfl_nextcu.c +++ b/libdwfl/dwfl_nextcu.c @@ -75,7 +75,7 @@ dwfl_nextcu (Dwfl *dwfl, Dwarf_Die *lastcu, Dwarf_Addr *bias) if (cu != NULL) { - *bias = mod->debug.bias; + *bias = DWBIAS (mod); return &cu->die; } @@ -88,12 +88,12 @@ dwfl_nextcu (Dwfl *dwfl, Dwarf_Die *lastcu, Dwarf_Addr *bias) return NULL; if (mod->dwerr == DWFL_E_NOERROR - && (mod->dw != NULL + && ((mod->debug.shared != NULL && mod->debug.shared->dw != NULL) || INTUSE(dwfl_module_getdwarf) (mod, bias) != NULL)) break; } while (mod->dwerr == DWFL_E_NO_DWARF); - error = mod->dwerr; + error = mod->dwerr ?: mod->debug.shared->dwerr; } while (error == DWFL_E_NOERROR); diff --git a/libdwfl/dwfl_report_elf.c b/libdwfl/dwfl_report_elf.c index 0e5d09bc..9711ba7a 100644 --- a/libdwfl/dwfl_report_elf.c +++ b/libdwfl/dwfl_report_elf.c @@ -226,34 +226,43 @@ __libdwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name, Dwfl_Module *m = INTUSE(dwfl_report_module) (dwfl, name, start, end); if (m != NULL) { - if (m->main.name == NULL) + if (m->main.shared == NULL) { - m->main.name = strdup (file_name); - m->main.fd = fd; - } - else if ((fd >= 0 && m->main.fd != fd) + Dwfl_Error err = __libdwfl_open_file (&m->main, file_name, fd, elf); + + /* Some code here duplicate to dwfl_module_getdwarf.c. */ + m->bias = ((m->low_addr & -m->main.shared->align) + - (m->main.shared->start & -m->main.shared->align)); + + m->e_type = ehdr->e_type; + + if (m->e_type == ET_EXEC + && m->bias != 0) + m->e_type = ET_DYN; + + + if (unlikely (err != DWFL_E_NOERROR)) + { + __libdwfl_seterrno (err); + return NULL; + } + else if (bias != m->bias) + { + __libdwfl_seterrno (DWFL_E_OVERLAP); + m = NULL; + } + } + else if (m->bias != base + || (fd >= 0 && m->main.shared->fd >= 0 && m->main.shared->fd != fd) || strcmp (m->main.name, file_name)) + /* This module has already been reported before, but with + different bias/fd/name. */ { elf_end (elf); - overlap: m->gc = true; __libdwfl_seterrno (DWFL_E_OVERLAP); m = NULL; } - - /* Preinstall the open ELF handle for the module. */ - if (m->main.elf == NULL) - { - m->main.elf = elf; - m->main.bias = bias; - m->e_type = ehdr->e_type; - } - else - { - elf_end (elf); - if (m->main.bias != base) - goto overlap; - } } return m; } diff --git a/libdwfl/dwfl_segment_report_module.c b/libdwfl/dwfl_segment_report_module.c index 50ed140e..53e59926 100644 --- a/libdwfl/dwfl_segment_report_module.c +++ b/libdwfl/dwfl_segment_report_module.c @@ -638,8 +638,14 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name, if (elf != NULL) { /* Install the file in the module. */ - mod->main.elf = elf; - mod->main.bias = bias; + if (__libdwfl_open_file (&mod->main, NULL, -1, elf)) + { + ndx = -1; + return finish (); + } + mod->main.shared->start = start; + mod->bias = ((mod->low_addr & -mod->main.shared->align) + - (mod->main.shared->start & -mod->main.shared->align)); } return finish (); diff --git a/libdwfl/dwfl_symtab.c b/libdwfl/dwfl_symtab.c new file mode 100644 index 00000000..dd60eb3a --- /dev/null +++ b/libdwfl/dwfl_symtab.c @@ -0,0 +1,370 @@ +/* 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" + +/* This function is called when load_symtab finds necessary sections + in ELF image. It sets up FILE data apropriately, signalling error + when one of the calls fails. */ +static Dwfl_Error +load_symtab_sections (struct dwfl_shared_file *file, Elf_Scn *symscn, + Elf_Scn *xndxscn, GElf_Word strshndx) +{ + /* This does some sanity checks on the string table section. */ + if (elf_strptr (file->elf, strshndx, 0) == NULL) + goto elferr; + + if (xndxscn == NULL) + file->symxndxdata = NULL; + else + { + file->symxndxdata = elf_getdata (xndxscn, NULL); + if (file->symxndxdata == NULL) + goto elferr; + } + + file->symdata = elf_getdata (symscn, NULL); + if (file->symdata != NULL) + return DWFL_E_NOERROR; + + elferr: + return file->symerr = DWFL_E (LIBELF, elf_errno ()); +} + +/* 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 +find_symtab (struct dwfl_shared_file *file, GElf_Word *strshndx) +{ + bool symtab = false; + Elf_Scn *symscn = NULL, *xndxscn = NULL; + 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; + *strshndx = shdr->sh_link; + file->syments = shdr->sh_size / shdr->sh_entsize; + if (xndxscn != NULL) + return load_symtab_sections (file, symscn, xndxscn, *strshndx); + break; + + case SHT_DYNSYM: + if (symtab) + break; + symscn = scn; + *strshndx = shdr->sh_link; + file->syments = shdr->sh_size / shdr->sh_entsize; + break; + + case SHT_SYMTAB_SHNDX: + xndxscn = scn; + if (symtab) + return load_symtab_sections (file, symscn, xndxscn, *strshndx); + break; + + default: + break; + } + } + + if (symtab) + /* We found one, though no SHT_SYMTAB_SHNDX to go with it. */ + return load_symtab_sections (file, symscn, xndxscn, *strshndx); + + /* We found no SHT_SYMTAB. */ + 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, const GElf_Ehdr *ehdr, size_t n, + GElf_Addr addrs[n], GElf_Off offs[n]) +{ + size_t unsolved = n; + for (uint_fast16_t i = 0; i < ehdr->e_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 Dwfl_Error +find_dynsym (struct dwfl_shared_file *file) +{ + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (file->elf, &ehdr_mem); + + for (uint_fast16_t i = 0; i < ehdr->e_phnum; ++i) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (file->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 (file->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 (file->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 (file->elf, ehdr, 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 (file->elf, + offs[i_hash] + entsz, entsz, + entsz == 4 ? ELF_T_WORD + : ELF_T_XWORD); + if (data != NULL) + file->syments = (entsz == 4 + ? *(const GElf_Word *) data->d_buf + : *(const GElf_Xword *) data->d_buf); + } + if (offs[i_gnu_hash] != 0 && file->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 (file->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 (file->elf) + * sizeof (Elf32_Word) + * header->maskwords)); + + data = elf_getdata_rawchunk (file->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 (file->elf, + hasharr_at, + sizeof (Elf32_Word), + ELF_T_WORD); + if (data != NULL + && (*(const Elf32_Word *) data->d_buf & 1u)) + { + file->syments = maxndx + 1; + break; + } + ++maxndx; + hasharr_at += sizeof (Elf32_Word); + } while (data != NULL); + } + } + } + if (offs[i_strtab] > offs[i_symtab] && file->syments == 0) + file->syments = ((offs[i_strtab] - offs[i_symtab]) + / gelf_fsize (file->elf, + ELF_T_SYM, 1, EV_CURRENT)); + + if (file->syments > 0) + { + file->symdata + = elf_getdata_rawchunk (file->elf, + offs[i_symtab], + gelf_fsize (file->elf, + ELF_T_SYM, + file->syments, + EV_CURRENT), + ELF_T_SYM); + if (file->symdata != NULL) + { + file->symstrdata + = elf_getdata_rawchunk (file->elf, + offs[i_strtab], + strsz, + ELF_T_BYTE); + if (file->symstrdata == NULL) + file->symdata = NULL; + } + if (file->symdata == NULL) + return DWFL_E (LIBELF, elf_errno ()); + else + return DWFL_E_NOERROR; + } + } + } + return DWFL_E_NO_SYMTAB; +} + +/* Try to find a symbol table or dynamic symbol table in FILE. */ +Dwfl_Error +internal_function +__libdwfl_find_symtab (struct dwfl_shared_file *file) +{ + if (file->symdata != NULL /* Already done. */ + || file->symerr != DWFL_E_NOERROR) /* Cached previous failure. */ + return file->symerr; + + /* First see if .symtab is present. */ + GElf_Word strshndx = 0; + file->symerr = find_symtab (file, &strshndx); + + if (file->symerr == DWFL_E_NOERROR) + file->is_symtab = true; + else if (file->symerr == DWFL_E_NO_SYMTAB) + { + /* .symtab not present, try .dynsym */ + file->symerr = find_dynsym (file); + + if (file->symerr != DWFL_E_NOERROR) + return file->symerr; + } + else + return file->symerr; + + file->symstrdata = elf_getdata (elf_getscn (file->elf, strshndx), NULL); + if (file->symstrdata == NULL) + file->symerr = DWFL_E (LIBELF, elf_errno ()); + else + file->symerr = DWFL_E_NOERROR; + + return file->symerr; +} diff --git a/libdwfl/find-debuginfo.c b/libdwfl/find-debuginfo.c index a01293e8..9f6f4c7f 100644 --- a/libdwfl/find-debuginfo.c +++ b/libdwfl/find-debuginfo.c @@ -91,27 +91,27 @@ check_crc (int fd, GElf_Word debuglink_crc) } static bool -validate (Dwfl_Module *mod, int fd, bool check, GElf_Word debuglink_crc) +validate (struct dwfl_build_id **build_idp, GElf_Addr bias, + int fd, bool check, GElf_Word debuglink_crc) { /* If we have a build ID, check only that. */ - if (mod->build_id_len > 0) + if (BUILD_ID_PTR (*build_idp)) { /* 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.elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL); - if (likely (__libdwfl_find_build_id (mod, false, mod->debug.elf) == 2)) - /* Also backdoor the gratuitous flag. */ - mod->debug.valid = true; - else + build ID note for validation. + XXX: Maybe backdoor the handle into the module data structure + since we had to open it early anyway? */ + + Elf * elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL); + if (unlikely (__libdwfl_find_build_id (build_idp, bias, + false, elf) != 2)) { - /* A mismatch! */ - elf_end (mod->debug.elf); - mod->debug.elf = NULL; - mod->debug.valid = false; + free (*build_idp); + *build_idp = NULL; } + elf_end (elf); - return mod->debug.valid; + return *build_idp != NULL; } return !check || check_crc (fd, debuglink_crc); @@ -203,7 +203,9 @@ find_debuginfo_in_path (Dwfl_Module *mod, const char *file_name, default: return -1; } - if (validate (mod, fd, check, debuglink_crc)) + + if (validate (&mod->main.shared->build_id, mod->bias, + fd, check, debuglink_crc)) { *debuginfo_file_name = fname; return fd; diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h index 6ba5c96e..6cf32eca 100644 --- a/libdwfl/libdwflP.h +++ b/libdwfl/libdwflP.h @@ -128,15 +128,43 @@ struct Dwfl #define OFFLINE_REDZONE 0x10000 -struct dwfl_file +struct dwfl_build_id +{ + GElf_Addr vaddr; /* Address where they reside. */ + int len; + unsigned char bits[0]; /* malloc'd copy of build ID bits. */ +}; + +struct dwfl_shared_file { - char *name; int fd; - bool valid; /* The build ID note has been matched. */ bool relocated; /* Partial relocation of all sections done. */ Elf *elf; - GElf_Addr bias; /* Actual load address - p_vaddr. */ + GElf_Addr start; /* p_vaddr */ + Dwfl_Error elferr; /* Previous failure to get Elf handle. */ + + Elf_Data *symdata; /* Data in the ELF symbol table section. */ + size_t syments; /* sh_size / sh_entsize of that section. */ + Elf_Data *symstrdata; /* Data for its string table. */ + Elf_Data *symxndxdata; /* Data in the extended section index table. */ + GElf_Addr align; /* Alignment requirements. */ + bool is_symtab; /* Whether the symfile has a .symtab or a .dynsym. */ + Dwfl_Error symerr; /* Previous failure to load symbols. */ + + struct dwfl_build_id *build_id; + + Dwarf *dw; /* libdw handle for its debugging info. */ + Dwfl_Error dwerr; /* Previous failure to load debuginfo. */ + Ebl *ebl; +}; + +struct dwfl_file +{ + char *name; + struct dwfl_shared_file *shared; /* Shared portion of file. */ + Dwfl_Error cberr; /* Error related to use of find_elf (main.cberr) + or find_debuginfo (debug.cberr) callbacks. */ }; struct Dwfl_Module @@ -149,26 +177,20 @@ struct Dwfl_Module char *name; /* Iterator name for this module. */ GElf_Addr low_addr, high_addr; - 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. */ - - struct dwfl_file main, debug; - Ebl *ebl; + struct dwfl_file main; + struct dwfl_file debug; GElf_Half e_type; /* GElf_Ehdr.e_type cache. */ - Dwfl_Error elferr; /* Previous failure to open main file. */ + + GElf_Addr bias; /* Actual load address - p_vaddr of main. */ + + struct dwfl_build_id *build_id; /* Build ID data are stored here + before we have 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. */ - Elf_Data *symstrdata; /* Data for its string table. */ - Elf_Data *symxndxdata; /* Data in the extended section index table. */ - Dwfl_Error symerr; /* Previous failure to load symbols. */ + struct dwfl_file *symfile; /* Points either to main or debug. */ - Dwarf *dw; /* libdw handle for its debugging info. */ - Dwfl_Error dwerr; /* Previous failure to load info. */ + Dwfl_Error dwerr; /* Previous failure to load debuginfo. */ /* Known CU's in this module. */ struct dwfl_cu *first_cu, **cu; @@ -241,18 +263,21 @@ extern void __libdwfl_module_free (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, + FILE->elf 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) +extern Dwfl_Error __libdwfl_relocate (Dwfl_Module *mod, + struct dwfl_shared_file *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, +extern Dwfl_Error __libdwfl_relocate_section (Dwfl_Module *mod, + struct dwfl_shared_file *relocated, Elf_Scn *relocscn, Elf_Scn *tscn, bool partial) internal_function; @@ -284,16 +309,25 @@ 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. 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) +/* Look in ELF for an NT_GNU_BUILD_ID note. If CHECK is NULL, store + it in MOD and return its length. If CHECK is non-NULL, instead + compare it to build ID stored there 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 (struct dwfl_build_id **build_idp, + GElf_Addr bias, bool set, Elf *elf) + internal_function; + +extern int __libdwfl_found_build_id (struct dwfl_build_id **build_idp, bool set, + const void *bits, int len, GElf_Addr vaddr) 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 bool __libdwfl_open_by_build_id (const Dwfl_Callbacks *const cb, + struct dwfl_file *file, + const struct dwfl_build_id *build_id, + GElf_Addr bias, + bool debug, char **file_name) + internal_function; extern uint32_t __libdwfl_crc32 (uint32_t crc, unsigned char *buf, size_t len) attribute_hidden; @@ -362,6 +396,33 @@ extern int dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size, /* Examine an ET_CORE file and report modules based on its contents. */ extern int dwfl_core_file_report (Dwfl *dwfl, Elf *elf, const GElf_Ehdr *ehdr); +/* Allocate dwfl_shared_file from path FILE_NAME. When successful, + fills in TGT structure. Caches previous lookups and will use the + same TGT->shared when the same file is asked for several times. + + FD may be -1 to open FILE_NAME, or you can provide pre-opened FD. + In both cases FD is consumed, and FILE_NAME is used to initialize + TGT->name. + + The case when FD is -1 and ELF is non-NULL is handled specially. + Such an ELF is considered to have no file backing, and is not + shared. + + Optional parameter ELF is used to initialize TGT->shared->elf + member; if NULL is provided instead, new Elf file is allocated via + elf_begin. In both cases ELF is consumed. */ +extern Dwfl_Error __libdwfl_open_file (struct dwfl_file *tgt, + const char *file_name, + int fd, Elf *elf) + internal_function; + +/* Close the file. */ +extern void __libdwfl_close_file (struct dwfl_file *file) + internal_function; + +extern Dwfl_Error __libdwfl_find_symtab (struct dwfl_shared_file *file) + internal_function; + /* Avoid PLT entries. */ INTDECL (dwfl_begin) @@ -408,6 +469,17 @@ INTDECL (dwfl_module_relocate_address) #define MODCB_ARGS(mod) (mod), &(mod)->userdata, (mod)->name, (mod)->low_addr #define CBFAIL (errno ? DWFL_E (ERRNO, errno) : DWFL_E_CB); +/* The addresses in an ET_EXEC file are absolute. The lowest p_vaddr + of the main file can differ from that of the debug file due to + prelink. But that doesn't not change addresses that symbols, + debuginfo, or sh_addr of any program sections refer to. */ +#define DWBIAS(mod) \ + (((mod)->e_type == ET_EXEC) ? 0 \ + : (mod)->bias - (mod)->main.shared->start + (mod)->debug.shared->start) +#define SYMBIAS(mod) ((mod)->bias - (mod)->main.shared->start + (mod)->symfile->shared->start) + +#define BUILD_ID_NOT_FOUND ((void *) -1L) +#define BUILD_ID_PTR(ptr) ((ptr) != NULL && (ptr) != BUILD_ID_NOT_FOUND) /* The default used by dwfl_standard_find_debuginfo. */ #define DEFAULT_DEBUGINFO_PATH ":.debug:/usr/lib/debug" diff --git a/libdwfl/link_map.c b/libdwfl/link_map.c index e9890384..81d8957c 100644 --- a/libdwfl/link_map.c +++ b/libdwfl/link_map.c @@ -399,7 +399,7 @@ report_r_debug (uint_fast8_t elfclass, uint_fast8_t elfdata, name will be the fallback when no build ID match is found. XXX hook for sysroot */ if (name != NULL - && mod->main.elf == NULL + && mod->main.shared == NULL && mod->main.name == NULL) mod->main.name = strdup (name); } @@ -448,7 +448,7 @@ consider_executable (Dwfl_Module *mod, GElf_Addr at_phdr, GElf_Addr at_entry, void *memory_callback_arg) { GElf_Ehdr ehdr; - if (unlikely (gelf_getehdr (mod->main.elf, &ehdr) == NULL)) + if (unlikely (gelf_getehdr (mod->main.shared->elf, &ehdr) == NULL)) return 0; if (at_entry != 0) @@ -478,7 +478,7 @@ consider_executable (Dwfl_Module *mod, GElf_Addr at_phdr, GElf_Addr at_entry, 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); + GElf_Phdr *phdr = gelf_getphdr (mod->main.shared->elf, i, &phdr_mem); if (phdr == NULL) break; @@ -517,11 +517,11 @@ consider_executable (Dwfl_Module *mod, GElf_Addr at_phdr, GElf_Addr at_entry, /* If we're changing the module's address range, we've just invalidated the module lookup table. */ - if (bias != mod->main.bias) + if (bias != mod->bias) { - mod->low_addr -= mod->main.bias; - mod->high_addr -= mod->main.bias; - mod->main.bias = bias; + mod->low_addr -= mod->bias; + mod->high_addr -= mod->bias; + mod->bias = bias; mod->low_addr += bias; mod->high_addr += bias; @@ -533,11 +533,12 @@ consider_executable (Dwfl_Module *mod, GElf_Addr at_phdr, GElf_Addr at_entry, if (phdr->p_type == PT_DYNAMIC) { - Elf_Data *data = elf_getdata_rawchunk (mod->main.elf, phdr->p_offset, + Elf_Data *data = elf_getdata_rawchunk (mod->main.shared->elf, + phdr->p_offset, phdr->p_filesz, ELF_T_DYN); if (data == NULL) continue; - const size_t entsize = gelf_fsize (mod->main.elf, + const size_t entsize = gelf_fsize (mod->main.shared->elf, ELF_T_DYN, 1, EV_CURRENT); const size_t n = data->d_size / entsize; for (size_t j = 0; j < n; ++j) @@ -556,7 +557,7 @@ consider_executable (Dwfl_Module *mod, GElf_Addr at_phdr, GElf_Addr at_entry, if (d_val_vaddr != 0) { /* Now we have the final address from which to read &r_debug. */ - d_val_vaddr += mod->main.bias; + d_val_vaddr += mod->bias; void *buffer = NULL; size_t buffer_available = addrsize (ehdr.e_ident[EI_CLASS]); @@ -613,7 +614,7 @@ find_executable (Dwfl *dwfl, GElf_Addr at_phdr, GElf_Addr at_entry, void *memory_callback_arg) { for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next) - if (mod->main.elf != NULL) + if (mod->main.shared != NULL) { GElf_Addr r_debug_vaddr = consider_executable (mod, at_phdr, at_entry, elfclass, elfdata, @@ -763,7 +764,7 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size, 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->main.shared == NULL || phdr_mod->e_type != ET_EXEC) ? find_executable (dwfl, phdr, entry, &elfclass, &elfdata, diff --git a/libdwfl/linux-kernel-modules.c b/libdwfl/linux-kernel-modules.c index 5bbb384a..ccc62c52 100644 --- a/libdwfl/linux-kernel-modules.c +++ b/libdwfl/linux-kernel-modules.c @@ -593,7 +593,7 @@ dwfl_linux_kernel_find_elf (Dwfl_Module *mod, Dwarf_Addr base __attribute__ ((unused)), char **file_name, Elf **elfp) { - if (mod->build_id_len > 0) + if (BUILD_ID_PTR (mod->build_id)) { int fd = INTUSE(dwfl_build_id_find_elf) (mod, NULL, NULL, 0, file_name, elfp); diff --git a/libdwfl/offline.c b/libdwfl/offline.c index b3a95dd9..578b8b75 100644 --- a/libdwfl/offline.c +++ b/libdwfl/offline.c @@ -70,7 +70,8 @@ dwfl_offline_section_address (Dwfl_Module *mod, assert (shdr->sh_addr == 0); assert (shdr->sh_flags & SHF_ALLOC); - if (mod->debug.elf == NULL) + if (mod->debug.shared == NULL + || mod->debug.shared->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; @@ -78,10 +79,11 @@ dwfl_offline_section_address (Dwfl_Module *mod, /* 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 *debug_elf = mod->debug.shared->elf; + Elf_Scn *ourscn = elf_getscn (debug_elf, shndx); Elf_Scn *scn = NULL; uint_fast32_t skip_alloc = 0; - while ((scn = elf_nextscn (mod->debug.elf, scn)) != ourscn) + while ((scn = elf_nextscn (debug_elf, scn)) != ourscn) { assert (scn != NULL); GElf_Shdr shdr_mem; @@ -93,7 +95,7 @@ dwfl_offline_section_address (Dwfl_Module *mod, } scn = NULL; - while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL) + while ((scn = elf_nextscn (mod->main.shared->elf, scn)) != NULL) { GElf_Shdr shdr_mem; GElf_Shdr *main_shdr = gelf_getshdr (scn, &shdr_mem); @@ -161,13 +163,6 @@ process_elf (Dwfl *dwfl, const char *name, const char *file_name, int fd, || 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; @@ -237,7 +232,7 @@ process_archive_member (Dwfl *dwfl, const char *name, const char *file_name, } } - /* We let __libdwfl_report_elf cache the fd in mod->main.fd, + /* 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); diff --git a/libdwfl/relocate.c b/libdwfl/relocate.c index abacc041..78705b89 100644 --- a/libdwfl/relocate.c +++ b/libdwfl/relocate.c @@ -101,100 +101,6 @@ __libdwfl_relocate_value (Dwfl_Module *mod, Elf *elf, size_t *shstrndx, } -/* 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 (*shndx) - { - case SHN_ABS: - case SHN_UNDEF: - case SHN_COMMON: - 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 @@ -202,27 +108,16 @@ relocate_getsym (Dwfl_Module *mod, 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, +resolve_symbol (Dwfl_Module *referer, struct dwfl_shared_file *symfile, 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)) + if (unlikely (sym->st_name >= symfile->symstrdata->d_size)) return DWFL_E_BADSTROFF; - const char *name = symtab->symstrdata->d_buf; + const char *name = symfile->symstrdata->d_buf; name += sym->st_name; for (Dwfl_Module *m = referer->dwfl->modulelist; m != NULL; m = m->next) @@ -231,15 +126,21 @@ resolve_symbol (Dwfl_Module *referer, struct reloc_symtab_cache *symtab, /* 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; + if (m->symfile == NULL + && m->main.cberr == DWFL_E_NOERROR + && m->debug.cberr == DWFL_E_NOERROR + && INTUSE(dwfl_module_getsymtab) (m) < 0) + { + Dwfl_Error err = m->main.cberr ?: m->debug.cberr; + if (err != DWFL_E_NO_SYMTAB) + return err; + } - for (size_t ndx = 1; ndx < m->syments; ++ndx) + struct dwfl_shared_file *m_shared = m->symfile->shared; + for (size_t ndx = 1; ndx < m_shared->syments; ++ndx) { - sym = gelf_getsymshndx (m->symdata, m->symxndxdata, + sym = gelf_getsymshndx (m_shared->symdata, + m_shared->symxndxdata, ndx, sym, &shndx); if (unlikely (sym == NULL)) return DWFL_E_LIBELF; @@ -253,9 +154,9 @@ resolve_symbol (Dwfl_Module *referer, struct reloc_symtab_cache *symtab, continue; /* Get this candidate symbol's name. */ - if (unlikely (sym->st_name >= m->symstrdata->d_size)) + if (unlikely (sym->st_name >= m_shared->symstrdata->d_size)) return DWFL_E_BADSTROFF; - const char *n = m->symstrdata->d_buf; + const char *n = m_shared->symstrdata->d_buf; n += sym->st_name; /* Does the name match? */ @@ -269,7 +170,7 @@ resolve_symbol (Dwfl_Module *referer, struct reloc_symtab_cache *symtab, /* 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, + return __libdwfl_relocate_value (m, m_shared->elf, &symshstrndx, shndx, &sym->st_value); } @@ -280,19 +181,21 @@ resolve_symbol (Dwfl_Module *referer, struct reloc_symtab_cache *symtab, } static Dwfl_Error -relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr, - size_t shstrndx, struct reloc_symtab_cache *reloc_symtab, +relocate_section (Dwfl_Module *mod, + struct dwfl_shared_file *relocated, + struct dwfl_shared_file *symfile, + const GElf_Ehdr *ehdr, size_t shstrndx, 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); + const char *tname = elf_strptr (relocated->elf, shstrndx, tshdr->sh_name); if (tname == NULL) return DWFL_E_LIBELF; - if (debugscn && ! ebl_debugscn_p (mod->ebl, tname)) + if (debugscn && ! ebl_debugscn_p (mod->main.shared->ebl, tname)) /* This relocation section is not for a debugging section. Nothing to do here. */ return DWFL_E_NOERROR; @@ -308,7 +211,7 @@ relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr, { /* First see if this is a reloc we can handle. If we are skipping it, don't bother resolving the symbol. */ - Elf_Type type = ebl_reloc_simple_type (mod->ebl, rtype); + Elf_Type type = ebl_reloc_simple_type (mod->main.shared->ebl, rtype); if (unlikely (type == ELF_T_NUM)) return DWFL_E_BADRELTYPE; @@ -327,19 +230,38 @@ relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr, { 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) + if (unlikely (gelf_getsymshndx (symfile->symdata, + symfile->symxndxdata, + symndx, &sym, &shndx) == NULL)) + return DWFL_E_LIBELF; + + if (sym.st_shndx != SHN_XINDEX) + shndx = sym.st_shndx; + + Dwfl_Error err = DWFL_E_NOERROR; + switch (shndx) { - /* Maybe we can figure it out anyway. */ - error = resolve_symbol (mod, reloc_symtab, &sym, shndx); - if (error != DWFL_E_NOERROR) - return error; + case SHN_ABS: + break; + case SHN_UNDEF: + case SHN_COMMON: + { + /* Maybe we can figure it out anyway. */ + err = resolve_symbol (mod, symfile, &sym, shndx); + break; + } + default: + { + err = __libdwfl_relocate_value (mod, relocated->elf, &shstrndx, + shndx, &sym.st_value); + break; + } } + if (unlikely (err != DWFL_E_NOERROR)) + return err; + value = sym.st_value; } @@ -401,7 +323,7 @@ relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr, else { /* Extract the original value and apply the reloc. */ - Elf_Data *d = gelf_xlatetom (relocated, &tmpdata, &rdata, + Elf_Data *d = gelf_xlatetom (relocated->elf, &tmpdata, &rdata, ehdr->e_ident[EI_DATA]); if (d == NULL) return DWFL_E_LIBELF; @@ -422,7 +344,7 @@ relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr, /* 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, + Elf_Data *s = gelf_xlatetof (relocated->elf, &rdata, &tmpdata, ehdr->e_ident[EI_DATA]); if (s == NULL) return DWFL_E_LIBELF; @@ -444,7 +366,7 @@ relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr, if (first_badreltype) { first_badreltype = false; - if (ebl_get_elfmachine (mod->ebl) == EM_NONE) + if (ebl_get_elfmachine (mod->main.shared->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; @@ -560,29 +482,50 @@ relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr, return result; } +static Dwfl_Error +find_relocation_symfile (Dwfl_Module *mod, + struct dwfl_shared_file *relocated, + struct dwfl_shared_file **symfile) +{ + Dwfl_Error err = __libdwfl_find_symtab (relocated); + if (err == DWFL_E_NOERROR) + *symfile = relocated; + else if (err == DWFL_E_NO_SYMTAB) + { + err = __libdwfl_find_symtab (mod->main.shared); + if (err == DWFL_E_NOERROR) + *symfile = relocated; + } + + return err; +} + Dwfl_Error internal_function -__libdwfl_relocate (Dwfl_Module *mod, Elf *debugfile, bool debug) +__libdwfl_relocate (Dwfl_Module *mod, struct dwfl_shared_file *debugfile, + bool debug) { assert (mod->e_type == ET_REL); GElf_Ehdr ehdr_mem; - const GElf_Ehdr *ehdr = gelf_getehdr (debugfile, &ehdr_mem); + const GElf_Ehdr *ehdr = gelf_getehdr (debugfile->elf, &ehdr_mem); if (ehdr == NULL) return DWFL_E_LIBELF; size_t d_shstrndx; - if (elf_getshstrndx (debugfile, &d_shstrndx) < 0) + if (elf_getshstrndx (debugfile->elf, &d_shstrndx) < 0) return DWFL_E_LIBELF; - RELOC_SYMTAB_CACHE (reloc_symtab); + struct dwfl_shared_file *symfile; + Dwfl_Error result = find_relocation_symfile (mod, debugfile, &symfile); + if (result != DWFL_E_NOERROR) + return result; /* 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) + && (scn = elf_nextscn (debugfile->elf, scn)) != NULL) { GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); @@ -592,12 +535,13 @@ __libdwfl_relocate (Dwfl_Module *mod, Elf *debugfile, bool debug) { /* It's a relocation section. */ - Elf_Scn *tscn = elf_getscn (debugfile, shdr->sh_info); + Elf_Scn *tscn = elf_getscn (debugfile->elf, 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, + result = relocate_section (mod, debugfile, symfile, + ehdr, d_shstrndx, + scn, shdr, tscn, debug, !debug); } } @@ -607,22 +551,29 @@ __libdwfl_relocate (Dwfl_Module *mod, Elf *debugfile, bool debug) Dwfl_Error internal_function -__libdwfl_relocate_section (Dwfl_Module *mod, Elf *relocated, +__libdwfl_relocate_section (Dwfl_Module *mod, + struct dwfl_shared_file *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_getshstrndx (relocated, &shstrndx) < 0) + if (elf_getshstrndx (relocated->elf, &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)); + Dwfl_Error error = __libdwfl_module_getebl (mod); + if (error != DWFL_E_NOERROR) + return error; + + struct dwfl_shared_file *symfile; + Dwfl_Error result = find_relocation_symfile (mod, relocated, &symfile); + if (result != DWFL_E_NOERROR) + return result; + + return relocate_section (mod, relocated, symfile, + gelf_getehdr (relocated->elf, &ehdr_mem), + shstrndx, + relocscn, gelf_getshdr (relocscn, &shdr_mem), + tscn, false, partial); } diff --git a/libelf/elf_getarhdr.c b/libelf/elf_getarhdr.c index 61e4e0a2..648bcbf7 100644 --- a/libelf/elf_getarhdr.c +++ b/libelf/elf_getarhdr.c @@ -76,8 +76,7 @@ elf_getarhdr (elf) } /* Make sure we have read the archive header. */ - if (parent->state.ar.elf_ar_hdr.ar_name == NULL - && __libelf_next_arhdr_wrlock (parent) != 0) + if (parent->state.ar.elf_ar_hdr.ar_name == NULL) { rwlock_wrlock (parent->lock); int st = __libelf_next_arhdr_wrlock (parent); diff --git a/tests/ChangeLog b/tests/ChangeLog index b1471c1b..5d82e594 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -29,6 +29,13 @@ * testfile47.bz2: New data file. * Makefile.am (TESTS, EXTRA_DIST): Add them. +2008-04-29 Petr Machata <pmachata@redhat.com> + + * run-debuginfo.sh: New file. + * debuginfo.c: New file. + * Makefile.am (noinst_PROGRAMS, TESTS, EXTRA_DIST): Add them. + (debuginfo_LDADD): New variable. + 2008-03-31 Roland McGrath <roland@redhat.com> * run-early-offscn.sh: New file. @@ -40,6 +47,20 @@ * run-addrname-test.sh: Add a new case. +2008-03-13 Petr Machata <pmachata@redhat.com> + + * run-relocate.sh: New test. + * relocate.c: New file. + +2008-03-13 Petr Machata <pmachata@redhat.com> + + * sharing1.c: Test feeding bogus file to libdwfl. + * run-sharing1.sh: Adjust to above. + +2008-03-05 Petr Machata <pmachata@redhat.com> + + * sharing1.c: Adjust for changes in libdwfl. + 2008-02-22 Roland McGrath <roland@redhat.com> * run-elflint-test.sh: Typo fix. @@ -163,6 +184,12 @@ * run-allregs.sh: Change expected output for powerpc spefscr. +2007-12-10 Petr Machata <pmachata@redhat.com> + + * run-sharing1.sh: New file. + * sharing1.c: New file. + * Makefile.am (noinst_PROGRAMS, EXTRA_DIST, TESTS): Add it. + 2007-10-20 Roland McGrath <roland@redhat.com> * run-dwfl-addr-sect.sh: Change expected output, no errors. diff --git a/tests/Makefile.am b/tests/Makefile.am index b533521c..c9d2bc47 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -60,7 +60,8 @@ noinst_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \ find-prologues funcretval allregs rdwrmmap \ dwfl-bug-addr-overflow arls dwfl-bug-fd-leak \ dwfl-addr-sect dwfl-bug-report early-offscn \ - dwfl-bug-getmodules + dwfl-bug-getmodules \ + sharing1 relocate debuginfo # get-ciefde asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \ asm-tst6 asm-tst7 asm-tst8 asm-tst9 @@ -86,6 +87,7 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \ dwfl-bug-fd-leak dwfl-bug-report \ run-dwfl-bug-offline-rel.sh run-dwfl-addr-sect.sh \ run-disasm-x86.sh run-disasm-x86-64.sh \ + sharing1 run-relocate.sh run-debuginfo.sh \ run-early-offscn.sh # run-show-ciefde.sh @@ -141,7 +143,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \ testfile44.S.bz2 testfile44.expect.bz2 run-disasm-x86.sh \ testfile45.S.bz2 testfile45.expect.bz2 run-disasm-x86-64.sh \ testfile46.bz2 testfile47.bz2 testfile48.bz2 testfile48.debug.bz2 \ - testfile49.bz2 + testfile49.bz2 run-relocate.sh installed_TESTS_ENVIRONMENT = libdir=$(DESTDIR)$(libdir) \ bindir=$(DESTDIR)$(bindir) \ @@ -233,6 +235,9 @@ dwfl_bug_report_LDADD = $(libdw) $(libebl) $(libelf) $(libmudflap) -ldl dwfl_bug_getmodules_LDADD = $(libdw) $(libebl) $(libelf) $(libmudflap) -ldl dwfl_addr_sect_LDADD = $(libdw) $(libebl) $(libelf) $(libmudflap) -ldl sha1_tst_LDADD = $(libeu) $(libmudflap) +sharing1_LDADD = $(libdw) $(libebl) $(libelf) $(libmudflap) -ldl +relocate_LDADD = $(libdw) $(libebl) $(libelf) $(libmudflap) -ldl +debuginfo_LDADD = $(libdw) $(libebl) $(libelf) $(libmudflap) -ldl CLEANFILES = xxx *.gcno *.gcda *gconv diff --git a/tests/debuginfo.c b/tests/debuginfo.c new file mode 100644 index 00000000..cf621ac7 --- /dev/null +++ b/tests/debuginfo.c @@ -0,0 +1,114 @@ +/* Copyright (C) 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. + + 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 ELFUTILS_HEADER(dwfl) + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +static char *filename = NULL; +static char *dfilename = NULL; +bool was_there = false; + +static char *debuginfo_path = NULL; + +static void +die (const char *message) +{ + fprintf (stderr, "%s\n", message); + exit (1); +} + +int +my_find_debuginfo (Dwfl_Module *mod, + void **userdata, + const char *modname, + GElf_Addr base, + const char *file_name, + const char *debuglink_file, + GElf_Word debuglink_crc, + char **debuginfo_file_name) +{ + int ret = dwfl_standard_find_debuginfo (mod, userdata, modname, base, + file_name, debuglink_file, + debuglink_crc, debuginfo_file_name); + + if (*debuginfo_file_name == NULL + || strcmp (*debuginfo_file_name, dfilename)) + die ("Unexpected debuginfo found."); + + was_there = true; + return ret; +} + +static const Dwfl_Callbacks my_callbacks = +{ + .find_debuginfo = my_find_debuginfo, + .debuginfo_path = &debuginfo_path, +}; + +int +main(int argc, char ** argv) +{ + if (argc != 3) + die ("Usage: debuginfo <binary> <matching debuginfo>\n"); + filename = argv[1]; + dfilename = argv[2]; + + Dwfl *dwfl = dwfl_begin (&my_callbacks); + if (dwfl == NULL) + die ("Couldn't create dwfl."); + dwfl_report_begin (dwfl); + Dwfl_Module *mod1 = dwfl_report_elf (dwfl, "mod1", filename, -1, 0); + if (mod1 == NULL) + die ("Couldn't create a module."); + dwfl_report_end (dwfl, NULL, NULL); + + const unsigned char *bits; + GElf_Addr vaddr; + GElf_Addr bias; + dwfl_module_getelf (mod1, &bias); + int bytes = dwfl_module_build_id (mod1, &bits, &vaddr); + if (bytes != 20) + die ("Expected 20 bytes of debuginfo."); + + Dwarf_Die *d = dwfl_module_nextcu (mod1, NULL, &bias); + if (!was_there) + die ("Suspicious: find_debuginfo hook not called."); + if (d == NULL) + die ("No dwarf die found in debuginfo."); + + dwfl_end (dwfl); + + return 0; +} diff --git a/tests/relocate.c b/tests/relocate.c new file mode 100644 index 00000000..2669a7c1 --- /dev/null +++ b/tests/relocate.c @@ -0,0 +1,109 @@ +/* Copyright (C) 2007,2008 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Petr Machata <pmachata@redhat.com>, 2007. + + 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>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include ELFUTILS_HEADER(dwfl) +#include <unistd.h> +#include <error.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <stdlib.h> + +static int my_find_elf (Dwfl_Module *, void **, const char *, Dwarf_Addr, + char **, Elf **); + +static const Dwfl_Callbacks my_callbacks = +{ + .find_elf = my_find_elf, +}; + +static int +my_find_elf (Dwfl_Module * mod __attribute__ ((unused)), + void ** userdata __attribute__ ((unused)), + const char * module_name __attribute__ ((unused)), + Dwarf_Addr addr __attribute__ ((unused)), + char ** file_name __attribute__ ((unused)), + Elf ** elf __attribute__ ((unused))) +{ + return 0; +} + +int +main(int argc, char ** argv) +{ + /* We use no threads here which can interfere with handling a stream. */ + (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER); + + /* Order is significant. If in doubt, check that libdwfl's + * resolve_symbol still gets hit: + * $ ftrace -sym=resolve_symbol ./relocate ~/testfile9 + * should give you roughly: + * 14395.14395 attached /home/ant/elfutils-mtn/build/tests/relocate + * 14395.14395 call libdw.so.1->resolve_symbol(0x96e6758, 0x96e6800, 0xbfa4b474, 0x0, 0xbfa4b470, 0xbfa4b4ac) = 12 + * 14395.14395 call libdw.so.1->resolve_symbol(0x96e6758, 0x96e6800, 0xbfa4b474, 0x0, 0xbfa4b470, 0xbfa4b4ac) = 12 + * 14395.14395 call libdw.so.1->resolve_symbol(0x96e6758, 0x96e6800, 0xbfa4b474, 0x0, 0xbfa4b470, 0xbfa4b4ac) = 12 + * 14395.14395 exited with status 0 + * + * Granted this test is very fragile, and depends a lot on knowledge + * of libdwfl internals. + */ + if (argc < 2) + exit(77); + char const *name = argv[1]; + char const *name2 = argv[0]; + + Dwfl *dwfl = dwfl_begin (&my_callbacks); + dwfl_report_begin (dwfl); + Dwfl_Module *module = dwfl_report_offline (dwfl, name, name, -1); + if (module == NULL) + { + fprintf (stderr, "DWFL error 1: %s\n", dwfl_errmsg (dwfl_errno ())); + exit (1); + } + Dwfl_Module *module2 = dwfl_report_offline (dwfl, name2, name2, -1); + if (module2 == NULL) + { + fprintf (stderr, "DWFL error 2: %s\n", dwfl_errmsg (dwfl_errno ())); + exit (1); + } + if (dwfl_report_end (dwfl, NULL, NULL) != 0) + { + fprintf (stderr, "DWFL error 3: %s\n", dwfl_errmsg (dwfl_errno ())); + exit (1); + } + Dwarf_Addr base; + Elf * elf = dwfl_module_getelf (module, &base); + if (elf == NULL) + { + fprintf (stderr, "DWFL error 4: %s\n", dwfl_errmsg (dwfl_errno ())); + exit (1); + } + + dwfl_end (dwfl); + exit(0); +} diff --git a/tests/run-debuginfo.sh b/tests/run-debuginfo.sh new file mode 100755 index 00000000..7a207911 --- /dev/null +++ b/tests/run-debuginfo.sh @@ -0,0 +1,34 @@ +#! /bin/sh +# Copyright (C) 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. +# +# 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>. + +. $srcdir/test-subr.sh + +files="testfile40 testfile40.debug" +testfiles $files + +export LC_ALL=C +testrun ./debuginfo $files + +exit 0 diff --git a/tests/run-relocate.sh b/tests/run-relocate.sh new file mode 100755 index 00000000..0929ed17 --- /dev/null +++ b/tests/run-relocate.sh @@ -0,0 +1,34 @@ +#! /bin/sh +# Copyright (C) 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. +# +# 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>. + +. $srcdir/test-subr.sh + +files=testfile9 +testfiles $files + +export LC_ALL=C +testrun ./relocate $files + +exit 0 diff --git a/tests/sharing1.c b/tests/sharing1.c new file mode 100644 index 00000000..7b03ce9b --- /dev/null +++ b/tests/sharing1.c @@ -0,0 +1,178 @@ +/* Copyright (C) 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Petr Machata <pmachata@redhat.com>, 2007. + + 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>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include ELFUTILS_HEADER(dwflP) +#include <unistd.h> +#include <error.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <string.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +static int bogus_find_elf (Dwfl_Module *, void **, + const char *, Dwarf_Addr, + char **, Elf **); + +static char *debuginfo_path; +static const Dwfl_Callbacks proc_callbacks = +{ + .find_debuginfo = dwfl_standard_find_debuginfo, + .debuginfo_path = &debuginfo_path, + + .find_elf = dwfl_linux_proc_find_elf, +}; + +static const Dwfl_Callbacks bogus_callbacks = +{ + .find_debuginfo = dwfl_standard_find_debuginfo, + .debuginfo_path = &debuginfo_path, + + .find_elf = bogus_find_elf, +}; + +struct testdata +{ + Dwfl_Module *module; + char *name; + Elf *elf; +}; + + +static int +bogus_find_elf (Dwfl_Module *mod __attribute__ ((unused)), + void **userdata __attribute__ ((unused)), + const char *modname __attribute__ ((unused)), + Dwarf_Addr base __attribute__ ((unused)), + char ** file_name __attribute__ ((unused)), + Elf ** elf __attribute__ ((unused))) +{ + char * fn = strdup ("."); + *file_name = fn; + int fd = open(fn, O_RDONLY); + return fd; +} + +int +each_module (Dwfl_Module *module, + void **userdata __attribute__ ((unused)), + const char *name, + Dwarf_Addr base __attribute__ ((unused)), + void *arg) +{ + struct testdata *t = (struct testdata *)arg; + + GElf_Addr bias; + Elf *elf = dwfl_module_getelf (module, &bias); + + if (elf == NULL + || dwfl_module_getsymtab (module) <= 0) + return DWARF_CB_OK; /* Continue iteration until first ordinary + mapping with symbol table is found. */ + else + { + t->name = strdup (name); + t->elf = elf; + t->module = module; + return DWARF_CB_ABORT; + } +} + +Dwfl * +initdwfl (pid_t pid, struct testdata *t, Dwfl_Callbacks const *cb, bool check) +{ + Dwfl *dwfl = dwfl_begin (cb); + if (dwfl == NULL) + error (2, 0, "dwfl_begin: %s", dwfl_errmsg (-1)); + + int result = dwfl_linux_proc_report (dwfl, pid); + if (result < 0) + error (2, 0, "dwfl_linux_proc_report: %s", dwfl_errmsg (-1)); + else if (result > 0) + error (2, result, "dwfl_linux_proc_report"); + + if (dwfl_report_end (dwfl, NULL, NULL) != 0) + error (2, 0, "dwfl_report_end: %s", dwfl_errmsg (-1)); + + t->module = NULL; + t->elf = NULL; + t->name = NULL; + dwfl_getmodules (dwfl, each_module, (void*)t, 0); + if (check && (t->elf == NULL || t->name == NULL || t->module == NULL)) + error (2, 0, "couldn't init testdata."); + + return dwfl; +} + +int +proc_test (void) +{ + struct testdata ta; + Dwfl *a = initdwfl (getpid(), &ta, &proc_callbacks, true); + + struct testdata tb; + Dwfl *b = initdwfl (getpid(), &tb, &proc_callbacks, true); + + if (ta.module == tb.module) + error (2, 0, "Strange. Both dwfls contain the same module?"); + + if (strcmp (ta.name, tb.name)) + error (2, 0, "ELF files from different files."); + + if (ta.elf != tb.elf) + error (2, 0, "ELF files not shared."); + + if (ta.module->symfile->shared->symdata != tb.module->symfile->shared->symdata) + error (2, 0, "Symdata not shared."); + + dwfl_end (a); + dwfl_end (b); + return 0; +} + +int +bogus_test (void) +{ + struct testdata ta; + Dwfl *a = initdwfl (getpid(), &ta, &bogus_callbacks, false); + dwfl_end (a); + return 0; +} + +int +main (void) +{ + /* We use no threads here which can interfere with handling a stream. */ + (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER); + + return proc_test () + | bogus_test (); +} |