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);  	}  }  | 
