refactor to remove arch-specific relocation code from dynamic linker
authorRich Felker <dalias@aerifal.cx>
Wed, 18 Jun 2014 06:44:02 +0000 (02:44 -0400)
committerRich Felker <dalias@aerifal.cx>
Wed, 18 Jun 2014 06:44:02 +0000 (02:44 -0400)
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
arch/i386/reloc.h
arch/microblaze/reloc.h
arch/mips/reloc.h
arch/powerpc/reloc.h
arch/sh/reloc.h
arch/x32/reloc.h
arch/x86_64/reloc.h
src/ldso/dynlink.c

index 27c606d4a9db6e48ecfee6eb58707b8544fde5ee..ee39b7fd5ba59feb1c28bed47c3de609e3230b9d 100644 (file)
 
 #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
index bc86e96e121cda1ce1ba7428aa7d42df6ed9f4da..12224b7927146fb7288e49f3b83403577fdd9134 100644 (file)
@@ -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;
 }
index f2f6c3a01581ca3329c9ef65be131dd456be6c5f..71a6219cb4a6b791736e5575894cb20b22cf8137 100644 (file)
 
 #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;
 }
index 08d139da740251104b2ce382690103717bebfd10..91fa09773f00e763bf96001aba234ec33778ae12 100644 (file)
 
 #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;
 }
index 1563450d8ba15482fa55e7a2b1a1b3a15b0201d8..73c583b7be6bf561ddfcd8e250492e25eb40172e 100644 (file)
@@ -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;
 }
index 9090ee3beed05f12f4282a4a2c703f7fcaab43c5..aeb02d058a932adf6297d746f32c9f944ec0c3f4 100644 (file)
@@ -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;
 }
index 1261fb5de7c0cc6ca8565dd35dd3dd2eaacecd13..fcfbf99e2653fb64fffa6276834de99200f40c3f 100644 (file)
@@ -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;
 }
index 30f6561446b5a640e80bccb07cb09eb167f2a9a5..ffab5566b68a77fa3709342709e9357bfd98ec54 100644 (file)
@@ -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;
 }
index 1cb3fb4c436da28e6285fa78f738995318a3e802..94531b33b478a0b7a611697bc25053ff03961c4f 100644 (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);
        }
 }