diff options
Diffstat (limited to 'src/ldso/dynlink.c')
| -rw-r--r-- | src/ldso/dynlink.c | 97 | 
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);  	}  } | 
