diff options
Diffstat (limited to 'elfutils/libdwfl/derelocate.c')
-rw-r--r-- | elfutils/libdwfl/derelocate.c | 189 |
1 files changed, 155 insertions, 34 deletions
diff --git a/elfutils/libdwfl/derelocate.c b/elfutils/libdwfl/derelocate.c index d110299e..0877276d 100644 --- a/elfutils/libdwfl/derelocate.c +++ b/elfutils/libdwfl/derelocate.c @@ -55,6 +55,7 @@ struct dwfl_relocation struct { Elf_Scn *scn; + Elf_Scn *relocs; const char *name; GElf_Addr start, end; } refs[0]; @@ -65,6 +66,7 @@ struct secref { struct secref *next; Elf_Scn *scn; + Elf_Scn *relocs; const char *name; GElf_Addr start, end; }; @@ -75,57 +77,90 @@ compare_secrefs (const void *a, const void *b) struct secref *const *p1 = a; struct secref *const *p2 = b; - return (*p1)->start - (*p2)->start; + /* No signed difference calculation is correct here, since the + terms are unsigned and could be more than INT64_MAX apart. */ + if ((*p1)->start < (*p2)->start) + return -1; + if ((*p1)->start > (*p2)->start) + return 1; + + return 0; } static int cache_sections (Dwfl_Module *mod) { - size_t symshstrndx; - if (elf_getshstrndx (mod->symfile->elf, &symshstrndx) < 0) + struct secref *refs = NULL; + size_t nrefs = 0; + + size_t shstrndx; + if (unlikely (elf_getshstrndx (mod->main.elf, &shstrndx) < 0)) { + elf_error: __libdwfl_seterrno (DWFL_E_LIBELF); return -1; } - struct secref *refs = NULL; - size_t nrefs = 0; - + bool check_reloc_sections = false; Elf_Scn *scn = NULL; - while ((scn = elf_nextscn (mod->symfile->elf, scn)) != NULL) + while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL) { GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); if (shdr == NULL) - return -1; + goto elf_error; if ((shdr->sh_flags & SHF_ALLOC) && shdr->sh_addr == 0) { /* This section might not yet have been looked at. */ - if (__libdwfl_relocate_value (mod, symshstrndx, elf_ndxscn (scn), + if (__libdwfl_relocate_value (mod, mod->main.elf, &shstrndx, + elf_ndxscn (scn), &shdr->sh_addr) != DWFL_E_NOERROR) continue; shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr == NULL) - return -1; + if (unlikely (shdr == NULL)) + goto elf_error; } if (shdr->sh_flags & SHF_ALLOC) { - const char *name = elf_strptr (mod->symfile->elf, symshstrndx, + const char *name = elf_strptr (mod->main.elf, shstrndx, shdr->sh_name); - if (name == NULL) - return -1; + if (unlikely (name == NULL)) + goto elf_error; struct secref *newref = alloca (sizeof *newref); newref->scn = scn; + newref->relocs = NULL; newref->name = name; - newref->start = shdr->sh_addr; - newref->end = shdr->sh_addr + shdr->sh_size; + newref->start = shdr->sh_addr + mod->main.bias; + newref->end = newref->start + shdr->sh_size; newref->next = refs; refs = newref; ++nrefs; } + + if (mod->e_type == ET_REL + && shdr->sh_size != 0 + && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA) + && mod->dwfl->callbacks->section_address != NULL) + { + if (shdr->sh_info < elf_ndxscn (scn)) + { + /* We've already looked at the section these relocs apply to. */ + Elf_Scn *tscn = elf_getscn (mod->main.elf, shdr->sh_info); + if (likely (tscn != NULL)) + for (struct secref *sec = refs; sec != NULL; sec = sec->next) + if (sec->scn == tscn) + { + sec->relocs = scn; + break; + } + } + else + /* We'll have to do a second pass. */ + check_reloc_sections = true; + } } mod->reloc_info = malloc (offsetof (struct dwfl_relocation, refs[nrefs])); @@ -147,10 +182,40 @@ cache_sections (Dwfl_Module *mod) { mod->reloc_info->refs[i].name = sortrefs[i]->name; mod->reloc_info->refs[i].scn = sortrefs[i]->scn; + mod->reloc_info->refs[i].relocs = sortrefs[i]->relocs; mod->reloc_info->refs[i].start = sortrefs[i]->start; mod->reloc_info->refs[i].end = sortrefs[i]->end; } + if (unlikely (check_reloc_sections)) + { + /* There was a reloc section that preceded its target section. + So we have to scan again now that we have cached all the + possible target sections we care about. */ + + scn = NULL; + while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + goto elf_error; + + if (shdr->sh_size != 0 + && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)) + { + Elf_Scn *tscn = elf_getscn (mod->main.elf, shdr->sh_info); + if (likely (tscn != NULL)) + for (size_t i = 0; i < nrefs; ++i) + if (mod->reloc_info->refs[i].scn == tscn) + { + mod->reloc_info->refs[i].relocs = scn; + break; + } + } + } + } + return nrefs; } @@ -164,13 +229,6 @@ dwfl_module_relocations (Dwfl_Module *mod) if (mod->reloc_info != NULL) return mod->reloc_info->count; - if (mod->dw == NULL) - { - Dwarf_Addr bias; - if (INTUSE(dwfl_module_getdwarf) (mod, &bias) == NULL) - return -1; - } - switch (mod->e_type) { case ET_REL: @@ -224,25 +282,41 @@ dwfl_module_relocation_info (Dwfl_Module *mod, unsigned int idx, return sections->refs[idx].name; } -int -dwfl_module_relocate_address (Dwfl_Module *mod, Dwarf_Addr *addr) +/* Check that MOD is valid and make sure its relocation has been done. */ +static bool +check_module (Dwfl_Module *mod) { - if (mod == NULL) - return -1; + if (INTUSE(dwfl_module_getsymtab) (mod) < 0) + { + Dwfl_Error error = dwfl_errno (); + if (error != DWFL_E_NO_SYMTAB) + { + __libdwfl_seterrno (error); + return true; + } + } if (mod->dw == NULL) { Dwarf_Addr bias; if (INTUSE(dwfl_module_getdwarf) (mod, &bias) == NULL) - return -1; + { + Dwfl_Error error = dwfl_errno (); + if (error != DWFL_E_NO_DWARF) + { + __libdwfl_seterrno (error); + return true; + } + } } - if (mod->e_type != ET_REL) - { - *addr -= mod->debug.bias; - return 0; - } + return false; +} +/* Find the index in MOD->reloc_info.refs containing *ADDR. */ +static int +find_section (Dwfl_Module *mod, Dwarf_Addr *addr) +{ if (unlikely (mod->reloc_info == NULL) && cache_sections (mod) < 0) return -1; @@ -272,7 +346,54 @@ dwfl_module_relocate_address (Dwfl_Module *mod, Dwarf_Addr *addr) } } - __libdw_seterrno (DWARF_E_NO_MATCH); + __libdwfl_seterrno (DWFL_E (LIBDW, DWARF_E_NO_MATCH)); return -1; } + +int +dwfl_module_relocate_address (Dwfl_Module *mod, Dwarf_Addr *addr) +{ + if (check_module (mod)) + return -1; + + if (mod->e_type != ET_REL) + { + *addr -= mod->debug.bias; + return 0; + } + + return find_section (mod, addr); +} INTDEF (dwfl_module_relocate_address) + +Elf_Scn * +dwfl_module_address_section (Dwfl_Module *mod, Dwarf_Addr *address, + Dwarf_Addr *bias) +{ + if (check_module (mod)) + return NULL; + + int idx = find_section (mod, address); + if (idx < 0) + return NULL; + + if (mod->reloc_info->refs[idx].relocs != NULL) + { + assert (mod->e_type == ET_REL); + + Elf_Scn *tscn = mod->reloc_info->refs[idx].scn; + Elf_Scn *relocscn = mod->reloc_info->refs[idx].relocs; + Dwfl_Error result = __libdwfl_relocate_section (mod, mod->main.elf, + relocscn, tscn, true); + if (likely (result == DWFL_E_NOERROR)) + mod->reloc_info->refs[idx].relocs = NULL; + else + { + __libdwfl_seterrno (result); + return NULL; + } + } + + *bias = mod->main.bias; + return mod->reloc_info->refs[idx].scn; +} |