diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/internal/dynlink.h | 17 | ||||
| -rw-r--r-- | src/ldso/dlstart.c | 65 | ||||
| -rw-r--r-- | src/ldso/dynlink.c | 134 | 
3 files changed, 202 insertions, 14 deletions
| diff --git a/src/internal/dynlink.h b/src/internal/dynlink.h index 369f183e..b9333926 100644 --- a/src/internal/dynlink.h +++ b/src/internal/dynlink.h @@ -49,16 +49,27 @@ struct fdpic_loadmap {  	struct fdpic_loadseg segs[];  }; +struct fdpic_dummy_loadmap { +	unsigned short version, nsegs; +	struct fdpic_loadseg segs[1]; +}; +  #include "reloc.h" -#define IS_RELATIVE(x) ( \ +#ifndef DL_FDPIC +#define DL_FDPIC 0 +#endif + +#if !DL_FDPIC +#define IS_RELATIVE(x,s) ( \  	(R_TYPE(x) == REL_RELATIVE) || \  	(R_TYPE(x) == REL_SYM_OR_REL && !R_SYM(x)) ) - -#define IS_FDPIC_RELATIVE(x,s) ( ( \ +#else +#define IS_RELATIVE(x,s) ( ( \  	(R_TYPE(x) == REL_FUNCDESC_VAL) || \  	(R_TYPE(x) == REL_SYMBOLIC) ) \  	&& (((s)[R_SYM(x)].st_info & 0xf) == STT_SECTION) ) +#endif  #ifndef NEED_MIPS_GOT_RELOCS  #define NEED_MIPS_GOT_RELOCS 0 diff --git a/src/ldso/dlstart.c b/src/ldso/dlstart.c index e84e073e..46f50114 100644 --- a/src/ldso/dlstart.c +++ b/src/ldso/dlstart.c @@ -33,10 +33,70 @@ void _dlstart_c(size_t *sp, size_t *dynv)  	for (i=0; auxv[i]; i+=2) if (auxv[i]<AUX_CNT)  		aux[auxv[i]] = auxv[i+1]; +#if DL_FDPIC +	struct fdpic_loadseg *segs, fakeseg; +	size_t j; +	if (dynv) { +		/* crt_arch.h entry point asm is responsible for reserving +		 * space and moving the extra fdpic arguments to the stack +		 * vector where they are easily accessible from C. */ +		segs = ((struct fdpic_loadmap *)(sp[-1] ? sp[-1] : sp[-2]))->segs; +	} else { +		/* If dynv is null, the entry point was started from loader +		 * that is not fdpic-aware. We can assume normal fixed- +		 * displacement ELF loading was performed, but when ldso was +		 * run as a command, finding the Ehdr is a heursitic: we +		 * have to assume Phdrs start in the first 4k of the file. */ +		base = aux[AT_BASE]; +		if (!base) base = aux[AT_PHDR] & -4096; +		segs = &fakeseg; +		segs[0].addr = base; +		segs[0].p_vaddr = 0; +		segs[0].p_memsz = -1; +		Ehdr *eh = (void *)base; +		Phdr *ph = (void *)(base + eh->e_phoff); +		size_t phnum = eh->e_phnum; +		size_t phent = eh->e_phentsize; +		while (phnum-- && ph->p_type != PT_DYNAMIC) +			ph = (void *)((size_t)ph + phent); +		dynv = (void *)(base + ph->p_vaddr); +	} +#endif +  	for (i=0; i<DYN_CNT; i++) dyn[i] = 0;  	for (i=0; dynv[i]; i+=2) if (dynv[i]<DYN_CNT)  		dyn[dynv[i]] = dynv[i+1]; +#if DL_FDPIC +	for (i=0; i<DYN_CNT; i++) { +		if (i==DT_RELASZ || i==DT_RELSZ) continue; +		if (!dyn[i]) continue; +		for (j=0; dyn[i]-segs[j].p_vaddr >= segs[j].p_memsz; j++); +		dyn[i] += segs[j].addr - segs[j].p_vaddr; +	} +	base = 0; + +	const Sym *syms = (void *)dyn[DT_SYMTAB]; + +	rel = (void *)dyn[DT_RELA]; +	rel_size = dyn[DT_RELASZ]; +	for (; rel_size; rel+=3, rel_size-=3*sizeof(size_t)) { +		if (!IS_RELATIVE(rel[1], syms)) continue; +		for (j=0; rel[0]-segs[j].p_vaddr >= segs[j].p_memsz; j++); +		size_t *rel_addr = (void *) +			(rel[0] + segs[j].addr - segs[j].p_vaddr); +		if (R_TYPE(rel[1]) == REL_FUNCDESC_VAL) { +			*rel_addr += segs[rel_addr[1]].addr +				- segs[rel_addr[1]].p_vaddr +				+ syms[R_SYM(rel[1])].st_value; +			rel_addr[1] = dyn[DT_PLTGOT]; +		} else { +			size_t val = syms[R_SYM(rel[1])].st_value; +			for (j=0; val-segs[j].p_vaddr >= segs[j].p_memsz; j++); +			*rel_addr = rel[2] + segs[j].addr - segs[j].p_vaddr + val; +		} +	} +#else  	/* If the dynamic linker is invoked as a command, its load  	 * address is not available in the aux vector. Instead, compute  	 * the load address as the difference between &_DYNAMIC and the @@ -68,7 +128,7 @@ void _dlstart_c(size_t *sp, size_t *dynv)  	rel = (void *)(base+dyn[DT_REL]);  	rel_size = dyn[DT_RELSZ];  	for (; rel_size; rel+=2, rel_size-=2*sizeof(size_t)) { -		if (!IS_RELATIVE(rel[1])) continue; +		if (!IS_RELATIVE(rel[1], 0)) continue;  		size_t *rel_addr = (void *)(base + rel[0]);  		*rel_addr += base;  	} @@ -76,10 +136,11 @@ void _dlstart_c(size_t *sp, size_t *dynv)  	rel = (void *)(base+dyn[DT_RELA]);  	rel_size = dyn[DT_RELASZ];  	for (; rel_size; rel+=3, rel_size-=3*sizeof(size_t)) { -		if (!IS_RELATIVE(rel[1])) continue; +		if (!IS_RELATIVE(rel[1], 0)) continue;  		size_t *rel_addr = (void *)(base + rel[0]);  		*rel_addr = base + rel[2];  	} +#endif  	stage2_func dls2;  	GETFUNCSYM(&dls2, __dls2, base+dyn[DT_PLTGOT]); diff --git a/src/ldso/dynlink.c b/src/ldso/dynlink.c index 4b52a5a6..4903dbd2 100644 --- a/src/ldso/dynlink.c +++ b/src/ldso/dynlink.c @@ -42,7 +42,11 @@ struct td_index {  };  struct dso { +#if DL_FDPIC +	struct fdpic_loadmap *loadmap; +#else  	unsigned char *base; +#endif  	char *name;  	size_t *dynv;  	struct dso *next, *prev; @@ -75,6 +79,16 @@ struct dso {  	struct td_index *td_index;  	struct dso *fini_next;  	char *shortname; +#if DL_FDPIC +	unsigned char *base; +#else +	struct fdpic_loadmap *loadmap; +#endif +	struct funcdesc { +		void *addr; +		size_t *got; +	} *funcdescs; +	size_t *got;  	char buf[];  }; @@ -112,6 +126,8 @@ static struct debug debug;  static size_t tls_cnt, tls_offset, tls_align = MIN_TLS_ALIGN;  static size_t static_tls_cnt;  static pthread_mutex_t init_fini_lock = { ._m_type = PTHREAD_MUTEX_RECURSIVE }; +static struct fdpic_loadmap *app_loadmap; +static struct fdpic_dummy_loadmap app_dummy_loadmap;  struct debug *_dl_debug_addr = &debug; @@ -123,7 +139,20 @@ static int dl_strcmp(const char *l, const char *r)  #define strcmp(l,r) dl_strcmp(l,r)  /* Compute load address for a virtual address in a given dso. */ +#ifdef DL_FDPIC +static void *laddr(const struct dso *p, size_t v) +{ +	size_t j=0; +	if (!p->loadmap) return p->base + v; +	for (j=0; v-p->loadmap->segs[j].p_vaddr >= p->loadmap->segs[j].p_memsz; j++); +	return (void *)(v - p->loadmap->segs[j].p_vaddr + p->loadmap->segs[j].addr); +} +#define fpaddr(p, v) ((void (*)())&(struct funcdesc){ \ +	laddr(p, v), (p)->got }) +#else  #define laddr(p, v) (void *)((p)->base + (v)) +#define fpaddr(p, v) ((void (*)())laddr(p, v)) +#endif  static void decode_vec(size_t *v, size_t *a, size_t cnt)  { @@ -284,7 +313,7 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri  	}  	for (; rel_size; rel+=stride, rel_size-=stride*sizeof(size_t)) { -		if (skip_relative && IS_RELATIVE(rel[1])) continue; +		if (skip_relative && IS_RELATIVE(rel[1], dso->syms)) continue;  		type = R_TYPE(rel[1]);  		if (type == REL_NONE) continue;  		sym_index = R_SYM(rel[1]); @@ -293,7 +322,9 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri  			sym = syms + sym_index;  			name = strings + sym->st_name;  			ctx = type==REL_COPY ? head->next : head; -			def = find_sym(ctx, name, type==REL_PLT); +			def = (sym->st_info&0xf) == STT_SECTION +				? (struct symdef){ .dso = dso, .sym = sym } +				: find_sym(ctx, name, type==REL_PLT);  			if (!def.sym && (sym->st_shndx != SHN_UNDEF  			    || sym->st_info>>4 != STB_WEAK)) {  				error("Error relocating %s: %s: symbol not found", @@ -349,6 +380,15 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri  			*(uint32_t *)reloc_addr = sym_val + addend  				- (size_t)reloc_addr;  			break; +		case REL_FUNCDESC: +			*reloc_addr = def.sym ? (size_t)(def.dso->funcdescs +				+ (def.sym - def.dso->syms)) : 0; +			break; +		case REL_FUNCDESC_VAL: +			if ((sym->st_info&0xf) == STT_SECTION) *reloc_addr += sym_val; +			else *reloc_addr = sym_val; +			reloc_addr[1] = def.sym ? (size_t)def.dso->got : 0; +			break;  		case REL_DTPMOD:  			*reloc_addr = def.dso->tls_id;  			break; @@ -430,6 +470,7 @@ static void reclaim_gaps(struct dso *dso)  	Phdr *ph = dso->phdr;  	size_t phcnt = dso->phnum; +	if (DL_FDPIC) return; // FIXME  	for (; phcnt--; ph=(void *)((char *)ph+dso->phentsize)) {  		if (ph->p_type!=PT_LOAD) continue;  		if ((ph->p_flags&(PF_R|PF_W))!=(PF_R|PF_W)) continue; @@ -698,6 +739,8 @@ static void decode_dyn(struct dso *p)  		p->rpath_orig = p->strings + dyn[DT_RPATH];  	if (dyn[0]&(1<<DT_RUNPATH))  		p->rpath_orig = p->strings + dyn[DT_RUNPATH]; +	if (dyn[0]&(1<<DT_PLTGOT)) +		p->got = laddr(p, dyn[DT_PLTGOT]);  	if (search_vec(p->dynv, dyn, DT_GNU_HASH))  		p->ghashtab = laddr(p, *dyn);  	if (search_vec(p->dynv, dyn, DT_VERSYM)) @@ -723,6 +766,46 @@ static size_t count_syms(struct dso *p)  	return nsym;  } +static void *dl_mmap(size_t n) +{ +	void *p; +	int prot = PROT_READ|PROT_WRITE, flags = MAP_ANONYMOUS|MAP_PRIVATE; +#ifdef SYS_mmap2 +	p = (void *)__syscall(SYS_mmap2, 0, n, prot, flags, -1, 0); +#else +	p = (void *)__syscall(SYS_mmap, 0, n, prot, flags, -1, 0); +#endif +	return p == MAP_FAILED ? 0 : p; +} + +static void makefuncdescs(struct dso *p) +{ +	static int self_done; +	size_t nsym = count_syms(p); +	size_t i, size = nsym * sizeof(*p->funcdescs); + +	if (!self_done) { +		p->funcdescs = dl_mmap(size); +		self_done = 1; +	} else { +		p->funcdescs = malloc(size); +	} +	if (!p->funcdescs) { +		if (!runtime) a_crash(); +		error("Error allocating function descriptors for %s", p->name); +		longjmp(*rtld_fail, 1); +	} +	for (i=0; i<nsym; i++) { +		if ((p->syms[i].st_info&0xf)==STT_FUNC && p->syms[i].st_shndx) { +			p->funcdescs[i].addr = laddr(p, p->syms[i].st_value); +			p->funcdescs[i].got = p->got; +		} else { +			p->funcdescs[i].addr = 0; +			p->funcdescs[i].got = 0; +		} +	} +} +  static struct dso *load_library(const char *name, struct dso *needed_by)  {  	char buf[2*NAME_MAX+2]; @@ -902,6 +985,8 @@ static struct dso *load_library(const char *name, struct dso *needed_by)  	p->prev = tail;  	tail = p; +	if (DL_FDPIC) makefuncdescs(p); +  	if (ldd_mode) dprintf(1, "\t%s => %s (%p)\n", name, pathname, p->base);  	return p; @@ -1034,7 +1119,7 @@ static void do_fini()  		}  #ifndef NO_LEGACY_INITFINI  		if ((dyn[0] & (1<<DT_FINI)) && dyn[DT_FINI]) -			((void (*)(void))laddr(p, dyn[DT_FINI]))(); +			fpaddr(p, dyn[DT_FINI])();  #endif  	}  } @@ -1057,7 +1142,7 @@ static void do_init_fini(struct dso *p)  		}  #ifndef NO_LEGACY_INITFINI  		if ((dyn[0] & (1<<DT_INIT)) && dyn[DT_INIT]) -			((void (*)(void))laddr(p, dyn[DT_INIT]))(); +			fpaddr(p, dyn[DT_INIT])();  #endif  		if (dyn[0] & (1<<DT_INIT_ARRAY)) {  			size_t n = dyn[DT_INIT_ARRAYSZ]/sizeof(size_t); @@ -1196,16 +1281,33 @@ static void update_tls_size()  void __dls2(unsigned char *base, size_t *sp)  { -	Ehdr *ehdr = (void *)base; -	ldso.base = base; +	if (DL_FDPIC) { +		void *p1 = (void *)sp[-2]; +		void *p2 = (void *)sp[-1]; +		if (!p1) { +			size_t *auxv, aux[AUX_CNT]; +			for (auxv=sp+1+*sp+1; *auxv; auxv++); auxv++; +			decode_vec(auxv, aux, AUX_CNT); +			if (aux[AT_BASE]) ldso.base = (void *)aux[AT_BASE]; +			else ldso.base = (void *)(aux[AT_PHDR] & -4096); +		} +		app_loadmap = p2 ? p1 : 0; +		ldso.loadmap = p2 ? p2 : p1; +		ldso.base = laddr(&ldso, 0); +	} else { +		ldso.base = base; +	} +	Ehdr *ehdr = (void *)ldso.base;  	ldso.name = ldso.shortname = "libc.so";  	ldso.global = 1;  	ldso.phnum = ehdr->e_phnum; -	ldso.phdr = (void *)(base + ehdr->e_phoff); +	ldso.phdr = laddr(&ldso, ehdr->e_phoff);  	ldso.phentsize = ehdr->e_phentsize;  	kernel_mapped_dso(&ldso);  	decode_dyn(&ldso); +	if (DL_FDPIC) makefuncdescs(&ldso); +  	/* Prepare storage for to save clobbered REL addends so they  	 * can be reused in stage 3. There should be very few. If  	 * something goes wrong and there are a huge number, abort @@ -1217,7 +1319,7 @@ void __dls2(unsigned char *base, size_t *sp)  	size_t symbolic_rel_cnt = 0;  	apply_addends_to = rel;  	for (; rel_size; rel+=2, rel_size-=2*sizeof(size_t)) -		if (!IS_RELATIVE(rel[1])) symbolic_rel_cnt++; +		if (!IS_RELATIVE(rel[1], ldso.syms)) symbolic_rel_cnt++;  	if (symbolic_rel_cnt >= ADDEND_LIMIT) a_crash();  	size_t addends[symbolic_rel_cnt+1];  	saved_addends = addends; @@ -1231,7 +1333,8 @@ void __dls2(unsigned char *base, size_t *sp)  	 * symbolically as a barrier against moving the address  	 * load across the above relocation processing. */  	struct symdef dls3_def = find_sym(&ldso, "__dls3", 0); -	((stage3_func)laddr(&ldso, dls3_def.sym->st_value))(sp); +	if (DL_FDPIC) ((stage3_func)&ldso.funcdescs[dls3_def.sym-ldso.syms])(sp); +	else ((stage3_func)laddr(&ldso, dls3_def.sym->st_value))(sp);  }  /* Stage 3 of the dynamic linker is called with the dynamic linker/libc @@ -1298,6 +1401,7 @@ _Noreturn void __dls3(size_t *sp)  				app.tls_align = phdr->p_align;  			}  		} +		if (DL_FDPIC) app.loadmap = app_loadmap;  		if (app.tls_size) app.tls_image = laddr(&app, tls_image);  		if (interp_off) ldso.name = laddr(&app, interp_off);  		if ((aux[0] & (1UL<<AT_EXECFN)) @@ -1384,6 +1488,16 @@ _Noreturn void __dls3(size_t *sp)  	}  	app.global = 1;  	decode_dyn(&app); +	if (DL_FDPIC) { +		makefuncdescs(&app); +		if (!app.loadmap) { +			app.loadmap = (void *)&app_dummy_loadmap; +			app.loadmap->nsegs = 1; +			app.loadmap->segs[0].addr = (size_t)app.base; +			app.loadmap->segs[0].p_memsz = -1; +		} +		argv[-3] = (void *)app.loadmap; +	}  	/* Attach to vdso, if provided by the kernel */  	if (search_vec(auxv, &vdso_base, AT_SYSINFO_EHDR)) { @@ -1512,6 +1626,8 @@ void *dlopen(const char *file, int mode)  				free(p->td_index);  				p->td_index = tmp;  			} +			if (p->funcdescs) +				free(p->funcdescs);  			if (p->rpath != p->rpath_orig)  				free(p->rpath);  			free(p->deps); | 
