summaryrefslogtreecommitdiffstats
path: root/libdwfl/relocate.c
diff options
context:
space:
mode:
Diffstat (limited to 'libdwfl/relocate.c')
-rw-r--r--libdwfl/relocate.c259
1 files changed, 105 insertions, 154 deletions
diff --git a/libdwfl/relocate.c b/libdwfl/relocate.c
index abacc041..78705b89 100644
--- a/libdwfl/relocate.c
+++ b/libdwfl/relocate.c
@@ -101,100 +101,6 @@ __libdwfl_relocate_value (Dwfl_Module *mod, Elf *elf, size_t *shstrndx,
}
-/* Cache used by relocate_getsym. */
-struct reloc_symtab_cache
-{
- Elf *symelf;
- Elf_Data *symdata;
- Elf_Data *symxndxdata;
- Elf_Data *symstrdata;
- size_t symshstrndx;
- size_t strtabndx;
-};
-#define RELOC_SYMTAB_CACHE(cache) \
- struct reloc_symtab_cache cache = \
- { NULL, NULL, NULL, NULL, SHN_UNDEF, SHN_UNDEF }
-
-/* This is just doing dwfl_module_getsym, except that we must always use
- the symbol table in RELOCATED itself when it has one, not MOD->symfile. */
-static Dwfl_Error
-relocate_getsym (Dwfl_Module *mod,
- Elf *relocated, struct reloc_symtab_cache *cache,
- int symndx, GElf_Sym *sym, GElf_Word *shndx)
-{
- if (cache->symdata == NULL)
- {
- if (mod->symfile == NULL || mod->symfile->elf != relocated)
- {
- /* We have to look up the symbol table in the file we are
- relocating, if it has its own. These reloc sections refer to
- the symbol table in this file, and a symbol table in the main
- file might not match. However, some tools did produce ET_REL
- .debug files with relocs but no symtab of their own. */
- Elf_Scn *scn = NULL;
- while ((scn = elf_nextscn (relocated, scn)) != NULL)
- {
- GElf_Shdr shdr_mem, *shdr = gelf_getshdr (scn, &shdr_mem);
- if (shdr != NULL)
- switch (shdr->sh_type)
- {
- default:
- continue;
- case SHT_SYMTAB:
- cache->symelf = relocated;
- cache->symdata = elf_getdata (scn, NULL);
- cache->strtabndx = shdr->sh_link;
- if (unlikely (cache->symdata == NULL))
- return DWFL_E_LIBELF;
- break;
- case SHT_SYMTAB_SHNDX:
- cache->symxndxdata = elf_getdata (scn, NULL);
- if (unlikely (cache->symxndxdata == NULL))
- return DWFL_E_LIBELF;
- break;
- }
- if (cache->symdata != NULL && cache->symxndxdata != NULL)
- break;
- }
- }
- if (cache->symdata == NULL)
- {
- /* We might not have looked for a symbol table file yet,
- when coming from __libdwfl_relocate_section. */
- if (unlikely (mod->symfile == NULL)
- && unlikely (INTUSE(dwfl_module_getsymtab) (mod) < 0))
- return dwfl_errno ();
-
- /* The symbol table we have already cached is the one from
- the file being relocated, so it's what we need. Or else
- this is an ET_REL .debug file with no .symtab of its own;
- the symbols refer to the section indices in the main file. */
- cache->symelf = mod->symfile->elf;
- cache->symdata = mod->symdata;
- cache->symxndxdata = mod->symxndxdata;
- cache->symstrdata = mod->symstrdata;
- }
- }
-
- if (unlikely (gelf_getsymshndx (cache->symdata, cache->symxndxdata,
- symndx, sym, shndx) == NULL))
- return DWFL_E_LIBELF;
-
- if (sym->st_shndx != SHN_XINDEX)
- *shndx = sym->st_shndx;
-
- switch (*shndx)
- {
- case SHN_ABS:
- case SHN_UNDEF:
- case SHN_COMMON:
- return DWFL_E_NOERROR;
- }
-
- return __libdwfl_relocate_value (mod, cache->symelf, &cache->symshstrndx,
- *shndx, &sym->st_value);
-}
-
/* Handle an undefined symbol. We really only support ET_REL for Linux
kernel modules, and offline archives. The behavior of the Linux module
loader is very simple and easy to mimic. It only matches magically
@@ -202,27 +108,16 @@ relocate_getsym (Dwfl_Module *mod,
answer except when the module's symbols are undefined and would prevent
it from being loaded. */
static Dwfl_Error
-resolve_symbol (Dwfl_Module *referer, struct reloc_symtab_cache *symtab,
+resolve_symbol (Dwfl_Module *referer, struct dwfl_shared_file *symfile,
GElf_Sym *sym, GElf_Word shndx)
{
/* First we need its name. */
if (sym->st_name != 0)
{
- if (symtab->symstrdata == NULL)
- {
- /* Cache the strtab for this symtab. */
- assert (referer->symfile == NULL
- || referer->symfile->elf != symtab->symelf);
- symtab->symstrdata = elf_getdata (elf_getscn (symtab->symelf,
- symtab->strtabndx),
- NULL);
- if (unlikely (symtab->symstrdata == NULL))
- return DWFL_E_LIBELF;
- }
- if (unlikely (sym->st_name >= symtab->symstrdata->d_size))
+ if (unlikely (sym->st_name >= symfile->symstrdata->d_size))
return DWFL_E_BADSTROFF;
- const char *name = symtab->symstrdata->d_buf;
+ const char *name = symfile->symstrdata->d_buf;
name += sym->st_name;
for (Dwfl_Module *m = referer->dwfl->modulelist; m != NULL; m = m->next)
@@ -231,15 +126,21 @@ resolve_symbol (Dwfl_Module *referer, struct reloc_symtab_cache *symtab,
/* Get this module's symtab.
If we got a fresh error reading the table, report it.
If we just have no symbols in this module, no harm done. */
- if (m->symdata == NULL
- && m->symerr == DWFL_E_NOERROR
- && INTUSE(dwfl_module_getsymtab) (m) < 0
- && m->symerr != DWFL_E_NO_SYMTAB)
- return m->symerr;
+ if (m->symfile == NULL
+ && m->main.cberr == DWFL_E_NOERROR
+ && m->debug.cberr == DWFL_E_NOERROR
+ && INTUSE(dwfl_module_getsymtab) (m) < 0)
+ {
+ Dwfl_Error err = m->main.cberr ?: m->debug.cberr;
+ if (err != DWFL_E_NO_SYMTAB)
+ return err;
+ }
- for (size_t ndx = 1; ndx < m->syments; ++ndx)
+ struct dwfl_shared_file *m_shared = m->symfile->shared;
+ for (size_t ndx = 1; ndx < m_shared->syments; ++ndx)
{
- sym = gelf_getsymshndx (m->symdata, m->symxndxdata,
+ sym = gelf_getsymshndx (m_shared->symdata,
+ m_shared->symxndxdata,
ndx, sym, &shndx);
if (unlikely (sym == NULL))
return DWFL_E_LIBELF;
@@ -253,9 +154,9 @@ resolve_symbol (Dwfl_Module *referer, struct reloc_symtab_cache *symtab,
continue;
/* Get this candidate symbol's name. */
- if (unlikely (sym->st_name >= m->symstrdata->d_size))
+ if (unlikely (sym->st_name >= m_shared->symstrdata->d_size))
return DWFL_E_BADSTROFF;
- const char *n = m->symstrdata->d_buf;
+ const char *n = m_shared->symstrdata->d_buf;
n += sym->st_name;
/* Does the name match? */
@@ -269,7 +170,7 @@ resolve_symbol (Dwfl_Module *referer, struct reloc_symtab_cache *symtab,
/* 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;
- return __libdwfl_relocate_value (m, m->symfile->elf,
+ return __libdwfl_relocate_value (m, m_shared->elf,
&symshstrndx,
shndx, &sym->st_value);
}
@@ -280,19 +181,21 @@ resolve_symbol (Dwfl_Module *referer, struct reloc_symtab_cache *symtab,
}
static Dwfl_Error
-relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr,
- size_t shstrndx, struct reloc_symtab_cache *reloc_symtab,
+relocate_section (Dwfl_Module *mod,
+ struct dwfl_shared_file *relocated,
+ struct dwfl_shared_file *symfile,
+ const GElf_Ehdr *ehdr, size_t shstrndx,
Elf_Scn *scn, GElf_Shdr *shdr,
Elf_Scn *tscn, bool debugscn, bool partial)
{
/* First, fetch the name of the section these relocations apply to. */
GElf_Shdr tshdr_mem;
GElf_Shdr *tshdr = gelf_getshdr (tscn, &tshdr_mem);
- const char *tname = elf_strptr (relocated, shstrndx, tshdr->sh_name);
+ const char *tname = elf_strptr (relocated->elf, shstrndx, tshdr->sh_name);
if (tname == NULL)
return DWFL_E_LIBELF;
- if (debugscn && ! ebl_debugscn_p (mod->ebl, tname))
+ if (debugscn && ! ebl_debugscn_p (mod->main.shared->ebl, tname))
/* This relocation section is not for a debugging section.
Nothing to do here. */
return DWFL_E_NOERROR;
@@ -308,7 +211,7 @@ relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr,
{
/* First see if this is a reloc we can handle.
If we are skipping it, don't bother resolving the symbol. */
- Elf_Type type = ebl_reloc_simple_type (mod->ebl, rtype);
+ Elf_Type type = ebl_reloc_simple_type (mod->main.shared->ebl, rtype);
if (unlikely (type == ELF_T_NUM))
return DWFL_E_BADRELTYPE;
@@ -327,19 +230,38 @@ relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr,
{
GElf_Sym sym;
GElf_Word shndx;
- Dwfl_Error error = relocate_getsym (mod, relocated, reloc_symtab,
- symndx, &sym, &shndx);
- if (unlikely (error != DWFL_E_NOERROR))
- return error;
- if (shndx == SHN_UNDEF || shndx == SHN_COMMON)
+ if (unlikely (gelf_getsymshndx (symfile->symdata,
+ symfile->symxndxdata,
+ symndx, &sym, &shndx) == NULL))
+ return DWFL_E_LIBELF;
+
+ if (sym.st_shndx != SHN_XINDEX)
+ shndx = sym.st_shndx;
+
+ Dwfl_Error err = DWFL_E_NOERROR;
+ switch (shndx)
{
- /* Maybe we can figure it out anyway. */
- error = resolve_symbol (mod, reloc_symtab, &sym, shndx);
- if (error != DWFL_E_NOERROR)
- return error;
+ case SHN_ABS:
+ break;
+ case SHN_UNDEF:
+ case SHN_COMMON:
+ {
+ /* Maybe we can figure it out anyway. */
+ err = resolve_symbol (mod, symfile, &sym, shndx);
+ break;
+ }
+ default:
+ {
+ err = __libdwfl_relocate_value (mod, relocated->elf, &shstrndx,
+ shndx, &sym.st_value);
+ break;
+ }
}
+ if (unlikely (err != DWFL_E_NOERROR))
+ return err;
+
value = sym.st_value;
}
@@ -401,7 +323,7 @@ relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr,
else
{
/* Extract the original value and apply the reloc. */
- Elf_Data *d = gelf_xlatetom (relocated, &tmpdata, &rdata,
+ Elf_Data *d = gelf_xlatetom (relocated->elf, &tmpdata, &rdata,
ehdr->e_ident[EI_DATA]);
if (d == NULL)
return DWFL_E_LIBELF;
@@ -422,7 +344,7 @@ relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr,
/* Now convert the relocated datum back to the target
format. This will write into rdata.d_buf, which
points into the raw section data being relocated. */
- Elf_Data *s = gelf_xlatetof (relocated, &rdata, &tmpdata,
+ Elf_Data *s = gelf_xlatetof (relocated->elf, &rdata, &tmpdata,
ehdr->e_ident[EI_DATA]);
if (s == NULL)
return DWFL_E_LIBELF;
@@ -444,7 +366,7 @@ relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr,
if (first_badreltype)
{
first_badreltype = false;
- if (ebl_get_elfmachine (mod->ebl) == EM_NONE)
+ if (ebl_get_elfmachine (mod->main.shared->ebl) == EM_NONE)
/* This might be because ebl_openbackend failed to find
any libebl_CPU.so library. Diagnose that clearly. */
result = DWFL_E_UNKNOWN_MACHINE;
@@ -560,29 +482,50 @@ relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr,
return result;
}
+static Dwfl_Error
+find_relocation_symfile (Dwfl_Module *mod,
+ struct dwfl_shared_file *relocated,
+ struct dwfl_shared_file **symfile)
+{
+ Dwfl_Error err = __libdwfl_find_symtab (relocated);
+ if (err == DWFL_E_NOERROR)
+ *symfile = relocated;
+ else if (err == DWFL_E_NO_SYMTAB)
+ {
+ err = __libdwfl_find_symtab (mod->main.shared);
+ if (err == DWFL_E_NOERROR)
+ *symfile = relocated;
+ }
+
+ return err;
+}
+
Dwfl_Error
internal_function
-__libdwfl_relocate (Dwfl_Module *mod, Elf *debugfile, bool debug)
+__libdwfl_relocate (Dwfl_Module *mod, struct dwfl_shared_file *debugfile,
+ bool debug)
{
assert (mod->e_type == ET_REL);
GElf_Ehdr ehdr_mem;
- const GElf_Ehdr *ehdr = gelf_getehdr (debugfile, &ehdr_mem);
+ const GElf_Ehdr *ehdr = gelf_getehdr (debugfile->elf, &ehdr_mem);
if (ehdr == NULL)
return DWFL_E_LIBELF;
size_t d_shstrndx;
- if (elf_getshstrndx (debugfile, &d_shstrndx) < 0)
+ if (elf_getshstrndx (debugfile->elf, &d_shstrndx) < 0)
return DWFL_E_LIBELF;
- RELOC_SYMTAB_CACHE (reloc_symtab);
+ struct dwfl_shared_file *symfile;
+ Dwfl_Error result = find_relocation_symfile (mod, debugfile, &symfile);
+ if (result != DWFL_E_NOERROR)
+ return result;
/* Look at each section in the debuginfo file, and process the
relocation sections for debugging sections. */
- Dwfl_Error result = DWFL_E_NOERROR;
Elf_Scn *scn = NULL;
while (result == DWFL_E_NOERROR
- && (scn = elf_nextscn (debugfile, scn)) != NULL)
+ && (scn = elf_nextscn (debugfile->elf, scn)) != NULL)
{
GElf_Shdr shdr_mem;
GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
@@ -592,12 +535,13 @@ __libdwfl_relocate (Dwfl_Module *mod, Elf *debugfile, bool debug)
{
/* It's a relocation section. */
- Elf_Scn *tscn = elf_getscn (debugfile, shdr->sh_info);
+ Elf_Scn *tscn = elf_getscn (debugfile->elf, shdr->sh_info);
if (unlikely (tscn == NULL))
result = DWFL_E_LIBELF;
else
- result = relocate_section (mod, debugfile, ehdr, d_shstrndx,
- &reloc_symtab, scn, shdr, tscn,
+ result = relocate_section (mod, debugfile, symfile,
+ ehdr, d_shstrndx,
+ scn, shdr, tscn,
debug, !debug);
}
}
@@ -607,22 +551,29 @@ __libdwfl_relocate (Dwfl_Module *mod, Elf *debugfile, bool debug)
Dwfl_Error
internal_function
-__libdwfl_relocate_section (Dwfl_Module *mod, Elf *relocated,
+__libdwfl_relocate_section (Dwfl_Module *mod,
+ struct dwfl_shared_file *relocated,
Elf_Scn *relocscn, Elf_Scn *tscn, bool partial)
{
GElf_Ehdr ehdr_mem;
GElf_Shdr shdr_mem;
- RELOC_SYMTAB_CACHE (reloc_symtab);
-
size_t shstrndx;
- if (elf_getshstrndx (relocated, &shstrndx) < 0)
+ if (elf_getshstrndx (relocated->elf, &shstrndx) < 0)
return DWFL_E_LIBELF;
- return (__libdwfl_module_getebl (mod)
- ?: relocate_section (mod, relocated,
- gelf_getehdr (relocated, &ehdr_mem), shstrndx,
- &reloc_symtab,
- relocscn, gelf_getshdr (relocscn, &shdr_mem),
- tscn, false, partial));
+ Dwfl_Error error = __libdwfl_module_getebl (mod);
+ if (error != DWFL_E_NOERROR)
+ return error;
+
+ struct dwfl_shared_file *symfile;
+ Dwfl_Error result = find_relocation_symfile (mod, relocated, &symfile);
+ if (result != DWFL_E_NOERROR)
+ return result;
+
+ return relocate_section (mod, relocated, symfile,
+ gelf_getehdr (relocated->elf, &ehdr_mem),
+ shstrndx,
+ relocscn, gelf_getshdr (relocscn, &shdr_mem),
+ tscn, false, partial);
}