diff options
-rw-r--r-- | arch/arm/reloc.h | 37 | ||||
-rw-r--r-- | arch/i386/reloc.h | 41 | ||||
-rw-r--r-- | arch/microblaze/reloc.h | 26 | ||||
-rw-r--r-- | arch/mips/reloc.h | 32 | ||||
-rw-r--r-- | arch/powerpc/reloc.h | 36 | ||||
-rw-r--r-- | arch/sh/reloc.h | 38 | ||||
-rw-r--r-- | arch/x32/reloc.h | 47 | ||||
-rw-r--r-- | arch/x86_64/reloc.h | 41 | ||||
-rw-r--r-- | 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<<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); } } |