From adf94c19666e687a728bbf398f9a88ea4ea19996 Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Wed, 18 Jun 2014 02:44:02 -0400 Subject: [PATCH] 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. --- arch/arm/reloc.h | 37 +++++----------- arch/i386/reloc.h | 41 +++++------------ arch/microblaze/reloc.h | 26 ++++------- arch/mips/reloc.h | 32 ++++---------- arch/powerpc/reloc.h | 36 +++++---------- arch/sh/reloc.h | 38 ++++++---------- arch/x32/reloc.h | 47 +++++++++----------- arch/x86_64/reloc.h | 41 +++++------------ src/ldso/dynlink.c | 97 +++++++++++++++++++++++++++++++++++++---- 9 files changed, 187 insertions(+), 208 deletions(-) diff --git a/arch/arm/reloc.h b/arch/arm/reloc.h index 27c606d4..ee39b7fd 100644 --- a/arch/arm/reloc.h +++ b/arch/arm/reloc.h @@ -16,42 +16,29 @@ #define LDSO_ARCH "arm" ENDIAN_SUFFIX FP_SUFFIX -#define IS_COPY(x) ((x)==R_ARM_COPY) -#define IS_PLT(x) ((x)==R_ARM_JUMP_SLOT) +#define NO_LEGACY_INITFINI + +#define TPOFF_K 8 -static inline int do_single_reloc( - struct dso *self, unsigned char *base_addr, - size_t *reloc_addr, int type, size_t addend, - Sym *sym, size_t sym_size, - struct symdef def, size_t sym_val) +static int remap_rel(int type) { switch(type) { case R_ARM_ABS32: - *reloc_addr += sym_val; - break; + return REL_SYMBOLIC; case R_ARM_GLOB_DAT: + return REL_GOT; case R_ARM_JUMP_SLOT: - *reloc_addr = sym_val; - break; + return REL_PLT; case R_ARM_RELATIVE: - *reloc_addr += (size_t)base_addr; - break; + return REL_RELATIVE; case R_ARM_COPY: - memcpy(reloc_addr, (void *)sym_val, sym_size); - break; + return REL_COPY; case R_ARM_TLS_DTPMOD32: - *reloc_addr = def.dso ? def.dso->tls_id : self->tls_id; - break; + return REL_DTPMOD; case R_ARM_TLS_DTPOFF32: - *reloc_addr += def.sym->st_value; - break; + return REL_DTPOFF; case R_ARM_TLS_TPOFF32: - *reloc_addr += def.sym - ? def.sym->st_value + def.dso->tls_offset + 8 - : self->tls_offset + 8; - break; + return REL_TPOFF; } return 0; } - -#define NO_LEGACY_INITFINI diff --git a/arch/i386/reloc.h b/arch/i386/reloc.h index bc86e96e..12224b79 100644 --- a/arch/i386/reloc.h +++ b/arch/i386/reloc.h @@ -3,48 +3,29 @@ #define LDSO_ARCH "i386" -#define IS_COPY(x) ((x)==R_386_COPY) -#define IS_PLT(x) ((x)==R_386_JMP_SLOT) - -static inline int do_single_reloc( - struct dso *self, unsigned char *base_addr, - size_t *reloc_addr, int type, size_t addend, - Sym *sym, size_t sym_size, - struct symdef def, size_t sym_val) +static int remap_rel(int type) { switch(type) { case R_386_32: - *reloc_addr += sym_val; - break; + return REL_SYMBOLIC; case R_386_PC32: - *reloc_addr += sym_val - (size_t)reloc_addr; - break; + return REL_OFFSET; case R_386_GLOB_DAT: + return REL_GOT; case R_386_JMP_SLOT: - *reloc_addr = sym_val; - break; + return REL_PLT; case R_386_RELATIVE: - *reloc_addr += (size_t)base_addr; - break; + return REL_RELATIVE; case R_386_COPY: - memcpy(reloc_addr, (void *)sym_val, sym_size); - break; + return REL_COPY; case R_386_TLS_DTPMOD32: - *reloc_addr = def.dso ? def.dso->tls_id : self->tls_id; - break; + return REL_DTPMOD; case R_386_TLS_DTPOFF32: - *reloc_addr = def.sym->st_value; - break; + return REL_DTPOFF; case R_386_TLS_TPOFF: - *reloc_addr += def.sym - ? def.sym->st_value - def.dso->tls_offset - : 0 - self->tls_offset; - break; + return REL_TPOFF; case R_386_TLS_TPOFF32: - *reloc_addr += def.sym - ? def.dso->tls_offset - def.sym->st_value - : self->tls_offset; - break; + return REL_TPOFF_NEG; } return 0; } diff --git a/arch/microblaze/reloc.h b/arch/microblaze/reloc.h index f2f6c3a0..71a6219c 100644 --- a/arch/microblaze/reloc.h +++ b/arch/microblaze/reloc.h @@ -10,33 +10,25 @@ #define LDSO_ARCH "microblaze" ENDIAN_SUFFIX -#define IS_COPY(x) ((x)==R_MICROBLAZE_COPY) -#define IS_PLT(x) ((x)==R_MICROBLAZE_JUMP_SLOT) +#define TPOFF_K 0 -static inline int do_single_reloc( - struct dso *self, unsigned char *base_addr, - size_t *reloc_addr, int type, size_t addend, - Sym *sym, size_t sym_size, - struct symdef def, size_t sym_val) +static int remap_rel(int type) { switch(type) { case R_MICROBLAZE_32: + return REL_SYMBOLIC; case R_MICROBLAZE_GLOB_DAT: + return REL_GOT; case R_MICROBLAZE_JUMP_SLOT: - *reloc_addr = sym_val + addend; - break; + return REL_PLT; case R_MICROBLAZE_REL: - *reloc_addr = (size_t)base_addr + addend; - break; + return REL_RELATIVE; case R_MICROBLAZE_COPY: - memcpy(reloc_addr, (void *)sym_val, sym_size); - break; + return REL_COPY; case R_MICROBLAZE_TLSDTPMOD32: - *reloc_addr = def.dso ? def.dso->tls_id : self->tls_id; - break; + return REL_DTPMOD; case R_MICROBLAZE_TLSDTPREL32: - *reloc_addr = def.sym->st_value + addend; - break; + return REL_DTPOFF; } return 0; } diff --git a/arch/mips/reloc.h b/arch/mips/reloc.h index 08d139da..91fa0977 100644 --- a/arch/mips/reloc.h +++ b/arch/mips/reloc.h @@ -16,37 +16,23 @@ #define LDSO_ARCH "mips" ENDIAN_SUFFIX FP_SUFFIX -#define IS_COPY(x) ((x)==R_MIPS_COPY) -#define IS_PLT(x) 1 +#define TPOFF_K (-0x7000) -static inline int do_single_reloc( - struct dso *self, unsigned char *base_addr, - size_t *reloc_addr, int type, size_t addend, - Sym *sym, size_t sym_size, - struct symdef def, size_t sym_val) +static int remap_rel(int type) { switch(type) { - case R_MIPS_JUMP_SLOT: - *reloc_addr = sym_val; - break; case R_MIPS_REL32: - if (sym_val) *reloc_addr += sym_val; - else *reloc_addr += (size_t)base_addr; - break; + return REL_SYM_OR_REL; + case R_MIPS_JUMP_SLOT: + return REL_PLT; case R_MIPS_COPY: - memcpy(reloc_addr, (void *)sym_val, sym_size); - break; + return REL_COPY; case R_MIPS_TLS_DTPMOD32: - *reloc_addr = def.dso ? def.dso->tls_id : self->tls_id; - break; + return REL_DTPMOD; case R_MIPS_TLS_DTPREL32: - *reloc_addr += def.sym->st_value; - break; + return REL_DTPOFF; case R_MIPS_TLS_TPREL32: - *reloc_addr += def.sym - ? def.sym->st_value + def.dso->tls_offset - 0x7000 - : self->tls_offset - 0x7000; - break; + return REL_TPOFF; } return 0; } diff --git a/arch/powerpc/reloc.h b/arch/powerpc/reloc.h index 1563450d..73c583b7 100644 --- a/arch/powerpc/reloc.h +++ b/arch/powerpc/reloc.h @@ -3,39 +3,27 @@ #define LDSO_ARCH "powerpc" -#define IS_COPY(x) ((x)==R_PPC_COPY) -#define IS_PLT(x) ((x)==R_PPC_JMP_SLOT) +#define TPOFF_K (-0x7000) -// see linux' arch/powerpc/include/asm/elf.h -static inline int do_single_reloc( - struct dso *self, unsigned char *base_addr, - size_t *reloc_addr, int type, size_t addend, - Sym *sym, size_t sym_size, - struct symdef def, size_t sym_val) +static int remap_rel(int type) { switch(type) { + case R_PPC_ADDR32: + return REL_SYMBOLIC; case R_PPC_GLOB_DAT: + return REL_GOT; case R_PPC_JMP_SLOT: - case R_PPC_ADDR32: - *reloc_addr = sym_val + addend; - break; - case R_PPC_COPY: - memcpy(reloc_addr, (void *)sym_val, sym_size); - break; + return REL_PLT; case R_PPC_RELATIVE: - *reloc_addr = (size_t)base_addr + addend; - break; + return REL_RELATIVE; + case R_PPC_COPY: + return REL_COPY; case R_PPC_DTPMOD32: - *reloc_addr = def.dso ? def.dso->tls_id : self->tls_id; - break; + return REL_DTPMOD; case R_PPC_DTPREL32: - *reloc_addr = def.sym->st_value + addend; - break; + return REL_DTPOFF; case R_PPC_TPREL32: - *reloc_addr = (def.sym - ? def.sym->st_value + def.dso->tls_offset - : self->tls_offset) - 0x7000 + addend; - break; + return REL_TPOFF; } return 0; } diff --git a/arch/sh/reloc.h b/arch/sh/reloc.h index 9090ee3b..aeb02d05 100644 --- a/arch/sh/reloc.h +++ b/arch/sh/reloc.h @@ -6,41 +6,29 @@ #define LDSO_ARCH "sh" ENDIAN_SUFFIX -#define IS_COPY(x) ((x) == R_SH_COPY) -#define IS_PLT(x) ((x) == R_SH_JMP_SLOT) +#define TPOFF_K 8 -static inline int do_single_reloc( - struct dso *self, unsigned char *base_addr, - size_t *reloc_addr, int type, size_t addend, - Sym *sym, size_t sym_size, - struct symdef def, size_t sym_val) +static int remap_rel(int type) { switch(type) { + case R_SH_DIR32: + return REL_SYMBOLIC; + case R_SH_REL32: + return REL_OFFSET; case R_SH_GLOB_DAT: + return REL_GOT; case R_SH_JMP_SLOT: - case R_SH_DIR32: - *reloc_addr = sym_val + addend; - break; + return REL_PLT; case R_SH_RELATIVE: - *reloc_addr = (size_t)base_addr + addend; - break; - case R_SH_REL32: - *reloc_addr = sym_val + addend - (size_t)reloc_addr; - break; + return REL_RELATIVE; case R_SH_COPY: - memcpy(reloc_addr, (void *)sym_val, sym_size); - break; + return REL_COPY; case R_SH_TLS_DTPMOD32: - *reloc_addr = def.dso ? def.dso->tls_id : self->tls_id; - break; + return REL_DTPMOD; case R_SH_TLS_DTPOFF32: - *reloc_addr = def.sym->st_value + addend; - break; + return REL_DTPOFF; case R_SH_TLS_TPOFF32: - *reloc_addr = (def.sym - ? def.sym->st_value + def.dso->tls_offset - : self->tls_offset) + 8 + addend; - break; + return REL_TPOFF; } return 0; } diff --git a/arch/x32/reloc.h b/arch/x32/reloc.h index 1261fb5d..fcfbf99e 100644 --- a/arch/x32/reloc.h +++ b/arch/x32/reloc.h @@ -4,44 +4,37 @@ #define LDSO_ARCH "x32" -#define IS_COPY(x) ((x)==R_X86_64_COPY) -#define IS_PLT(x) ((x)==R_X86_64_JUMP_SLOT) +/* FIXME: x32 is very strange in its use of 64-bit relocation types in + * a 32-bit environment. As long as the memory at reloc_addr is + * zero-filled prior to relocations, just treating 64-bit relocations + * as operating on 32-bit slots should be fine, but this should be + * checked. In particular, R_X86_64_64, R_X86_64_DTPOFF64, and + * R_X86_64_TPOFF64 may need checking. */ -static inline int do_single_reloc( - struct dso *self, unsigned char *base_addr, - size_t *reloc_addr, int type, size_t addend, - Sym *sym, size_t sym_size, - struct symdef def, size_t sym_val) +static int remap_rel(int type) { switch(type) { - case R_X86_64_GLOB_DAT: - case R_X86_64_JUMP_SLOT: case R_X86_64_64: - *reloc_addr = sym_val + addend; - break; case R_X86_64_32: - *(uint32_t *)reloc_addr = sym_val + addend; - break; + return REL_SYMBOLIC; case R_X86_64_PC32: - *reloc_addr = sym_val + addend - (size_t)reloc_addr + (size_t)base_addr; - break; + return REL_OFFSET; + case R_X86_64_GLOB_DAT: + return REL_GOT; + case R_X86_64_JUMP_SLOT: + return REL_PLT; case R_X86_64_RELATIVE: - *reloc_addr = (size_t)base_addr + addend; - break; + return REL_RELATIVE; case R_X86_64_COPY: - memcpy(reloc_addr, (void *)sym_val, sym_size); - break; + return REL_COPY; case R_X86_64_DTPMOD64: - *reloc_addr = def.dso ? def.dso->tls_id : self->tls_id; - break; + return REL_DTPMOD; case R_X86_64_DTPOFF64: - *reloc_addr = def.sym->st_value + addend; - break; + case R_X86_64_DTPOFF32: + return REL_DTPOFF; case R_X86_64_TPOFF64: - *reloc_addr = (def.sym - ? def.sym->st_value - def.dso->tls_offset - : 0 - self->tls_offset) + addend; - break; + case R_X86_64_TPOFF32: + return REL_TPOFF; } return 0; } diff --git a/arch/x86_64/reloc.h b/arch/x86_64/reloc.h index 30f65614..ffab5566 100644 --- a/arch/x86_64/reloc.h +++ b/arch/x86_64/reloc.h @@ -4,44 +4,27 @@ #define LDSO_ARCH "x86_64" -#define IS_COPY(x) ((x)==R_X86_64_COPY) -#define IS_PLT(x) ((x)==R_X86_64_JUMP_SLOT) - -static inline int do_single_reloc( - struct dso *self, unsigned char *base_addr, - size_t *reloc_addr, int type, size_t addend, - Sym *sym, size_t sym_size, - struct symdef def, size_t sym_val) +static int remap_rel(int type) { switch(type) { - case R_X86_64_GLOB_DAT: - case R_X86_64_JUMP_SLOT: case R_X86_64_64: - *reloc_addr = sym_val + addend; - break; - case R_X86_64_32: - *(uint32_t *)reloc_addr = sym_val + addend; - break; + return REL_SYMBOLIC; case R_X86_64_PC32: - *reloc_addr = sym_val + addend - (size_t)reloc_addr + (size_t)base_addr; - break; + return REL_OFFSET32; + case R_X86_64_GLOB_DAT: + return REL_GOT; + case R_X86_64_JUMP_SLOT: + return REL_PLT; case R_X86_64_RELATIVE: - *reloc_addr = (size_t)base_addr + addend; - break; + return REL_RELATIVE; case R_X86_64_COPY: - memcpy(reloc_addr, (void *)sym_val, sym_size); - break; + return REL_COPY; case R_X86_64_DTPMOD64: - *reloc_addr = def.dso ? def.dso->tls_id : self->tls_id; - break; + return REL_DTPMOD; case R_X86_64_DTPOFF64: - *reloc_addr = def.sym->st_value + addend; - break; + return REL_DTPOFF; case R_X86_64_TPOFF64: - *reloc_addr = (def.sym - ? def.sym->st_value - def.dso->tls_offset - : 0 - self->tls_offset) + addend; - break; + return REL_TPOFF; } return 0; } 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<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<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); } } -- 2.25.1