On Wed, 10 Oct 2012 01:59:23 +0200, Roland McGrath wrote:
For this kind of thing, I think it's extremely helpful if you
give a
short description and example of the API change you are proposing. I
can read and respond to that much more quickly than I can review the
implementation.
There is new:
libebl/ebl-hooks.h:
+/* *SYM must be STT_FUNC. Then if it describes a function descriptor (PPC64)
+ convert in-place its data and return a possibly different new name for it.
+ The name is valid as long as EBL is valid. */
+const char *EBLHOOK(get_func_pc) (Ebl *ebl, struct Dwfl_Module *mod,
+ GElf_Sym *sym);
And dwfl_module_addrsym starts working on ppc64 the way it should.
There is also (used only by ppc64 backend now):
libebl/libeblP.h:
@@ -62,6 +62,9 @@ struct ebl
+ /* Data specific to the backend. */
+ void *backend;
I haven't looked at the details, but it feels like entirely
wrong
layering if any libebl code uses libdwfl.h types or interfaces at all.
There is more such code in the unwinder branch. Various arch-dependent code
needs for example to read inferior memory. This means to access core files
data or PTRACE_PEEKDATA depending on libdwfl initializers the application
used.
That said, as a general thing, in cases like this where you just
need
the right pointer type but the #include's become cumbersome, often the
right thing to do is just use a forward declaration "struct
Dwfl_Module;" and use "struct Dwfl_Module *arg" in prototypes.
Sorry, I somehow forgot now, fixed.
Cleaned it up a bit as:
jankratochvil/ppc64-opd
(inherits jankratochvil/vdso-bias)
Thanks,
Jan
commit ba08d3004630416f506e1e9b7fd77d70d3ac3cb8
Author: Jan Kratochvil <jan.kratochvil(a)redhat.com>
Date: Wed Oct 10 09:48:10 2012 +0200
backends/
2012-10-09 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* Makefile.am (INCLUDES): Add libdwfl.
(ppc64_SRCS): Add ppc64_get_func_pc.c.
* ppc64_get_func_pc.c: New file.
* ppc64_init.c (ppc64_init): Install get_func_pc and destr.
libdwfl/
2012-10-09 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* dwfl_module_addrsym.c (dwfl_module_addrsym) (get_section): New
function from ...
(dwfl_module_addrsym) (same_section): ... here. Call it.
(dwfl_module_addrsym) (found_sym): New function from ...
(dwfl_module_addrsym) (search_table): ... here. Call it. Try second
time with ebl_get_func_pc.
libebl/
2012-10-09 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* Makefile.am (gen_SOURCES): Add eblgetfuncpc.c.
* ebl-hooks.h (get_func_pc): New entry.
* eblgetfuncpc.c: New file.
* libebl.h (struct Dwfl_Module): New declaration.
(ebl_get_func_pc): New declaration.
* libeblP.h (struct ebl): New field backend.
tests/
2012-10-09 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* run-addrname-test.sh: New testcase for ppc64 function descriptors.
* testfile66.bz2: New file.
Signed-off-by: Jan Kratochvil <jan.kratochvil(a)redhat.com>
diff --git a/backends/Makefile.am b/backends/Makefile.am
index 982ff2a..fa85593 100644
--- a/backends/Makefile.am
+++ b/backends/Makefile.am
@@ -29,7 +29,7 @@
## not, see <
http://www.gnu.org/licenses/>.
include $(top_srcdir)/config/eu.am
INCLUDES += -I$(top_srcdir)/libebl -I$(top_srcdir)/libasm \
- -I$(top_srcdir)/libelf -I$(top_srcdir)/libdw
+ -I$(top_srcdir)/libelf -I$(top_srcdir)/libdw -I$(top_srcdir)/libdwfl
modules = i386 sh x86_64 ia64 alpha arm sparc ppc ppc64 s390 tilegx
@@ -90,7 +90,8 @@ libebl_ppc_pic_a_SOURCES = $(ppc_SRCS)
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
+ ppc64_corenote.c ppc_regs.c ppc_auxv.c ppc_attrs.c ppc_syscall.c \
+ ppc64_get_func_pc.c
libebl_ppc64_pic_a_SOURCES = $(ppc64_SRCS)
am_libebl_ppc64_pic_a_OBJECTS = $(ppc64_SRCS:.c=.os)
diff --git a/backends/ppc64_get_func_pc.c b/backends/ppc64_get_func_pc.c
new file mode 100644
index 0000000..5038b00
--- /dev/null
+++ b/backends/ppc64_get_func_pc.c
@@ -0,0 +1,195 @@
+/* Convert function descriptor SYM to the function PC value in-place.
+ Copyright (C) 2012 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 "libdwfl.h"
+#include <endian.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define BACKEND ppc64_
+#include "libebl_CPU.h"
+
+struct pc_entry
+{
+ /* sym_from must be the very first element for the use in bsearch below. */
+ GElf_Sym sym_from;
+ Elf64_Addr st_value_to;
+ const char *name_to;
+};
+
+struct pc_table
+{
+ size_t nelem;
+ struct pc_entry a[];
+ /* Here follow strings allocated for pc_entry->name_to. */
+};
+
+static int
+compar (const void *a_voidp, const void *b_voidp)
+{
+ const struct pc_entry *a = a_voidp;
+ const struct pc_entry *b = b_voidp;
+
+ return memcmp (&a->sym_from, &b->sym_from, sizeof (a->sym_from));
+}
+
+static void
+init (Ebl *ebl, Dwfl_Module *mod)
+{
+ int syments = dwfl_module_getsymtab (mod);
+ assert (syments >= 0);
+ size_t funcs = 0;
+ size_t names_size = 0;
+ Elf *elf = ebl->elf;
+ if (elf == NULL)
+ return;
+ GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
+ if (ehdr == NULL)
+ return;
+ GElf_Word shndx, opd_shndx = 0;
+ /* Needless initialization for old GCCs. */
+ Elf_Data *opd_data = NULL;
+ /* Needless initialization for old GCCs. */
+ GElf_Shdr opd_shdr_mem, *opd_shdr = NULL;
+ Dwarf_Addr symbias;
+ dwfl_module_info (mod, NULL, NULL, NULL, NULL, &symbias, NULL, NULL);
+ for (int symi = 1; symi < syments; symi++)
+ {
+ GElf_Sym sym;
+ const char *symname = dwfl_module_getsym (mod, symi, &sym, &shndx);
+ if (symname == NULL || GELF_ST_TYPE (sym.st_info) != STT_FUNC)
+ continue;
+ if (sym.st_shndx != SHN_XINDEX)
+ shndx = sym.st_shndx;
+ /* Zero is invalid value but it could crash this code. */
+ if (shndx == 0)
+ continue;
+ if (opd_shndx == 0)
+ {
+ Elf_Scn *scn = elf_getscn (elf, shndx);
+ if (scn == NULL)
+ continue;
+ opd_shdr = gelf_getshdr (scn, &opd_shdr_mem);
+ if (opd_shdr == NULL)
+ 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;
+ assert (opd_data->d_size == opd_shdr->sh_size);
+ opd_shndx = shndx;
+ }
+ if (shndx != opd_shndx)
+ continue;
+ Elf64_Addr val;
+ if (sym.st_value < opd_shdr->sh_addr + symbias
+ || sym.st_value > (opd_shdr->sh_addr + symbias
+ + opd_shdr->sh_size - sizeof (val)))
+ continue;
+ funcs++;
+ names_size += 1 + strlen (symname) + 1;
+ }
+ struct pc_table *pc_table;
+ pc_table = malloc (sizeof (*pc_table) + funcs * sizeof (*pc_table->a)
+ + names_size);
+ if (pc_table == NULL)
+ return;
+ ebl->backend = pc_table;
+ pc_table->nelem = 0;
+ if (funcs == 0)
+ return;
+ struct pc_entry *dest = pc_table->a;
+ char *names = (void *) (pc_table->a + funcs), *names_dest = names;
+ for (int symi = 1; symi < syments; symi++)
+ {
+ GElf_Sym sym;
+ const char *symname = dwfl_module_getsym (mod, symi, &sym, &shndx);
+ if (symname == NULL || GELF_ST_TYPE (sym.st_info) != STT_FUNC)
+ continue;
+ if (sym.st_shndx != SHN_XINDEX)
+ shndx = sym.st_shndx;
+ if (shndx != opd_shndx)
+ continue;
+ uint64_t val64;
+ if (sym.st_value < opd_shdr->sh_addr + symbias
+ || sym.st_value > (opd_shdr->sh_addr + symbias
+ + opd_shdr->sh_size - sizeof (val64)))
+ continue;
+ val64 = *(const uint64_t *) (opd_data->d_buf + sym.st_value
+ - (opd_shdr->sh_addr + symbias));
+ val64 = (elf_getident (elf, NULL)[EI_DATA] == ELFDATA2MSB
+ ? be64toh (val64) : le64toh (val64));
+ assert (dest < pc_table->a + funcs);
+ dest->sym_from = sym;
+ dest->st_value_to = val64 + symbias;
+ dest->name_to = names_dest;
+ *names_dest++ = '.';
+ names_dest = stpcpy (names_dest, symname) + 1;
+ dest++;
+ pc_table->nelem++;
+ }
+ assert (pc_table->nelem == funcs);
+ assert (dest == pc_table->a + pc_table->nelem);
+ assert (names_dest == names + names_size);
+ qsort (pc_table->a, pc_table->nelem, sizeof (*pc_table->a), compar);
+}
+
+const char *
+ppc64_get_func_pc (Ebl *ebl, Dwfl_Module *mod, GElf_Sym *sym)
+{
+ if (ebl->backend == NULL)
+ init (ebl, mod);
+ if (ebl->backend == NULL)
+ return NULL;
+ const struct pc_table *pc_table = ebl->backend;
+ const struct pc_entry *found;
+ found = bsearch (sym, pc_table->a, pc_table->nelem, sizeof (*pc_table->a),
+ compar);
+ if (found == NULL)
+ return NULL;
+ sym->st_value = found->st_value_to;
+ return found->name_to;
+}
+
+void
+ppc64_destr (Ebl *ebl)
+{
+ if (ebl->backend == NULL)
+ return;
+ struct pc_table *pc_table = ebl->backend;
+ free (pc_table);
+ ebl->backend = NULL;
+}
diff --git a/backends/ppc64_init.c b/backends/ppc64_init.c
index 90d4f2b..da7d02c 100644
--- a/backends/ppc64_init.c
+++ b/backends/ppc64_init.c
@@ -64,6 +64,8 @@ ppc64_init (elf, machine, eh, ehlen)
HOOK (eh, syscall_abi);
HOOK (eh, core_note);
HOOK (eh, auxv_info);
+ HOOK (eh, get_func_pc);
+ HOOK (eh, destr);
return MODVERSION;
}
diff --git a/libdwfl/dwfl_module_addrsym.c b/libdwfl/dwfl_module_addrsym.c
index fdc95fc..f2c00d5 100644
--- a/libdwfl/dwfl_module_addrsym.c
+++ b/libdwfl/dwfl_module_addrsym.c
@@ -39,6 +39,23 @@ dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr,
if (syments < 0)
return NULL;
+ /* Return section where FIND_ADDR lies. Return SHN_ABS otherwise. */
+ inline GElf_Word get_section (GElf_Addr find_addr)
+ {
+ GElf_Addr mod_addr = dwfl_deadjust_st_value (mod, find_addr);
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (mod->symfile->elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (likely (shdr != NULL)
+ && mod_addr >= shdr->sh_addr
+ && mod_addr < shdr->sh_addr + shdr->sh_size)
+ return elf_ndxscn (scn);
+ }
+ return SHN_ABS;
+ }
+
/* 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)
@@ -49,23 +66,7 @@ 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);
- Elf_Scn *scn = NULL;
- addr_shndx = SHN_ABS;
- while ((scn = elf_nextscn (mod->symfile->elf, scn)) != NULL)
- {
- GElf_Shdr shdr_mem;
- GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
- if (likely (shdr != NULL)
- && mod_addr >= shdr->sh_addr
- && mod_addr < shdr->sh_addr + shdr->sh_size)
- {
- addr_shndx = elf_ndxscn (scn);
- break;
- }
- }
- }
+ addr_shndx = get_section (addr);
return shndx == addr_shndx;
}
@@ -83,91 +84,107 @@ dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr,
/* Keep track of the lowest address a relevant sizeless symbol could have. */
GElf_Addr min_label = 0;
- /* Look through the symbol table for a matching symbol. */
- inline void search_table (int start, int end)
+ /* Consider one symbol SYM. */
+ inline void found_sym (const GElf_Sym *sym, GElf_Word shndx, const char *name)
{
- for (int i = start; i < end; ++i)
+ if (name != NULL && name[0] != '\0'
+ && sym->st_shndx != SHN_UNDEF
+ && sym->st_value <= addr
+ && GELF_ST_TYPE (sym->st_info) != STT_SECTION
+ && GELF_ST_TYPE (sym->st_info) != STT_FILE
+ && GELF_ST_TYPE (sym->st_info) != STT_TLS)
{
- GElf_Sym sym;
- GElf_Word shndx;
- const char *name = INTUSE(dwfl_module_getsym) (mod, i, &sym, &shndx);
- if (name != NULL && name[0] != '\0'
- && sym.st_shndx != SHN_UNDEF
- && sym.st_value <= addr
- && GELF_ST_TYPE (sym.st_info) != STT_SECTION
- && GELF_ST_TYPE (sym.st_info) != STT_FILE
- && GELF_ST_TYPE (sym.st_info) != STT_TLS)
- {
- /* Even if we don't choose this symbol, its existence excludes
- any sizeless symbol (assembly label) that is below its upper
- bound. */
- if (sym.st_value + sym.st_size > min_label)
- min_label = sym.st_value + sym.st_size;
+ /* Even if we don't choose this symbol, its existence excludes
+ any sizeless symbol (assembly label) that is below its upper
+ bound. */
+ if (sym->st_value + sym->st_size > min_label)
+ min_label = sym->st_value + sym->st_size;
- if (sym.st_size == 0 || addr - sym.st_value < sym.st_size)
+ if (sym->st_size == 0 || addr - sym->st_value < sym->st_size)
+ {
+ /* Return GELF_ST_BIND as higher-is-better integer. */
+ inline int binding_value (const GElf_Sym *symp)
{
- /* Return GELF_ST_BIND as higher-is-better integer. */
- inline int binding_value (const GElf_Sym *symp)
- {
- switch (GELF_ST_BIND (symp->st_info))
- {
- case STB_GLOBAL:
- return 3;
- case STB_WEAK:
- return 2;
- case STB_LOCAL:
- return 1;
- default:
- return 0;
- }
- }
- /* This symbol is a better candidate than the current one
- if it's closer to ADDR or is global when it was local. */
- if (closest_name == NULL
- || closest_sym->st_value < sym.st_value
- || binding_value (closest_sym) < binding_value (&sym))
- {
- if (sym.st_size != 0)
- {
- *closest_sym = sym;
- closest_shndx = shndx;
- closest_name = name;
- }
- else if (closest_name == NULL
- && sym.st_value >= min_label
- && same_section (&sym, shndx))
- {
- /* Handwritten assembly symbols sometimes have no
- st_size. If no symbol with proper size includes
- the address, we'll use the closest one that is in
- the same section as ADDR. */
- sizeless_sym = sym;
- sizeless_shndx = shndx;
- sizeless_name = name;
- }
- }
- /* When the beginning of its range is no closer,
- the end of its range might be. Otherwise follow
- GELF_ST_BIND preference. If all are equal prefer
- the first symbol found. */
- else if (sym.st_size != 0
- && closest_sym->st_value == sym.st_value
- && ((closest_sym->st_size > sym.st_size
- && (binding_value (closest_sym)
- <= binding_value (&sym)))
- || (closest_sym->st_size >= sym.st_size
- && (binding_value (closest_sym)
- < binding_value (&sym)))))
+ switch (GELF_ST_BIND (symp->st_info))
+ {
+ case STB_GLOBAL:
+ return 3;
+ case STB_WEAK:
+ return 2;
+ case STB_LOCAL:
+ return 1;
+ default:
+ return 0;
+ }
+ }
+ /* This symbol is a better candidate than the current one
+ if it's closer to ADDR or is global when it was local. */
+ if (closest_name == NULL
+ || closest_sym->st_value < sym->st_value
+ || binding_value (closest_sym) < binding_value (sym))
+ {
+ if (sym->st_size != 0)
{
- *closest_sym = sym;
+ *closest_sym = *sym;
closest_shndx = shndx;
closest_name = name;
}
+ else if (closest_name == NULL
+ && sym->st_value >= min_label
+ && same_section (sym, shndx))
+ {
+ /* Handwritten assembly symbols sometimes have no
+ st_size. If no symbol with proper size includes
+ the address, we'll use the closest one that is in
+ the same section as ADDR. */
+ sizeless_sym = *sym;
+ sizeless_shndx = shndx;
+ sizeless_name = name;
+ }
+ }
+ /* When the beginning of its range is no closer,
+ the end of its range might be. Otherwise follow
+ GELF_ST_BIND preference. If all are equal prefer
+ the first symbol found. */
+ else if (sym->st_size != 0
+ && closest_sym->st_value == sym->st_value
+ && ((closest_sym->st_size > sym->st_size
+ && (binding_value (closest_sym)
+ <= binding_value (sym)))
+ || (closest_sym->st_size >= sym->st_size
+ && (binding_value (closest_sym)
+ < binding_value (sym)))))
+ {
+ *closest_sym = *sym;
+ closest_shndx = shndx;
+ closest_name = name;
}
}
}
}
+ /* Look through the symbol table for a matching symbol. */
+ inline void search_table (int start, int end)
+ {
+ for (int i = start; i < end; ++i)
+ {
+ GElf_Sym sym;
+ GElf_Word shndx;
+ const char *name = INTUSE(dwfl_module_getsym) (mod, i, &sym, &shndx);
+ found_sym (&sym, shndx, name);
+ if (name == NULL || GELF_ST_TYPE (sym.st_info) != STT_FUNC)
+ continue;
+ Dwfl_Error error = __libdwfl_module_getebl (mod);
+ if (error != DWFL_E_NOERROR)
+ continue;
+ name = ebl_get_func_pc (mod->ebl, mod, &sym);
+ if (name == NULL)
+ continue;
+ shndx = get_section (sym.st_value);
+ found_sym (&sym, shndx, name);
+ }
+ }
+
/* 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
diff --git a/libebl/Makefile.am b/libebl/Makefile.am
index 65e6b5b..1ab08e7 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
+ eblstother.c eblgetfuncpc.c
libebl_a_SOURCES = $(gen_SOURCES)
diff --git a/libebl/ebl-hooks.h b/libebl/ebl-hooks.h
index f629bce..5651dd6 100644
--- a/libebl/ebl-hooks.h
+++ b/libebl/ebl-hooks.h
@@ -154,5 +154,11 @@ int EBLHOOK(disasm) (const uint8_t **startp, const uint8_t *end,
/* Supply the machine-specific state of CFI before CIE initial programs. */
int EBLHOOK(abi_cfi) (Ebl *ebl, Dwarf_CIE *abi_info);
+/* *SYM must be STT_FUNC. Then if it describes a function descriptor (PPC64)
+ convert in-place its data and return a possibly different new name for it.
+ The name is valid as long as EBL is valid. */
+const char *EBLHOOK(get_func_pc) (Ebl *ebl, struct Dwfl_Module *mod,
+ GElf_Sym *sym);
+
/* Destructor for ELF backend handle. */
void EBLHOOK(destr) (struct ebl *);
diff --git a/libebl/eblgetfuncpc.c b/libebl/eblgetfuncpc.c
new file mode 100644
index 0000000..57ef159
--- /dev/null
+++ b/libebl/eblgetfuncpc.c
@@ -0,0 +1,46 @@
+/* Convert function descriptor SYM to the function PC value in-place.
+ Copyright (C) 2012 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>
+
+const char *
+ebl_get_func_pc (Ebl *ebl, struct Dwfl_Module *mod, GElf_Sym *sym)
+{
+ if (ebl == NULL)
+ return NULL;
+ assert (sym != NULL);
+ assert (GELF_ST_TYPE (sym->st_info) == STT_FUNC);
+ if (ebl->get_func_pc == NULL)
+ return NULL;
+ return ebl->get_func_pc (ebl, mod, sym);
+}
diff --git a/libebl/libebl.h b/libebl/libebl.h
index 0d5621d..34c245e 100644
--- a/libebl/libebl.h
+++ b/libebl/libebl.h
@@ -375,6 +375,12 @@ extern int ebl_auxv_info (Ebl *ebl, GElf_Xword a_type,
const char **name, const char **format)
__nonnull_attribute__ (1, 3, 4);
+/* Convert function descriptor SYM to the function PC value in-place. */
+struct Dwfl_Module;
+extern const char *ebl_get_func_pc (Ebl *ebl, struct Dwfl_Module *mod,
+ GElf_Sym *sym)
+ __nonnull_attribute__ (1, 2, 3);
+
#ifdef __cplusplus
}
diff --git a/libebl/libeblP.h b/libebl/libeblP.h
index 5ec26a4..c8196bd 100644
--- a/libebl/libeblP.h
+++ b/libebl/libeblP.h
@@ -62,6 +62,9 @@ struct ebl
/* Internal data. */
void *dlhandle;
+
+ /* Data specific to the backend. */
+ void *backend;
};
diff --git a/tests/run-addrname-test.sh b/tests/run-addrname-test.sh
index 4feb193..4dd6018 100755
--- a/tests/run-addrname-test.sh
+++ b/tests/run-addrname-test.sh
@@ -298,4 +298,12 @@ __vdso_time
??:0
EOF
+testfiles testfile66
+testrun_compare ../src/addr2line -S -e testfile66 0x10340 0x250 <<\EOF
+func
+??:0
+.func
+??:0
+EOF
+
exit 0
diff --git a/tests/testfile66.bz2 b/tests/testfile66.bz2
new file mode 100755
index 0000000000000000000000000000000000000000..db07f25415729927f4e2b4985c5668c8e2ee10a2
GIT binary patch
literal 569
zcmV-90>=G9T4*^jL0KkKSrloeYybi*|NH;`Q+Gmd_t9I$azOv*{$$8R5dZ){00;sg
zN(3VzzyZ?W%~)z`O*V<ChK5r#X)zd>fu;fm0WvhiG}9)JQGyXYL8JhU5NHRe007X?
z$Qoh*0BMi_;L)Q%&}a;pfXL9u01Y(A003Zs0!0dH8USbj00000000000H3&^h5+h?
zHGH8oS#&k;tR%<0nFK6&-#1i4RnYl;-bpN9k2InZwNKHPIa5z$l?D9dtf^2;aFZ>;
z6H2`TaFp50B|IFU9;meM2nk4CXKdt!?6iqh`+DOs7204JFHHaexzT9oele)vNXcO|
z&aXz&bgUpHL5Q+$&J@iFQZPa%v>5P$Ld;?WwHgw?W$OZSUT)aw=i|cU?V{^+uO+T&
zsc1>77*MKpaR&z&l3<3FBTF`AA*kg!f+LXQdlF0z1|<OGVw|;E3MDuVYg}lF1kIB*
z2MO?2(vv|()kGjFhM>|aV;0;9Y&kKvJQ6|?q76u^Sl}`<LIO+NDfU1C1=+J>CXwR7
zvQ|1@iR(qMOPC<M!U)iAS_QSrP$be&teWK@Xh4y??kxH~E_K((z?!P4lv4z@9Y0sD
zK?cn^FLD~401iNn#iXf~Yy5H5(sqZpp=+#)n|DQ6r81NSyLB;KQD(6uE>?9m>Ok-8
z(YXp^O0BHJBpo)oV3bgUCvFNTfZoo@a?O8}_!iyT909f+z2<0!_i&kLzs1~<P81{s
H8flvV7_Id;
literal 0
HcmV?d00001