refactor to remove arch-specific relocation code from dynamic linker

this was one of the main instances of ugly code duplication: all archs
use basically the same types of relocations, but roughly equivalent
logic was duplicated for each arch to account for the different naming
and numbering of relocation types and variation in whether REL or RELA
records are used.

as an added bonus, both REL and RELA are now supported on all archs,
regardless of which is used by the standard toolchain.
This commit is contained in:
Rich Felker
2014-06-18 02:44:02 -04:00
parent 94cf991bf4
commit adf94c1966
9 changed files with 189 additions and 210 deletions
+89 -8
View File
@@ -89,6 +89,23 @@ struct symdef {
struct dso *dso;
};
enum {
REL_ERR,
REL_SYMBOLIC,
REL_GOT,
REL_PLT,
REL_RELATIVE,
REL_OFFSET,
REL_OFFSET32,
REL_COPY,
REL_SYM_OR_REL,
REL_TLS, /* everything past here is TLS */
REL_DTPMOD,
REL_DTPOFF,
REL_TPOFF,
REL_TPOFF_NEG,
};
#include "reloc.h"
int __init_tp(void *);
@@ -227,6 +244,8 @@ static struct symdef find_sym(struct dso *dso, const char *s, int need_def)
return def;
}
#define NO_INLINE_ADDEND (1<<REL_COPY | 1<<REL_GOT | 1<<REL_PLT)
static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stride)
{
unsigned char *base = dso->base;
@@ -235,18 +254,34 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri
Sym *sym;
const char *name;
void *ctx;
int type;
int astype, type;
int sym_index;
struct symdef def;
size_t *reloc_addr;
size_t sym_val;
size_t tls_val;
size_t addend;
for (; rel_size; rel+=stride, rel_size-=stride*sizeof(size_t)) {
type = R_TYPE(rel[1]);
astype = R_TYPE(rel[1]);
if (!astype) continue;
type = remap_rel(astype);
if (!type) {
snprintf(errbuf, sizeof errbuf,
"Error relocating %s: unsupported relocation type %d",
dso->name, astype);
if (runtime) longjmp(*rtld_fail, 1);
dprintf(2, "%s\n", errbuf);
ldso_fail = 1;
continue;
}
sym_index = R_SYM(rel[1]);
reloc_addr = (void *)(base + rel[0]);
if (sym_index) {
sym = syms + sym_index;
name = strings + sym->st_name;
ctx = IS_COPY(type) ? head->next : head;
def = find_sym(ctx, name, IS_PLT(type));
ctx = type==REL_COPY ? head->next : head;
def = find_sym(ctx, name, type==REL_PLT);
if (!def.sym && (sym->st_shndx != SHN_UNDEF
|| sym->st_info>>4 != STB_WEAK)) {
snprintf(errbuf, sizeof errbuf,
@@ -260,11 +295,57 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri
} else {
sym = 0;
def.sym = 0;
def.dso = 0;
def.dso = dso;
}
addend = stride>2 ? rel[2]
: (1<<type & NO_INLINE_ADDEND) ? 0
: *reloc_addr;
sym_val = def.sym ? (size_t)def.dso->base+def.sym->st_value : 0;
tls_val = def.sym ? def.sym->st_value : 0;
switch(type) {
case REL_OFFSET:
addend -= (size_t)reloc_addr;
case REL_SYMBOLIC:
case REL_GOT:
case REL_PLT:
*reloc_addr = sym_val + addend;
break;
case REL_RELATIVE:
*reloc_addr = (size_t)base + addend;
break;
case REL_SYM_OR_REL:
if (sym) *reloc_addr = sym_val + addend;
else *reloc_addr = (size_t)base + addend;
break;
case REL_COPY:
memcpy(reloc_addr, (void *)sym_val, sym->st_size);
break;
case REL_OFFSET32:
*(uint32_t *)reloc_addr = sym_val + addend
- (size_t)reloc_addr;
break;
case REL_DTPMOD:
*reloc_addr = def.dso->tls_id;
break;
case REL_DTPOFF:
*reloc_addr = tls_val + addend;
break;
#ifdef TLS_ABOVE_TP
case REL_TPOFF:
*reloc_addr = tls_val + def.dso->tls_offset + TPOFF_K + addend;
break;
#else
case REL_TPOFF:
*reloc_addr = tls_val - def.dso->tls_offset + addend;
break;
case REL_TPOFF_NEG:
*reloc_addr = def.dso->tls_offset - tls_val + addend;
break;
#endif
}
do_single_reloc(dso, base, (void *)(base + rel[0]), type,
stride>2 ? rel[2] : 0, sym, sym?sym->st_size:0, def,
def.sym?(size_t)(def.dso->base+def.sym->st_value):0);
}
}