mailing list of musl libc
 help / color / mirror / code / Atom feed
* [PATCH 1/3] ldso: when run via CLI, set auxv as if the app was loaded by the kernel
@ 2019-09-10  4:56 Rodger Combs
  2019-09-10  4:56 ` [PATCH 2/3] ldso: move (un)map_library functions to separate file Rodger Combs
  2019-09-10  4:56 ` [PATCH 3/3] crt: add dcrt1, with support for locating the dynamic loader at runtime Rodger Combs
  0 siblings, 2 replies; 4+ messages in thread
From: Rodger Combs @ 2019-09-10  4:56 UTC (permalink / raw)
  To: musl

---
 ldso/dynlink.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/ldso/dynlink.c b/ldso/dynlink.c
index 5ef2111..22181e3 100644
--- a/ldso/dynlink.c
+++ b/ldso/dynlink.c
@@ -1805,6 +1805,22 @@ void __dls3(size_t *sp)
 			}
 			dprintf(1, "\t%s (%p)\n", ldso.name, ldso.base);
 		}
+
+		// Make it look to the app as if it was loaded by the kernel
+		for (i=0; auxv[i]; i+=2) {
+			if (auxv[i] == AT_BASE)
+				auxv[i + 1] = (size_t)ldso.base;
+			if (auxv[i] == AT_PHDR)
+				auxv[i + 1] = (size_t)app.phdr;
+			if (auxv[i] == AT_ENTRY)
+				auxv[i + 1] = aux[AT_ENTRY];
+			if (auxv[i] == AT_PHNUM)
+				auxv[i + 1] = app.phnum;
+			if (auxv[i] == AT_PHENT)
+				auxv[i + 1] = app.phentsize;
+			if (auxv[i] == AT_EXECFN)
+				auxv[i + 1] = (size_t)app.name;
+		}
 	}
 	if (app.tls.size) {
 		libc.tls_head = tls_tail = &app.tls;
-- 
2.7.4



^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 2/3] ldso: move (un)map_library functions to separate file
  2019-09-10  4:56 [PATCH 1/3] ldso: when run via CLI, set auxv as if the app was loaded by the kernel Rodger Combs
@ 2019-09-10  4:56 ` Rodger Combs
  2019-09-10  4:56 ` [PATCH 3/3] crt: add dcrt1, with support for locating the dynamic loader at runtime Rodger Combs
  1 sibling, 0 replies; 4+ messages in thread
From: Rodger Combs @ 2019-09-10  4:56 UTC (permalink / raw)
  To: musl

---
 ldso/dynlink.c     | 264 +--------------------------------------------------
 ldso/map_library.h | 270 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 272 insertions(+), 262 deletions(-)
 create mode 100644 ldso/map_library.h

diff --git a/ldso/dynlink.c b/ldso/dynlink.c
index 22181e3..6b3966e 100644
--- a/ldso/dynlink.c
+++ b/ldso/dynlink.c
@@ -152,6 +152,8 @@ extern hidden void (*const __init_array_end)(void), (*const __fini_array_end)(vo
 weak_alias(__init_array_start, __init_array_end);
 weak_alias(__fini_array_start, __fini_array_end);
 
+#include "map_library.h"
+
 static int dl_strcmp(const char *l, const char *r)
 {
 	for (; *l==*r && *l; l++, r++);
@@ -159,38 +161,6 @@ 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. */
-#if 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);
-}
-static void *laddr_pg(const struct dso *p, size_t v)
-{
-	size_t j=0;
-	size_t pgsz = PAGE_SIZE;
-	if (!p->loadmap) return p->base + v;
-	for (j=0; ; j++) {
-		size_t a = p->loadmap->segs[j].p_vaddr;
-		size_t b = a + p->loadmap->segs[j].p_memsz;
-		a &= -pgsz;
-		b += pgsz-1;
-		b &= -pgsz;
-		if (v-a<b-a) break;
-	}
-	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 laddr_pg(p, v) laddr(p, v)
-#define fpaddr(p, v) ((void (*)())laddr(p, v))
-#endif
-
 static void decode_vec(size_t *v, size_t *a, size_t cnt)
 {
 	size_t i;
@@ -551,236 +521,6 @@ static void reclaim_gaps(struct dso *dso)
 	}
 }
 
-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 (!no_map_fixed) {
-		q = mmap(p, n, prot, flags|MAP_FIXED, fd, off);
-		if (!DL_NOMMU_SUPPORT || q != MAP_FAILED || errno != EINVAL)
-			return q;
-		no_map_fixed = 1;
-	}
-	/* Fallbacks for MAP_FIXED failure on NOMMU kernels. */
-	if (flags & MAP_ANONYMOUS) {
-		memset(p, 0, n);
-		return p;
-	}
-	ssize_t r;
-	if (lseek(fd, off, SEEK_SET) < 0) return MAP_FAILED;
-	for (q=p; n; q+=r, off+=r, n-=r) {
-		r = read(fd, q, n);
-		if (r < 0 && errno != EINTR) return MAP_FAILED;
-		if (!r) {
-			memset(q, 0, n);
-			break;
-		}
-	}
-	return p;
-}
-
-static void unmap_library(struct dso *dso)
-{
-	if (dso->loadmap) {
-		size_t i;
-		for (i=0; i<dso->loadmap->nsegs; i++) {
-			if (!dso->loadmap->segs[i].p_memsz)
-				continue;
-			munmap((void *)dso->loadmap->segs[i].addr,
-				dso->loadmap->segs[i].p_memsz);
-		}
-		free(dso->loadmap);
-	} else if (dso->map && dso->map_len) {
-		munmap(dso->map, dso->map_len);
-	}
-}
-
-static void *map_library(int fd, struct dso *dso)
-{
-	Ehdr buf[(896+sizeof(Ehdr))/sizeof(Ehdr)];
-	void *allocated_buf=0;
-	size_t phsize;
-	size_t addr_min=SIZE_MAX, addr_max=0, map_len;
-	size_t this_min, this_max;
-	size_t nsegs = 0;
-	off_t off_start;
-	Ehdr *eh;
-	Phdr *ph, *ph0;
-	unsigned prot;
-	unsigned char *map=MAP_FAILED, *base;
-	size_t dyn=0;
-	size_t tls_image=0;
-	size_t i;
-
-	ssize_t l = read(fd, buf, sizeof buf);
-	eh = buf;
-	if (l<0) return 0;
-	if (l<sizeof *eh || (eh->e_type != ET_DYN && eh->e_type != ET_EXEC))
-		goto noexec;
-	phsize = eh->e_phentsize * eh->e_phnum;
-	if (phsize > sizeof buf - sizeof *eh) {
-		allocated_buf = malloc(phsize);
-		if (!allocated_buf) return 0;
-		l = pread(fd, allocated_buf, phsize, eh->e_phoff);
-		if (l < 0) goto error;
-		if (l != phsize) goto noexec;
-		ph = ph0 = allocated_buf;
-	} else if (eh->e_phoff + phsize > l) {
-		l = pread(fd, buf+1, phsize, eh->e_phoff);
-		if (l < 0) goto error;
-		if (l != phsize) goto noexec;
-		ph = ph0 = (void *)(buf + 1);
-	} else {
-		ph = ph0 = (void *)((char *)buf + eh->e_phoff);
-	}
-	for (i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) {
-		if (ph->p_type == PT_DYNAMIC) {
-			dyn = ph->p_vaddr;
-		} else if (ph->p_type == PT_TLS) {
-			tls_image = ph->p_vaddr;
-			dso->tls.align = ph->p_align;
-			dso->tls.len = ph->p_filesz;
-			dso->tls.size = ph->p_memsz;
-		} else if (ph->p_type == PT_GNU_RELRO) {
-			dso->relro_start = ph->p_vaddr & -PAGE_SIZE;
-			dso->relro_end = (ph->p_vaddr + ph->p_memsz) & -PAGE_SIZE;
-		} else if (ph->p_type == PT_GNU_STACK) {
-			if (!runtime && ph->p_memsz > __default_stacksize) {
-				__default_stacksize =
-					ph->p_memsz < DEFAULT_STACK_MAX ?
-					ph->p_memsz : DEFAULT_STACK_MAX;
-			}
-		}
-		if (ph->p_type != PT_LOAD) continue;
-		nsegs++;
-		if (ph->p_vaddr < addr_min) {
-			addr_min = ph->p_vaddr;
-			off_start = ph->p_offset;
-			prot = (((ph->p_flags&PF_R) ? PROT_READ : 0) |
-				((ph->p_flags&PF_W) ? PROT_WRITE: 0) |
-				((ph->p_flags&PF_X) ? PROT_EXEC : 0));
-		}
-		if (ph->p_vaddr+ph->p_memsz > addr_max) {
-			addr_max = ph->p_vaddr+ph->p_memsz;
-		}
-	}
-	if (!dyn) goto noexec;
-	if (DL_FDPIC && !(eh->e_flags & FDPIC_CONSTDISP_FLAG)) {
-		dso->loadmap = calloc(1, sizeof *dso->loadmap
-			+ nsegs * sizeof *dso->loadmap->segs);
-		if (!dso->loadmap) goto error;
-		dso->loadmap->nsegs = nsegs;
-		for (ph=ph0, i=0; i<nsegs; ph=(void *)((char *)ph+eh->e_phentsize)) {
-			if (ph->p_type != PT_LOAD) continue;
-			prot = (((ph->p_flags&PF_R) ? PROT_READ : 0) |
-				((ph->p_flags&PF_W) ? PROT_WRITE: 0) |
-				((ph->p_flags&PF_X) ? PROT_EXEC : 0));
-			map = mmap(0, ph->p_memsz + (ph->p_vaddr & PAGE_SIZE-1),
-				prot, MAP_PRIVATE,
-				fd, ph->p_offset & -PAGE_SIZE);
-			if (map == MAP_FAILED) {
-				unmap_library(dso);
-				goto error;
-			}
-			dso->loadmap->segs[i].addr = (size_t)map +
-				(ph->p_vaddr & PAGE_SIZE-1);
-			dso->loadmap->segs[i].p_vaddr = ph->p_vaddr;
-			dso->loadmap->segs[i].p_memsz = ph->p_memsz;
-			i++;
-			if (prot & PROT_WRITE) {
-				size_t brk = (ph->p_vaddr & PAGE_SIZE-1)
-					+ ph->p_filesz;
-				size_t pgbrk = brk + PAGE_SIZE-1 & -PAGE_SIZE;
-				size_t pgend = brk + ph->p_memsz - ph->p_filesz
-					+ PAGE_SIZE-1 & -PAGE_SIZE;
-				if (pgend > pgbrk && mmap_fixed(map+pgbrk,
-					pgend-pgbrk, prot,
-					MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS,
-					-1, off_start) == MAP_FAILED)
-					goto error;
-				memset(map + brk, 0, pgbrk-brk);
-			}
-		}
-		map = (void *)dso->loadmap->segs[0].addr;
-		map_len = 0;
-		goto done_mapping;
-	}
-	addr_max += PAGE_SIZE-1;
-	addr_max &= -PAGE_SIZE;
-	addr_min &= -PAGE_SIZE;
-	off_start &= -PAGE_SIZE;
-	map_len = addr_max - addr_min + off_start;
-	/* The first time, we map too much, possibly even more than
-	 * the length of the file. This is okay because we will not
-	 * use the invalid part; we just need to reserve the right
-	 * amount of virtual address space to map over later. */
-	map = DL_NOMMU_SUPPORT
-		? mmap((void *)addr_min, map_len, PROT_READ|PROT_WRITE|PROT_EXEC,
-			MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
-		: mmap((void *)addr_min, map_len, prot,
-			MAP_PRIVATE, fd, off_start);
-	if (map==MAP_FAILED) goto error;
-	dso->map = map;
-	dso->map_len = map_len;
-	/* If the loaded file is not relocatable and the requested address is
-	 * not available, then the load operation must fail. */
-	if (eh->e_type != ET_DYN && addr_min && map!=(void *)addr_min) {
-		errno = EBUSY;
-		goto error;
-	}
-	base = map - addr_min;
-	dso->phdr = 0;
-	dso->phnum = 0;
-	for (ph=ph0, i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) {
-		if (ph->p_type != PT_LOAD) continue;
-		/* Check if the programs headers are in this load segment, and
-		 * if so, record the address for use by dl_iterate_phdr. */
-		if (!dso->phdr && eh->e_phoff >= ph->p_offset
-		    && eh->e_phoff+phsize <= ph->p_offset+ph->p_filesz) {
-			dso->phdr = (void *)(base + ph->p_vaddr
-				+ (eh->e_phoff-ph->p_offset));
-			dso->phnum = eh->e_phnum;
-			dso->phentsize = eh->e_phentsize;
-		}
-		this_min = ph->p_vaddr & -PAGE_SIZE;
-		this_max = ph->p_vaddr+ph->p_memsz+PAGE_SIZE-1 & -PAGE_SIZE;
-		off_start = ph->p_offset & -PAGE_SIZE;
-		prot = (((ph->p_flags&PF_R) ? PROT_READ : 0) |
-			((ph->p_flags&PF_W) ? PROT_WRITE: 0) |
-			((ph->p_flags&PF_X) ? PROT_EXEC : 0));
-		/* Reuse the existing mapping for the lowest-address LOAD */
-		if ((ph->p_vaddr & -PAGE_SIZE) != addr_min || DL_NOMMU_SUPPORT)
-			if (mmap_fixed(base+this_min, this_max-this_min, prot, MAP_PRIVATE|MAP_FIXED, fd, off_start) == MAP_FAILED)
-				goto error;
-		if (ph->p_memsz > ph->p_filesz && (ph->p_flags&PF_W)) {
-			size_t brk = (size_t)base+ph->p_vaddr+ph->p_filesz;
-			size_t pgbrk = brk+PAGE_SIZE-1 & -PAGE_SIZE;
-			memset((void *)brk, 0, pgbrk-brk & PAGE_SIZE-1);
-			if (pgbrk-(size_t)base < this_max && mmap_fixed((void *)pgbrk, (size_t)base+this_max-pgbrk, prot, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) == MAP_FAILED)
-				goto error;
-		}
-	}
-	for (i=0; ((size_t *)(base+dyn))[i]; i+=2)
-		if (((size_t *)(base+dyn))[i]==DT_TEXTREL) {
-			if (mprotect(map, map_len, PROT_READ|PROT_WRITE|PROT_EXEC)
-			    && errno != ENOSYS)
-				goto error;
-			break;
-		}
-done_mapping:
-	dso->base = base;
-	dso->dynv = laddr(dso, dyn);
-	if (dso->tls.size) dso->tls.image = laddr(dso, tls_image);
-	free(allocated_buf);
-	return map;
-noexec:
-	errno = ENOEXEC;
-error:
-	if (map!=MAP_FAILED) unmap_library(dso);
-	free(allocated_buf);
-	return 0;
-}
-
 static int path_open(const char *name, const char *s, char *buf, size_t buf_size)
 {
 	size_t l;
diff --git a/ldso/map_library.h b/ldso/map_library.h
new file mode 100644
index 0000000..3609a0f
--- /dev/null
+++ b/ldso/map_library.h
@@ -0,0 +1,270 @@
+#include <errno.h>
+#include <features.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include "dynlink.h"
+#include "pthread_impl.h"
+
+/* Compute load address for a virtual address in a given dso. */
+#if DL_FDPIC
+static inline 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);
+}
+static inline void *laddr_pg(const struct dso *p, size_t v)
+{
+	size_t j=0;
+	size_t pgsz = PAGE_SIZE;
+	if (!p->loadmap) return p->base + v;
+	for (j=0; ; j++) {
+		size_t a = p->loadmap->segs[j].p_vaddr;
+		size_t b = a + p->loadmap->segs[j].p_memsz;
+		a &= -pgsz;
+		b += pgsz-1;
+		b &= -pgsz;
+		if (v-a<b-a) break;
+	}
+	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 laddr_pg(p, v) laddr(p, v)
+#define fpaddr(p, v) ((void (*)())laddr(p, v))
+#endif
+
+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 (!no_map_fixed) {
+		q = mmap(p, n, prot, flags|MAP_FIXED, fd, off);
+		if (!DL_NOMMU_SUPPORT || q != MAP_FAILED || errno != EINVAL)
+			return q;
+		no_map_fixed = 1;
+	}
+	/* Fallbacks for MAP_FIXED failure on NOMMU kernels. */
+	if (flags & MAP_ANONYMOUS) {
+		memset(p, 0, n);
+		return p;
+	}
+	ssize_t r;
+	if (lseek(fd, off, SEEK_SET) < 0) return MAP_FAILED;
+	for (q=p; n; q+=r, off+=r, n-=r) {
+		r = read(fd, q, n);
+		if (r < 0 && errno != EINTR) return MAP_FAILED;
+		if (!r) {
+			memset(q, 0, n);
+			break;
+		}
+	}
+	return p;
+}
+
+static inline void unmap_library(struct dso *dso)
+{
+	if (dso->loadmap) {
+		size_t i;
+		for (i=0; i<dso->loadmap->nsegs; i++) {
+			if (!dso->loadmap->segs[i].p_memsz)
+				continue;
+			munmap((void *)dso->loadmap->segs[i].addr,
+				dso->loadmap->segs[i].p_memsz);
+		}
+		free(dso->loadmap);
+	} else if (dso->map && dso->map_len) {
+		munmap(dso->map, dso->map_len);
+	}
+}
+
+static inline void *map_library(int fd, struct dso *dso)
+{
+	Ehdr buf[(896+sizeof(Ehdr))/sizeof(Ehdr)];
+	void *allocated_buf=0;
+	size_t phsize;
+	size_t ph_allocated_size;
+	size_t addr_min=SIZE_MAX, addr_max=0, map_len;
+	size_t this_min, this_max;
+	size_t nsegs = 0;
+	off_t off_start;
+	Ehdr *eh;
+	Phdr *ph, *ph0;
+	unsigned prot;
+	unsigned char *map=MAP_FAILED, *base;
+	size_t dyn=0;
+	size_t i;
+	size_t tls_image=0;
+
+	ssize_t l = read(fd, buf, sizeof buf);
+	eh = buf;
+	if (l<0) return 0;
+	if (l<sizeof *eh || (eh->e_type != ET_DYN && eh->e_type != ET_EXEC))
+		goto noexec;
+	phsize = eh->e_phentsize * eh->e_phnum;
+	if (phsize > sizeof buf - sizeof *eh) {
+		allocated_buf = malloc(phsize);
+		if (!allocated_buf) return 0;
+		l = pread(fd, allocated_buf, phsize, eh->e_phoff);
+		if (l < 0) goto error;
+		if (l != phsize) goto noexec;
+		ph = ph0 = allocated_buf;
+	} else if (eh->e_phoff + phsize > l) {
+		l = pread(fd, buf+1, phsize, eh->e_phoff);
+		if (l < 0) goto error;
+		if (l != phsize) goto noexec;
+		ph = ph0 = (void *)(buf + 1);
+	} else {
+		ph = ph0 = (void *)((char *)buf + eh->e_phoff);
+	}
+	for (i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) {
+		if (ph->p_type == PT_DYNAMIC) {
+			dyn = ph->p_vaddr;
+		} else if (ph->p_type == PT_TLS) {
+			tls_image = ph->p_vaddr;
+			dso->tls.align = ph->p_align;
+			dso->tls.len = ph->p_filesz;
+			dso->tls.size = ph->p_memsz;
+		} else if (ph->p_type == PT_GNU_RELRO) {
+			dso->relro_start = ph->p_vaddr & -PAGE_SIZE;
+			dso->relro_end = (ph->p_vaddr + ph->p_memsz) & -PAGE_SIZE;
+		} else if (ph->p_type == PT_GNU_STACK) {
+			if (!runtime && ph->p_memsz > __default_stacksize) {
+				__default_stacksize =
+					ph->p_memsz < DEFAULT_STACK_MAX ?
+					ph->p_memsz : DEFAULT_STACK_MAX;
+			}
+		}
+		if (ph->p_type != PT_LOAD) continue;
+		nsegs++;
+		if (ph->p_vaddr < addr_min) {
+			addr_min = ph->p_vaddr;
+			off_start = ph->p_offset;
+			prot = (((ph->p_flags&PF_R) ? PROT_READ : 0) |
+				((ph->p_flags&PF_W) ? PROT_WRITE: 0) |
+				((ph->p_flags&PF_X) ? PROT_EXEC : 0));
+		}
+		if (ph->p_vaddr+ph->p_memsz > addr_max) {
+			addr_max = ph->p_vaddr+ph->p_memsz;
+		}
+	}
+	if (!dyn) goto noexec;
+	if (DL_FDPIC && !(eh->e_flags & FDPIC_CONSTDISP_FLAG)) {
+		dso->loadmap = calloc(1, sizeof *dso->loadmap
+			+ nsegs * sizeof *dso->loadmap->segs);
+		if (!dso->loadmap) goto error;
+		dso->loadmap->nsegs = nsegs;
+		for (ph=ph0, i=0; i<nsegs; ph=(void *)((char *)ph+eh->e_phentsize)) {
+			if (ph->p_type != PT_LOAD) continue;
+			prot = (((ph->p_flags&PF_R) ? PROT_READ : 0) |
+				((ph->p_flags&PF_W) ? PROT_WRITE: 0) |
+				((ph->p_flags&PF_X) ? PROT_EXEC : 0));
+			map = mmap(0, ph->p_memsz + (ph->p_vaddr & PAGE_SIZE-1),
+				prot, MAP_PRIVATE,
+				fd, ph->p_offset & -PAGE_SIZE);
+			if (map == MAP_FAILED) {
+				unmap_library(dso);
+				goto error;
+			}
+			dso->loadmap->segs[i].addr = (size_t)map +
+				(ph->p_vaddr & PAGE_SIZE-1);
+			dso->loadmap->segs[i].p_vaddr = ph->p_vaddr;
+			dso->loadmap->segs[i].p_memsz = ph->p_memsz;
+			i++;
+			if (prot & PROT_WRITE) {
+				size_t brk = (ph->p_vaddr & PAGE_SIZE-1)
+					+ ph->p_filesz;
+				size_t pgbrk = brk + PAGE_SIZE-1 & -PAGE_SIZE;
+				size_t pgend = brk + ph->p_memsz - ph->p_filesz
+					+ PAGE_SIZE-1 & -PAGE_SIZE;
+				if (pgend > pgbrk && mmap_fixed(map+pgbrk,
+					pgend-pgbrk, prot,
+					MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS,
+					-1, off_start) == MAP_FAILED)
+					goto error;
+				memset(map + brk, 0, pgbrk-brk);
+			}
+		}
+		map = (void *)dso->loadmap->segs[0].addr;
+		map_len = 0;
+		goto done_mapping;
+	}
+	addr_max += PAGE_SIZE-1;
+	addr_max &= -PAGE_SIZE;
+	addr_min &= -PAGE_SIZE;
+	off_start &= -PAGE_SIZE;
+	map_len = addr_max - addr_min + off_start;
+	/* The first time, we map too much, possibly even more than
+	 * the length of the file. This is okay because we will not
+	 * use the invalid part; we just need to reserve the right
+	 * amount of virtual address space to map over later. */
+	map = DL_NOMMU_SUPPORT
+		? mmap((void *)addr_min, map_len, PROT_READ|PROT_WRITE|PROT_EXEC,
+			MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
+		: mmap((void *)addr_min, map_len, prot,
+			MAP_PRIVATE, fd, off_start);
+	if (map==MAP_FAILED) goto error;
+	dso->map = map;
+	dso->map_len = map_len;
+	/* If the loaded file is not relocatable and the requested address is
+	 * not available, then the load operation must fail. */
+	if (eh->e_type != ET_DYN && addr_min && map!=(void *)addr_min) {
+		errno = EBUSY;
+		goto error;
+	}
+	base = map - addr_min;
+	dso->phdr = 0;
+	dso->phnum = 0;
+	for (ph=ph0, i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) {
+		if (ph->p_type != PT_LOAD) continue;
+		/* Check if the programs headers are in this load segment, and
+		 * if so, record the address for use by dl_iterate_phdr. */
+		if (!dso->phdr && eh->e_phoff >= ph->p_offset
+		    && eh->e_phoff+phsize <= ph->p_offset+ph->p_filesz) {
+			dso->phdr = (void *)(base + ph->p_vaddr
+				+ (eh->e_phoff-ph->p_offset));
+			dso->phnum = eh->e_phnum;
+			dso->phentsize = eh->e_phentsize;
+		}
+		this_min = ph->p_vaddr & -PAGE_SIZE;
+		this_max = ph->p_vaddr+ph->p_memsz+PAGE_SIZE-1 & -PAGE_SIZE;
+		off_start = ph->p_offset & -PAGE_SIZE;
+		prot = (((ph->p_flags&PF_R) ? PROT_READ : 0) |
+			((ph->p_flags&PF_W) ? PROT_WRITE: 0) |
+			((ph->p_flags&PF_X) ? PROT_EXEC : 0));
+		/* Reuse the existing mapping for the lowest-address LOAD */
+		if ((ph->p_vaddr & -PAGE_SIZE) != addr_min || DL_NOMMU_SUPPORT)
+			if (mmap_fixed(base+this_min, this_max-this_min, prot, MAP_PRIVATE|MAP_FIXED, fd, off_start) == MAP_FAILED)
+				goto error;
+		if (ph->p_memsz > ph->p_filesz && (ph->p_flags&PF_W)) {
+			size_t brk = (size_t)base+ph->p_vaddr+ph->p_filesz;
+			size_t pgbrk = brk+PAGE_SIZE-1 & -PAGE_SIZE;
+			memset((void *)brk, 0, pgbrk-brk & PAGE_SIZE-1);
+			if (pgbrk-(size_t)base < this_max && mmap_fixed((void *)pgbrk, (size_t)base+this_max-pgbrk, prot, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) == MAP_FAILED)
+				goto error;
+		}
+	}
+	for (i=0; ((size_t *)(base+dyn))[i]; i+=2)
+		if (((size_t *)(base+dyn))[i]==DT_TEXTREL) {
+			if (mprotect(map, map_len, PROT_READ|PROT_WRITE|PROT_EXEC)
+			    && errno != ENOSYS)
+				goto error;
+			break;
+		}
+done_mapping:
+	dso->base = base;
+	dso->dynv = laddr(dso, dyn);
+	if (dso->tls.size) dso->tls.image = laddr(dso, tls_image);
+	free(allocated_buf);
+	return map;
+noexec:
+	errno = ENOEXEC;
+error:
+	if (map!=MAP_FAILED) unmap_library(dso);
+	free(allocated_buf);
+	return 0;
+}
-- 
2.7.4



^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 3/3] crt: add dcrt1, with support for locating the dynamic loader at runtime
  2019-09-10  4:56 [PATCH 1/3] ldso: when run via CLI, set auxv as if the app was loaded by the kernel Rodger Combs
  2019-09-10  4:56 ` [PATCH 2/3] ldso: move (un)map_library functions to separate file Rodger Combs
@ 2019-09-10  4:56 ` Rodger Combs
  1 sibling, 0 replies; 4+ messages in thread
From: Rodger Combs @ 2019-09-10  4:56 UTC (permalink / raw)
  To: musl

---
 Makefile                |  10 +-
 crt/dcrt1.c             | 365 ++++++++++++++++++++++++++++++++++++++++++++++++
 ldso/dlstart.c          |  59 +++++---
 ldso/dynlink.c          |  13 ++
 ldso/map_library.h      |  59 ++++----
 tools/ld.musl-clang.in  |  17 ++-
 tools/musl-clang.in     |   8 ++
 tools/musl-gcc.specs.sh |   4 +-
 8 files changed, 483 insertions(+), 52 deletions(-)
 create mode 100644 crt/dcrt1.c

diff --git a/Makefile b/Makefile
index b46f8ca..163bf03 100644
--- a/Makefile
+++ b/Makefile
@@ -105,13 +105,15 @@ obj/src/internal/version.h: $(wildcard $(srcdir)/VERSION $(srcdir)/.git)
 
 obj/src/internal/version.o obj/src/internal/version.lo: obj/src/internal/version.h
 
-obj/crt/rcrt1.o obj/ldso/dlstart.lo obj/ldso/dynlink.lo: $(srcdir)/src/internal/dynlink.h $(srcdir)/arch/$(ARCH)/reloc.h
+obj/crt/rcrt1.o obj/crt/dcrt1.o obj/ldso/dlstart.lo obj/ldso/dynlink.lo: $(srcdir)/src/internal/dynlink.h $(srcdir)/arch/$(ARCH)/reloc.h
 
-obj/crt/crt1.o obj/crt/scrt1.o obj/crt/rcrt1.o obj/ldso/dlstart.lo: $(srcdir)/arch/$(ARCH)/crt_arch.h
+obj/crt/crt1.o obj/crt/scrt1.o obj/crt/rcrt1.o obj/crt/dcrt1.o obj/ldso/dlstart.lo: $(srcdir)/arch/$(ARCH)/crt_arch.h
 
-obj/crt/rcrt1.o: $(srcdir)/ldso/dlstart.c
+obj/crt/rcrt1.o obj/crt/dcrt1.o: $(srcdir)/ldso/dlstart.c
 
-obj/crt/Scrt1.o obj/crt/rcrt1.o: CFLAGS_ALL += -fPIC
+obj/crt/Scrt1.o obj/crt/rcrt1.o obj/crt/dcrt1.o: CFLAGS_ALL += -fPIC
+
+obj/crt/dcrt1.o: CFLAGS_ALL += -DLDSO_PATHNAME=\"$(LDSO_PATHNAME)\"
 
 OPTIMIZE_SRCS = $(wildcard $(OPTIMIZE_GLOBS:%=$(srcdir)/src/%))
 $(OPTIMIZE_SRCS:$(srcdir)/%.c=obj/%.o) $(OPTIMIZE_SRCS:$(srcdir)/%.c=obj/%.lo): CFLAGS += -O3
diff --git a/crt/dcrt1.c b/crt/dcrt1.c
new file mode 100644
index 0000000..b22082e
--- /dev/null
+++ b/crt/dcrt1.c
@@ -0,0 +1,365 @@
+#define SYSCALL_NO_TLS
+
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <features.h>
+#include <libgen.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <unistd.h>
+#include "atomic.h"
+#include "dynlink.h"
+#include "syscall.h"
+
+extern weak hidden const size_t _DYNAMIC[];
+
+int main();
+weak void _init();
+weak void _fini();
+weak _Noreturn int __libc_start_main(int (*)(), int, char **,
+	void (*)(), void(*)(), void(*)());
+
+#define START "_start"
+#define _dlstart_c _start_c
+#define DL_DNI
+#include "../ldso/dlstart.c"
+
+struct dso {
+	unsigned char *base;
+	struct fdpic_loadmap *loadmap;
+	size_t *dynv;
+	Phdr *phdr;
+	int phnum;
+	size_t phentsize;
+	unsigned char *map;
+	size_t map_len;
+};
+
+static size_t page_size;
+
+#ifndef PAGE_SIZE
+#define PAGE_SIZE page_size
+#endif
+
+#ifdef SYS_mmap2
+#define mmap(start, len, prot, flags, fd, off) (void*)__syscall(SYS_mmap2, start, len, prot, flags, fd, off/SYSCALL_MMAP2_UNIT)
+#else
+#define mmap(start, len, prot, flags, fd, off) (void*)__syscall(SYS_mmap, start, len, prot, flags, fd, off)
+#endif
+
+#define munmap(ptr, len) __syscall(SYS_munmap, ptr, len)
+
+static inline int crt_mprotect(void *addr, size_t len, int prot)
+{
+	size_t start, end;
+	start = (size_t)addr & -PAGE_SIZE;
+	end = (size_t)((char *)addr + len + PAGE_SIZE-1) & -PAGE_SIZE;
+	return __syscall(SYS_mprotect, start, end-start, prot);
+}
+#define mprotect crt_mprotect
+
+#define read(fd, buf, size) __syscall(SYS_read, fd, buf, size)
+#define pread(fd, buf, size, ofs) __syscall(SYS_pread, fd, buf, size, __SYSCALL_LL_PRW(ofs))
+
+static inline off_t crt_lseek(int fd, off_t offset, int whence)
+{
+#ifdef SYS__llseek
+	off_t result;
+	return __syscall(SYS__llseek, fd, offset>>32, offset, &result, whence) ? -1 : result;
+#else
+	return __syscall(SYS_lseek, fd, offset, whence);
+#endif
+}
+#define lseek crt_lseek
+
+static inline void *map_library_allocz(size_t *size)
+{
+	void *ret;
+	*size = (*size + SYSCALL_MMAP2_UNIT - 1) & -SYSCALL_MMAP2_UNIT;
+	ret = mmap(0, *size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	if (ret == MAP_FAILED)
+		return NULL;
+	return ret;
+}
+
+static inline void map_library_free(void *ptr, size_t size)
+{
+	munmap(ptr, size);
+}
+
+#define map_library_failed(val) ((unsigned long)val > -4096UL)
+#define map_library_error(ret) (-(long)ret)
+
+#ifdef SYS_readlink
+#define readlink(path, buf, bufsize) __syscall(SYS_readlink, path, buf, bufsize)
+#else
+#define readlink(path, buf, bufsize) __syscall(SYS_readlinkat, AT_FDCWD, path, buf, bufsize)
+#endif
+
+#ifdef SYS_access
+#define access(filename, amode) __syscall(SYS_access, filename, amode)
+#else
+#define access(filename, amode) __syscall(SYS_faccessat, AT_FDCWD, filename, amode, 0)
+#endif
+
+static void *crt_memcpy(void *restrict dest, const void *restrict src, size_t n)
+{
+	unsigned char *d = dest;
+	const unsigned char *s = src;
+	for (; n; n--) *d++ = *s++;
+	return dest;
+}
+
+static void *crt_memset(void *dest, int c, size_t n)
+{
+	unsigned char *s = dest;
+	for (; n; n--, s++) *s = c;
+	return dest;
+}
+#define memset crt_memset
+
+static size_t crt_strlen(const char *s)
+{
+	const char *a = s;
+	for (; *s; s++);
+	return s-a;
+}
+
+static char *crt_strchrnul(const char *s, int c)
+{
+	c = (unsigned char)c;
+	if (!c) return (char *)s + crt_strlen(s);
+	for (; *s && *(unsigned char *)s != c; s++);
+	return (char *)s;
+}
+
+static int crt_strncmp(const char *_l, const char *_r, size_t n)
+{
+	const unsigned char *l=(void *)_l, *r=(void *)_r;
+	if (!n--) return 0;
+	for (; *l && *r && n && *l == *r ; l++, r++, n--);
+	return *l - *r;
+}
+
+static char *crt_getenv(const char *name, char **environ)
+{
+	size_t l = crt_strchrnul(name, '=') - name;
+	if (l && !name[l] && environ)
+		for (char **e = environ; *e; e++)
+			if (!crt_strncmp(name, *e, l) && l[*e] == '=')
+				return *e + l+1;
+	return 0;
+}
+
+#define LD_CRT
+#include "../ldso/map_library.h"
+
+static void decode_vec(const 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];
+		a[v[0]] = v[1];
+	}
+}
+
+static void get_rpath(const char **runpath, const size_t *dyn, unsigned char *base)
+{
+	/* DT_STRTAB is pre-relocated for us by dlstart */
+	const char *strings = (char*)base + dyn[DT_STRTAB];
+
+	if (dyn[0] & (1 << DT_RPATH))
+		*runpath = strings + dyn[DT_RPATH];
+	if (dyn[0] & (1 << DT_RUNPATH))
+		*runpath = strings + dyn[DT_RUNPATH];
+}
+
+static size_t find_linker(char *outbuf, size_t bufsize, const char *this_path, size_t thisl, const size_t *dyn, unsigned char *base, char **environ, int secure)
+{
+	const char *paths[3] = {0}; // rpath, envpath, runpath
+	size_t i;
+	int fd;
+
+	// In the suid/secure case, skip everything and use the fixed path
+	if (secure)
+		goto default_path;
+
+	// Strip filename
+	if (thisl)
+		thisl--;
+	while (thisl > 1 && this_path[thisl] == '/')
+		thisl--;
+	while (thisl > 0 && this_path[thisl] != '/')
+		thisl--;
+
+	const char *envpath = crt_getenv("LD_LOADER_PATH", environ);
+	if (envpath) {
+		size_t envlen = crt_strlen(envpath);
+		if (envlen < bufsize) {
+			crt_memcpy(outbuf, envpath, envlen + 1);
+			return envlen + 1;
+		}
+	}
+
+	get_rpath(&paths[1], dyn, base);
+
+	paths[0] = crt_getenv("LD_LIBRARY_PATH", environ);
+
+	for (i = 0; i < 2; i++) {
+		const char *p = paths[i];
+		char *o = outbuf;
+		if (!p)
+			continue;
+		for (;;) {
+			if (!crt_strncmp(p, "$ORIGIN", 7) ||
+					!crt_strncmp(p, "${ORIGIN}", 9)) {
+				if (o + thisl + 1 < outbuf + bufsize) {
+					crt_memcpy(o, this_path, thisl);
+					o += thisl;
+				} else {
+					o = outbuf + bufsize - 1;
+				}
+				p += (p[1] == '{' ? 9 : 7);
+			} else if (*p == ':' || !*p) {
+#define LDSO_FILENAME "ld-musl-" LDSO_ARCH ".so.1"
+				if (o + sizeof(LDSO_FILENAME) + 1 < outbuf + bufsize) {
+					*o++ = '/';
+					crt_memcpy(o, LDSO_FILENAME, sizeof(LDSO_FILENAME));
+					if (!access(outbuf, R_OK | X_OK))
+						return (o + sizeof(LDSO_FILENAME)) - outbuf;
+				}
+				if (!*p)
+					break;
+				o = outbuf;
+				p++;
+			} else {
+				if (o < outbuf + bufsize)
+					*o++ = *p;
+				p++;
+			}
+		}
+	}
+
+	default_path:
+	// Didn't find a usable loader anywhere (or in secure mode), so try the default
+	crt_memcpy(outbuf, LDSO_PATHNAME, sizeof(LDSO_PATHNAME));
+	return sizeof(LDSO_PATHNAME);
+}
+
+hidden _Noreturn void __dls2(unsigned char *base, size_t *p)
+{
+	int argc = p[0];
+	char **argv = (void *)(p+1);
+	int fd;
+	int secure;
+	int prot = PROT_READ;
+	struct dso loader;
+	Ehdr *loader_hdr;
+	Phdr *new_hdr;
+	void *entry;
+	char this_path[PATH_MAX] = {0};
+	size_t thisl;
+	char linker_path[PATH_MAX] = {0};
+	size_t linker_len;
+	size_t i;
+	size_t aux[AUX_CNT];
+	size_t *auxv;
+	size_t dyn[DYN_CNT];
+	char **environ = argv + argc + 1;
+
+	// We're already finished here; just run main.
+	if (__libc_start_main)
+		__libc_start_main(main, argc, argv, _init, _fini, 0);
+
+	/* Find aux vector just past environ[] and use it to initialize
+	* global data that may be needed before we can make syscalls. */
+	for (i = argc + 1; argv[i]; i++);
+	auxv = (void *)(argv + i + 1);
+	decode_vec(auxv, aux, AUX_CNT);
+	secure = ((aux[0] & 0x7800) != 0x7800 || aux[AT_UID] != aux[AT_EUID]
+		|| aux[AT_GID] != aux[AT_EGID] || aux[AT_SECURE]);
+
+	page_size = aux[AT_PAGESZ];
+
+	decode_vec(_DYNAMIC, dyn, DYN_CNT);
+
+	thisl = readlink("/proc/self/exe", this_path, sizeof this_path);
+	linker_len = find_linker(linker_path, sizeof linker_path, this_path, thisl, dyn, base, environ, secure);
+
+	fd = __sys_open2(, linker_path, O_RDONLY);
+	if (fd < 0)
+		goto error;
+
+	loader_hdr = map_library(fd, &loader);
+	if (!loader_hdr)
+		goto error;
+
+	__syscall(SYS_close, fd);
+
+	// Copy the program headers into an anonymous mapping
+	new_hdr = mmap(0, (aux[AT_PHENT] * (aux[AT_PHNUM] + 2) + linker_len + PAGE_SIZE - 1) & -PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	if (map_library_failed(new_hdr))
+		goto error;
+
+	// Point it back at the original kernel-provided base
+	new_hdr->p_type = PT_PHDR;
+	new_hdr->p_vaddr = (size_t)new_hdr - (size_t)base;
+
+	((Phdr*)((char*)new_hdr + aux[AT_PHENT]))->p_type = PT_INTERP;
+	((Phdr*)((char*)new_hdr + aux[AT_PHENT]))->p_vaddr = new_hdr->p_vaddr + aux[AT_PHENT] * (aux[AT_PHNUM] + 2);
+
+	crt_memcpy((char*)new_hdr + aux[AT_PHENT] * (aux[AT_PHNUM] + 2), linker_path, linker_len);
+
+	for (i = 0; i < aux[AT_PHNUM]; i++) {
+		Phdr *hdr = (void*)((char*)aux[AT_PHDR] + aux[AT_PHENT] * i);
+		Phdr *dst = (void*)((char*)new_hdr + aux[AT_PHENT] * (i + 2));
+		if (hdr->p_type == PT_PHDR || hdr->p_type == PT_INTERP) {
+			// Can't have a duplicate
+			dst->p_type = PT_NULL;
+		} else {
+			crt_memcpy(dst, hdr, aux[AT_PHENT]);
+		}
+	}
+
+	if (mprotect(new_hdr, aux[AT_PHENT] * (aux[AT_PHNUM] + 2) + linker_len, PROT_READ))
+		goto error;
+
+	for (i=0; auxv[i]; i+=2) {
+		if (auxv[i] == AT_BASE)
+			auxv[i + 1] = (size_t)loader_hdr;
+		if (auxv[i] == AT_PHDR)
+			auxv[i + 1] = (size_t)new_hdr;
+		if (auxv[i] == AT_PHNUM)
+			auxv[i + 1] += 2;
+	}
+
+	entry = laddr(&loader, loader_hdr->e_entry);
+
+#ifndef LD_FDPIC
+	/* Undo the relocations performed by dlstart */
+
+	if (NEED_MIPS_GOT_RELOCS) {
+		const size_t *dynv = _DYNAMIC;
+		size_t local_cnt = 0;
+		size_t *got = (void *)(base + dyn[DT_PLTGOT]);
+		for (i=0; dynv[i]; i+=2) if (dynv[i]==DT_MIPS_LOCAL_GOTNO)
+			local_cnt = dynv[i+1];
+		for (i=0; i<local_cnt; i++) got[i] -= (size_t)base;
+	}
+
+	size_t *rel = (void *)((size_t)base+dyn[DT_REL]);
+	size_t rel_size = dyn[DT_RELSZ];
+	for (; rel_size; rel+=2, rel_size-=2*sizeof(size_t)) {
+		if (!IS_RELATIVE(rel[1], 0)) continue;
+		size_t *rel_addr = (void *)((size_t)base + rel[0]);
+		*rel_addr -= (size_t)base;
+	}
+#endif
+
+	CRTJMP(entry, argv - 1);
+
+error:
+	for(;;) a_crash();
+}
diff --git a/ldso/dlstart.c b/ldso/dlstart.c
index 20d50f2..49e6a99 100644
--- a/ldso/dlstart.c
+++ b/ldso/dlstart.c
@@ -21,7 +21,7 @@
 hidden void _dlstart_c(size_t *sp, size_t *dynv)
 {
 	size_t i, aux[AUX_CNT], dyn[DYN_CNT];
-	size_t *rel, rel_size, base;
+	size_t *rel, rel_size, base, loader_phdr;
 
 	int argc = *sp;
 	char **argv = (void *)(sp+1);
@@ -41,6 +41,13 @@ hidden void _dlstart_c(size_t *sp, size_t *dynv)
 		 * 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;
+		if (aux[AT_BASE]) {
+			Ehdr *eh = (void*)aux[AT_BASE];
+			for (i = 0; eh->e_phoff - segs[i].p_vaddr >= segs[i].p_memsz; i++);
+			loader_phdr = (eh->e_phoff - segs[i].p_vaddr + segs[i].addr);
+		} else {
+			loader_phdr = aux[AT_PHDR];
+		}
 	} else {
 		/* If dynv is null, the entry point was started from loader
 		 * that is not fdpic-aware. We can assume normal fixed-
@@ -55,6 +62,7 @@ hidden void _dlstart_c(size_t *sp, size_t *dynv)
 		segs[0].p_memsz = -1;
 		Ehdr *eh = (void *)base;
 		Phdr *ph = (void *)(base + eh->e_phoff);
+		loader_phdr = (size_t)ph;
 		size_t phnum = eh->e_phnum;
 		size_t phent = eh->e_phentsize;
 		while (phnum-- && ph->p_type != PT_DYNAMIC)
@@ -69,13 +77,42 @@ hidden void _dlstart_c(size_t *sp, size_t *dynv)
 
 #if DL_FDPIC
 	for (i=0; i<DYN_CNT; i++) {
-		if (i==DT_RELASZ || i==DT_RELSZ) continue;
+		if (i==DT_RELASZ || i==DT_RELSZ || i==DT_RPATH || i==DT_RUNPATH) 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;
+#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
+	 * virtual address in the PT_DYNAMIC program header. */
+	base = aux[AT_BASE];
+	if (!base) {
+		size_t phnum = aux[AT_PHNUM];
+		size_t phentsize = aux[AT_PHENT];
+		Phdr *ph = (void *)aux[AT_PHDR];
+		for (i=phnum; i--; ph = (void *)((char *)ph + phentsize)) {
+			if (ph->p_type == PT_DYNAMIC) {
+				base = (size_t)dynv - ph->p_vaddr;
+				break;
+			}
+		}
+	}
+	loader_phdr = base + ((Ehdr*)base)->e_phoff;
+#endif
 
+#ifdef DL_DNI
+	/* If AT_PHDR doesn't match the PHDR in AT_BASE, then we've been loaded as a
+	 * dynamic executable and ld.so has already been run, either by  the kernel,
+	 * or by dcrt. This means relocs are already finished (and doing them again
+	 * would break DT_RELs), so we can just skip to the stage-2 jump. */
+	if (aux[AT_PHDR] != loader_phdr)
+		goto skip_relocs;
+#endif
+
+#if DL_FDPIC
 	const Sym *syms = (void *)dyn[DT_SYMTAB];
 
 	rel = (void *)dyn[DT_RELA];
@@ -97,23 +134,6 @@ hidden void _dlstart_c(size_t *sp, size_t *dynv)
 		}
 	}
 #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
-	 * virtual address in the PT_DYNAMIC program header. */
-	base = aux[AT_BASE];
-	if (!base) {
-		size_t phnum = aux[AT_PHNUM];
-		size_t phentsize = aux[AT_PHENT];
-		Phdr *ph = (void *)aux[AT_PHDR];
-		for (i=phnum; i--; ph = (void *)((char *)ph + phentsize)) {
-			if (ph->p_type == PT_DYNAMIC) {
-				base = (size_t)dynv - ph->p_vaddr;
-				break;
-			}
-		}
-	}
-
 	/* MIPS uses an ugly packed form for GOT relocations. Since we
 	 * can't make function calls yet and the code is tiny anyway,
 	 * it's simply inlined here. */
@@ -143,6 +163,7 @@ hidden void _dlstart_c(size_t *sp, size_t *dynv)
 #endif
 
 	stage2_func dls2;
+skip_relocs:
 	GETFUNCSYM(&dls2, __dls2, base+dyn[DT_PLTGOT]);
 	dls2((void *)base, sp);
 }
diff --git a/ldso/dynlink.c b/ldso/dynlink.c
index 6b3966e..4c6ae57 100644
--- a/ldso/dynlink.c
+++ b/ldso/dynlink.c
@@ -152,6 +152,19 @@ extern hidden void (*const __init_array_end)(void), (*const __fini_array_end)(vo
 weak_alias(__init_array_start, __init_array_end);
 weak_alias(__fini_array_start, __fini_array_end);
 
+static inline void *map_library_allocz(size_t *size)
+{
+	return calloc(1, *size);
+}
+
+static inline void map_library_free(void *ptr, size_t size)
+{
+	return free(ptr);
+}
+
+#define map_library_failed(ptr) (ptr == MAP_FAILED)
+#define map_library_error(val) (errno)
+
 #include "map_library.h"
 
 static int dl_strcmp(const char *l, const char *r)
diff --git a/ldso/map_library.h b/ldso/map_library.h
index 3609a0f..27d2a7e 100644
--- a/ldso/map_library.h
+++ b/ldso/map_library.h
@@ -1,11 +1,3 @@
-#include <errno.h>
-#include <features.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <unistd.h>
-#include "dynlink.h"
-#include "pthread_impl.h"
-
 /* Compute load address for a virtual address in a given dso. */
 #if DL_FDPIC
 static inline void *laddr(const struct dso *p, size_t v)
@@ -44,7 +36,7 @@ static void *mmap_fixed(void *p, size_t n, int prot, int flags, int fd, off_t of
 	char *q;
 	if (!no_map_fixed) {
 		q = mmap(p, n, prot, flags|MAP_FIXED, fd, off);
-		if (!DL_NOMMU_SUPPORT || q != MAP_FAILED || errno != EINVAL)
+		if (!DL_NOMMU_SUPPORT || !map_library_failed(q) || map_library_error(q) != EINVAL)
 			return q;
 		no_map_fixed = 1;
 	}
@@ -57,7 +49,7 @@ static void *mmap_fixed(void *p, size_t n, int prot, int flags, int fd, off_t of
 	if (lseek(fd, off, SEEK_SET) < 0) return MAP_FAILED;
 	for (q=p; n; q+=r, off+=r, n-=r) {
 		r = read(fd, q, n);
-		if (r < 0 && errno != EINTR) return MAP_FAILED;
+		if (r < 0 && map_library_error(r) != EINTR) return MAP_FAILED;
 		if (!r) {
 			memset(q, 0, n);
 			break;
@@ -66,6 +58,11 @@ static void *mmap_fixed(void *p, size_t n, int prot, int flags, int fd, off_t of
 	return p;
 }
 
+static inline int map_library_error_check(int ret, int expected)
+{
+	return ret && map_library_error(ret) != expected;
+}
+
 static inline void unmap_library(struct dso *dso)
 {
 	if (dso->loadmap) {
@@ -76,7 +73,7 @@ static inline void unmap_library(struct dso *dso)
 			munmap((void *)dso->loadmap->segs[i].addr,
 				dso->loadmap->segs[i].p_memsz);
 		}
-		free(dso->loadmap);
+		map_library_free(dso->loadmap, dso->map_len);
 	} else if (dso->map && dso->map_len) {
 		munmap(dso->map, dso->map_len);
 	}
@@ -86,8 +83,8 @@ static inline void *map_library(int fd, struct dso *dso)
 {
 	Ehdr buf[(896+sizeof(Ehdr))/sizeof(Ehdr)];
 	void *allocated_buf=0;
+	size_t allocated_buf_size;
 	size_t phsize;
-	size_t ph_allocated_size;
 	size_t addr_min=SIZE_MAX, addr_max=0, map_len;
 	size_t this_min, this_max;
 	size_t nsegs = 0;
@@ -98,7 +95,9 @@ static inline void *map_library(int fd, struct dso *dso)
 	unsigned char *map=MAP_FAILED, *base;
 	size_t dyn=0;
 	size_t i;
+#ifndef LD_CRT
 	size_t tls_image=0;
+#endif
 
 	ssize_t l = read(fd, buf, sizeof buf);
 	eh = buf;
@@ -107,7 +106,8 @@ static inline void *map_library(int fd, struct dso *dso)
 		goto noexec;
 	phsize = eh->e_phentsize * eh->e_phnum;
 	if (phsize > sizeof buf - sizeof *eh) {
-		allocated_buf = malloc(phsize);
+		allocated_buf_size = phsize;
+		allocated_buf = map_library_allocz(&allocated_buf_size);
 		if (!allocated_buf) return 0;
 		l = pread(fd, allocated_buf, phsize, eh->e_phoff);
 		if (l < 0) goto error;
@@ -124,6 +124,7 @@ static inline void *map_library(int fd, struct dso *dso)
 	for (i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) {
 		if (ph->p_type == PT_DYNAMIC) {
 			dyn = ph->p_vaddr;
+#ifndef LD_CRT
 		} else if (ph->p_type == PT_TLS) {
 			tls_image = ph->p_vaddr;
 			dso->tls.align = ph->p_align;
@@ -138,6 +139,7 @@ static inline void *map_library(int fd, struct dso *dso)
 					ph->p_memsz < DEFAULT_STACK_MAX ?
 					ph->p_memsz : DEFAULT_STACK_MAX;
 			}
+#endif
 		}
 		if (ph->p_type != PT_LOAD) continue;
 		nsegs++;
@@ -154,8 +156,8 @@ static inline void *map_library(int fd, struct dso *dso)
 	}
 	if (!dyn) goto noexec;
 	if (DL_FDPIC && !(eh->e_flags & FDPIC_CONSTDISP_FLAG)) {
-		dso->loadmap = calloc(1, sizeof *dso->loadmap
-			+ nsegs * sizeof *dso->loadmap->segs);
+		dso->map_len = sizeof *dso->loadmap + nsegs * sizeof *dso->loadmap->segs;
+		dso->loadmap = map_library_allocz(&dso->map_len);
 		if (!dso->loadmap) goto error;
 		dso->loadmap->nsegs = nsegs;
 		for (ph=ph0, i=0; i<nsegs; ph=(void *)((char *)ph+eh->e_phentsize)) {
@@ -166,7 +168,7 @@ static inline void *map_library(int fd, struct dso *dso)
 			map = mmap(0, ph->p_memsz + (ph->p_vaddr & PAGE_SIZE-1),
 				prot, MAP_PRIVATE,
 				fd, ph->p_offset & -PAGE_SIZE);
-			if (map == MAP_FAILED) {
+			if (map_library_failed(map)) {
 				unmap_library(dso);
 				goto error;
 			}
@@ -181,10 +183,10 @@ static inline void *map_library(int fd, struct dso *dso)
 				size_t pgbrk = brk + PAGE_SIZE-1 & -PAGE_SIZE;
 				size_t pgend = brk + ph->p_memsz - ph->p_filesz
 					+ PAGE_SIZE-1 & -PAGE_SIZE;
-				if (pgend > pgbrk && mmap_fixed(map+pgbrk,
+				if (pgend > pgbrk && !map_library_failed(mmap_fixed(map+pgbrk,
 					pgend-pgbrk, prot,
 					MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS,
-					-1, off_start) == MAP_FAILED)
+					-1, off_start)))
 					goto error;
 				memset(map + brk, 0, pgbrk-brk);
 			}
@@ -207,13 +209,15 @@ static inline void *map_library(int fd, struct dso *dso)
 			MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
 		: mmap((void *)addr_min, map_len, prot,
 			MAP_PRIVATE, fd, off_start);
-	if (map==MAP_FAILED) goto error;
+	if (map_library_failed(map)) goto error;
 	dso->map = map;
 	dso->map_len = map_len;
 	/* If the loaded file is not relocatable and the requested address is
 	 * not available, then the load operation must fail. */
 	if (eh->e_type != ET_DYN && addr_min && map!=(void *)addr_min) {
+#ifndef LD_CRT
 		errno = EBUSY;
+#endif
 		goto error;
 	}
 	base = map - addr_min;
@@ -238,33 +242,36 @@ static inline void *map_library(int fd, struct dso *dso)
 			((ph->p_flags&PF_X) ? PROT_EXEC : 0));
 		/* Reuse the existing mapping for the lowest-address LOAD */
 		if ((ph->p_vaddr & -PAGE_SIZE) != addr_min || DL_NOMMU_SUPPORT)
-			if (mmap_fixed(base+this_min, this_max-this_min, prot, MAP_PRIVATE|MAP_FIXED, fd, off_start) == MAP_FAILED)
+			if (map_library_failed(mmap_fixed(base+this_min, this_max-this_min, prot, MAP_PRIVATE|MAP_FIXED, fd, off_start)))
 				goto error;
 		if (ph->p_memsz > ph->p_filesz && (ph->p_flags&PF_W)) {
 			size_t brk = (size_t)base+ph->p_vaddr+ph->p_filesz;
 			size_t pgbrk = brk+PAGE_SIZE-1 & -PAGE_SIZE;
 			memset((void *)brk, 0, pgbrk-brk & PAGE_SIZE-1);
-			if (pgbrk-(size_t)base < this_max && mmap_fixed((void *)pgbrk, (size_t)base+this_max-pgbrk, prot, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) == MAP_FAILED)
+			if (pgbrk-(size_t)base < this_max && map_library_failed(mmap_fixed((void *)pgbrk, (size_t)base+this_max-pgbrk, prot, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0)))
 				goto error;
 		}
 	}
 	for (i=0; ((size_t *)(base+dyn))[i]; i+=2)
 		if (((size_t *)(base+dyn))[i]==DT_TEXTREL) {
-			if (mprotect(map, map_len, PROT_READ|PROT_WRITE|PROT_EXEC)
-			    && errno != ENOSYS)
+			if (map_library_error_check(mprotect(map, map_len, PROT_READ|PROT_WRITE|PROT_EXEC), ENOSYS))
 				goto error;
 			break;
 		}
 done_mapping:
 	dso->base = base;
 	dso->dynv = laddr(dso, dyn);
+#ifndef LD_CRT
 	if (dso->tls.size) dso->tls.image = laddr(dso, tls_image);
-	free(allocated_buf);
+#endif
+	map_library_free(allocated_buf, allocated_buf_size);
 	return map;
 noexec:
+#ifndef LD_CRT
 	errno = ENOEXEC;
+#endif
 error:
-	if (map!=MAP_FAILED) unmap_library(dso);
-	free(allocated_buf);
+	if (!map_library_failed(map)) unmap_library(dso);
+	map_library_free(allocated_buf, allocated_buf_size);
 	return 0;
 }
diff --git a/tools/ld.musl-clang.in b/tools/ld.musl-clang.in
index 93763d6..02d9893 100644
--- a/tools/ld.musl-clang.in
+++ b/tools/ld.musl-clang.in
@@ -7,6 +7,18 @@ shared=
 userlinkdir=
 userlink=
 
+Scrt="$libc_lib/Scrt1.o"
+dynamic_linker_args="-dynamic-linker \"$ldso\""
+
+for x ; do
+    case "$x" in
+        -l-dni)
+            dynamic_linker_args="-no-dynamic-linker"
+            Scrt="$libc_lib/dcrt1.o"
+            ;;
+    esac
+done
+
 for x ; do
     test "$cleared" || set -- ; cleared=1
 
@@ -42,10 +54,13 @@ for x ; do
             ;;
         -sysroot=*|--sysroot=*)
             ;;
+        $libc_lib/Scrt1.o)
+            set -- "$@" $Scrt
+            ;;
         *)
             set -- "$@" "$x"
             ;;
     esac
 done
 
-exec $($cc -print-prog-name=ld) -nostdlib "$@" -lc -dynamic-linker "$ldso"
+exec $($cc -print-prog-name=ld) -nostdlib "$@" -lc "$dynamic_linker_args"
diff --git a/tools/musl-clang.in b/tools/musl-clang.in
index 623de6f..49cba1b 100644
--- a/tools/musl-clang.in
+++ b/tools/musl-clang.in
@@ -5,14 +5,21 @@ libc_inc="@INCDIR@"
 libc_lib="@LIBDIR@"
 thisdir="`cd "$(dirname "$0")"; pwd`"
 
+cleared=
+
 # prevent clang from running the linker (and erroring) on no input.
 sflags=
 eflags=
+dniflags=
 for x ; do
+    test "$cleared" || set -- ; cleared=1
+
     case "$x" in
+        --dni) dniflags=-l-dni; continue ;;
         -l*) input=1 ;;
         *) input= ;;
     esac
+    set -- "$@" "$x"
     if test "$input" ; then
         sflags="-l-user-start"
         eflags="-l-user-end"
@@ -29,6 +36,7 @@ exec $cc \
     -isystem "$libc_inc" \
     -L-user-start \
     $sflags \
+    $dniflags \
     "$@" \
     $eflags \
     -L"$libc_lib" \
diff --git a/tools/musl-gcc.specs.sh b/tools/musl-gcc.specs.sh
index 3049257..86374f1 100644
--- a/tools/musl-gcc.specs.sh
+++ b/tools/musl-gcc.specs.sh
@@ -17,13 +17,13 @@ cat <<EOF
 libgcc.a%s %:if-exists(libgcc_eh.a%s)
 
 *startfile:
-%{!shared: $libdir/Scrt1.o} $libdir/crti.o crtbeginS.o%s
+%{!shared: %{-dni:$libdir/dcrt1.o;:$libdir/Scrt1.o}} $libdir/crti.o crtbeginS.o%s
 
 *endfile:
 crtendS.o%s $libdir/crtn.o
 
 *link:
--dynamic-linker $ldso -nostdlib %{shared:-shared} %{static:-static} %{rdynamic:-export-dynamic}
+%{-dni:-no-dynamic-linker;:--dynamic-linker $ldso} -nostdlib %{shared:-shared} %{static:-static} %{rdynamic:-export-dynamic}
 
 *esp_link:
 
-- 
2.7.4



^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 2/3] ldso: move (un)map_library functions to separate file
  2019-04-27  1:13 [PATCH 1/3] ldso: when run via CLI, set auxv as if the app was loaded by the kernel Rodger Combs
@ 2019-04-27  1:13 ` Rodger Combs
  0 siblings, 0 replies; 4+ messages in thread
From: Rodger Combs @ 2019-04-27  1:13 UTC (permalink / raw)
  To: musl

---
 ldso/dynlink.c     | 264 +--------------------------------------------------
 ldso/map_library.h | 270 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 272 insertions(+), 262 deletions(-)
 create mode 100644 ldso/map_library.h

diff --git a/ldso/dynlink.c b/ldso/dynlink.c
index e311069..d761837 100644
--- a/ldso/dynlink.c
+++ b/ldso/dynlink.c
@@ -153,6 +153,8 @@ extern hidden void (*const __init_array_end)(void), (*const __fini_array_end)(vo
 weak_alias(__init_array_start, __init_array_end);
 weak_alias(__fini_array_start, __fini_array_end);
 
+#include "map_library.h"
+
 static int dl_strcmp(const char *l, const char *r)
 {
 	for (; *l==*r && *l; l++, r++);
@@ -160,38 +162,6 @@ 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. */
-#if 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);
-}
-static void *laddr_pg(const struct dso *p, size_t v)
-{
-	size_t j=0;
-	size_t pgsz = PAGE_SIZE;
-	if (!p->loadmap) return p->base + v;
-	for (j=0; ; j++) {
-		size_t a = p->loadmap->segs[j].p_vaddr;
-		size_t b = a + p->loadmap->segs[j].p_memsz;
-		a &= -pgsz;
-		b += pgsz-1;
-		b &= -pgsz;
-		if (v-a<b-a) break;
-	}
-	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 laddr_pg(p, v) laddr(p, v)
-#define fpaddr(p, v) ((void (*)())laddr(p, v))
-#endif
-
 static void decode_vec(size_t *v, size_t *a, size_t cnt)
 {
 	size_t i;
@@ -540,236 +510,6 @@ static void reclaim_gaps(struct dso *dso)
 	}
 }
 
-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 (!no_map_fixed) {
-		q = mmap(p, n, prot, flags|MAP_FIXED, fd, off);
-		if (!DL_NOMMU_SUPPORT || q != MAP_FAILED || errno != EINVAL)
-			return q;
-		no_map_fixed = 1;
-	}
-	/* Fallbacks for MAP_FIXED failure on NOMMU kernels. */
-	if (flags & MAP_ANONYMOUS) {
-		memset(p, 0, n);
-		return p;
-	}
-	ssize_t r;
-	if (lseek(fd, off, SEEK_SET) < 0) return MAP_FAILED;
-	for (q=p; n; q+=r, off+=r, n-=r) {
-		r = read(fd, q, n);
-		if (r < 0 && errno != EINTR) return MAP_FAILED;
-		if (!r) {
-			memset(q, 0, n);
-			break;
-		}
-	}
-	return p;
-}
-
-static void unmap_library(struct dso *dso)
-{
-	if (dso->loadmap) {
-		size_t i;
-		for (i=0; i<dso->loadmap->nsegs; i++) {
-			if (!dso->loadmap->segs[i].p_memsz)
-				continue;
-			munmap((void *)dso->loadmap->segs[i].addr,
-				dso->loadmap->segs[i].p_memsz);
-		}
-		free(dso->loadmap);
-	} else if (dso->map && dso->map_len) {
-		munmap(dso->map, dso->map_len);
-	}
-}
-
-static void *map_library(int fd, struct dso *dso)
-{
-	Ehdr buf[(896+sizeof(Ehdr))/sizeof(Ehdr)];
-	void *allocated_buf=0;
-	size_t phsize;
-	size_t addr_min=SIZE_MAX, addr_max=0, map_len;
-	size_t this_min, this_max;
-	size_t nsegs = 0;
-	off_t off_start;
-	Ehdr *eh;
-	Phdr *ph, *ph0;
-	unsigned prot;
-	unsigned char *map=MAP_FAILED, *base;
-	size_t dyn=0;
-	size_t tls_image=0;
-	size_t i;
-
-	ssize_t l = read(fd, buf, sizeof buf);
-	eh = buf;
-	if (l<0) return 0;
-	if (l<sizeof *eh || (eh->e_type != ET_DYN && eh->e_type != ET_EXEC))
-		goto noexec;
-	phsize = eh->e_phentsize * eh->e_phnum;
-	if (phsize > sizeof buf - sizeof *eh) {
-		allocated_buf = malloc(phsize);
-		if (!allocated_buf) return 0;
-		l = pread(fd, allocated_buf, phsize, eh->e_phoff);
-		if (l < 0) goto error;
-		if (l != phsize) goto noexec;
-		ph = ph0 = allocated_buf;
-	} else if (eh->e_phoff + phsize > l) {
-		l = pread(fd, buf+1, phsize, eh->e_phoff);
-		if (l < 0) goto error;
-		if (l != phsize) goto noexec;
-		ph = ph0 = (void *)(buf + 1);
-	} else {
-		ph = ph0 = (void *)((char *)buf + eh->e_phoff);
-	}
-	for (i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) {
-		if (ph->p_type == PT_DYNAMIC) {
-			dyn = ph->p_vaddr;
-		} else if (ph->p_type == PT_TLS) {
-			tls_image = ph->p_vaddr;
-			dso->tls.align = ph->p_align;
-			dso->tls.len = ph->p_filesz;
-			dso->tls.size = ph->p_memsz;
-		} else if (ph->p_type == PT_GNU_RELRO) {
-			dso->relro_start = ph->p_vaddr & -PAGE_SIZE;
-			dso->relro_end = (ph->p_vaddr + ph->p_memsz) & -PAGE_SIZE;
-		} else if (ph->p_type == PT_GNU_STACK) {
-			if (!runtime && ph->p_memsz > __default_stacksize) {
-				__default_stacksize =
-					ph->p_memsz < DEFAULT_STACK_MAX ?
-					ph->p_memsz : DEFAULT_STACK_MAX;
-			}
-		}
-		if (ph->p_type != PT_LOAD) continue;
-		nsegs++;
-		if (ph->p_vaddr < addr_min) {
-			addr_min = ph->p_vaddr;
-			off_start = ph->p_offset;
-			prot = (((ph->p_flags&PF_R) ? PROT_READ : 0) |
-				((ph->p_flags&PF_W) ? PROT_WRITE: 0) |
-				((ph->p_flags&PF_X) ? PROT_EXEC : 0));
-		}
-		if (ph->p_vaddr+ph->p_memsz > addr_max) {
-			addr_max = ph->p_vaddr+ph->p_memsz;
-		}
-	}
-	if (!dyn) goto noexec;
-	if (DL_FDPIC && !(eh->e_flags & FDPIC_CONSTDISP_FLAG)) {
-		dso->loadmap = calloc(1, sizeof *dso->loadmap
-			+ nsegs * sizeof *dso->loadmap->segs);
-		if (!dso->loadmap) goto error;
-		dso->loadmap->nsegs = nsegs;
-		for (ph=ph0, i=0; i<nsegs; ph=(void *)((char *)ph+eh->e_phentsize)) {
-			if (ph->p_type != PT_LOAD) continue;
-			prot = (((ph->p_flags&PF_R) ? PROT_READ : 0) |
-				((ph->p_flags&PF_W) ? PROT_WRITE: 0) |
-				((ph->p_flags&PF_X) ? PROT_EXEC : 0));
-			map = mmap(0, ph->p_memsz + (ph->p_vaddr & PAGE_SIZE-1),
-				prot, MAP_PRIVATE,
-				fd, ph->p_offset & -PAGE_SIZE);
-			if (map == MAP_FAILED) {
-				unmap_library(dso);
-				goto error;
-			}
-			dso->loadmap->segs[i].addr = (size_t)map +
-				(ph->p_vaddr & PAGE_SIZE-1);
-			dso->loadmap->segs[i].p_vaddr = ph->p_vaddr;
-			dso->loadmap->segs[i].p_memsz = ph->p_memsz;
-			i++;
-			if (prot & PROT_WRITE) {
-				size_t brk = (ph->p_vaddr & PAGE_SIZE-1)
-					+ ph->p_filesz;
-				size_t pgbrk = brk + PAGE_SIZE-1 & -PAGE_SIZE;
-				size_t pgend = brk + ph->p_memsz - ph->p_filesz
-					+ PAGE_SIZE-1 & -PAGE_SIZE;
-				if (pgend > pgbrk && mmap_fixed(map+pgbrk,
-					pgend-pgbrk, prot,
-					MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS,
-					-1, off_start) == MAP_FAILED)
-					goto error;
-				memset(map + brk, 0, pgbrk-brk);
-			}
-		}
-		map = (void *)dso->loadmap->segs[0].addr;
-		map_len = 0;
-		goto done_mapping;
-	}
-	addr_max += PAGE_SIZE-1;
-	addr_max &= -PAGE_SIZE;
-	addr_min &= -PAGE_SIZE;
-	off_start &= -PAGE_SIZE;
-	map_len = addr_max - addr_min + off_start;
-	/* The first time, we map too much, possibly even more than
-	 * the length of the file. This is okay because we will not
-	 * use the invalid part; we just need to reserve the right
-	 * amount of virtual address space to map over later. */
-	map = DL_NOMMU_SUPPORT
-		? mmap((void *)addr_min, map_len, PROT_READ|PROT_WRITE|PROT_EXEC,
-			MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
-		: mmap((void *)addr_min, map_len, prot,
-			MAP_PRIVATE, fd, off_start);
-	if (map==MAP_FAILED) goto error;
-	dso->map = map;
-	dso->map_len = map_len;
-	/* If the loaded file is not relocatable and the requested address is
-	 * not available, then the load operation must fail. */
-	if (eh->e_type != ET_DYN && addr_min && map!=(void *)addr_min) {
-		errno = EBUSY;
-		goto error;
-	}
-	base = map - addr_min;
-	dso->phdr = 0;
-	dso->phnum = 0;
-	for (ph=ph0, i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) {
-		if (ph->p_type != PT_LOAD) continue;
-		/* Check if the programs headers are in this load segment, and
-		 * if so, record the address for use by dl_iterate_phdr. */
-		if (!dso->phdr && eh->e_phoff >= ph->p_offset
-		    && eh->e_phoff+phsize <= ph->p_offset+ph->p_filesz) {
-			dso->phdr = (void *)(base + ph->p_vaddr
-				+ (eh->e_phoff-ph->p_offset));
-			dso->phnum = eh->e_phnum;
-			dso->phentsize = eh->e_phentsize;
-		}
-		this_min = ph->p_vaddr & -PAGE_SIZE;
-		this_max = ph->p_vaddr+ph->p_memsz+PAGE_SIZE-1 & -PAGE_SIZE;
-		off_start = ph->p_offset & -PAGE_SIZE;
-		prot = (((ph->p_flags&PF_R) ? PROT_READ : 0) |
-			((ph->p_flags&PF_W) ? PROT_WRITE: 0) |
-			((ph->p_flags&PF_X) ? PROT_EXEC : 0));
-		/* Reuse the existing mapping for the lowest-address LOAD */
-		if ((ph->p_vaddr & -PAGE_SIZE) != addr_min || DL_NOMMU_SUPPORT)
-			if (mmap_fixed(base+this_min, this_max-this_min, prot, MAP_PRIVATE|MAP_FIXED, fd, off_start) == MAP_FAILED)
-				goto error;
-		if (ph->p_memsz > ph->p_filesz && (ph->p_flags&PF_W)) {
-			size_t brk = (size_t)base+ph->p_vaddr+ph->p_filesz;
-			size_t pgbrk = brk+PAGE_SIZE-1 & -PAGE_SIZE;
-			memset((void *)brk, 0, pgbrk-brk & PAGE_SIZE-1);
-			if (pgbrk-(size_t)base < this_max && mmap_fixed((void *)pgbrk, (size_t)base+this_max-pgbrk, prot, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) == MAP_FAILED)
-				goto error;
-		}
-	}
-	for (i=0; ((size_t *)(base+dyn))[i]; i+=2)
-		if (((size_t *)(base+dyn))[i]==DT_TEXTREL) {
-			if (mprotect(map, map_len, PROT_READ|PROT_WRITE|PROT_EXEC)
-			    && errno != ENOSYS)
-				goto error;
-			break;
-		}
-done_mapping:
-	dso->base = base;
-	dso->dynv = laddr(dso, dyn);
-	if (dso->tls.size) dso->tls.image = laddr(dso, tls_image);
-	free(allocated_buf);
-	return map;
-noexec:
-	errno = ENOEXEC;
-error:
-	if (map!=MAP_FAILED) unmap_library(dso);
-	free(allocated_buf);
-	return 0;
-}
-
 static int path_open(const char *name, const char *s, char *buf, size_t buf_size)
 {
 	size_t l;
diff --git a/ldso/map_library.h b/ldso/map_library.h
new file mode 100644
index 0000000..3609a0f
--- /dev/null
+++ b/ldso/map_library.h
@@ -0,0 +1,270 @@
+#include <errno.h>
+#include <features.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include "dynlink.h"
+#include "pthread_impl.h"
+
+/* Compute load address for a virtual address in a given dso. */
+#if DL_FDPIC
+static inline 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);
+}
+static inline void *laddr_pg(const struct dso *p, size_t v)
+{
+	size_t j=0;
+	size_t pgsz = PAGE_SIZE;
+	if (!p->loadmap) return p->base + v;
+	for (j=0; ; j++) {
+		size_t a = p->loadmap->segs[j].p_vaddr;
+		size_t b = a + p->loadmap->segs[j].p_memsz;
+		a &= -pgsz;
+		b += pgsz-1;
+		b &= -pgsz;
+		if (v-a<b-a) break;
+	}
+	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 laddr_pg(p, v) laddr(p, v)
+#define fpaddr(p, v) ((void (*)())laddr(p, v))
+#endif
+
+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 (!no_map_fixed) {
+		q = mmap(p, n, prot, flags|MAP_FIXED, fd, off);
+		if (!DL_NOMMU_SUPPORT || q != MAP_FAILED || errno != EINVAL)
+			return q;
+		no_map_fixed = 1;
+	}
+	/* Fallbacks for MAP_FIXED failure on NOMMU kernels. */
+	if (flags & MAP_ANONYMOUS) {
+		memset(p, 0, n);
+		return p;
+	}
+	ssize_t r;
+	if (lseek(fd, off, SEEK_SET) < 0) return MAP_FAILED;
+	for (q=p; n; q+=r, off+=r, n-=r) {
+		r = read(fd, q, n);
+		if (r < 0 && errno != EINTR) return MAP_FAILED;
+		if (!r) {
+			memset(q, 0, n);
+			break;
+		}
+	}
+	return p;
+}
+
+static inline void unmap_library(struct dso *dso)
+{
+	if (dso->loadmap) {
+		size_t i;
+		for (i=0; i<dso->loadmap->nsegs; i++) {
+			if (!dso->loadmap->segs[i].p_memsz)
+				continue;
+			munmap((void *)dso->loadmap->segs[i].addr,
+				dso->loadmap->segs[i].p_memsz);
+		}
+		free(dso->loadmap);
+	} else if (dso->map && dso->map_len) {
+		munmap(dso->map, dso->map_len);
+	}
+}
+
+static inline void *map_library(int fd, struct dso *dso)
+{
+	Ehdr buf[(896+sizeof(Ehdr))/sizeof(Ehdr)];
+	void *allocated_buf=0;
+	size_t phsize;
+	size_t ph_allocated_size;
+	size_t addr_min=SIZE_MAX, addr_max=0, map_len;
+	size_t this_min, this_max;
+	size_t nsegs = 0;
+	off_t off_start;
+	Ehdr *eh;
+	Phdr *ph, *ph0;
+	unsigned prot;
+	unsigned char *map=MAP_FAILED, *base;
+	size_t dyn=0;
+	size_t i;
+	size_t tls_image=0;
+
+	ssize_t l = read(fd, buf, sizeof buf);
+	eh = buf;
+	if (l<0) return 0;
+	if (l<sizeof *eh || (eh->e_type != ET_DYN && eh->e_type != ET_EXEC))
+		goto noexec;
+	phsize = eh->e_phentsize * eh->e_phnum;
+	if (phsize > sizeof buf - sizeof *eh) {
+		allocated_buf = malloc(phsize);
+		if (!allocated_buf) return 0;
+		l = pread(fd, allocated_buf, phsize, eh->e_phoff);
+		if (l < 0) goto error;
+		if (l != phsize) goto noexec;
+		ph = ph0 = allocated_buf;
+	} else if (eh->e_phoff + phsize > l) {
+		l = pread(fd, buf+1, phsize, eh->e_phoff);
+		if (l < 0) goto error;
+		if (l != phsize) goto noexec;
+		ph = ph0 = (void *)(buf + 1);
+	} else {
+		ph = ph0 = (void *)((char *)buf + eh->e_phoff);
+	}
+	for (i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) {
+		if (ph->p_type == PT_DYNAMIC) {
+			dyn = ph->p_vaddr;
+		} else if (ph->p_type == PT_TLS) {
+			tls_image = ph->p_vaddr;
+			dso->tls.align = ph->p_align;
+			dso->tls.len = ph->p_filesz;
+			dso->tls.size = ph->p_memsz;
+		} else if (ph->p_type == PT_GNU_RELRO) {
+			dso->relro_start = ph->p_vaddr & -PAGE_SIZE;
+			dso->relro_end = (ph->p_vaddr + ph->p_memsz) & -PAGE_SIZE;
+		} else if (ph->p_type == PT_GNU_STACK) {
+			if (!runtime && ph->p_memsz > __default_stacksize) {
+				__default_stacksize =
+					ph->p_memsz < DEFAULT_STACK_MAX ?
+					ph->p_memsz : DEFAULT_STACK_MAX;
+			}
+		}
+		if (ph->p_type != PT_LOAD) continue;
+		nsegs++;
+		if (ph->p_vaddr < addr_min) {
+			addr_min = ph->p_vaddr;
+			off_start = ph->p_offset;
+			prot = (((ph->p_flags&PF_R) ? PROT_READ : 0) |
+				((ph->p_flags&PF_W) ? PROT_WRITE: 0) |
+				((ph->p_flags&PF_X) ? PROT_EXEC : 0));
+		}
+		if (ph->p_vaddr+ph->p_memsz > addr_max) {
+			addr_max = ph->p_vaddr+ph->p_memsz;
+		}
+	}
+	if (!dyn) goto noexec;
+	if (DL_FDPIC && !(eh->e_flags & FDPIC_CONSTDISP_FLAG)) {
+		dso->loadmap = calloc(1, sizeof *dso->loadmap
+			+ nsegs * sizeof *dso->loadmap->segs);
+		if (!dso->loadmap) goto error;
+		dso->loadmap->nsegs = nsegs;
+		for (ph=ph0, i=0; i<nsegs; ph=(void *)((char *)ph+eh->e_phentsize)) {
+			if (ph->p_type != PT_LOAD) continue;
+			prot = (((ph->p_flags&PF_R) ? PROT_READ : 0) |
+				((ph->p_flags&PF_W) ? PROT_WRITE: 0) |
+				((ph->p_flags&PF_X) ? PROT_EXEC : 0));
+			map = mmap(0, ph->p_memsz + (ph->p_vaddr & PAGE_SIZE-1),
+				prot, MAP_PRIVATE,
+				fd, ph->p_offset & -PAGE_SIZE);
+			if (map == MAP_FAILED) {
+				unmap_library(dso);
+				goto error;
+			}
+			dso->loadmap->segs[i].addr = (size_t)map +
+				(ph->p_vaddr & PAGE_SIZE-1);
+			dso->loadmap->segs[i].p_vaddr = ph->p_vaddr;
+			dso->loadmap->segs[i].p_memsz = ph->p_memsz;
+			i++;
+			if (prot & PROT_WRITE) {
+				size_t brk = (ph->p_vaddr & PAGE_SIZE-1)
+					+ ph->p_filesz;
+				size_t pgbrk = brk + PAGE_SIZE-1 & -PAGE_SIZE;
+				size_t pgend = brk + ph->p_memsz - ph->p_filesz
+					+ PAGE_SIZE-1 & -PAGE_SIZE;
+				if (pgend > pgbrk && mmap_fixed(map+pgbrk,
+					pgend-pgbrk, prot,
+					MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS,
+					-1, off_start) == MAP_FAILED)
+					goto error;
+				memset(map + brk, 0, pgbrk-brk);
+			}
+		}
+		map = (void *)dso->loadmap->segs[0].addr;
+		map_len = 0;
+		goto done_mapping;
+	}
+	addr_max += PAGE_SIZE-1;
+	addr_max &= -PAGE_SIZE;
+	addr_min &= -PAGE_SIZE;
+	off_start &= -PAGE_SIZE;
+	map_len = addr_max - addr_min + off_start;
+	/* The first time, we map too much, possibly even more than
+	 * the length of the file. This is okay because we will not
+	 * use the invalid part; we just need to reserve the right
+	 * amount of virtual address space to map over later. */
+	map = DL_NOMMU_SUPPORT
+		? mmap((void *)addr_min, map_len, PROT_READ|PROT_WRITE|PROT_EXEC,
+			MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
+		: mmap((void *)addr_min, map_len, prot,
+			MAP_PRIVATE, fd, off_start);
+	if (map==MAP_FAILED) goto error;
+	dso->map = map;
+	dso->map_len = map_len;
+	/* If the loaded file is not relocatable and the requested address is
+	 * not available, then the load operation must fail. */
+	if (eh->e_type != ET_DYN && addr_min && map!=(void *)addr_min) {
+		errno = EBUSY;
+		goto error;
+	}
+	base = map - addr_min;
+	dso->phdr = 0;
+	dso->phnum = 0;
+	for (ph=ph0, i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) {
+		if (ph->p_type != PT_LOAD) continue;
+		/* Check if the programs headers are in this load segment, and
+		 * if so, record the address for use by dl_iterate_phdr. */
+		if (!dso->phdr && eh->e_phoff >= ph->p_offset
+		    && eh->e_phoff+phsize <= ph->p_offset+ph->p_filesz) {
+			dso->phdr = (void *)(base + ph->p_vaddr
+				+ (eh->e_phoff-ph->p_offset));
+			dso->phnum = eh->e_phnum;
+			dso->phentsize = eh->e_phentsize;
+		}
+		this_min = ph->p_vaddr & -PAGE_SIZE;
+		this_max = ph->p_vaddr+ph->p_memsz+PAGE_SIZE-1 & -PAGE_SIZE;
+		off_start = ph->p_offset & -PAGE_SIZE;
+		prot = (((ph->p_flags&PF_R) ? PROT_READ : 0) |
+			((ph->p_flags&PF_W) ? PROT_WRITE: 0) |
+			((ph->p_flags&PF_X) ? PROT_EXEC : 0));
+		/* Reuse the existing mapping for the lowest-address LOAD */
+		if ((ph->p_vaddr & -PAGE_SIZE) != addr_min || DL_NOMMU_SUPPORT)
+			if (mmap_fixed(base+this_min, this_max-this_min, prot, MAP_PRIVATE|MAP_FIXED, fd, off_start) == MAP_FAILED)
+				goto error;
+		if (ph->p_memsz > ph->p_filesz && (ph->p_flags&PF_W)) {
+			size_t brk = (size_t)base+ph->p_vaddr+ph->p_filesz;
+			size_t pgbrk = brk+PAGE_SIZE-1 & -PAGE_SIZE;
+			memset((void *)brk, 0, pgbrk-brk & PAGE_SIZE-1);
+			if (pgbrk-(size_t)base < this_max && mmap_fixed((void *)pgbrk, (size_t)base+this_max-pgbrk, prot, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) == MAP_FAILED)
+				goto error;
+		}
+	}
+	for (i=0; ((size_t *)(base+dyn))[i]; i+=2)
+		if (((size_t *)(base+dyn))[i]==DT_TEXTREL) {
+			if (mprotect(map, map_len, PROT_READ|PROT_WRITE|PROT_EXEC)
+			    && errno != ENOSYS)
+				goto error;
+			break;
+		}
+done_mapping:
+	dso->base = base;
+	dso->dynv = laddr(dso, dyn);
+	if (dso->tls.size) dso->tls.image = laddr(dso, tls_image);
+	free(allocated_buf);
+	return map;
+noexec:
+	errno = ENOEXEC;
+error:
+	if (map!=MAP_FAILED) unmap_library(dso);
+	free(allocated_buf);
+	return 0;
+}
-- 
2.7.4



^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2019-09-10  4:56 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-09-10  4:56 [PATCH 1/3] ldso: when run via CLI, set auxv as if the app was loaded by the kernel Rodger Combs
2019-09-10  4:56 ` [PATCH 2/3] ldso: move (un)map_library functions to separate file Rodger Combs
2019-09-10  4:56 ` [PATCH 3/3] crt: add dcrt1, with support for locating the dynamic loader at runtime Rodger Combs
  -- strict thread matches above, loose matches on Subject: below --
2019-04-27  1:13 [PATCH 1/3] ldso: when run via CLI, set auxv as if the app was loaded by the kernel Rodger Combs
2019-04-27  1:13 ` [PATCH 2/3] ldso: move (un)map_library functions to separate file Rodger Combs

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/musl/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).