Add an auxiliary symbol table dwfl_file aux_sym to Dwfl_Module if all we have is the dynsym table. The main file might contain a .gnu_debugdata section. The .gnu_debugdata section is a compressed embedded ELF file that contains the text (code) section symbols from the symtab table that are not in the main file dynsym table. dwfl_module_getsymtab (), dwfl_module_addrsym () and dwfl_module_getsym () will use the auxiliary symbol table when available (and no full symtab is available from the debug file).
* libdwflP.h (struct Dwfl_Module): Add aux_sym, aux_symdata, aux_syments, aux_symstrdata and aux_symxndxdata. (dwfl_adjusted_aux_sym_addr): New function. (dwfl_deadjust_aux_sym_addr): Likewise. (dwfl_adjusted_st_value): Take and check symfile argument. (dwfl_deadjust_st_value): Likewise. * dwfl_module_getdwarf.c (find_prelink_address_sync): Take and use dwfl_file as argument to set address_sync. (find_debuginfo): Call find_prelink_address_sync with debug file. (find_aux_sym): New function. (find_symtab): Use find_aux_sym if all we have is the dynsym table and fill in aux DwflModule fields. (dwfl_module_getsymtab): Return syments plus aux_syments. * dwfl_module_addrsym.c (dwfl_module_addrsym): Check symfile when using same_section. * dwfl_module.c (__libdwfl_module_free): Free aux_sym. * dwfl_module_getsym.c (dwfl_module_getsym): Use auxsym table to retrieve symbol and name if necessary. * dwfl_module_info.c (dwfl_module_info): Call dwfl_adjusted_st_value with symfile. * relocate.c (resolve_symbol): Likewise. https://fedoraproject.org/wiki/Features/MiniDebugInfo Signed-off-by: Mark Wielaard <[email protected]> --- libdwfl/ChangeLog | 24 +++++ libdwfl/dwfl_module.c | 1 + libdwfl/dwfl_module_addrsym.c | 15 ++- libdwfl/dwfl_module_getdwarf.c | 186 ++++++++++++++++++++++++++++++++++++++-- libdwfl/dwfl_module_getsym.c | 21 ++++- libdwfl/dwfl_module_info.c | 2 +- libdwfl/libdwflP.h | 40 +++++++-- libdwfl/relocate.c | 3 +- 8 files changed, 266 insertions(+), 26 deletions(-) diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog index 896ae39..49e3df6 100644 --- a/libdwfl/ChangeLog +++ b/libdwfl/ChangeLog @@ -1,3 +1,27 @@ +2012-12-21 Mark Wielaard <[email protected]> + + * libdwflP.h (struct Dwfl_Module): Add aux_sym, aux_symdata, + aux_syments, aux_symstrdata and aux_symxndxdata. + (dwfl_adjusted_aux_sym_addr): New function. + (dwfl_deadjust_aux_sym_addr): Likewise. + (dwfl_adjusted_st_value): Take and check symfile argument. + (dwfl_deadjust_st_value): Likewise. + * dwfl_module_getdwarf.c (find_prelink_address_sync): Take and + use dwfl_file as argument to set address_sync. + (find_debuginfo): Call find_prelink_address_sync with debug file. + (find_aux_sym): New function. + (find_symtab): Use find_aux_sym if all we have is the dynsym table + and fill in aux DwflModule fields. + (dwfl_module_getsymtab): Return syments plus aux_syments. + * dwfl_module_addrsym.c (dwfl_module_addrsym): Check symfile + when using same_section. + * dwfl_module.c (__libdwfl_module_free): Free aux_sym. + * dwfl_module_getsym.c (dwfl_module_getsym): Use auxsym table + to retrieve symbol and name if necessary. + * dwfl_module_info.c (dwfl_module_info): Call dwfl_adjusted_st_value + with symfile. + * relocate.c (resolve_symbol): Likewise. + 2012-12-11 Mark Wielaard <[email protected]> * linux-kernel-modules.c (report_kernel): Only free fname if diff --git a/libdwfl/dwfl_module.c b/libdwfl/dwfl_module.c index e703d27..f914b3a 100644 --- a/libdwfl/dwfl_module.c +++ b/libdwfl/dwfl_module.c @@ -79,6 +79,7 @@ __libdwfl_module_free (Dwfl_Module *mod) if (mod->debug.elf != mod->main.elf) free_file (&mod->debug); free_file (&mod->main); + free_file (&mod->aux_sym); if (mod->build_id_bits != NULL) free (mod->build_id_bits); diff --git a/libdwfl/dwfl_module_addrsym.c b/libdwfl/dwfl_module_addrsym.c index fdc95fc..4cb0aab 100644 --- a/libdwfl/dwfl_module_addrsym.c +++ b/libdwfl/dwfl_module_addrsym.c @@ -1,5 +1,5 @@ /* Find debugging and symbol information for a module in libdwfl. - Copyright (C) 2005-2011 Red Hat, Inc. + Copyright (C) 2005-2012 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -41,7 +41,8 @@ dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr, /* Return true iff we consider ADDR to lie in the same section as SYM. */ GElf_Word addr_shndx = SHN_UNDEF; - inline bool same_section (const GElf_Sym *sym, GElf_Word shndx) + inline bool same_section (const GElf_Sym *sym, struct dwfl_file *symfile, + GElf_Word shndx) { /* For absolute symbols and the like, only match exactly. */ if (shndx >= SHN_LORESERVE) @@ -50,10 +51,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 = dwfl_deadjust_st_value (mod, addr); + GElf_Addr mod_addr = dwfl_deadjust_st_value (mod, symfile, addr); Elf_Scn *scn = NULL; addr_shndx = SHN_ABS; - while ((scn = elf_nextscn (mod->symfile->elf, scn)) != NULL) + while ((scn = elf_nextscn (symfile->elf, scn)) != NULL) { GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); @@ -135,7 +136,11 @@ dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr, } else if (closest_name == NULL && sym.st_value >= min_label - && same_section (&sym, shndx)) + && same_section (&sym, + ((size_t) i < mod->syments + ? mod->symfile + : &mod->aux_sym), + shndx)) { /* Handwritten assembly symbols sometimes have no st_size. If no symbol with proper size includes diff --git a/libdwfl/dwfl_module_getdwarf.c b/libdwfl/dwfl_module_getdwarf.c index 025cb8a..48b9b57 100644 --- a/libdwfl/dwfl_module_getdwarf.c +++ b/libdwfl/dwfl_module_getdwarf.c @@ -1,5 +1,5 @@ /* Find debugging and symbol information for a module in libdwfl. - Copyright (C) 2005-2011 Red Hat, Inc. + Copyright (C) 2005-2012 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -31,6 +31,7 @@ #include <string.h> #include <unistd.h> #include "../libdw/libdwP.h" /* DWARF_E_* values are here. */ +#include "../libelf/libelfP.h" /* Open libelf FILE->fd and compute the load base of ELF as loaded in MOD. @@ -285,7 +286,7 @@ find_debuglink (Elf *elf, GElf_Word *crc) looked like before prelink juggled them--when they still had a direct correspondence to the debug file. */ static Dwfl_Error -find_prelink_address_sync (Dwfl_Module *mod) +find_prelink_address_sync (Dwfl_Module *mod, struct dwfl_file *file) { /* The magic section is only identified by name. */ size_t shstrndx; @@ -512,8 +513,8 @@ find_prelink_address_sync (Dwfl_Module *mod) consider_shdr (undo_interp, shdr.s64[i].sh_type, shdr.s64[i].sh_flags, shdr.s64[i].sh_addr, shdr.s64[i].sh_size); - if (highest > mod->debug.vaddr) - mod->debug.address_sync = highest; + if (highest > file->vaddr) + file->address_sync = highest; else return DWFL_E_BAD_PRELINK; } @@ -539,7 +540,7 @@ find_debuginfo (Dwfl_Module *mod) &mod->debug.name); Dwfl_Error result = open_elf (mod, &mod->debug); if (result == DWFL_E_NOERROR && mod->debug.address_sync != 0) - result = find_prelink_address_sync (mod); + result = find_prelink_address_sync (mod, &mod->debug); return result; } @@ -814,6 +815,138 @@ find_dynsym (Dwfl_Module *mod) } } +/* Try to find the auxiliary symbol table embedded in the main elf file + section .gnu_debugdata. Only matters if the symbol information comes + from the main file dynsym. No harm done if not found. */ +static void +find_aux_sym (Dwfl_Module *mod __attribute__ ((unused)), + Elf_Scn **aux_symscn __attribute__ ((unused)), + Elf_Scn **aux_xndxscn __attribute__ ((unused)), + GElf_Word *aux_strshndx __attribute__ ((unused))) +{ + /* Since a .gnu_debugdata section is compressed using lzma don't do + anything unless we have support for that. */ +#if USE_LZMA + Elf *elf = mod->main.elf; + + size_t shstrndx; + if (elf_getshdrstrndx (elf, &shstrndx) < 0) + return; + + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + return; + + const char *name = elf_strptr (elf, shstrndx, shdr->sh_name); + if (name == NULL) + return; + + if (!strcmp (name, ".gnu_debugdata")) + break; + } + + if (scn == NULL) + return; + + /* Found the .gnu_debugdata section. Uncompress the lzma image and + turn it into an ELF image. */ + Elf_Data *rawdata = elf_rawdata (scn, NULL); + if (rawdata == NULL) + return; + + Dwfl_Error error; + void *buffer = NULL; + size_t size = 0; + error = __libdw_unlzma (-1, 0, rawdata->d_buf, rawdata->d_size, + &buffer, &size); + if (error == DWFL_E_NOERROR) + { + if (unlikely (size == 0)) + free (buffer); + else + { + mod->aux_sym.elf = elf_memory (buffer, size); + if (mod->aux_sym.elf == NULL) + free (buffer); + else + { + mod->aux_sym.fd = -1; + mod->aux_sym.elf->flags |= ELF_F_MALLOCED; + if (open_elf (mod, &mod->aux_sym) != DWFL_E_NOERROR) + return; + /* Don't trust the phdrs in the minisymtab elf file to be + setup correctly. The address_sync is equal to the main + file it is embedded in at first. The shdrs are setup + OK to make find_prelink_address_sync () do the right + thing if necessary though. */ + mod->aux_sym.address_sync = mod->main.address_sync; + if (mod->aux_sym.address_sync != 0) + { + error = find_prelink_address_sync (mod, &mod->aux_sym); + if (error != DWFL_E_NOERROR) + { + elf_end (mod->aux_sym.elf); + mod->aux_sym.elf = NULL; + return; + } + } + + /* So far, so good. Get minisymtab table data and cache it. */ + bool minisymtab = false; + scn = NULL; + while ((scn = elf_nextscn (mod->aux_sym.elf, scn)) != NULL) + { + GElf_Shdr shdr_mem, *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr != NULL) + switch (shdr->sh_type) + { + case SHT_SYMTAB: + minisymtab = true; + *aux_symscn = scn; + *aux_strshndx = shdr->sh_link; + mod->aux_syments = shdr->sh_size / shdr->sh_entsize; + /* Combining two tables means we can no longer + easily optimize based on first global. + Pretend we never saw the hint. */ + mod->first_global = -1; + if (*aux_xndxscn != NULL) + return; + break; + + case SHT_SYMTAB_SHNDX: + *aux_xndxscn = scn; + if (minisymtab) + return; + break; + + default: + break; + } + } + + if (minisymtab) + /* We found one, though no SHT_SYMTAB_SHNDX to go with it. */ + return; + + /* We found no SHT_SYMTAB, so everything else is bogus. */ + *aux_xndxscn = NULL; + *aux_strshndx = 0; + mod->aux_syments = 0; + elf_end (mod->aux_sym.elf); + mod->aux_sym.elf = NULL; + return; + } + } + } + else + free (buffer); +#endif +} + /* Try to find a symbol table in either MOD->main.elf or MOD->debug.elf. */ static void find_symtab (Dwfl_Module *mod) @@ -831,7 +964,8 @@ find_symtab (Dwfl_Module *mod) /* First see if the main ELF file has the debugging information. */ Elf_Scn *symscn = NULL, *xndxscn = NULL; - GElf_Word strshndx; + Elf_Scn *aux_symscn = NULL, *aux_xndxscn = NULL; + GElf_Word strshndx, aux_strshndx = 0; mod->symerr = load_symtab (&mod->main, &mod->symfile, &symscn, &xndxscn, &mod->syments, &mod->first_global, &strshndx); @@ -875,6 +1009,9 @@ find_symtab (Dwfl_Module *mod) { /* We still have the dynamic symbol table. */ mod->symerr = DWFL_E_NOERROR; + + /* The dynsym table might be extended by an auxiliary table. */ + find_aux_sym (mod, &aux_symscn, &aux_xndxscn, &aux_strshndx); break; } @@ -890,7 +1027,7 @@ find_symtab (Dwfl_Module *mod) { elferr: mod->symerr = DWFL_E (LIBELF, elf_errno ()); - return; + goto aux_cleanup; } /* Cache the data; MOD->syments and MOD->first_global were set above. */ @@ -912,6 +1049,39 @@ find_symtab (Dwfl_Module *mod) mod->symdata = elf_getdata (symscn, NULL); if (mod->symdata == NULL) goto elferr; + + /* Cache any auxiliary symbol info, when it fails, just ignore aux_sym. */ + if (aux_symscn != NULL) + { + /* This does some sanity checks on the string table section. */ + if (elf_strptr (mod->aux_sym.elf, aux_strshndx, 0) == NULL) + { + aux_cleanup: + mod->aux_syments = 0; + elf_end (mod->aux_sym.elf); + mod->aux_sym.elf = NULL; + return; + } + + mod->aux_symstrdata = elf_getdata (elf_getscn (mod->aux_sym.elf, + aux_strshndx), + NULL); + if (mod->aux_symstrdata == NULL) + goto aux_cleanup; + + if (aux_xndxscn == NULL) + mod->aux_symxndxdata = NULL; + else + { + mod->aux_symxndxdata = elf_getdata (xndxscn, NULL); + if (mod->aux_symxndxdata == NULL) + goto aux_cleanup; + } + + mod->aux_symdata = elf_getdata (aux_symscn, NULL); + if (mod->aux_symdata == NULL) + goto aux_cleanup; + } } @@ -1067,7 +1237,7 @@ dwfl_module_getsymtab (Dwfl_Module *mod) find_symtab (mod); if (mod->symerr == DWFL_E_NOERROR) - return mod->syments; + return mod->syments + mod->aux_syments; __libdwfl_seterrno (mod->symerr); return -1; diff --git a/libdwfl/dwfl_module_getsym.c b/libdwfl/dwfl_module_getsym.c index 9103380..27eb22d 100644 --- a/libdwfl/dwfl_module_getsym.c +++ b/libdwfl/dwfl_module_getsym.c @@ -43,7 +43,14 @@ dwfl_module_getsym (Dwfl_Module *mod, int ndx, } GElf_Word shndx; - sym = gelf_getsymshndx (mod->symdata, mod->symxndxdata, ndx, sym, &shndx); + if ((size_t) ndx < mod->syments) + sym = gelf_getsymshndx (mod->symdata, mod->symxndxdata, ndx, sym, &shndx); + else if (mod->aux_symdata != NULL) + sym = gelf_getsymshndx (mod->aux_symdata, mod->aux_symxndxdata, + ndx - mod->syments, sym, &shndx); + else + sym = NULL; + if (unlikely (sym == NULL)) { __libdwfl_seterrno (DWFL_E_LIBELF); @@ -93,15 +100,21 @@ dwfl_module_getsym (Dwfl_Module *mod, int ndx, } else if (alloc) /* Apply the bias to the symbol value. */ - sym->st_value = dwfl_adjusted_st_value (mod, sym->st_value); + sym->st_value = dwfl_adjusted_st_value (mod, + ((size_t) ndx < mod->syments + ? mod->symfile + : &mod->aux_sym), + sym->st_value); break; } - if (unlikely (sym->st_name >= mod->symstrdata->d_size)) + Elf_Data *symstrdata = ((size_t) ndx < mod->syments + ? mod->symstrdata : mod->aux_symstrdata); + if (unlikely (sym->st_name >= symstrdata->d_size)) { __libdwfl_seterrno (DWFL_E_BADSTROFF); return NULL; } - return (const char *) mod->symstrdata->d_buf + sym->st_name; + return (const char *) 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 e14002c..fdb4202 100644 --- a/libdwfl/dwfl_module_info.c +++ b/libdwfl/dwfl_module_info.c @@ -49,7 +49,7 @@ dwfl_module_info (Dwfl_Module *mod, void ***userdata, : dwfl_adjusted_dwarf_addr (mod, 0)); if (symbias) *symbias = (mod->symfile == NULL ? (Dwarf_Addr) -1 - : dwfl_adjusted_st_value (mod, 0)); + : dwfl_adjusted_st_value (mod, mod->symfile, 0)); if (mainfile) *mainfile = mod->main.name; diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h index 806ebcd..8b0c85d 100644 --- a/libdwfl/libdwflP.h +++ b/libdwfl/libdwflP.h @@ -142,7 +142,7 @@ struct Dwfl_Module char *name; /* Iterator name for this module. */ GElf_Addr low_addr, high_addr; - struct dwfl_file main, debug; + struct dwfl_file main, debug, aux_sym; GElf_Addr main_bias; Ebl *ebl; GElf_Half e_type; /* GElf_Ehdr.e_type cache. */ @@ -152,10 +152,14 @@ struct Dwfl_Module struct dwfl_file *symfile; /* Either main or debug. */ Elf_Data *symdata; /* Data in the ELF symbol table section. */ + Elf_Data *aux_symdata; /* Data in the auxiliary ELF symbol table. */ size_t syments; /* sh_size / sh_entsize of that section. */ + size_t aux_syments; /* sh_size / sh_entsize of aux_sym section. */ int first_global; /* Index of first global symbol of table. */ Elf_Data *symstrdata; /* Data for its string table. */ + Elf_Data *aux_symstrdata; /* Data for aux_sym string table. */ Elf_Data *symxndxdata; /* Data in the extended section index table. */ + Elf_Data *aux_symxndxdata; /* Data in the extended auxiliary table. */ Dwarf *dw; /* libdw handle for its debugging info. */ @@ -255,20 +259,42 @@ dwfl_deadjust_dwarf_addr (Dwfl_Module *mod, Dwarf_Addr addr) + mod->debug.address_sync); } +static inline Dwarf_Addr +dwfl_adjusted_aux_sym_addr (Dwfl_Module *mod, Dwarf_Addr addr) +{ + return dwfl_adjusted_address (mod, (addr + - mod->aux_sym.address_sync + + mod->main.address_sync)); +} + +static inline Dwarf_Addr +dwfl_deadjust_aux_sym_addr (Dwfl_Module *mod, Dwarf_Addr addr) +{ + return (dwfl_deadjust_address (mod, addr) + - mod->main.address_sync + + mod->aux_sym.address_sync); +} + static inline GElf_Addr -dwfl_adjusted_st_value (Dwfl_Module *mod, GElf_Addr addr) +dwfl_adjusted_st_value (Dwfl_Module *mod, struct dwfl_file *symfile, + GElf_Addr addr) { - if (mod->symfile == &mod->main) + if (symfile == &mod->main) return dwfl_adjusted_address (mod, addr); - return dwfl_adjusted_dwarf_addr (mod, addr); + if (symfile == &mod->debug) + return dwfl_adjusted_dwarf_addr (mod, addr); + return dwfl_adjusted_aux_sym_addr (mod, addr); } static inline GElf_Addr -dwfl_deadjust_st_value (Dwfl_Module *mod, GElf_Addr addr) +dwfl_deadjust_st_value (Dwfl_Module *mod, struct dwfl_file *symfile, + GElf_Addr addr) { - if (mod->symfile == &mod->main) + if (symfile == &mod->main) return dwfl_deadjust_address (mod, addr); - return dwfl_deadjust_dwarf_addr (mod, addr); + if (symfile == &mod->debug) + return dwfl_deadjust_dwarf_addr (mod, addr); + return dwfl_deadjust_aux_sym_addr (mod, addr); } /* This describes a contiguous address range that lies in a single CU. diff --git a/libdwfl/relocate.c b/libdwfl/relocate.c index 2c24bd5..e06819d 100644 --- a/libdwfl/relocate.c +++ b/libdwfl/relocate.c @@ -254,7 +254,8 @@ resolve_symbol (Dwfl_Module *referer, struct reloc_symtab_cache *symtab, if (m->e_type != ET_REL) { - sym->st_value = dwfl_adjusted_st_value (m, sym->st_value); + sym->st_value = dwfl_adjusted_st_value (m, m->symfile, + sym->st_value); return DWFL_E_NOERROR; } -- 1.7.1 _______________________________________________ elfutils-devel mailing list [email protected] https://lists.fedorahosted.org/mailman/listinfo/elfutils-devel
