diff options
author | Jan Kratochvil <jan.kratochvil@redhat.com> | 2012-10-12 19:50:25 +0200 |
---|---|---|
committer | Jan Kratochvil <jan.kratochvil@redhat.com> | 2012-10-12 19:50:25 +0200 |
commit | 293d54bdc44f18bb7d2904ef9b751cdf32bd8424 (patch) | |
tree | 1a3433dac87994703a3452efb54974a9290ce5f6 | |
parent | 16bf256beb006e37acd12c940826df1fe11cac0a (diff) | |
parent | 95b126a9d0fe52c3e50219448bbcba008e172df2 (diff) | |
download | elfutils-293d54bdc44f18bb7d2904ef9b751cdf32bd8424.tar.gz |
Merge branch 'jankratochvil/ppc64-opd' into pending
-rw-r--r-- | backends/Makefile.am | 5 | ||||
-rw-r--r-- | backends/ppc64_get_func_pc.c | 195 | ||||
-rw-r--r-- | backends/ppc64_init.c | 2 | ||||
-rw-r--r-- | libdwfl/dwfl_module_addrsym.c | 195 | ||||
-rw-r--r-- | libebl/Makefile.am | 2 | ||||
-rw-r--r-- | libebl/ebl-hooks.h | 6 | ||||
-rw-r--r-- | libebl/eblgetfuncpc.c | 46 | ||||
-rw-r--r-- | libebl/libebl.h | 6 | ||||
-rw-r--r-- | libebl/libeblP.h | 3 | ||||
-rwxr-xr-x | tests/run-addrname-test.sh | 8 | ||||
-rwxr-xr-x | tests/testfile66.bz2 | bin | 0 -> 569 bytes |
11 files changed, 376 insertions, 92 deletions
diff --git a/backends/Makefile.am b/backends/Makefile.am index 982ff2a7..fa85593f 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 00000000..5038b00b --- /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 90d4f2ba..da7d02ce 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 fdc95fc0..f2c00d59 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 65e6b5b4..1ab08e7d 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 f629bce7..5651dd6a 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 00000000..57ef1597 --- /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 0d5621d6..34c245ef 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 5ec26a4b..c8196bd1 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 4feb1936..4dd6018f 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 Binary files differnew file mode 100755 index 00000000..db07f254 --- /dev/null +++ b/tests/testfile66.bz2 |