summaryrefslogtreecommitdiff
path: root/src/ldso
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2014-06-18 02:44:02 -0400
committerRich Felker <dalias@aerifal.cx>2014-06-18 02:44:02 -0400
commitadf94c19666e687a728bbf398f9a88ea4ea19996 (patch)
tree8393ad8b9c7db518d0351b36cccc4930614146f6 /src/ldso
parent94cf991bf4b18bb87a15a96e7b5e7d92fab787ba (diff)
downloadmusl-adf94c19666e687a728bbf398f9a88ea4ea19996.tar.gz
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.
Diffstat (limited to 'src/ldso')
-rw-r--r--src/ldso/dynlink.c97
1 files changed, 89 insertions, 8 deletions
diff --git a/src/ldso/dynlink.c b/src/ldso/dynlink.c
index 1cb3fb4c..94531b33 100644
--- a/src/ldso/dynlink.c
+++ b/src/ldso/dynlink.c
@@ -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);
}
}