diff options
Diffstat (limited to 'ldso')
| -rw-r--r-- | ldso/dlstart.c | 17 | ||||
| -rw-r--r-- | ldso/dynlink.c | 195 | 
2 files changed, 176 insertions, 36 deletions
| diff --git a/ldso/dlstart.c b/ldso/dlstart.c index 20d50f2c..4aac42bc 100644 --- a/ldso/dlstart.c +++ b/ldso/dlstart.c @@ -45,7 +45,7 @@ hidden void _dlstart_c(size_t *sp, size_t *dynv)  		/* 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 +		 * run as a command, finding the Ehdr is a heuristic: we  		 * have to assume Phdrs start in the first 4k of the file. */  		base = aux[AT_BASE];  		if (!base) base = aux[AT_PHDR] & -4096; @@ -140,6 +140,21 @@ hidden void _dlstart_c(size_t *sp, size_t *dynv)  		size_t *rel_addr = (void *)(base + rel[0]);  		*rel_addr = base + rel[2];  	} + +	rel = (void *)(base+dyn[DT_RELR]); +	rel_size = dyn[DT_RELRSZ]; +	size_t *relr_addr = 0; +	for (; rel_size; rel++, rel_size-=sizeof(size_t)) { +		if ((rel[0]&1) == 0) { +			relr_addr = (void *)(base + rel[0]); +			*relr_addr++ += base; +		} else { +			for (size_t i=0, bitmap=rel[0]; bitmap>>=1; i++) +				if (bitmap&1) +					relr_addr[i] += base; +			relr_addr += 8*sizeof(size_t)-1; +		} +	}  #endif  	stage2_func dls2; diff --git a/ldso/dynlink.c b/ldso/dynlink.c index afec985a..715948f4 100644 --- a/ldso/dynlink.c +++ b/ldso/dynlink.c @@ -1,6 +1,5 @@  #define _GNU_SOURCE  #define SYSCALL_NO_TLS 1 -#include <stdio.h>  #include <stdlib.h>  #include <stdarg.h>  #include <stddef.h> @@ -21,11 +20,26 @@  #include <semaphore.h>  #include <sys/membarrier.h>  #include "pthread_impl.h" +#include "fork_impl.h"  #include "libc.h"  #include "dynlink.h" -#include "malloc_impl.h" -static void error(const char *, ...); +static size_t ldso_page_size; +/* libc.h may have defined a macro for dynamic PAGE_SIZE already, but + * PAGESIZE is only defined if it's constant for the arch. */ +#ifndef PAGESIZE +#undef PAGE_SIZE +#define PAGE_SIZE ldso_page_size +#endif + +#define malloc __libc_malloc +#define calloc __libc_calloc +#define realloc __libc_realloc +#define free __libc_free + +static void error_impl(const char *, ...); +static void error_noop(const char *, ...); +static void (*error)(const char *, ...) = error_noop;  #define MAXP2(a,b) (-(-(a)&-(b)))  #define ALIGN(x,y) ((x)+(y)-1 & -(y)) @@ -79,7 +93,7 @@ struct dso {  	struct dso **deps, *needed_by;  	size_t ndeps_direct;  	size_t next_dep; -	int ctor_visitor; +	pthread_t ctor_visitor;  	char *rpath_orig, *rpath;  	struct tls_module tls;  	size_t tls_id; @@ -145,6 +159,8 @@ static struct fdpic_dummy_loadmap app_dummy_loadmap;  struct debug *_dl_debug_addr = &debug; +extern weak hidden char __ehdr_start[]; +  extern hidden int __malloc_replaced;  hidden void (*const __init_array_start)(void)=0, (*const __fini_array_start)(void)=0; @@ -204,7 +220,8 @@ static void decode_vec(size_t *v, size_t *a, size_t cnt)  	size_t i;  	for (i=0; i<cnt; i++) a[i] = 0;  	for (; v[0]; v+=2) if (v[0]-1<cnt-1) { -		a[0] |= 1UL<<v[0]; +		if (v[0] < 8*sizeof(long)) +			a[0] |= 1UL<<v[0];  		a[v[0]] = v[1];  	}  } @@ -330,6 +347,35 @@ static struct symdef find_sym(struct dso *dso, const char *s, int need_def)  	return find_sym2(dso, s, need_def, 0);  } +static struct symdef get_lfs64(const char *name) +{ +	const char *p; +	static const char lfs64_list[] = +		"aio_cancel\0aio_error\0aio_fsync\0aio_read\0aio_return\0" +		"aio_suspend\0aio_write\0alphasort\0creat\0fallocate\0" +		"fgetpos\0fopen\0freopen\0fseeko\0fsetpos\0fstat\0" +		"fstatat\0fstatfs\0fstatvfs\0ftello\0ftruncate\0ftw\0" +		"getdents\0getrlimit\0glob\0globfree\0lio_listio\0" +		"lockf\0lseek\0lstat\0mkostemp\0mkostemps\0mkstemp\0" +		"mkstemps\0mmap\0nftw\0open\0openat\0posix_fadvise\0" +		"posix_fallocate\0pread\0preadv\0prlimit\0pwrite\0" +		"pwritev\0readdir\0scandir\0sendfile\0setrlimit\0" +		"stat\0statfs\0statvfs\0tmpfile\0truncate\0versionsort\0" +		"__fxstat\0__fxstatat\0__lxstat\0__xstat\0"; +	if (!strcmp(name, "readdir64_r")) +		return find_sym(&ldso, "readdir_r", 1); +	size_t l = strnlen(name, 18); +	if (l<2 || name[l-2]!='6' || name[l-1]!='4' || name[l]) +		goto nomatch; +	for (p=lfs64_list; *p; p++) { +		if (!strncmp(name, p, l-2) && !p[l-2]) +			return find_sym(&ldso, p, 1); +		while (*p) p++; +	} +nomatch: +	return (struct symdef){ 0 }; +} +  static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stride)  {  	unsigned char *base = dso->base; @@ -383,6 +429,7 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri  			def = (sym->st_info>>4) == STB_LOCAL  				? (struct symdef){ .dso = dso, .sym = sym }  				: find_sym(ctx, name, type==REL_PLT); +			if (!def.sym) def = get_lfs64(name);  			if (!def.sym && (sym->st_shndx != SHN_UNDEF  			    || sym->st_info>>4 != STB_WEAK)) {  				if (dso->lazy && (type==REL_PLT || type==REL_GOT)) { @@ -415,8 +462,6 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri  		}  		switch(type) { -		case REL_NONE: -			break;  		case REL_OFFSET:  			addend -= (size_t)reloc_addr;  		case REL_SYMBOLIC: @@ -469,7 +514,7 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri  			break;  #endif  		case REL_TLSDESC: -			if (stride<3) addend = reloc_addr[1]; +			if (stride<3) addend = reloc_addr[!TLSDESC_BACKWARDS];  			if (def.dso->tls_id > static_tls_cnt) {  				struct td_index *new = malloc(sizeof *new);  				if (!new) { @@ -494,13 +539,13 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri  					+ addend;  #endif  			} -#ifdef TLSDESC_BACKWARDS  			/* Some archs (32-bit ARM at least) invert the order of  			 * the descriptor members. Fix them up here. */ -			size_t tmp = reloc_addr[0]; -			reloc_addr[0] = reloc_addr[1]; -			reloc_addr[1] = tmp; -#endif +			if (TLSDESC_BACKWARDS) { +				size_t tmp = reloc_addr[0]; +				reloc_addr[0] = reloc_addr[1]; +				reloc_addr[1] = tmp; +			}  			break;  		default:  			error("Error relocating %s: unsupported relocation type %d", @@ -511,6 +556,24 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri  	}  } +static void do_relr_relocs(struct dso *dso, size_t *relr, size_t relr_size) +{ +	if (dso == &ldso) return; /* self-relocation was done in _dlstart */ +	unsigned char *base = dso->base; +	size_t *reloc_addr; +	for (; relr_size; relr++, relr_size-=sizeof(size_t)) +		if ((relr[0]&1) == 0) { +			reloc_addr = laddr(dso, relr[0]); +			*reloc_addr++ += (size_t)base; +		} else { +			int i = 0; +			for (size_t bitmap=relr[0]; (bitmap>>=1); i++) +				if (bitmap&1) +					reloc_addr[i] += (size_t)base; +			reloc_addr += 8*sizeof(size_t)-1; +		} +} +  static void redo_lazy_relocs()  {  	struct dso *p = lazy_head, *next; @@ -553,16 +616,32 @@ static void reclaim_gaps(struct dso *dso)  	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; +		if (ph->p_memsz == 0) continue;  		reclaim(dso, ph->p_vaddr & -PAGE_SIZE, ph->p_vaddr);  		reclaim(dso, ph->p_vaddr+ph->p_memsz,  			ph->p_vaddr+ph->p_memsz+PAGE_SIZE-1 & -PAGE_SIZE);  	}  } +static ssize_t read_loop(int fd, void *p, size_t n) +{ +	for (size_t i=0; i<n; ) { +		ssize_t l = read(fd, (char *)p+i, n-i); +		if (l<0) { +			if (errno==EINTR) continue; +			else return -1; +		} +		if (l==0) return i; +		i += l; +	} +	return n; +} +  static void *mmap_fixed(void *p, size_t n, int prot, int flags, int fd, off_t off)  {  	static int no_map_fixed;  	char *q; +	if (!n) return p;  	if (!no_map_fixed) {  		q = mmap(p, n, prot, flags|MAP_FIXED, fd, off);  		if (!DL_NOMMU_SUPPORT || q != MAP_FAILED || errno != EINVAL) @@ -849,7 +928,7 @@ static int fixup_rpath(struct dso *p, char *buf, size_t buf_size)  		case ENOENT:  		case ENOTDIR:  		case EACCES: -			break; +			return 0;  		default:  			return -1;  		} @@ -1063,13 +1142,17 @@ static struct dso *load_library(const char *name, struct dso *needed_by)  				snprintf(etc_ldso_path, sizeof etc_ldso_path,  					"%.*s/etc/ld-musl-" LDSO_ARCH ".path",  					(int)prefix_len, prefix); -				FILE *f = fopen(etc_ldso_path, "rbe"); -				if (f) { -					if (getdelim(&sys_path, (size_t[1]){0}, 0, f) <= 0) { +				fd = open(etc_ldso_path, O_RDONLY|O_CLOEXEC); +				if (fd>=0) { +					size_t n = 0; +					if (!fstat(fd, &st)) n = st.st_size; +					if ((sys_path = malloc(n+1))) +						sys_path[n] = 0; +					if (!sys_path || read_loop(fd, sys_path, n)<0) {  						free(sys_path);  						sys_path = "";  					} -					fclose(f); +					close(fd);  				} else if (errno != ENOENT) {  					sys_path = "";  				} @@ -1334,13 +1417,17 @@ static void reloc_all(struct dso *p)  			2+(dyn[DT_PLTREL]==DT_RELA));  		do_relocs(p, laddr(p, dyn[DT_REL]), dyn[DT_RELSZ], 2);  		do_relocs(p, laddr(p, dyn[DT_RELA]), dyn[DT_RELASZ], 3); - -		if (head != &ldso && p->relro_start != p->relro_end && -		    mprotect(laddr(p, p->relro_start), p->relro_end-p->relro_start, PROT_READ) -		    && errno != ENOSYS) { -			error("Error relocating %s: RELRO protection failed: %m", -				p->name); -			if (runtime) longjmp(*rtld_fail, 1); +		if (!DL_FDPIC) +			do_relr_relocs(p, laddr(p, dyn[DT_RELR]), dyn[DT_RELRSZ]); + +		if (head != &ldso && p->relro_start != p->relro_end) { +			long ret = __syscall(SYS_mprotect, laddr(p, p->relro_start), +				p->relro_end-p->relro_start, PROT_READ); +			if (ret != 0 && ret != -ENOSYS) { +				error("Error relocating %s: RELRO protection failed: %m", +					p->name); +				if (runtime) longjmp(*rtld_fail, 1); +			}  		}  		p->relocated = 1; @@ -1381,7 +1468,7 @@ void __libc_exit_fini()  {  	struct dso *p;  	size_t dyn[DYN_CNT]; -	int self = __pthread_self()->tid; +	pthread_t self = __pthread_self();  	/* Take both locks before setting shutting_down, so that  	 * either lock is sufficient to read its value. The lock @@ -1407,6 +1494,17 @@ void __libc_exit_fini()  	}  } +void __ldso_atfork(int who) +{ +	if (who<0) { +		pthread_rwlock_wrlock(&lock); +		pthread_mutex_lock(&init_fini_lock); +	} else { +		pthread_mutex_unlock(&init_fini_lock); +		pthread_rwlock_unlock(&lock); +	} +} +  static struct dso **queue_ctors(struct dso *dso)  {  	size_t cnt, qpos, spos, i; @@ -1465,6 +1563,13 @@ static struct dso **queue_ctors(struct dso *dso)  	}  	queue[qpos] = 0;  	for (i=0; i<qpos; i++) queue[i]->mark = 0; +	for (i=0; i<qpos; i++) +		if (queue[i]->ctor_visitor && queue[i]->ctor_visitor->tid < 0) { +			error("State of %s is inconsistent due to multithreaded fork\n", +				queue[i]->name); +			free(queue); +			if (runtime) longjmp(*rtld_fail, 1); +		}  	return queue;  } @@ -1473,7 +1578,7 @@ static void do_init_fini(struct dso **queue)  {  	struct dso *p;  	size_t dyn[DYN_CNT], i; -	int self = __pthread_self()->tid; +	pthread_t self = __pthread_self();  	pthread_mutex_lock(&init_fini_lock);  	for (i=0; (p=queue[i]); i++) { @@ -1582,7 +1687,7 @@ static void install_new_tls(void)  	/* Install new dtv for each thread. */  	for (j=0, td=self; !j || td!=self; j++, td=td->next) { -		td->dtv = td->dtv_copy = newdtv[j]; +		td->dtv = newdtv[j];  	}  	__tl_unlock(); @@ -1620,11 +1725,12 @@ hidden void __dls2(unsigned char *base, size_t *sp)  	} else {  		ldso.base = base;  	} -	Ehdr *ehdr = (void *)ldso.base; +	Ehdr *ehdr = __ehdr_start ? (void *)__ehdr_start : (void *)ldso.base;  	ldso.name = ldso.shortname = "libc.so";  	ldso.phnum = ehdr->e_phnum;  	ldso.phdr = laddr(&ldso, ehdr->e_phoff);  	ldso.phentsize = ehdr->e_phentsize; +	search_vec(auxv, &ldso_page_size, AT_PAGESZ);  	kernel_mapped_dso(&ldso);  	decode_dyn(&ldso); @@ -1717,6 +1823,9 @@ void __dls3(size_t *sp, size_t *auxv)  		env_preload = getenv("LD_PRELOAD");  	} +	/* Activate error handler function */ +	error = error_impl; +  	/* If the main program was already loaded by the kernel,  	 * AT_PHDR will point to some location other than the dynamic  	 * linker's program headers. */ @@ -1792,7 +1901,7 @@ void __dls3(size_t *sp, size_t *auxv)  			dprintf(2, "%s: cannot load %s: %s\n", ldname, argv[0], strerror(errno));  			_exit(1);  		} -		Ehdr *ehdr = (void *)map_library(fd, &app); +		Ehdr *ehdr = map_library(fd, &app);  		if (!ehdr) {  			dprintf(2, "%s: %s: Not a valid dynamic program\n", ldname, argv[0]);  			_exit(1); @@ -1884,6 +1993,10 @@ void __dls3(size_t *sp, size_t *auxv)  			size_t *ptr = (size_t *) app.dynv[i+1];  			*ptr = (size_t)&debug;  		} +		if (app.dynv[i]==DT_DEBUG_INDIRECT_REL) { +			size_t *ptr = (size_t *)((size_t)&app.dynv[i] + app.dynv[i+1]); +			*ptr = (size_t)&debug; +		}  	}  	/* This must be done before final relocations, since it calls @@ -1938,6 +2051,8 @@ void __dls3(size_t *sp, size_t *auxv)  	 * possibility of incomplete replacement. */  	if (find_sym(head, "malloc", 1).dso != &ldso)  		__malloc_replaced = 1; +	if (find_sym(head, "aligned_alloc", 1).dso != &ldso) +		__aligned_alloc_replaced = 1;  	/* Switch to runtime mode: any further failures in the dynamic  	 * linker are a reportable failure rather than a fatal startup @@ -1948,7 +2063,7 @@ void __dls3(size_t *sp, size_t *auxv)  	debug.bp = dl_debug_state;  	debug.head = head;  	debug.base = ldso.base; -	debug.state = 0; +	debug.state = RT_CONSISTENT;  	_dl_debug_state();  	if (replace_argv0) argv[0] = replace_argv0; @@ -1997,6 +2112,9 @@ void *dlopen(const char *file, int mode)  	pthread_rwlock_wrlock(&lock);  	__inhibit_ptc(); +	debug.state = RT_ADD; +	_dl_debug_state(); +  	p = 0;  	if (shutting_down) {  		error("Cannot dlopen while program is exiting."); @@ -2056,8 +2174,9 @@ void *dlopen(const char *file, int mode)  	load_deps(p);  	extend_bfs_deps(p);  	pthread_mutex_lock(&init_fini_lock); -	if (!p->constructed) ctor_queue = queue_ctors(p); +	int constructed = p->constructed;  	pthread_mutex_unlock(&init_fini_lock); +	if (!constructed) ctor_queue = queue_ctors(p);  	if (!p->relocated && (mode & RTLD_LAZY)) {  		prepare_lazy(p);  		for (i=0; p->deps[i]; i++) @@ -2089,9 +2208,10 @@ void *dlopen(const char *file, int mode)  	update_tls_size();  	if (tls_cnt != orig_tls_cnt)  		install_new_tls(); -	_dl_debug_state();  	orig_tail = tail;  end: +	debug.state = RT_CONSISTENT; +	_dl_debug_state();  	__release_ptc();  	if (p) gencnt++;  	pthread_rwlock_unlock(&lock); @@ -2285,7 +2405,8 @@ int dl_iterate_phdr(int(*callback)(struct dl_phdr_info *info, size_t size, void  		info.dlpi_adds      = gencnt;  		info.dlpi_subs      = 0;  		info.dlpi_tls_modid = current->tls_id; -		info.dlpi_tls_data  = current->tls.image; +		info.dlpi_tls_data = !current->tls_id ? 0 : +			__tls_get_addr((tls_mod_off_t[]){current->tls_id,0});  		ret = (callback)(&info, sizeof (info), data); @@ -2298,7 +2419,7 @@ int dl_iterate_phdr(int(*callback)(struct dl_phdr_info *info, size_t size, void  	return ret;  } -static void error(const char *fmt, ...) +static void error_impl(const char *fmt, ...)  {  	va_list ap;  	va_start(ap, fmt); @@ -2312,3 +2433,7 @@ static void error(const char *fmt, ...)  	__dl_vseterr(fmt, ap);  	va_end(ap);  } + +static void error_noop(const char *fmt, ...) +{ +} | 
