#define SYSCALL_NO_TLS #include #include #include #include #include #include #include #include #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 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