This is the mail archive of the
elfutils-devel@sourceware.org
mailing list for the elfutils project.
Re: [patch] Resolve ppc64 func descriptors as .func (via .opd)
- From: Jan Kratochvil <jan dot kratochvil at redhat dot com>
- To: elfutils-devel at lists dot fedorahosted dot org
- Date: Tue, 20 Nov 2012 14:12:24 +0100
- Subject: Re: [patch] Resolve ppc64 func descriptors as .func (via .opd)
Hi Mark,
jankratochvil/ppc64-opd3
On Mon, 19 Nov 2012 20:23:23 +0100, Mark Wielaard wrote:
> > /* Find the symbol that is nearest ADDRESS, and return its name. Return
> > the real symbol addressin *FUNCADDR. Return also possibly dereferenced
> > function descriptors. */
> > extern const char *dwfl_module_func_addrname (Dwfl_Module *mod,
> > GElf_Addr address,
> > GElf_Addr *funcaddr);
>
> Yes, or maybe an GElf_Off that provides the offset from the start of the
> named code block associated with the given address. Since I think the name
> and the offset from the start of the code block is what is usually needed.
> One can of course derive one from the other, so it doesn't really matter
> I think whether you provide the address or offset.
Chose GElf_Addr when you have left that option.
> > Finding just function types is not useful in practice, we need to find any
> > symbol type. And then the new interface fully supersedes the old interface.
>
> Maybe if we can express this generically as a "filter" that lets you select
> which symbols you are interested in and how to transform the matching
> function symbol value (through architecture specific hooks)?
> But I do think this is a different function from the generic symbol (name)
> one. This is to get code name/addresses.
Not sure what did you mean by the paragraph. The code picks now arbitrary
symbol type, the same like dwfl_module_addrname or dwfl_module_addrsym do.
Thanks,
Jan
commit 2263ce06754b2156fa39af355aebfb460b64ce51
Author: Jan Kratochvil <jan.kratochvil@redhat.com>
Date: Wed Oct 10 09:48:10 2012 +0200
./
* NEWS (Version 0.156): New, with one note.
backends/
* 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.
libdw/
* libdw.map (ELFUTILS_0.156): New.
libdwfl/
* dwfl_module_addrname.c (dwfl_module_codename): New function.
* dwfl_module_addrsym.c (dwfl_module_addrsym): Rename to ...
(addrsym): ... here, make it static and add new parameters
funcaddr_return and also_func_pc. New variable funcaddr_local, use it
for funcaddr_return.
(addrsym) (get_section): New function from ...
(addrsym) (same_section): ... here. Call it. Change parameter sym to
funcaddr, use it.
(addrsym): New variable sizeless_funcaddr.
(dwfl_module_addrsym) (found_sym): New function from ...
(dwfl_module_addrsym) (search_table): ... here. Call it. Use FUNCADDR
instead of SYM.ST_VALUE. Try second time with ebl_get_func_pc if
ALSO_FUNC_PC.
(addrsym): Use SIZELESS_FUNCADDR instead of SIZELESS_SYM.ST_VALUE.
Return also *FUNCADDR_RETURN.
(dwfl_module_addrsym, dwfl_module_codesym): New functions.
* libdwfl.h (dwfl_module_codename, dwfl_module_codesym): New
declarations.
* libdwflP.h (dwfl_module_codesym): New INTDECL.
libebl/
* 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.
src/
* addr2line.c (print_addrsym): Remove variables s and shndx, add
variable funcaddr. Call dwfl_module_codename instead of
dwfl_module_addrsym. Adjust the code for FUNCADDR.
(handle_address): Use dwfl_module_codename instead of
dwfl_module_addrsym.
* readelf.c (format_dwarf_addr): Remove variable sym, add variable
funcaddr. Call dwfl_module_codename instead of dwfl_module_addrsym.
Use FUNCADDR instead of SYM.
tests/
* run-addrname-test.sh: New testcase for ppc64 function descriptors.
* testfile66.bz2: New file.
* Makefile.am (EXTRA_DIST): Add testfile66.bz2.
Signed-off-by: Jan Kratochvil <jan.kratochvil@redhat.com>
diff --git a/ChangeLog b/ChangeLog
index a05ebdf..9c206bd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2012-11-20 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * NEWS (Version 0.156): New, with one note.
+
2012-10-01 Mark Wielaard <mjw@redhat.com>
* configure.ac: Add --enable-valgrind check.
diff --git a/NEWS b/NEWS
index e38e60c..c27c3e0 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,7 @@
+Version 0.156
+
+libdwfl: New functions dwfl_module_codename and dwfl_module_codesym.
+
Version 0.155
libelf: elf*_xlatetomd now works for cross-endian ELF note data.
diff --git a/backends/ChangeLog b/backends/ChangeLog
index cca7113..3e42989 100644
--- a/backends/ChangeLog
+++ b/backends/ChangeLog
@@ -1,3 +1,10 @@
+2012-11-20 Jan Kratochvil <jan.kratochvil@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.
+
2012-10-12 Jan Kratochvil <jan.kratochvil@redhat.com>
* linux-core-note.c (prstatus_items): Rename groups of sigpend and
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..98d3ecb
--- /dev/null
+++ b/backends/ppc64_get_func_pc.c
@@ -0,0 +1,196 @@
+/* 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 must be the very first element for the use in bsearch below. */
+ GElf_Sym sym;
+ Elf64_Addr funcaddr;
+ const char *new_name;
+};
+
+struct pc_table
+{
+ size_t nelem;
+ struct pc_entry a[];
+ /* Here follow strings allocated for pc_entry->new_name. */
+};
+
+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, &b->sym, sizeof (a->sym));
+}
+
+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 = sym;
+ dest->funcaddr = val64 + symbias;
+ dest->new_name = 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, const GElf_Sym *sym,
+ GElf_Addr *funcaddr)
+{
+ 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;
+ *funcaddr = found->funcaddr;
+ return found->new_name;
+}
+
+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/libdw/ChangeLog b/libdw/ChangeLog
index 0d35ca9..e2b691b 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,7 @@
+2012-11-20 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * libdw.map (ELFUTILS_0.156): New.
+
2012-10-09 Petr Machata <pmachata@redhat.com>
* dwarf_getlocation.c (__libdw_intern_expression): Handle
diff --git a/libdw/libdw.map b/libdw/libdw.map
index 1f71d03..6e90e15 100644
--- a/libdw/libdw.map
+++ b/libdw/libdw.map
@@ -254,3 +254,9 @@ ELFUTILS_0.149 {
dwfl_dwarf_line;
} ELFUTILS_0.148;
+
+ELFUTILS_0.156 {
+ global:
+ dwfl_module_codename;
+ dwfl_module_codesym;
+} ELFUTILS_0.149;
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index bdd9440..355e664 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -1,3 +1,25 @@
+2012-11-20 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * dwfl_module_addrname.c (dwfl_module_codename): New function.
+ * dwfl_module_addrsym.c (dwfl_module_addrsym): Rename to ...
+ (addrsym): ... here, make it static and add new parameters
+ funcaddr_return and also_func_pc. New variable funcaddr_local, use it
+ for funcaddr_return.
+ (addrsym) (get_section): New function from ...
+ (addrsym) (same_section): ... here. Call it. Change parameter sym to
+ funcaddr, use it.
+ (addrsym): New variable sizeless_funcaddr.
+ (dwfl_module_addrsym) (found_sym): New function from ...
+ (dwfl_module_addrsym) (search_table): ... here. Call it. Use FUNCADDR
+ instead of SYM.ST_VALUE. Try second time with ebl_get_func_pc if
+ ALSO_FUNC_PC.
+ (addrsym): Use SIZELESS_FUNCADDR instead of SIZELESS_SYM.ST_VALUE.
+ Return also *FUNCADDR_RETURN.
+ (dwfl_module_addrsym, dwfl_module_codesym): New functions.
+ * libdwfl.h (dwfl_module_codename, dwfl_module_codesym): New
+ declarations.
+ * libdwflP.h (dwfl_module_codesym): New INTDECL.
+
2012-10-17 Jan Kratochvil <jan.kratochvil@redhat.com>
* dwfl_module_getdwarf.c (mod_verify_build_id): New function with code
diff --git a/libdwfl/dwfl_module_addrname.c b/libdwfl/dwfl_module_addrname.c
index 6ae0123..5a8f228 100644
--- a/libdwfl/dwfl_module_addrname.c
+++ b/libdwfl/dwfl_module_addrname.c
@@ -34,3 +34,12 @@ dwfl_module_addrname (Dwfl_Module *mod, GElf_Addr addr)
GElf_Sym sym;
return INTUSE(dwfl_module_addrsym) (mod, addr, &sym, NULL);
}
+
+const char *
+dwfl_module_codename (Dwfl_Module *mod, GElf_Addr address, GElf_Addr *funcaddr)
+{
+ GElf_Sym sym;
+ const char *name = INTUSE(dwfl_module_codesym) (mod, address, &sym, NULL,
+ funcaddr);
+ return name;
+}
diff --git a/libdwfl/dwfl_module_addrsym.c b/libdwfl/dwfl_module_addrsym.c
index fdc95fc..4014494 100644
--- a/libdwfl/dwfl_module_addrsym.c
+++ b/libdwfl/dwfl_module_addrsym.c
@@ -31,41 +31,46 @@
/* Returns the name of the symbol "closest" to ADDR.
Never returns symbols at addresses above ADDR. */
-const char *
-dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr,
- GElf_Sym *closest_sym, GElf_Word *shndxp)
+static const char *
+addrsym (Dwfl_Module *mod, GElf_Addr addr, GElf_Sym *closest_sym,
+ GElf_Word *shndxp, GElf_Addr *funcaddr_return, bool also_func_pc)
{
int syments = INTUSE(dwfl_module_getsymtab) (mod);
if (syments < 0)
return NULL;
+ GElf_Addr funcaddr_local;
+ if (funcaddr_return == NULL)
+ funcaddr_return = &funcaddr_local;
+
+ /* 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)
+ inline bool same_section (GElf_Addr funcaddr, GElf_Word shndx)
{
/* For absolute symbols and the like, only match exactly. */
if (shndx >= SHN_LORESERVE)
- return sym->st_value == addr;
+ return funcaddr == 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;
}
@@ -79,95 +84,117 @@ dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr,
const char *sizeless_name = NULL;
GElf_Sym sizeless_sym = { 0, 0, 0, 0, 0, SHN_UNDEF };
GElf_Word sizeless_shndx = SHN_UNDEF;
+ GElf_Addr sizeless_funcaddr = 0;
/* 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,
+ GElf_Addr funcaddr)
{
- for (int i = start; i < end; ++i)
+ if (name != NULL && name[0] != '\0'
+ && sym->st_shndx != SHN_UNDEF
+ && funcaddr <= 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 (funcaddr + sym->st_size > min_label)
+ min_label = funcaddr + sym->st_size;
- if (sym.st_size == 0 || addr - sym.st_value < sym.st_size)
+ if (sym->st_size == 0 || addr - funcaddr < 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
+ || *funcaddr_return < funcaddr
+ || binding_value (closest_sym) < binding_value (sym))
+ {
+ if (sym->st_size != 0)
{
- *closest_sym = sym;
+ *closest_sym = *sym;
closest_shndx = shndx;
closest_name = name;
+ *funcaddr_return = funcaddr;
+ }
+ else if (closest_name == NULL
+ && funcaddr >= min_label
+ && same_section (funcaddr, 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;
+ sizeless_funcaddr = funcaddr;
}
}
+ /* 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
+ && *funcaddr_return == funcaddr
+ && ((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;
+ *funcaddr_return = funcaddr;
+ }
}
}
}
+ /* 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, sym.st_value);
+ if (!also_func_pc || name == NULL)
+ continue;
+ Dwfl_Error error = __libdwfl_module_getebl (mod);
+ if (error != DWFL_E_NOERROR)
+ continue;
+ GElf_Addr funcaddr;
+ name = ebl_get_func_pc (mod->ebl, mod, &sym, &funcaddr);
+ if (name == NULL)
+ continue;
+ shndx = get_section (funcaddr);
+ found_sym (&sym, shndx, name, funcaddr);
+ }
+ }
+
/* 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
@@ -177,21 +204,37 @@ dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr,
/* 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
- && (sizeless_name == NULL || sizeless_sym.st_value != addr))
+ && (sizeless_name == NULL || sizeless_funcaddr != addr))
search_table (1, mod->first_global);
/* If we found no proper sized symbol to use, fall back to the best
candidate sizeless symbol we found, if any. */
if (closest_name == NULL
- && sizeless_name != NULL && sizeless_sym.st_value >= min_label)
+ && sizeless_name != NULL && sizeless_funcaddr >= min_label)
{
*closest_sym = sizeless_sym;
closest_shndx = sizeless_shndx;
closest_name = sizeless_name;
+ *funcaddr_return = sizeless_funcaddr;
}
if (shndxp != NULL)
*shndxp = closest_shndx;
return closest_name;
}
+
+const char *
+dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr, GElf_Sym *closest_sym,
+ GElf_Word *shndxp)
+{
+ return addrsym (mod, addr, closest_sym, shndxp, NULL, false);
+}
INTDEF (dwfl_module_addrsym)
+
+const char *
+dwfl_module_codesym (Dwfl_Module *mod, GElf_Addr address, GElf_Sym *closest_sym,
+ GElf_Word *shndxp, GElf_Addr *funcaddr)
+{
+ return addrsym (mod, address, closest_sym, shndxp, funcaddr, true);
+}
+INTDEF (dwfl_module_codesym)
diff --git a/libdwfl/libdwfl.h b/libdwfl/libdwfl.h
index 000d582..5eb585a 100644
--- a/libdwfl/libdwfl.h
+++ b/libdwfl/libdwfl.h
@@ -424,12 +424,27 @@ extern const char *dwfl_module_getsym (Dwfl_Module *mod, int ndx,
/* Find the symbol that ADDRESS lies inside, and return its name. */
extern const char *dwfl_module_addrname (Dwfl_Module *mod, GElf_Addr address);
+/* Find the symbol that ADDRESS lies inside, and return its name.
+ Return also its starting code addrees *FUNCADDR. This function can
+ handle function descriptors, contrary to dwfl_module_addrname. */
+extern const char *dwfl_module_codename (Dwfl_Module *mod, GElf_Addr address,
+ GElf_Addr *funcaddr);
+
/* Find the symbol that ADDRESS lies inside, and return detailed
information as for dwfl_module_getsym (above). */
extern const char *dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr address,
GElf_Sym *sym, GElf_Word *shndxp)
__nonnull_attribute__ (3);
+/* Find the symbol that ADDRESS lies inside, and return detailed
+ information as for dwfl_module_getsym (above). Return also its
+ starting code addrees *FUNCADDR. This function can handle function
+ descriptors, contrary to dwfl_module_addrsym. */
+extern const char *dwfl_module_codesym (Dwfl_Module *mod, GElf_Addr address,
+ GElf_Sym *sym, GElf_Word *shndxp,
+ GElf_Addr *funcaddr)
+ __nonnull_attribute__ (3);
+
/* Find the ELF section that *ADDRESS lies inside and return it.
On success, adjusts *ADDRESS to be relative to the section,
and sets *BIAS to the difference between addresses used in
diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h
index ca8be2f..d37261c 100644
--- a/libdwfl/libdwflP.h
+++ b/libdwfl/libdwflP.h
@@ -478,6 +478,7 @@ INTDECL (dwfl_offline_section_address)
INTDECL (dwfl_module_relocate_address)
INTDECL (dwfl_module_dwarf_cfi)
INTDECL (dwfl_module_eh_cfi)
+INTDECL (dwfl_module_codesym)
/* Leading arguments standard to callbacks passed a Dwfl_Module. */
#define MODCB_ARGS(mod) (mod), &(mod)->userdata, (mod)->name, (mod)->low_addr
diff --git a/libebl/ChangeLog b/libebl/ChangeLog
index e881ce7..004ff9d 100644
--- a/libebl/ChangeLog
+++ b/libebl/ChangeLog
@@ -1,3 +1,12 @@
+2012-11-20 Jan Kratochvil <jan.kratochvil@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.
+
2012-10-12 Jan Kratochvil <jan.kratochvil@redhat.com>
* ebl-hooks.h (abi_cfi): Extend its comment for return value.
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 d3cf3e6..967d261 100644
--- a/libebl/ebl-hooks.h
+++ b/libebl/ebl-hooks.h
@@ -155,5 +155,11 @@ int EBLHOOK(disasm) (const uint8_t **startp, const uint8_t *end,
Function returns 0 on success and -1 on error. */
int EBLHOOK(abi_cfi) (Ebl *ebl, Dwarf_CIE *abi_info);
+/* Find for function descriptor SYM its starting code address *FUNCADDR
+ and return its new name. This function implements handling of
+ function descriptors. The name is valid as long as EBL is valid. */
+const char *EBLHOOK(get_func_pc) (Ebl *ebl, struct Dwfl_Module *mod,
+ const GElf_Sym *sym, GElf_Addr *funcaddr);
+
/* 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..0f5795f
--- /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, const GElf_Sym *sym,
+ GElf_Addr *funcaddr)
+{
+ if (ebl == NULL)
+ return NULL;
+ assert (sym != NULL);
+ if (ebl->get_func_pc == NULL)
+ return NULL;
+ return ebl->get_func_pc (ebl, mod, sym, funcaddr);
+}
diff --git a/libebl/libebl.h b/libebl/libebl.h
index cae31c9..a677887 100644
--- a/libebl/libebl.h
+++ b/libebl/libebl.h
@@ -378,6 +378,14 @@ extern int ebl_auxv_info (Ebl *ebl, GElf_Xword a_type,
const char **name, const char **format)
__nonnull_attribute__ (1, 3, 4);
+/* Find for function descriptor SYM its starting code address *FUNCADDR
+ and return its new name. This function implements handling of
+ function descriptors. */
+struct Dwfl_Module;
+extern const char *ebl_get_func_pc (Ebl *ebl, struct Dwfl_Module *mod,
+ const GElf_Sym *sym, GElf_Addr *funcaddr)
+ __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/src/ChangeLog b/src/ChangeLog
index 9d6fbe3..d48fc90 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,14 @@
+2012-11-20 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * addr2line.c (print_addrsym): Remove variables s and shndx, add
+ variable funcaddr. Call dwfl_module_codename instead of
+ dwfl_module_addrsym. Adjust the code for FUNCADDR.
+ (handle_address): Use dwfl_module_codename instead of
+ dwfl_module_addrsym.
+ * readelf.c (format_dwarf_addr): Remove variable sym, add variable
+ funcaddr. Call dwfl_module_codename instead of dwfl_module_addrsym.
+ Use FUNCADDR instead of SYM.
+
2012-10-12 Jan Kratochvil <jan.kratochvil@redhat.com>
* readelf.c (ITEM_WRAP_COLUMN, REGISTER_WRAP_COLUMN): Merge to ...
diff --git a/src/addr2line.c b/src/addr2line.c
index 7d241f3..54b2898 100644
--- a/src/addr2line.c
+++ b/src/addr2line.c
@@ -326,9 +326,8 @@ print_dwarf_function (Dwfl_Module *mod, Dwarf_Addr addr)
static void
print_addrsym (Dwfl_Module *mod, GElf_Addr addr)
{
- GElf_Sym s;
- GElf_Word shndx;
- const char *name = dwfl_module_addrsym (mod, addr, &s, &shndx);
+ GElf_Addr funcaddr;
+ const char *name = dwfl_module_codename (mod, addr, &funcaddr);
if (name == NULL)
{
/* No symbol name. Get a section name instead. */
@@ -340,10 +339,10 @@ print_addrsym (Dwfl_Module *mod, GElf_Addr addr)
else
printf ("(%s)+%#" PRIx64 "\n", name, addr);
}
- else if (addr == s.st_value)
+ else if (addr == funcaddr)
puts (name);
else
- printf ("%s+%#" PRIx64 "\n", name, addr - s.st_value);
+ printf ("%s+%#" PRIx64 "\n", name, addr - funcaddr);
}
static int
@@ -495,7 +494,7 @@ handle_address (const char *string, Dwfl *dwfl)
/* First determine the function name. Use the DWARF information if
possible. */
if (! print_dwarf_function (mod, addr) && !show_symbols)
- puts (dwfl_module_addrname (mod, addr) ?: "??");
+ puts (dwfl_module_codename (mod, addr, NULL) ?: "??");
}
if (show_symbols)
diff --git a/src/readelf.c b/src/readelf.c
index 1801c1c..a9ab070 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -3114,11 +3114,11 @@ format_dwarf_addr (Dwfl_Module *dwflmod,
int address_size, Dwarf_Addr address)
{
/* See if there is a name we can give for this address. */
- GElf_Sym sym;
+ GElf_Addr funcaddr;
const char *name = print_address_names
- ? dwfl_module_addrsym (dwflmod, address, &sym, NULL) : NULL;
+ ? dwfl_module_codename (dwflmod, address, &funcaddr) : NULL;
if (name != NULL)
- sym.st_value = address - sym.st_value;
+ funcaddr = address - funcaddr;
/* Relativize the address. */
int n = dwfl_module_relocations (dwflmod);
@@ -3130,24 +3130,24 @@ format_dwarf_addr (Dwfl_Module *dwflmod,
char *result;
if ((name != NULL
- ? (sym.st_value != 0
+ ? (funcaddr != 0
? (scn != NULL
? (address_size == 0
? asprintf (&result,
gettext ("%s+%#" PRIx64 " <%s+%#" PRIx64 ">"),
- scn, address, name, sym.st_value)
+ scn, address, name, funcaddr)
: asprintf (&result,
gettext ("%s+%#0*" PRIx64 " <%s+%#" PRIx64 ">"),
scn, 2 + address_size * 2, address,
- name, sym.st_value))
+ name, funcaddr))
: (address_size == 0
? asprintf (&result,
gettext ("%#" PRIx64 " <%s+%#" PRIx64 ">"),
- address, name, sym.st_value)
+ address, name, funcaddr)
: asprintf (&result,
gettext ("%#0*" PRIx64 " <%s+%#" PRIx64 ">"),
2 + address_size * 2, address,
- name, sym.st_value)))
+ name, funcaddr)))
: (scn != NULL
? (address_size == 0
? asprintf (&result,
diff --git a/tests/ChangeLog b/tests/ChangeLog
index edb82b4..7320624 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,9 @@
+2012-11-20 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * run-addrname-test.sh: New testcase for ppc64 function descriptors.
+ * testfile66.bz2: New file.
+ * Makefile.am (EXTRA_DIST): Add testfile66.bz2.
+
2012-10-27 Jan Kratochvil <jan.kratochvil@redhat.com>
* Makefile.am (EXTRA_DIST): Add testfile64.bz2, testfile65.bz2,
diff --git a/tests/Makefile.am b/tests/Makefile.am
index f2d2484..975ef5c 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -180,7 +180,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
testfile60.bz2 testfile61.bz2 \
run-readelf-vmcoreinfo.sh testfile62.bz2 \
run-readelf-mixed-corenote.sh testfile63.bz2 testfile64.bz2 \
- testfile65.bz2 testfile69.core.bz2 testfile69.so.bz2
+ testfile65.bz2 testfile69.core.bz2 testfile69.so.bz2 testfile66.bz2
if USE_VALGRIND
valgrind_cmd="valgrind -q --trace-children=yes --error-exitcode=1"
diff --git a/tests/run-addrname-test.sh b/tests/run-addrname-test.sh
index cc8aa33..78c70f0 100755
--- a/tests/run-addrname-test.sh
+++ b/tests/run-addrname-test.sh
@@ -298,6 +298,14 @@ __vdso_time
??:0
EOF
+testfiles testfile66
+testrun_compare ../src/addr2line -S -e testfile66 0x10340 0x250 <<\EOF
+func
+??:0
+.func
+??:0
+EOF
+
testfiles testfile69.core testfile69.so
testrun_compare ../src/addr2line --core=./testfile69.core -S 0x7f0bc6a33535 0x7f0bc6a33546 <<\EOF
libstatic+0x9
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(a)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(a)iR(qMOPC<M!U)iAS_QSrP$be&teWK(a)Xh4y??kxH~E_K((z?!P4lv4z(a)9Y0sD
zK?cn^FLD~401iNn#iXf~Yy5H5(sqZpp=+#)n|DQ6r81NSyLB;KQD(6uE>?9m>Ok-8
z(YXp^O0BHJBpo)oV3bgUCvFNTfZoo(a)a?O8}_!iyT909f+z2<0!_i&kLzs1~<P81{s
H8flvV7_Id;
literal 0
HcmV?d00001