diff options
-rw-r--r-- | libdwfl/ChangeLog | 27 | ||||
-rw-r--r-- | libdwfl/dwfl_module.c | 1 | ||||
-rw-r--r-- | libdwfl/dwfl_module_addrsym.c | 31 | ||||
-rw-r--r-- | libdwfl/dwfl_module_getdwarf.c | 186 | ||||
-rw-r--r-- | libdwfl/dwfl_module_getsym.c | 61 | ||||
-rw-r--r-- | libdwfl/dwfl_module_info.c | 2 | ||||
-rw-r--r-- | libdwfl/libdwflP.h | 41 | ||||
-rw-r--r-- | libdwfl/relocate.c | 3 |
8 files changed, 314 insertions, 38 deletions
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog index 62973368..828db083 100644 --- a/libdwfl/ChangeLog +++ b/libdwfl/ChangeLog @@ -1,3 +1,30 @@ +2013-01-16 Mark Wielaard <mjw@redhat.com> + + * libdwflP.h (struct Dwfl_Module): Add aux_sym, aux_symdata, + aux_syments, aux_symstrdata, aux_symxndxdata and aux_first_global. + (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. + (load_symtab): Always set first_global. + * dwfl_module_addrsym.c (dwfl_module_addrsym): Check symfile + when using same_section. Calculate first_global based on both + mod->first_global and mod->aux_first_global. + * 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, making sure all locals + from any table come before any globals. + * dwfl_module_info.c (dwfl_module_info): Call dwfl_adjusted_st_value + with symfile. + * relocate.c (resolve_symbol): Likewise. + 2013-01-07 Roland McGrath <roland@hack.frob.com> * link_map.c (auxv_format_probe): Handle unaligned 64-bit data, but diff --git a/libdwfl/dwfl_module.c b/libdwfl/dwfl_module.c index e703d27e..f914b3a3 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 fdc95fc0..d2059ea4 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 @@ -168,17 +173,19 @@ dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr, } } - /* First go through global symbols. mod->first_global is setup by - dwfl_module_getsymtab to the index of the first global symbol in - the module's symbol table, or -1 when unknown. All symbols with - local binding come first in the symbol table, then all globals. */ - search_table (mod->first_global < 0 ? 1 : mod->first_global, syments); + /* First go through global symbols. mod->first_global and + mod->aux_first_global are setup by dwfl_module_getsymtab to the + index of the first global symbol in the module's symbol table. Both + are zero when unknown. All symbols with local binding come first in + the symbol table, then all globals. */ + int first_global = mod->first_global + mod->aux_first_global - 1; + search_table (first_global < 0 ? 1 : first_global, syments); /* If we found nothing searching the global symbols, then try the locals. Unless we have a global sizeless symbol that matches exactly. */ - if (closest_name == NULL && mod->first_global > 1 + if (closest_name == NULL && first_global > 1 && (sizeless_name == NULL || sizeless_sym.st_value != addr)) - search_table (1, mod->first_global); + search_table (1, first_global); /* If we found no proper sized symbol to use, fall back to the best candidate sizeless symbol we found, if any. */ diff --git a/libdwfl/dwfl_module_getdwarf.c b/libdwfl/dwfl_module_getdwarf.c index 025cb8ac..ffbe5898 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; } @@ -579,6 +580,7 @@ load_symtab (struct dwfl_file *file, struct dwfl_file **symfile, *symfile = file; *strshndx = shdr->sh_link; *syments = shdr->sh_size / shdr->sh_entsize; + *first_global = shdr->sh_info; break; case SHT_SYMTAB_SHNDX: @@ -814,6 +816,135 @@ 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 - 1; + mod->aux_first_global = shdr->sh_info; + 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) @@ -827,11 +958,10 @@ find_symtab (Dwfl_Module *mod) if (mod->symerr != DWFL_E_NOERROR) return; - mod->first_global = -1; /* Unknown, unless explicitly set by load_symtab. */ - /* First see if the main ELF file has the debugging information. */ Elf_Scn *symscn = NULL, *xndxscn = NULL; - GElf_Word strshndx; + 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 +1005,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 +1023,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 +1045,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 +1233,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 9103380e..3e4d9f61 100644 --- a/libdwfl/dwfl_module_getsym.c +++ b/libdwfl/dwfl_module_getsym.c @@ -1,5 +1,5 @@ /* Find debugging and symbol information for a module in libdwfl. - Copyright (C) 2006-2010 Red Hat, Inc. + Copyright (C) 2006-2013 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -42,8 +42,55 @@ dwfl_module_getsym (Dwfl_Module *mod, int ndx, return NULL; } + /* All local symbols should come before all global symbols. If we + have an auxiliary table make sure all the main locals come first, + then all aux locals, then all main globals and finally all aux globals. + And skip the auxiliary table zero undefined entry. */ GElf_Word shndx; - sym = gelf_getsymshndx (mod->symdata, mod->symxndxdata, ndx, sym, &shndx); + int tndx = ndx; + struct dwfl_file *file; + Elf_Data *symdata; + Elf_Data *symxndxdata; + Elf_Data *symstrdata; + if (mod->aux_symdata == NULL + || ndx < mod->first_global) + { + /* main symbol table (locals). */ + tndx = ndx; + file = mod->symfile; + symdata = mod->symdata; + symxndxdata = mod->symxndxdata; + symstrdata = mod->symstrdata; + } + else if (ndx < mod->first_global + mod->aux_first_global - 1) + { + /* aux symbol table (locals). */ + tndx = ndx - mod->first_global + 1; + file = &mod->aux_sym; + symdata = mod->aux_symdata; + symxndxdata = mod->aux_symxndxdata; + symstrdata = mod->aux_symstrdata; + } + else if ((size_t) ndx < mod->syments + mod->aux_first_global - 1) + { + /* main symbol table (globals). */ + tndx = ndx - mod->aux_first_global + 1; + file = mod->symfile; + symdata = mod->symdata; + symxndxdata = mod->symxndxdata; + symstrdata = mod->symstrdata; + } + else + { + /* aux symbol table (globals). */ + tndx = ndx - mod->syments + 1; + file = &mod->aux_sym; + symdata = mod->aux_symdata; + symxndxdata = mod->aux_symxndxdata; + symstrdata = mod->aux_symstrdata; + } + sym = gelf_getsymshndx (symdata, symxndxdata, tndx, sym, &shndx); + if (unlikely (sym == NULL)) { __libdwfl_seterrno (DWFL_E_LIBELF); @@ -60,7 +107,7 @@ dwfl_module_getsym (Dwfl_Module *mod, int ndx, || (sym->st_shndx < SHN_LORESERVE && sym->st_shndx != SHN_UNDEF))) { GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (elf_getscn (mod->symfile->elf, shndx), + GElf_Shdr *shdr = gelf_getshdr (elf_getscn (file->elf, shndx), &shdr_mem); alloc = unlikely (shdr == NULL) || (shdr->sh_flags & SHF_ALLOC); } @@ -82,7 +129,7 @@ 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, + Dwfl_Error result = __libdwfl_relocate_value (mod, file->elf, &symshstrndx, shndx, &sym->st_value); if (unlikely (result != DWFL_E_NOERROR)) @@ -93,15 +140,15 @@ 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, file, sym->st_value); break; } - if (unlikely (sym->st_name >= mod->symstrdata->d_size)) + 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 e14002c1..fdb4202a 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 806ebcd9..5aaa7785 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,15 @@ 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. */ + int aux_first_global; /* Index of first global of aux_sym 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 +260,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 2c24bd5a..e06819d0 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; } |