From beee55f6d64ddbdfb2fe1ff9c0afc7000e01dc25 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Tue, 29 Jun 2010 18:47:17 -0700 Subject: [PATCH] Wire libdw relocation support into libdwfl. --- libdw/ChangeLog | 10 ++++ libdw/libdwP.h | 4 +- libdw/relocate.c | 81 ++++++++------------------ libdw/relocate.h | 8 +++ libdwfl/ChangeLog | 15 +++++ libdwfl/derelocate.c | 32 +++-------- libdwfl/dwfl_module.c | 12 +++- libdwfl/dwfl_module_getdwarf.c | 31 +++++++++- libdwfl/dwfl_module_getsym.c | 5 +- libdwfl/dwfl_report_elf.c | 2 + libdwfl/libdwflP.h | 1 + libdwfl/relocate.c | 100 +++++++++++++++++++++++++++++++++ 12 files changed, 209 insertions(+), 92 deletions(-) diff --git a/libdw/ChangeLog b/libdw/ChangeLog index 0d1854c6f..c51abc604 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,5 +1,15 @@ 2010-06-29 Roland McGrath + * libdwP.h (dwarf_file_reloc): Remove resolve_symbol member, + add dwflmod member instead. + * relocate.h: Declare __libdwfl_relocate_setup and + __libdwfl_relocate_symbol. + * relocate.c (noresolve_symbol): Removed. + (__libdw_relocate_begin): Initialize dwflmod instead of resolve_symbol. + (__libdw_relocate_shndx): Removed, now in libdwfl. + (digest_relocs): Call __libdwfl_relocate_setup. + (__libdw_relocate_address): Call __libdwfl_relocate_symbol. + * dwarf_lineaddr.c: Add INTDEF. * libdwP.h: Add INTDECL. diff --git a/libdw/libdwP.h b/libdw/libdwP.h index 8d0cb6ad2..e7f66a2ff 100644 --- a/libdw/libdwP.h +++ b/libdw/libdwP.h @@ -218,9 +218,7 @@ struct dwarf_file_reloc struct ebl *ebl; - int (*resolve_symbol) (bool undef, Dwarf *dbg, - GElf_Sym *sym, GElf_Word shndx) - internal_function; + struct Dwfl_Module *dwflmod; }; diff --git a/libdw/relocate.c b/libdw/relocate.c index a7ee6b750..8ac07ff92 100644 --- a/libdw/relocate.c +++ b/libdw/relocate.c @@ -58,17 +58,6 @@ #include -static int -internal_function -noresolve_symbol (bool undef, - Dwarf *dbg __attribute__ ((unused)), - GElf_Sym *sym __attribute__ ((unused)), - GElf_Word shndx __attribute__ ((unused))) -{ - __libdw_seterrno (undef ? DWARF_E_RELUNDEF : DWARF_E_RELOC); - return -1; -} - void internal_function __libdw_relocate_begin (Dwarf *dbg, Elf_Scn *relscn[IDX_last], bool incomplete) @@ -96,7 +85,7 @@ __libdw_relocate_begin (Dwarf *dbg, Elf_Scn *relscn[IDX_last], bool incomplete) dbg->relocate = libdw_typed_alloc (dbg, struct dwarf_file_reloc); dbg->relocate->ebl = NULL; - dbg->relocate->resolve_symbol = &noresolve_symbol; + dbg->relocate->dwflmod = NULL; /* All we do to start with is cache the section pointers. We'll do the rest on demand in digest_relocs, below. */ @@ -112,7 +101,6 @@ void internal_function __libdw_relocate_end (Dwarf *dbg) { - // XXX let dwfl preinstall, don't close here ebl_closebackend (dbg->relocate->ebl); } @@ -240,27 +228,24 @@ digest_relocs (Dwarf *dbg, Elf_Data *data, struct dwarf_section_reloc *r) if (unlikely (gelf_getshdr (r->scn, &shdr) == NULL)) assert (!"impossible gelf_getshdr failure"); - /* XXX let dwfl supply defaults from main file for separate debug - with relocs pointing to SHT_NOBITS symtab - r->symdata = dbg->relocate->symdata; - r->symstrdata = dbg->relocate->symstrdata; - */ - { - GElf_Shdr symshdr; - Elf_Scn *const symscn = elf_getscn (r->scn->elf, shdr.sh_link); - if (unlikely (gelf_getshdr (symscn, &symshdr) == NULL)) - return DWARF_E_RELBADSYM; - if (symshdr.sh_type != SHT_NOBITS) - { - r->symdata = elf_getdata (symscn, NULL); - if (unlikely (r->symdata == NULL)) - return DWARF_E_RELBADSYM; - r->symstrdata = elf_getdata (elf_getscn (r->scn->elf, symshdr.sh_link), - NULL); - if (unlikely (r->symstrdata == NULL)) - return DWARF_E_RELBADSYM; - } - } + /* Look for the symtab section this reloc section refers to. */ + GElf_Shdr symshdr; + Elf_Scn *const symscn = elf_getscn (r->scn->elf, shdr.sh_link); + if (likely (gelf_getshdr (symscn, &symshdr) != NULL) + && symshdr.sh_type != SHT_NOBITS) + { + r->symdata = elf_getdata (symscn, NULL); + r->symstrdata = elf_getdata (elf_getscn (r->scn->elf, symshdr.sh_link), + NULL); + } + + /* Let libdwfl set up ebl and symtab pointers if it can. */ + if (dbg->relocate->dwflmod != NULL) + __libdwfl_relocate_setup (dbg, r); + + /* Fail if neither direct pointers nor libdwfl backup found a symtab. */ + if (unlikely (r->symdata == NULL) || unlikely (r->symstrdata == NULL)) + return DWARF_E_RELBADSYM; if (dbg->relocate->ebl == NULL) { @@ -478,22 +463,6 @@ __libdw_relocatable_getsym (Dwarf *dbg, int sec_index, return result; } -int -internal_function -__libdw_relocate_shndx (Dwarf *dbg, GElf_Word shndx, GElf_Sxword addend, - Dwarf_Addr *val) -{ - GElf_Sym sym = - { - .st_shndx = shndx < SHN_LORESERVE ? shndx : SHN_XINDEX, - .st_info = GELF_ST_INFO (STB_LOCAL, STT_SECTION), - }; - int result = (*dbg->relocate->resolve_symbol) (false, dbg, &sym, shndx); - if (result == 0) - *val = sym.st_value + addend; - return result; -} - int internal_function __libdw_relocate_address (Dwarf *dbg, int sec_index, @@ -506,13 +475,11 @@ __libdw_relocate_address (Dwarf *dbg, int sec_index, int result = __libdw_relocatable_getsym (dbg, sec_index, datum, width, &symndx, &sym, &shndx, &addend); if (result > 0 && symndx != STN_UNDEF) - { - result = (*dbg->relocate->resolve_symbol) - (sym.st_shndx == SHN_UNDEF || sym.st_shndx == SHN_COMMON - || GELF_ST_BIND (sym.st_info) > STB_WEAK, - dbg, &sym, sym.st_shndx == SHN_XINDEX ? shndx : sym.st_shndx); - addend += sym.st_value; - } + result = __libdwfl_relocate_symbol + (dbg->relocate->sectionrel[sec_index], + sym.st_shndx == SHN_UNDEF || sym.st_shndx == SHN_COMMON + || GELF_ST_BIND (sym.st_info) > STB_WEAK, + dbg, &sym, sym.st_shndx == SHN_XINDEX ? shndx : sym.st_shndx, &addend); if (result >= 0) *val = addend; return result; diff --git a/libdw/relocate.h b/libdw/relocate.h index 6c9751879..495558bd4 100644 --- a/libdw/relocate.h +++ b/libdw/relocate.h @@ -105,3 +105,11 @@ extern ptrdiff_t __libdw_ranges_relocatable (struct Dwarf_CU *cu, Dwarf_Relocatable *endp, Dwarf_Block *exprloc) __nonnull_attribute__ (1, 4, 5, 6) internal_function; + +extern void __libdwfl_relocate_setup (Dwarf *dbg, struct dwarf_section_reloc *r) + __nonnull_attribute__ (1, 2) internal_function; + +extern int __libdwfl_relocate_symbol (struct dwarf_section_reloc *r, bool undef, + Dwarf *dbg, GElf_Sym *sym, + GElf_Word shndx, GElf_Sxword *addend) + __nonnull_attribute__ (1, 3, 4) internal_function; diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog index 38cf62a08..19a5eb181 100644 --- a/libdwfl/ChangeLog +++ b/libdwfl/ChangeLog @@ -1,5 +1,20 @@ 2010-06-29 Roland McGrath + * derelocate.c (check_module): Don't call dwfl_module_getdwarf. + + * dwfl_module_getdwarf.c (__libdwfl_relocate_setup): New function. + (load_dw): Set MOD->dw->relocate->dwflmod. + * dwfl_module.c (__libdwfl_module_free): Clear MOD->dw->relocate->ebl + if we set it. + * relocate.c (__libdw_relocate_shndx): New function. + (__libdwfl_relocate_symbol): New function. + + * libdwflP.h (struct dwfl_file): Add member shstrndx. + * dwfl_module_getdwarf.c (open_elf): Set it. + * dwfl_report_elf.c (__libdwfl_report_elf): Likewise. + * derelocate.c (cache_sections): Use it. + * dwfl_module_getsym.c: Likewise. + * dwfl_lineinfo.c: Use dwarf_lineaddr. * relocate.c (relocate_section): Remove PARTIAL flag argument. diff --git a/libdwfl/derelocate.c b/libdwfl/derelocate.c index ee869e061..25b2c37d4 100644 --- a/libdwfl/derelocate.c +++ b/libdwfl/derelocate.c @@ -93,14 +93,6 @@ cache_sections (Dwfl_Module *mod) struct secref *refs = NULL; size_t nrefs = 0; - size_t shstrndx; - if (unlikely (elf_getshdrstrndx (mod->main.elf, &shstrndx) < 0)) - { - elf_error: - __libdwfl_seterrno (DWFL_E_LIBELF); - return -1; - } - bool check_reloc_sections = false; Elf_Scn *scn = NULL; while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL) @@ -108,13 +100,17 @@ cache_sections (Dwfl_Module *mod) GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); if (shdr == NULL) - goto elf_error; + { + elf_error: + __libdwfl_seterrno (DWFL_E_LIBELF); + return -1; + } if ((shdr->sh_flags & SHF_ALLOC) && shdr->sh_addr == 0 && mod->e_type == ET_REL) { /* This section might not yet have been looked at. */ - if (__libdwfl_relocate_value (mod, mod->main.elf, &shstrndx, + if (__libdwfl_relocate_value (mod, mod->main.elf, &mod->main.shstrndx, elf_ndxscn (scn), &shdr->sh_addr) != DWFL_E_NOERROR) continue; @@ -125,7 +121,7 @@ cache_sections (Dwfl_Module *mod) if (shdr->sh_flags & SHF_ALLOC) { - const char *name = elf_strptr (mod->main.elf, shstrndx, + const char *name = elf_strptr (mod->main.elf, mod->main.shstrndx, shdr->sh_name); if (unlikely (name == NULL)) goto elf_error; @@ -298,20 +294,6 @@ check_module (Dwfl_Module *mod) } } - if (mod->dw == NULL) - { - Dwarf_Addr bias; - if (INTUSE(dwfl_module_getdwarf) (mod, &bias) == NULL) - { - Dwfl_Error error = dwfl_errno (); - if (error != DWFL_E_NO_DWARF) - { - __libdwfl_seterrno (error); - return true; - } - } - } - return false; } diff --git a/libdwfl/dwfl_module.c b/libdwfl/dwfl_module.c index d7e541386..4dbb1fccc 100644 --- a/libdwfl/dwfl_module.c +++ b/libdwfl/dwfl_module.c @@ -1,5 +1,5 @@ /* Maintenance of module list in libdwfl. - Copyright (C) 2005, 2006, 2007, 2008 Red Hat, Inc. + Copyright (C) 2005-2010 Red Hat, Inc. This file is part of Red Hat elfutils. Red Hat elfutils is free software; you can redistribute it and/or modify @@ -92,7 +92,15 @@ __libdwfl_module_free (Dwfl_Module *mod) } if (mod->dw != NULL) - INTUSE(dwarf_end) (mod->dw); + { + if (mod->dw->relocate != NULL) + { + if (mod->dw->relocate->ebl == mod->ebl) + mod->dw->relocate->ebl = NULL; + } + + INTUSE(dwarf_end) (mod->dw); + } if (mod->ebl != NULL) ebl_closebackend (mod->ebl); diff --git a/libdwfl/dwfl_module_getdwarf.c b/libdwfl/dwfl_module_getdwarf.c index 42d817a4e..7f4dc062b 100644 --- a/libdwfl/dwfl_module_getdwarf.c +++ b/libdwfl/dwfl_module_getdwarf.c @@ -51,7 +51,7 @@ #include #include #include -#include "../libdw/libdwP.h" /* DWARF_E_* values are here. */ +#include "../libdw/relocate.h" /* Open libelf FILE->fd and compute the load base of ELF as loaded in MOD. @@ -93,6 +93,9 @@ open_elf (Dwfl_Module *mod, struct dwfl_file *file) return DWFL_E (LIBELF, elf_errno ()); } + if (unlikely (elf_getshdrstrndx (file->elf, &file->shstrndx))) + file->shstrndx = SHN_UNDEF; + /* The addresses in an ET_EXEC file are absolute. The lowest p_vaddr of the main file can differ from that of the debug file due to prelink. But that doesn't not change addresses that symbols, debuginfo, or @@ -679,7 +682,7 @@ load_dw (Dwfl_Module *mod, struct dwfl_file *debugfile) if (mod->dw->relocate != NULL) { assert (mod->e_type == ET_REL); - // mod->dw->relocate->dwflmod = mod; + mod->dw->relocate->dwflmod = mod; } /* Until we have iterated through all CU's, we might do lazy lookups. */ @@ -688,6 +691,30 @@ load_dw (Dwfl_Module *mod, struct dwfl_file *debugfile) return DWFL_E_NOERROR; } +/* This is called from libdw/relocate.c when we need to digest relocs. */ +void +internal_function +__libdwfl_relocate_setup (Dwarf *dbg, struct dwarf_section_reloc *r) +{ + Dwfl_Module *const mod = dbg->relocate->dwflmod; + + assert (mod->dw == dbg); + + if (dbg->relocate->ebl == NULL) + { + (void) __libdwfl_module_getebl (mod); + dbg->relocate->ebl = mod->ebl; + } + + if (r->symdata == NULL) + { + find_symtab (mod); + r->symdata = mod->symdata; + r->symstrdata = mod->symstrdata; + r->symxndxdata = mod->symxndxdata; + } +} + /* Try to start up libdw on either the main file or the debuginfo file. */ static void find_dw (Dwfl_Module *mod) diff --git a/libdwfl/dwfl_module_getsym.c b/libdwfl/dwfl_module_getsym.c index f78e6ec03..8cf207840 100644 --- a/libdwfl/dwfl_module_getsym.c +++ b/libdwfl/dwfl_module_getsym.c @@ -1,5 +1,5 @@ /* Find debugging and symbol information for a module in libdwfl. - Copyright (C) 2006,2007,2009 Red Hat, Inc. + Copyright (C) 2006-2010 Red Hat, Inc. This file is part of Red Hat elfutils. Red Hat elfutils is free software; you can redistribute it and/or modify @@ -102,9 +102,8 @@ dwfl_module_getsym (Dwfl_Module *mod, int ndx, { /* In an ET_REL file, the symbol table values are relative to the section, not to the module's load base. */ - size_t symshstrndx = SHN_UNDEF; Dwfl_Error result = __libdwfl_relocate_value (mod, mod->symfile->elf, - &symshstrndx, + &mod->symfile->shstrndx, shndx, &sym->st_value); if (unlikely (result != DWFL_E_NOERROR)) { diff --git a/libdwfl/dwfl_report_elf.c b/libdwfl/dwfl_report_elf.c index 062a647fd..0a10ad1b7 100644 --- a/libdwfl/dwfl_report_elf.c +++ b/libdwfl/dwfl_report_elf.c @@ -250,6 +250,8 @@ __libdwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name, m->main.elf = elf; m->main.bias = bias; m->e_type = ehdr->e_type; + if (unlikely (elf_getshdrstrndx (elf, &m->main.shstrndx))) + m->main.shstrndx = SHN_UNDEF; } else { diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h index 504b0fb1c..555c5bf04 100644 --- a/libdwfl/libdwflP.h +++ b/libdwfl/libdwflP.h @@ -140,6 +140,7 @@ struct dwfl_file bool relocated; /* Partial relocation of all sections done. */ Elf *elf; + size_t shstrndx; /* Cache of elf_getshdrstrndx on elf. */ GElf_Addr bias; /* Actual load address - p_vaddr. */ }; diff --git a/libdwfl/relocate.c b/libdwfl/relocate.c index 0313749a1..b89de37ef 100644 --- a/libdwfl/relocate.c +++ b/libdwfl/relocate.c @@ -48,6 +48,7 @@ . */ #include "libdwflP.h" +#include "../libdw/relocate.h" typedef uint8_t GElf_Byte; @@ -615,3 +616,102 @@ __libdwfl_relocate_section (Dwfl_Module *mod, Elf *relocated, relocscn, gelf_getshdr (relocscn, &shdr_mem), tscn, false)); } + +int +internal_function +__libdw_relocate_shndx (Dwarf *dbg, GElf_Word shndx, GElf_Sxword addend, + Dwarf_Addr *val) +{ + Dwfl_Module *const mod = dbg->relocate->dwflmod; + + if (unlikely (mod == NULL) + || unlikely (mod->dwfl->callbacks->section_address == NULL)) + { + __libdw_seterrno (DWARF_E_RELOC); + return -1; + } + + assert (mod->dw == dbg); + assert (dbg->elf == mod->debug.elf); + + *val = addend; + switch (__libdwfl_relocate_value (mod, mod->debug.elf, &mod->debug.shstrndx, + shndx, val)) + { + case DWFL_E_NOERROR: + return 0; + + case DWFL_E_LIBELF: + __libdw_seterrno (DWARF_E_RELBADSYM); + break; + + default: + __libdw_seterrno (DWARF_E_RELUNDEF); + break; + } + + return -1; +} + +int +internal_function +__libdwfl_relocate_symbol (struct dwarf_section_reloc *r, bool undef, + Dwarf *dbg, GElf_Sym *sym, GElf_Word shndx, + GElf_Sxword *addend) +{ + if (!undef) + { + if (likely (__libdw_relocate_shndx (dbg, shndx, sym->st_value, + &sym->st_value) == 0)) + { + *addend += sym->st_value; + return 1; + } + } + else + { + Dwfl_Module *const mod = dbg->relocate->dwflmod; + + if (unlikely (mod == NULL) + || unlikely (mod->dwfl->callbacks->section_address == NULL)) + { + __libdw_seterrno (DWARF_E_RELOC); + return -1; + } + + assert (mod->dw == dbg); + assert (dbg->elf == mod->debug.elf); + + assert (r->symdata != NULL); + assert (r->symstrdata != NULL); + + Elf *symelf = ((Elf_Data_Scn *) r->symdata)->s->elf; + assert (symelf == mod->debug.elf || symelf == mod->main.elf); + struct reloc_symtab_cache symtab = + { + .symelf = symelf, + .symdata = r->symdata, + .symstrdata = r->symstrdata, + .symxndxdata = r->symxndxdata, + .symshstrndx = (symelf == mod->main.elf ? mod->main.shstrndx + : mod->debug.shstrndx), + }; + + switch (resolve_symbol (mod, &symtab, sym, shndx)) + { + case DWFL_E_NOERROR: + return 0; + + case DWFL_E_LIBELF: + case DWFL_E_BADSTROFF: + __libdw_seterrno (DWARF_E_RELBADSYM); + break; + + default: + __libdw_seterrno (DWARF_E_RELUNDEF); + break; + } + } + + return -1; +} -- 2.47.2