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 <mjw(a)redhat.com>
---
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 <mjw(a)redhat.com>
+
+ * 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 <mjw(a)redhat.com>
* 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