summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--backends/Makefile.am2
-rw-r--r--backends/ppc64_get_symbol.c264
-rw-r--r--backends/ppc64_init.c3
-rw-r--r--libdwfl/dwfl_module_addrsym.c21
-rw-r--r--libdwfl/dwfl_module_getdwarf.c18
-rw-r--r--libdwfl/dwfl_module_getsym.c48
-rw-r--r--libdwfl/libdwflP.h5
-rw-r--r--libebl/Makefile.am2
-rw-r--r--libebl/ebl-hooks.h9
-rw-r--r--libebl/eblgetsymbol.c57
-rw-r--r--libebl/libebl.h28
-rw-r--r--libebl/libeblP.h3
-rw-r--r--tests/Makefile.am3
-rw-r--r--tests/dwflsyms.c9
-rwxr-xr-xtests/run-addrname-test.sh39
-rwxr-xr-xtests/run-dwflsyms.sh100
-rwxr-xr-xtests/testfile66.bz2bin0 -> 741 bytes
-rw-r--r--tests/testfile66.core.bz2bin0 -> 56448 bytes
18 files changed, 586 insertions, 25 deletions
diff --git a/backends/Makefile.am b/backends/Makefile.am
index 9bca9941..40460ae7 100644
--- a/backends/Makefile.am
+++ b/backends/Makefile.am
@@ -101,7 +101,7 @@ am_libebl_ppc_pic_a_OBJECTS = $(ppc_SRCS:.c=.os)
ppc64_SRCS = ppc64_init.c ppc64_symbol.c ppc64_retval.c \
ppc64_corenote.c ppc_regs.c ppc_auxv.c ppc_attrs.c ppc_syscall.c \
- ppc_cfi.c
+ ppc_cfi.c ppc64_get_symbol.c
libebl_ppc64_pic_a_SOURCES = $(ppc64_SRCS)
am_libebl_ppc64_pic_a_OBJECTS = $(ppc64_SRCS:.c=.os)
diff --git a/backends/ppc64_get_symbol.c b/backends/ppc64_get_symbol.c
new file mode 100644
index 00000000..4be84b10
--- /dev/null
+++ b/backends/ppc64_get_symbol.c
@@ -0,0 +1,264 @@
+/* Provide virtual symbols for ppc64 function descriptors.
+ Copyright (C) 2013 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ 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 copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+#include <endian.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define BACKEND ppc64_
+#include "libebl_CPU.h"
+
+/* Pointer to the first entry is stored into ebl->backend.
+ It has Dwfl_Module->ebl_syments entries and its memory is followed by
+ strings for the NAME entries. */
+
+struct sym_entry
+{
+ GElf_Sym sym;
+ GElf_Word shndx;
+ const char *name;
+};
+
+/* Find section containing ADDR in its address range. */
+
+static Elf_Scn *
+scnfindaddr (Elf *elf, GElf_Addr addr)
+{
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (likely (shdr != NULL)
+ && likely ((shdr->sh_flags & SHF_ALLOC) != 0)
+ && addr >= shdr->sh_addr
+ && addr < shdr->sh_addr + shdr->sh_size)
+ break;
+ }
+ return scn;
+}
+
+static int
+symnames_get_compar (const void *ap, const void *bp)
+{
+ const char *const *a = ap;
+ const char *const *b = bp;
+ return strcmp (*a, *b);
+}
+
+static const char **
+symnames_get (size_t syments, ebl_getsym_t *getsym, void *arg, bool is_linux,
+ size_t *symnames_countp)
+{
+ assert (syments > 0);
+ const char **symnames = malloc (syments * sizeof (*symnames));
+ if (symnames == NULL)
+ return NULL;
+ *symnames_countp = 0;
+ for (size_t symi = 0; symi < syments; symi++)
+ {
+ GElf_Sym sym;
+ const char *symname = getsym (arg, symi, &sym, NULL, NULL);
+ if (symname == NULL || symname[0] != '.')
+ continue;
+ if (GELF_ST_TYPE (sym.st_info) != STT_FUNC
+ && (! is_linux || GELF_ST_TYPE (sym.st_info) != STT_GNU_IFUNC))
+ continue;
+ symnames[(*symnames_countp)++] = &symname[1];
+ }
+ qsort (symnames, *symnames_countp, sizeof (*symnames), symnames_get_compar);
+ return symnames;
+}
+
+/* Scan all the symbols of EBL and create table of struct sym_entry entries for
+ every function symbol pointing to the .opd section. This includes automatic
+ derefencing of the symbols via the .opd section content to create the
+ virtual symbols for the struct sym_entry entries. As GETSYM points to
+ dwfl_module_getsym we scan all the available symbols, either
+ Dwfl_Module.main plus Dwfl_Module.aux_sym or Dwfl_Module.debug.
+ Symbols from all the available files are included in the SYMENTS count.
+
+ .opd section contents is described in:
+ http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html#FUNC-DES
+ http://www.ibm.com/developerworks/library/l-ppc/
+ - see: Function descriptors -- the .opd section */
+
+bool
+ppc64_init_symbols (Ebl *ebl, size_t syments, GElf_Addr main_bias,
+ ebl_getsym_t *getsym, void *arg, size_t *ebl_symentsp,
+ int *ebl_first_globalp)
+{
+ assert (ebl != NULL);
+ assert (ebl->backend == NULL);
+ if (syments == 0)
+ return true;
+ Elf *elf = ebl->elf;
+ GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
+ if (ehdr == NULL)
+ return false;
+ GElf_Shdr opd_shdr_mem, *opd_shdr;
+ Elf_Data *opd_data = NULL;
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ opd_shdr = gelf_getshdr (scn, &opd_shdr_mem);
+ if (opd_shdr == NULL || (opd_shdr->sh_flags & SHF_ALLOC) == 0)
+ continue;
+ if (strcmp (elf_strptr (elf, ehdr->e_shstrndx, opd_shdr->sh_name), ".opd")
+ != 0)
+ continue;
+ opd_data = elf_getdata (scn, NULL);
+ /* SHT_NOBITS will produce NULL D_BUF. */
+ if (opd_data == NULL || opd_data->d_buf == NULL)
+ return false;
+ assert (opd_data->d_size == opd_shdr->sh_size);
+ break;
+ }
+ if (opd_data == NULL)
+ return true;
+ char *ident = elf_getident (elf, NULL);
+ bool is_linux = ident != NULL && ident[EI_OSABI] == ELFOSABI_LINUX;
+ size_t symnames_count;
+ const char **symnames = symnames_get (syments, getsym, arg, is_linux,
+ &symnames_count);
+ if (symnames == NULL)
+ return false;
+ struct sym_entry *sym_table = malloc (syments * sizeof (*sym_table));
+ if (sym_table == NULL)
+ {
+ free (symnames);
+ return false;
+ }
+ ebl->backend = sym_table;
+ size_t names_size = 0;
+ size_t ebl_syments = 0;
+ int ebl_first_global = 0;
+ for (size_t symi = 0; symi < syments; symi++)
+ {
+ GElf_Sym sym;
+ GElf_Word sym_shndx;
+ Elf *sym_elf;
+ const char *symname = getsym (arg, symi, &sym, &sym_shndx, &sym_elf);
+ if (symname == NULL || symname[0] == '.')
+ continue;
+ if (GELF_ST_TYPE (sym.st_info) != STT_FUNC
+ && (! is_linux || GELF_ST_TYPE (sym.st_info) != STT_GNU_IFUNC))
+ continue;
+ Elf_Scn *sym_scn = elf_getscn (sym_elf, sym_shndx);
+ if (sym_scn == NULL)
+ continue;
+ GElf_Shdr sym_shdr_mem, *sym_shdr;
+ sym_shdr = gelf_getshdr (sym_scn, &sym_shdr_mem);
+ if (sym_shdr == NULL)
+ continue;
+ GElf_Ehdr sym_ehdr_mem, *sym_ehdr = gelf_getehdr (sym_elf, &sym_ehdr_mem);
+ if (sym_ehdr == NULL)
+ continue;
+ if (strcmp (elf_strptr (sym_elf, sym_ehdr->e_shstrndx, sym_shdr->sh_name),
+ ".opd")
+ != 0)
+ continue;
+ if (sym.st_value < opd_shdr->sh_addr + main_bias
+ || sym.st_value > (opd_shdr->sh_addr + main_bias
+ + opd_shdr->sh_size - sizeof (uint64_t)))
+ continue;
+ if (bsearch (&symname, symnames, symnames_count, sizeof (*symnames),
+ symnames_get_compar)
+ != 0)
+ continue;
+ uint64_t val64 = *(const uint64_t *) (opd_data->d_buf + sym.st_value
+ - (opd_shdr->sh_addr + main_bias));
+ val64 = (elf_getident (elf, NULL)[EI_DATA] == ELFDATA2MSB
+ ? be64toh (val64) : le64toh (val64));
+ Elf_Scn *entry_scn = scnfindaddr (elf, val64);
+ if (entry_scn == NULL)
+ continue;
+ if (GELF_ST_BIND (sym.st_info) == STB_LOCAL)
+ ebl_first_global++;
+ struct sym_entry *dest = &sym_table[ebl_syments];
+ dest->sym = sym;
+ dest->sym.st_value = val64 + main_bias;
+ dest->shndx = elf_ndxscn (entry_scn);
+ dest->sym.st_shndx = (dest->shndx > SHN_HIRESERVE
+ ? SHN_XINDEX : dest->shndx);
+ dest->name = symname;
+ names_size += 1 + strlen (symname) + 1;
+ ebl_syments++;
+ }
+ free (symnames);
+ if (ebl_syments == 0)
+ {
+ free (sym_table);
+ ebl->backend = NULL;
+ return true;
+ }
+ sym_table = realloc (sym_table,
+ ebl_syments * sizeof (*sym_table) + names_size);
+ if (sym_table == NULL)
+ return false;
+ ebl->backend = sym_table;
+ char *names = (void *) &sym_table[ebl_syments];
+ char *names_dest = names;
+ for (size_t symi = 0; symi < ebl_syments; symi++)
+ {
+ struct sym_entry *dest = &sym_table[symi];
+ const char *symname = dest->name;
+ dest->name = names_dest;
+ *names_dest++ = '.';
+ names_dest = stpcpy (names_dest, symname) + 1;
+ }
+ assert (names_dest == names + names_size);
+ *ebl_symentsp = ebl_syments;
+ *ebl_first_globalp = ebl_first_global;
+ return true;
+}
+
+const char *
+ppc64_get_symbol (Ebl *ebl, size_t ndx, GElf_Sym *symp, GElf_Word *shndxp)
+{
+ assert (ebl != NULL);
+ if (ebl->backend == NULL)
+ return NULL;
+ struct sym_entry *sym_table = ebl->backend;
+ const struct sym_entry *found = &sym_table[ndx];
+ *symp = found->sym;
+ if (shndxp)
+ *shndxp = found->shndx;
+ return found->name;
+}
+
+void
+ppc64_destr (Ebl *ebl)
+{
+ free (ebl->backend);
+}
diff --git a/backends/ppc64_init.c b/backends/ppc64_init.c
index 14358754..3ed882bb 100644
--- a/backends/ppc64_init.c
+++ b/backends/ppc64_init.c
@@ -65,6 +65,9 @@ ppc64_init (elf, machine, eh, ehlen)
HOOK (eh, core_note);
HOOK (eh, auxv_info);
HOOK (eh, abi_cfi);
+ HOOK (eh, init_symbols);
+ HOOK (eh, get_symbol);
+ HOOK (eh, destr);
return MODVERSION;
}
diff --git a/libdwfl/dwfl_module_addrsym.c b/libdwfl/dwfl_module_addrsym.c
index 320d41f1..b11ba756 100644
--- a/libdwfl/dwfl_module_addrsym.c
+++ b/libdwfl/dwfl_module_addrsym.c
@@ -179,16 +179,17 @@ dwfl_module_addrsym_elf (Dwfl_Module *mod, GElf_Addr addr,
}
}
- /* 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 those symbol tables. Both
- are non-zero when the table exist, except when there is only a
- dynsym table loaded through phdrs, then first_global is zero and
- there will be no auxiliary table. All symbols with local binding
- come first in the symbol table, then all globals. The zeroth,
- null entry, in the auxiliary table is skipped if there is a main
- table. */
- int first_global = mod->first_global + mod->aux_first_global;
+ /* First go through global symbols. mod->first_global,
+ mod->aux_first_global and mod->ebl_first_global are setup by
+ dwfl_module_getsymtab to the index of the first global symbol in
+ those symbol tables. Both are non-zero when the table exist,
+ except when there is only a dynsym table loaded through phdrs, then
+ first_global is zero and there will be no auxiliary table. All
+ symbols with local binding come first in the symbol table, then all
+ globals. The zeroth, null entry, in the auxiliary table is skipped
+ if there is a main table. */
+ int first_global = (mod->first_global + mod->aux_first_global
+ + mod->ebl_first_global);
if (mod->syments > 0 && mod->aux_syments > 0)
first_global--;
search_table (first_global == 0 ? 1 : first_global, syments);
diff --git a/libdwfl/dwfl_module_getdwarf.c b/libdwfl/dwfl_module_getdwarf.c
index dd76f257..223374cd 100644
--- a/libdwfl/dwfl_module_getdwarf.c
+++ b/libdwfl/dwfl_module_getdwarf.c
@@ -962,6 +962,13 @@ find_aux_sym (Dwfl_Module *mod __attribute__ ((unused)),
#endif
}
+static const char *
+getsym_helper (void *arg, int ndx, GElf_Sym *sym, GElf_Word *shndxp, Elf **elfp)
+{
+ Dwfl_Module *mod = arg;
+ return dwfl_module_getsym_elf (mod, ndx, sym, shndxp, elfp, NULL);
+}
+
/* Try to find a symbol table in either MOD->main.elf or MOD->debug.elf. */
static void
find_symtab (Dwfl_Module *mod)
@@ -1081,7 +1088,7 @@ find_symtab (Dwfl_Module *mod)
mod->aux_syments = 0;
elf_end (mod->aux_sym.elf);
mod->aux_sym.elf = NULL;
- return;
+ goto aux_done;
}
mod->aux_symstrdata = elf_getdata (elf_getscn (mod->aux_sym.elf,
@@ -1102,7 +1109,14 @@ find_symtab (Dwfl_Module *mod)
mod->aux_symdata = elf_getdata (aux_symscn, NULL);
if (mod->aux_symdata == NULL)
goto aux_cleanup;
+ aux_done:;
}
+
+ Dwfl_Error error = __libdwfl_module_getebl (mod);
+ if (error == DWFL_E_NOERROR)
+ ebl_init_symbols (mod->ebl, mod->syments + mod->aux_syments,
+ mod->main_bias, getsym_helper, mod,
+ &mod->ebl_syments, &mod->ebl_first_global);
}
@@ -1259,7 +1273,7 @@ dwfl_module_getsymtab (Dwfl_Module *mod)
find_symtab (mod);
if (mod->symerr == DWFL_E_NOERROR)
/* We will skip the auxiliary zero entry if there is another one. */
- return (mod->syments + mod->aux_syments
+ return (mod->syments + mod->aux_syments + mod->ebl_syments
- (mod->syments > 0 && mod->aux_syments > 0 ? 1 : 0));
__libdwfl_seterrno (mod->symerr);
diff --git a/libdwfl/dwfl_module_getsym.c b/libdwfl/dwfl_module_getsym.c
index 319f9758..42eb43bb 100644
--- a/libdwfl/dwfl_module_getsym.c
+++ b/libdwfl/dwfl_module_getsym.c
@@ -54,8 +54,7 @@ dwfl_module_getsym_elf (Dwfl_Module *mod, int ndx,
Elf_Data *symdata;
Elf_Data *symxndxdata;
Elf_Data *symstrdata;
- if (mod->aux_symdata == NULL
- || ndx < mod->first_global)
+ if (ndx < mod->first_global)
{
/* main symbol table (locals). */
tndx = ndx;
@@ -73,24 +72,61 @@ dwfl_module_getsym_elf (Dwfl_Module *mod, int ndx,
symxndxdata = mod->aux_symxndxdata;
symstrdata = mod->aux_symstrdata;
}
- else if ((size_t) ndx < mod->syments + mod->aux_first_global - skip_aux_zero)
+ else if (ndx < (mod->first_global + mod->aux_first_global
+ + mod->ebl_first_global - skip_aux_zero))
+ {
+ /* ebl symbol lookup (locals). */
+ symdata = NULL;
+ tndx = ndx - (mod->first_global + mod->aux_first_global - skip_aux_zero);
+ }
+ else if ((size_t) ndx < (mod->syments + mod->aux_first_global
+ + mod->ebl_first_global - skip_aux_zero))
{
/* main symbol table (globals). */
- tndx = ndx - mod->aux_first_global + skip_aux_zero;
+ tndx = ndx - (mod->aux_first_global + mod->ebl_first_global
+ - skip_aux_zero);
elf = mod->symfile->elf;
symdata = mod->symdata;
symxndxdata = mod->symxndxdata;
symstrdata = mod->symstrdata;
}
- else
+ else if ((size_t) ndx < (mod->syments + mod->aux_syments
+ + mod->ebl_first_global - skip_aux_zero))
{
/* aux symbol table (globals). */
- tndx = ndx - mod->syments + skip_aux_zero;
+ tndx = ndx - (mod->syments + mod->ebl_first_global - skip_aux_zero);
elf = mod->aux_sym.elf;
symdata = mod->aux_symdata;
symxndxdata = mod->aux_symxndxdata;
symstrdata = mod->aux_symstrdata;
}
+ else if ((size_t) ndx < (mod->syments + mod->aux_syments
+ + mod->ebl_syments - skip_aux_zero))
+ {
+ /* ebl symbol lookup (globals). */
+ symdata = NULL;
+ tndx = ndx - (mod->syments + mod->aux_syments - skip_aux_zero);
+ }
+ else
+ {
+ /* out of range NDX. */
+ __libdwfl_seterrno (DWFL_E_INVALID_INDEX);
+ return NULL;
+ }
+
+ if (symdata == NULL)
+ {
+ const char *name = ebl_get_symbol (mod->ebl, tndx, sym, shndxp);
+ if (likely (name != NULL))
+ {
+ if (elfp)
+ *elfp = mod->main.elf;
+ return name;
+ }
+ __libdwfl_seterrno (DWFL_E_LIBEBL);
+ return NULL;
+ }
+
sym = gelf_getsymshndx (symdata, symxndxdata, tndx, sym, &shndx);
if (unlikely (sym == NULL))
diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h
index ba1c758d..89e78fc1 100644
--- a/libdwfl/libdwflP.h
+++ b/libdwfl/libdwflP.h
@@ -89,7 +89,8 @@ typedef struct Dwfl_Process Dwfl_Process;
DWFL_ERROR (ATTACH_STATE_CONFLICT, N_("Dwfl already has attached state")) \
DWFL_ERROR (NO_ATTACH_STATE, N_("Dwfl has no attached state")) \
DWFL_ERROR (NO_UNWIND, N_("Unwinding not supported for this architecture")) \
- DWFL_ERROR (INVALID_ARGUMENT, N_("Invalid argument"))
+ DWFL_ERROR (INVALID_ARGUMENT, N_("Invalid argument")) \
+ DWFL_ERROR (INVALID_INDEX, N_("invalid section index"))
#define DWFL_ERROR(name, text) DWFL_E_##name,
typedef enum { DWFL_ERRORS DWFL_E_NUM } Dwfl_Error;
@@ -173,8 +174,10 @@ struct Dwfl_Module
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. */
+ size_t ebl_syments; /* Number of symbols from ebl_getsym. */
int first_global; /* Index of first global symbol of table. */
int aux_first_global; /* Index of first global of aux_sym table. */
+ int ebl_first_global; /* Index of first global from ebl_getsym. */
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. */
diff --git a/libebl/Makefile.am b/libebl/Makefile.am
index 4487c5f9..1fb3da31 100644
--- a/libebl/Makefile.am
+++ b/libebl/Makefile.am
@@ -54,7 +54,7 @@ gen_SOURCES = eblopenbackend.c eblclosebackend.c eblstrtab.c \
eblreginfo.c eblnonerelocp.c eblrelativerelocp.c \
eblsysvhashentrysize.c eblauxvinfo.c eblcheckobjattr.c \
ebl_check_special_section.c ebl_syscall_abi.c eblabicfi.c \
- eblstother.c eblinitreg.c
+ eblstother.c eblinitreg.c eblgetsymbol.c
libebl_a_SOURCES = $(gen_SOURCES)
diff --git a/libebl/ebl-hooks.h b/libebl/ebl-hooks.h
index cb52fee4..7204514b 100644
--- a/libebl/ebl-hooks.h
+++ b/libebl/ebl-hooks.h
@@ -162,5 +162,14 @@ bool EBLHOOK(set_initial_registers_tid) (pid_t tid,
ebl_tid_registers_t *setfunc,
void *arg);
+/* See ebl_init_symbols. */
+bool EBLHOOK(init_symbols) (Ebl *ebl, size_t syments, GElf_Addr main_bias,
+ ebl_getsym_t *getsym, void *arg,
+ size_t *ebl_symentsp, int *ebl_first_globalp);
+
+/* See ebl_get_symbol. */
+const char *EBLHOOK(get_symbol) (Ebl *ebl, size_t ndx, GElf_Sym *symp,
+ GElf_Word *shndxp);
+
/* Destructor for ELF backend handle. */
void EBLHOOK(destr) (struct ebl *);
diff --git a/libebl/eblgetsymbol.c b/libebl/eblgetsymbol.c
new file mode 100644
index 00000000..ef664bf6
--- /dev/null
+++ b/libebl/eblgetsymbol.c
@@ -0,0 +1,57 @@
+/* Provide virtual symbols from backend.
+ Copyright (C) 2013 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ 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 copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <libeblP.h>
+#include <assert.h>
+
+bool
+ebl_init_symbols (Ebl *ebl, size_t syments, GElf_Addr main_bias,
+ ebl_getsym_t *getsym, void *arg, size_t *ebl_symentsp,
+ int *ebl_first_globalp)
+{
+ *ebl_symentsp = 0;
+ *ebl_first_globalp = 0;
+ if (ebl == NULL)
+ return false;
+ if (ebl->init_symbols == NULL)
+ return true;
+ return ebl->init_symbols (ebl, syments, main_bias, getsym, arg, ebl_symentsp,
+ ebl_first_globalp);
+}
+
+const char *
+ebl_get_symbol (Ebl *ebl, size_t ndx, GElf_Sym *symp, GElf_Word *shndxp)
+{
+ if (ebl == NULL || ebl->get_symbol == NULL)
+ return NULL;
+ return ebl->get_symbol (ebl, ndx, symp, shndxp);
+}
diff --git a/libebl/libebl.h b/libebl/libebl.h
index 045a1980..d8171532 100644
--- a/libebl/libebl.h
+++ b/libebl/libebl.h
@@ -402,6 +402,34 @@ extern bool ebl_set_initial_registers_tid (Ebl *ebl,
extern size_t ebl_frame_nregs (Ebl *ebl)
__nonnull_attribute__ (1);
+/* Callback type for ebl_init_symbols,
+ it is forwarded to dwfl_module_getsym_elf. */
+typedef const char *(ebl_getsym_t) (void *arg, int ndx, GElf_Sym *symp,
+ GElf_Word *shndxp, Elf **elfp)
+ __nonnull_attribute__ (3);
+
+/* Initialize virtual backend symbol table for EBL->elf currently containing
+ SYMENTS symbols, EBL->elf is using MAIN_BIAS. GETSYM is a callback to fetch
+ the existing EBL->elf symbols, ARG is an opaque parameter for GETSYM.
+ Fill in *EBL_SYMENTSP with the total number of virtual symbols found,
+ *EBL_FIRST_GLOBALP of them are local. Function must be called exactly once
+ for new EBL. Function returns false if there was an error; *EBL_SYMENTSP
+ and *EBL_FIRST_GLOBALP are left as zero in such case. If function returns
+ true (success) *EBL_SYMENTSP and *EBL_FIRST_GLOBALP still can be zero,
+ if the file does not contain any matching symbols. */
+extern bool ebl_init_symbols (Ebl *ebl, size_t syments, Dwarf_Addr symbias,
+ ebl_getsym_t *getsym, void *arg,
+ size_t *ebl_symentsp, int *ebl_first_globalp)
+ __nonnull_attribute__ (1, 4, 6, 7);
+
+/* Return NDXth virtual backend symbol from MOD, store it to *SYM and its
+ section to *SHNDX. Return its name. NDX must be less than *EBL_SYMENTSP
+ returned by init_symbols above. SHNDXP may be NULL. Returned name is valid
+ as long as EBL is valid. */
+extern const char *ebl_get_symbol (Ebl *ebl, size_t ndx, GElf_Sym *symp,
+ GElf_Word *shndxp)
+ __nonnull_attribute__ (1, 3);
+
#ifdef __cplusplus
}
#endif
diff --git a/libebl/libeblP.h b/libebl/libeblP.h
index 4f4137d5..32820507 100644
--- a/libebl/libeblP.h
+++ b/libebl/libeblP.h
@@ -66,6 +66,9 @@ struct ebl
/* Internal data. */
void *dlhandle;
+
+ /* Data specific to the backend. */
+ void *backend;
};
diff --git a/tests/Makefile.am b/tests/Makefile.am
index a74830ba..f57333c8 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -245,7 +245,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
run-backtrace-native-core.sh run-backtrace-native-core-biarch.sh \
run-backtrace-core-x86_64.sh run-backtrace-core-i386.sh \
backtrace-subr.sh backtrace.i386.core.bz2 backtrace.i386.exec.bz2 \
- backtrace.x86_64.core.bz2 backtrace.x86_64.exec.bz2
+ backtrace.x86_64.core.bz2 backtrace.x86_64.exec.bz2 \
+ testfile66.bz2 testfile66.core.bz2
if USE_VALGRIND
valgrind_cmd='valgrind -q --trace-children=yes --error-exitcode=1 --run-libc-freeres=no'
diff --git a/tests/dwflsyms.c b/tests/dwflsyms.c
index 10c01f1f..19f1dbeb 100644
--- a/tests/dwflsyms.c
+++ b/tests/dwflsyms.c
@@ -106,13 +106,16 @@ addr_in_section (Elf *elf, GElf_Word shndx, GElf_Addr addr)
static int
list_syms (struct Dwfl_Module *mod,
- void **user __attribute__ ((unused)),
- const char *mod_name __attribute__ ((unused)),
+ void **user __attribute__ ((unused)), const char *mod_name,
Dwarf_Addr low_addr __attribute__ ((unused)),
void *arg __attribute__ ((unused)))
{
int syms = dwfl_module_getsymtab (mod);
- assert (syms >= 0);
+ if (syms < 0)
+ {
+ printf ("%s: %s\n", mod_name, dwfl_errmsg (-1));
+ return DWARF_CB_OK;
+ }
for (int ndx = 0; ndx < syms; ndx++)
{
diff --git a/tests/run-addrname-test.sh b/tests/run-addrname-test.sh
index 8624074f..bdf8ea6a 100755
--- a/tests/run-addrname-test.sh
+++ b/tests/run-addrname-test.sh
@@ -298,6 +298,45 @@ __vdso_time
??:0
EOF
+# .section ".text"
+# .globl _start
+# .section ".opd","aw"
+#_start: .quad .L._start,.TOC.@tocbase
+# .previous
+# .type _start, @function
+#.L._start:
+# .byte 0x7d, 0x82, 0x10, 0x08
+# .size _start,.-.L._start
+testfiles testfile66 testfile66.core
+testrun_compare ${abs_top_builddir}/src/addr2line -S -e testfile66 _start 0x103d0 0x103d3 0x103d4 ._start 0x2d8 0x2db 0x2dc <<\EOF
+_start
+??:0
+_start
+??:0
+_start+0x3
+??:0
+()+0x103d4
+??:0
+._start
+??:0
+._start
+??:0
+._start+0x3
+??:0
+()+0x2dc
+??:0
+EOF
+testrun_compare ${abs_top_builddir}/src/addr2line -S -e testfile66 --core=testfile66.core _start 0x461c03d0 ._start 0x461b02d8 <<\EOF
+_start
+??:0
+_start
+??:0
+._start
+??:0
+._start
+??:0
+EOF
+
testfiles testfile69.core testfile69.so
testrun_compare ${abs_top_builddir}/src/addr2line --core=./testfile69.core -S 0x7f0bc6a33535 0x7f0bc6a33546 <<\EOF
libstatic+0x9
diff --git a/tests/run-dwflsyms.sh b/tests/run-dwflsyms.sh
index 3cd7bf36..60909051 100755
--- a/tests/run-dwflsyms.sh
+++ b/tests/run-dwflsyms.sh
@@ -362,4 +362,104 @@ testrun_compare ${abs_builddir}/dwflsyms -e testfilebasmin <<\EOF
8: FUNC GLOBAL bar (44) 0x40017a, rel: 0x40017a (.text)
EOF
+testfiles testfile66
+testrun_compare ${abs_builddir}/dwflsyms -e testfile66 <<\EOF
+ 0: NOTYPE LOCAL (0) 0
+ 1: SECTION LOCAL (0) 0x190
+ 2: SECTION LOCAL (0) 0x1a4
+ 3: SECTION LOCAL (0) 0x1c8
+ 4: SECTION LOCAL (0) 0x1f8
+ 5: SECTION LOCAL (0) 0x288
+ 6: SECTION LOCAL (0) 0x2a8
+ 7: SECTION LOCAL (0) 0x2d8
+ 8: SECTION LOCAL (0) 0x102e0
+ 9: SECTION LOCAL (0) 0x103d0
+ 10: SECTION LOCAL (0) 0x103e8
+ 11: SECTION LOCAL (0) 0x103e8
+ 12: OBJECT LOCAL _DYNAMIC (0) 0x102e0
+ 13: FUNC GLOBAL _start (4) 0x103d0, rel: 0x103d0 (.opd)
+ 14: NOTYPE GLOBAL __bss_start (0) 0x103f0
+ 15: NOTYPE GLOBAL _edata (0) 0x103f0
+ 16: NOTYPE GLOBAL _end (0) 0x103f0
+ 17: FUNC GLOBAL ._start (4) 0x2d8, rel: 0x2d8 (.text)
+EOF
+
+testfiles testfile66.core
+testrun_compare ${abs_builddir}/dwflsyms -e testfile66 --core=testfile66.core <<\EOF
+ 0: NOTYPE LOCAL (0) 0
+ 1: SECTION LOCAL (0) 0xfffb1af0410
+ 2: NOTYPE GLOBAL __kernel_datapage_offset (0) 0xfffb1af05dc
+ 3: OBJECT GLOBAL LINUX_2.6.15 (0) 0
+ 4: NOTYPE GLOBAL __kernel_clock_getres (64) 0xfffb1af052c
+ 5: NOTYPE GLOBAL __kernel_get_tbfreq (24) 0xfffb1af0620
+ 6: NOTYPE GLOBAL __kernel_gettimeofday (84) 0xfffb1af0440
+ 7: NOTYPE GLOBAL __kernel_sync_dicache (20) 0xfffb1af06c4
+ 8: NOTYPE GLOBAL __kernel_sync_dicache_p5 (20) 0xfffb1af06c4
+ 9: NOTYPE GLOBAL __kernel_sigtramp_rt64 (12) 0xfffb1af0418
+ 10: NOTYPE GLOBAL __kernel_clock_gettime (152) 0xfffb1af0494
+ 11: NOTYPE GLOBAL __kernel_get_syscall_map (44) 0xfffb1af05f4
+ld64.so.1: Callback returned failure
+ 0: NOTYPE LOCAL (0) 0
+ 1: SECTION LOCAL (0) 0x461b0190
+ 2: SECTION LOCAL (0) 0x461b01a4
+ 3: SECTION LOCAL (0) 0x461b01c8
+ 4: SECTION LOCAL (0) 0x461b01f8
+ 5: SECTION LOCAL (0) 0x461b0288
+ 6: SECTION LOCAL (0) 0x461b02a8
+ 7: SECTION LOCAL (0) 0x461b02d8
+ 8: SECTION LOCAL (0) 0x461c02e0
+ 9: SECTION LOCAL (0) 0x461c03d0
+ 10: SECTION LOCAL (0) 0x461c03e8
+ 11: SECTION LOCAL (0) 0x461c03e8
+ 12: OBJECT LOCAL _DYNAMIC (0) 0x102e0
+ 13: FUNC GLOBAL _start (4) 0x461c03d0, rel: 0x103d0 (.opd)
+ 14: NOTYPE GLOBAL __bss_start (0) 0x103f0
+ 15: NOTYPE GLOBAL _edata (0) 0x103f0
+ 16: NOTYPE GLOBAL _end (0) 0x103f0
+ 17: FUNC GLOBAL ._start (4) 0x461b02d8, rel: 0x2d8 (.text)
+EOF
+
+# Test the already present dot-prefixed names do not get duplicated.
+testfiles hello_ppc64.ko
+testrun_compare ${abs_builddir}/dwflsyms -e hello_ppc64.ko <<\EOF
+ 0: NOTYPE LOCAL (0) 0
+ 1: SECTION LOCAL (0) 0
+ 2: SECTION LOCAL (0) 0x94
+ 3: SECTION LOCAL (0) 0xba
+ 4: SECTION LOCAL (0) 0xd0
+ 5: SECTION LOCAL (0) 0x13a
+ 6: SECTION LOCAL (0) 0x13a
+ 7: SECTION LOCAL (0) 0x150
+ 8: SECTION LOCAL (0) 0x170
+ 9: SECTION LOCAL (0) 0x188
+ 10: SECTION LOCAL (0) 0x410
+ 11: SECTION LOCAL (0) 0x434
+ 12: SECTION LOCAL (0) 0x438
+ 13: SECTION LOCAL (0) 0x438
+ 14: SECTION LOCAL (0) 0
+ 15: SECTION LOCAL (0) 0
+ 16: SECTION LOCAL (0) 0
+ 17: SECTION LOCAL (0) 0
+ 18: SECTION LOCAL (0) 0
+ 19: SECTION LOCAL (0) 0
+ 20: SECTION LOCAL (0) 0
+ 21: SECTION LOCAL (0) 0
+ 22: SECTION LOCAL (0) 0
+ 23: SECTION LOCAL (0) 0
+ 24: FILE LOCAL init.c (0) 0
+ 25: FILE LOCAL exit.c (0) 0
+ 26: FILE LOCAL hello.mod.c (0) 0
+ 27: OBJECT LOCAL __mod_srcversion23 (35) 0xd0
+ 28: OBJECT LOCAL __module_depends (9) 0xf8
+ 29: OBJECT LOCAL __mod_vermagic5 (50) 0x108
+ 30: OBJECT GLOBAL __this_module (648) 0x188
+ 31: FUNC GLOBAL .cleanup_module (72) 0x4c, rel: 0x4c (.text)
+ 32: FUNC GLOBAL cleanup_module (24) 0x160, rel: 0x10 (.opd)
+ 33: NOTYPE GLOBAL .printk (0) 0
+ 34: FUNC GLOBAL init_module (24) 0x150, rel: 0 (.opd)
+ 35: NOTYPE GLOBAL ._mcount (0) 0
+ 36: FUNC GLOBAL .init_module (76) 0, rel: 0 (.text)
+ 37: NOTYPE GLOBAL _mcount (0) 0
+EOF
+
exit 0
diff --git a/tests/testfile66.bz2 b/tests/testfile66.bz2
new file mode 100755
index 00000000..4797590e
--- /dev/null
+++ b/tests/testfile66.bz2
Binary files differ
diff --git a/tests/testfile66.core.bz2 b/tests/testfile66.core.bz2
new file mode 100644
index 00000000..12e2d444
--- /dev/null
+++ b/tests/testfile66.core.bz2
Binary files differ