mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
Get cosmo_dlopen() working better on System Five
Imported functions are now aspected with a trampoline that blocks signals and changes the thread-local storage register. This means bigger more complicated libraries can now be imported even though the whole technique remains fundamentally unsafe.
This commit is contained in:
parent
3a470ed356
commit
e4584ace81
6 changed files with 304 additions and 190 deletions
1
Makefile
1
Makefile
|
@ -444,6 +444,7 @@ COSMOPOLITAN_H_PKGS = \
|
|||
THIRD_PARTY_GDTOA \
|
||||
THIRD_PARTY_GETOPT \
|
||||
THIRD_PARTY_MUSL \
|
||||
THIRD_PARTY_ZLIB \
|
||||
THIRD_PARTY_REGEX
|
||||
|
||||
COSMOCC_PKGS = \
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "libc/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
|
@ -32,6 +33,7 @@
|
|||
#include "libc/elf/struct/phdr.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/nt/dll.h"
|
||||
|
@ -49,6 +51,7 @@
|
|||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
@ -71,25 +74,14 @@ __static_yoink(".dlopen.x86_64.glibc.elf");
|
|||
__static_yoink(".dlopen.x86_64.freebsd.elf");
|
||||
__static_yoink(".dlopen.aarch64.glibc.elf");
|
||||
|
||||
#define PAGE_SIZE 4096
|
||||
|
||||
#define XNU_RTLD_LAZY 1
|
||||
#define XNU_RTLD_NOW 2
|
||||
#define XNU_RTLD_LOCAL 4
|
||||
#define XNU_RTLD_GLOBAL 8
|
||||
|
||||
#define BEGIN_FOREIGN \
|
||||
{ \
|
||||
BLOCK_SIGNALS; \
|
||||
struct CosmoTib *cosmo_tib = __get_tls(); \
|
||||
pthread_mutex_lock(&foreign.lock); \
|
||||
__set_tls(foreign.tib)
|
||||
|
||||
#define END_FOREIGN \
|
||||
__set_tls(cosmo_tib); \
|
||||
pthread_mutex_unlock(&foreign.lock); \
|
||||
ALLOW_SIGNALS; \
|
||||
}
|
||||
|
||||
struct loaded {
|
||||
struct Loaded {
|
||||
char *base;
|
||||
char *entry;
|
||||
Elf64_Ehdr eh;
|
||||
|
@ -100,7 +92,6 @@ static struct {
|
|||
atomic_uint once;
|
||||
bool is_supported;
|
||||
struct CosmoTib *tib;
|
||||
pthread_mutex_t lock;
|
||||
void *(*dlopen)(const char *, int);
|
||||
void *(*dlsym)(void *, const char *);
|
||||
int (*dlclose)(void *);
|
||||
|
@ -108,19 +99,29 @@ static struct {
|
|||
jmp_buf jb;
|
||||
} foreign;
|
||||
|
||||
static _Thread_local const char *last_dlerror;
|
||||
long __sysv2nt14();
|
||||
|
||||
static const unsigned align_mask = 4095;
|
||||
static _Thread_local char dlerror_buf[128];
|
||||
|
||||
static uintptr_t pgtrunc(uintptr_t x) {
|
||||
return x & ~align_mask;
|
||||
// on system five we sadly need this brutal trampoline
|
||||
// todo(jart): add tls trampoline to sigaction() handlers
|
||||
// todo(jart): morph binary to get tls from host c library
|
||||
static long foreign_tramp(long a, long b, long c, long d, long e,
|
||||
long func(long, long, long, long, long)) {
|
||||
long res;
|
||||
sigset_t mask;
|
||||
sigset_t block = -1;
|
||||
struct CosmoTib *tib;
|
||||
sys_sigprocmask(SIG_SETMASK, &block, &mask);
|
||||
tib = __get_tls();
|
||||
__set_tls(foreign.tib);
|
||||
res = func(a, b, c, d, e);
|
||||
__set_tls(tib);
|
||||
sys_sigprocmask(SIG_SETMASK, &mask, 0);
|
||||
return res;
|
||||
}
|
||||
|
||||
static uintptr_t pground(uintptr_t x) {
|
||||
return pgtrunc(x + align_mask);
|
||||
}
|
||||
|
||||
static unsigned pflags(unsigned x) {
|
||||
static unsigned get_elf_prot(unsigned x) {
|
||||
unsigned r = 0;
|
||||
if (x & PF_R) r += PROT_READ;
|
||||
if (x & PF_W) r += PROT_WRITE;
|
||||
|
@ -128,32 +129,63 @@ static unsigned pflags(unsigned x) {
|
|||
return r;
|
||||
}
|
||||
|
||||
static int get_host_elf_machine(void) {
|
||||
#ifdef __x86_64__
|
||||
return EM_NEXGEN32E;
|
||||
#elif defined(__aarch64__)
|
||||
return EM_AARCH64;
|
||||
#elif defined(__powerpc64__)
|
||||
return EM_PPC64;
|
||||
#elif defined(__riscv)
|
||||
return EM_RISCV;
|
||||
#elif defined(__s390x__)
|
||||
return EM_S390;
|
||||
#else
|
||||
#error "unsupported architecture"
|
||||
#endif
|
||||
}
|
||||
|
||||
static char *elf_map(int fd, Elf64_Ehdr *ehdr, Elf64_Phdr *phdr) {
|
||||
uintptr_t minva = -1;
|
||||
uintptr_t maxva = 0;
|
||||
uintptr_t minva = -1;
|
||||
for (Elf64_Phdr *p = phdr; p < &phdr[ehdr->e_phnum]; p++) {
|
||||
if (p->p_type != PT_LOAD) continue;
|
||||
if (p->p_vaddr < minva) minva = p->p_vaddr;
|
||||
if (p->p_vaddr + p->p_memsz > maxva) maxva = p->p_vaddr + p->p_memsz;
|
||||
if (p->p_type != PT_LOAD) {
|
||||
continue;
|
||||
}
|
||||
if (p->p_vaddr < minva) {
|
||||
minva = p->p_vaddr;
|
||||
}
|
||||
if (p->p_vaddr + p->p_memsz > maxva) {
|
||||
maxva = p->p_vaddr + p->p_memsz;
|
||||
}
|
||||
}
|
||||
minva = minva & -PAGE_SIZE;
|
||||
uint8_t *base =
|
||||
__sys_mmap(0, maxva - minva, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0, 0);
|
||||
if (base == MAP_FAILED) {
|
||||
return MAP_FAILED;
|
||||
}
|
||||
minva = pgtrunc(minva);
|
||||
maxva = pground(maxva);
|
||||
uint8_t *base = __sys_mmap(0, maxva - minva, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0, 0);
|
||||
if (base == MAP_FAILED) return MAP_FAILED;
|
||||
__sys_munmap(base, maxva - minva);
|
||||
for (Elf64_Phdr *p = phdr; p < &phdr[ehdr->e_phnum]; p++) {
|
||||
if (p->p_type != PT_LOAD) continue;
|
||||
uintptr_t off = p->p_vaddr & align_mask;
|
||||
uint8_t *start = base;
|
||||
start += pgtrunc(p->p_vaddr);
|
||||
size_t sz = pground(p->p_memsz + off);
|
||||
uint8_t *m = __sys_mmap(start, sz, PROT_WRITE,
|
||||
if (p->p_type != PT_LOAD) {
|
||||
continue;
|
||||
}
|
||||
uintptr_t skew = p->p_vaddr & (PAGE_SIZE - 1);
|
||||
uint8_t *start = base + p->p_vaddr - skew;
|
||||
size_t mapsize = skew + p->p_memsz;
|
||||
uint8_t *m = __sys_mmap(start, mapsize, PROT_READ | PROT_WRITE,
|
||||
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0, 0);
|
||||
if (m == MAP_FAILED) return MAP_FAILED;
|
||||
ssize_t rr = pread(fd, m + off, p->p_filesz, p->p_offset);
|
||||
if (rr != (ssize_t)p->p_filesz) return MAP_FAILED;
|
||||
sys_mprotect(m, sz, pflags(p->p_flags));
|
||||
if (m == MAP_FAILED) {
|
||||
return MAP_FAILED;
|
||||
}
|
||||
ssize_t rr = pread(fd, m + skew, p->p_filesz, p->p_offset);
|
||||
if (rr != (ssize_t)p->p_filesz) {
|
||||
return MAP_FAILED;
|
||||
}
|
||||
if (sys_mprotect(m, mapsize, get_elf_prot(p->p_flags))) {
|
||||
return MAP_FAILED;
|
||||
}
|
||||
}
|
||||
return (void *)base;
|
||||
}
|
||||
|
@ -162,19 +194,29 @@ static int elf_open(const char *file) {
|
|||
return open(file, O_RDONLY | O_CLOEXEC);
|
||||
}
|
||||
|
||||
static bool elf_slurp(struct loaded *l, int fd, const char *file) {
|
||||
if (pread(fd, &l->eh, 64, 0) != 64) return false;
|
||||
if (!IsElf64Binary(&l->eh, 64)) return false;
|
||||
if (l->eh.e_phnum > ARRAYLEN(l->ph)) return false;
|
||||
static bool elf_slurp(struct Loaded *l, int fd, const char *file) {
|
||||
if (pread(fd, &l->eh, 64, 0) != 64) {
|
||||
return false;
|
||||
}
|
||||
if (!IsElf64Binary(&l->eh, 64) || //
|
||||
l->eh.e_phnum > sizeof(l->ph) / sizeof(*l->ph) || //
|
||||
l->eh.e_machine != get_host_elf_machine()) {
|
||||
enoexec();
|
||||
return false;
|
||||
}
|
||||
int bytes = l->eh.e_phnum * sizeof(l->ph[0]);
|
||||
if (pread(fd, l->ph, bytes, l->eh.e_phoff) != bytes) return false;
|
||||
if (pread(fd, l->ph, bytes, l->eh.e_phoff) != bytes) {
|
||||
return false;
|
||||
}
|
||||
l->entry = (char *)l->eh.e_entry;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool elf_load(struct loaded *l, const char *file) {
|
||||
static bool elf_load(struct Loaded *l, const char *file) {
|
||||
int fd;
|
||||
if ((fd = elf_open(file)) == -1) return false;
|
||||
if ((fd = elf_open(file)) == -1) {
|
||||
return false;
|
||||
}
|
||||
if (!elf_slurp(l, fd, file)) {
|
||||
close(fd);
|
||||
return false;
|
||||
|
@ -190,8 +232,10 @@ static bool elf_load(struct loaded *l, const char *file) {
|
|||
|
||||
static bool elf_interp(char *buf, size_t bsz, const char *file) {
|
||||
int fd;
|
||||
if ((fd = elf_open(file)) == -1) return false;
|
||||
struct loaded l;
|
||||
if ((fd = elf_open(file)) == -1) {
|
||||
return false;
|
||||
}
|
||||
struct Loaded l;
|
||||
if (!elf_slurp(&l, fd, file)) {
|
||||
close(fd);
|
||||
return false;
|
||||
|
@ -219,30 +263,44 @@ static long *push_strs(long *sp, char **list, int count) {
|
|||
|
||||
static void elf_exec(const char *file, const char *iinterp, int argc,
|
||||
char **argv, char **envp) {
|
||||
|
||||
// get elf information
|
||||
struct loaded prog;
|
||||
struct Loaded prog;
|
||||
if (!elf_load(&prog, file)) return;
|
||||
struct loaded interp;
|
||||
|
||||
struct Loaded interp;
|
||||
if (!elf_load(&interp, iinterp)) return;
|
||||
|
||||
// count environment variables
|
||||
int envc = 0;
|
||||
while (envp[envc]) envc++;
|
||||
|
||||
// create a stack for the new process just beneath our stack
|
||||
long *sp = (long *)__builtin_frame_address(0) - 256;
|
||||
// count auxiliary values
|
||||
int auxc = 0;
|
||||
Elf64_auxv_t *av;
|
||||
for (av = (Elf64_auxv_t *)__auxv; av->a_type; ++av) auxc++;
|
||||
|
||||
// create environment block for embedded process
|
||||
// the platform libc will save its location for getenv(), etc.
|
||||
// we need just enough stack memory beneath it for initialization
|
||||
char *map;
|
||||
size_t stksize = 65536;
|
||||
size_t stkalign = sizeof(char *) * 2;
|
||||
size_t argsize = (argc + 1 + envc + 1 + auxc * 2 + 1) * sizeof(char *);
|
||||
size_t mapsize = (stksize + argsize + (PAGE_SIZE - 1)) & -PAGE_SIZE;
|
||||
size_t skew = (mapsize - argsize) & (stkalign - 1);
|
||||
map = __sys_mmap(0, mapsize, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0, 0);
|
||||
if (map == MAP_FAILED) return;
|
||||
long *sp = (long *)(map + mapsize - skew);
|
||||
|
||||
// push auxiliary values
|
||||
*--sp = 0;
|
||||
Elf64_auxv_t *av;
|
||||
unsigned long key, val;
|
||||
for (av = (Elf64_auxv_t *)__auxv; (key = av->a_type); ++av) {
|
||||
val = av->a_un.a_val;
|
||||
if (key == AT_PHDR) val = (long)(prog.base + prog.eh.e_phoff);
|
||||
if (key == AT_PHENT) val = prog.eh.e_phentsize;
|
||||
if (key == AT_PHNUM) val = prog.eh.e_phnum;
|
||||
if (key == AT_PAGESZ) val = 0x1000;
|
||||
if (key == AT_PAGESZ) val = PAGE_SIZE;
|
||||
if (key == AT_BASE) val = (long)interp.base;
|
||||
if (key == AT_FLAGS) val = 0;
|
||||
if (key == AT_ENTRY) val = (long)prog.entry;
|
||||
|
@ -256,6 +314,9 @@ static void elf_exec(const char *file, const char *iinterp, int argc,
|
|||
sp = push_strs(sp, argv, argc);
|
||||
*--sp = argc;
|
||||
|
||||
STRACE("running dlopen importer %p...", interp.entry);
|
||||
|
||||
// XXX: ideally we should set most registers to zero
|
||||
#ifdef __x86_64__
|
||||
struct ps_strings {
|
||||
char **argv;
|
||||
|
@ -285,130 +346,72 @@ static void elf_exec(const char *file, const char *iinterp, int argc,
|
|||
#endif
|
||||
}
|
||||
|
||||
static wontreturn void foreign_helper(void **p) {
|
||||
foreign.dlopen = p[0];
|
||||
foreign.dlsym = p[1];
|
||||
foreign.dlclose = p[2];
|
||||
foreign.dlerror = p[3];
|
||||
longjmp(foreign.jb, 1);
|
||||
static char *dlerror_set(const char *str) {
|
||||
strlcpy(dlerror_buf, str, sizeof(dlerror_buf));
|
||||
return dlerror_buf;
|
||||
}
|
||||
|
||||
static void foreign_setup(void) {
|
||||
char interp[256] = {0};
|
||||
if (!elf_interp(interp, sizeof(interp), "/usr/bin/env")) return;
|
||||
const char *dlopen_helper = 0;
|
||||
#ifdef __x86_64__
|
||||
if (IsFreebsd()) {
|
||||
dlopen_helper = "/zip/.dlopen.x86_64.freebsd.elf";
|
||||
} else if (IsLinux()) {
|
||||
if (fileexists("/lib64/ld-linux-x86-64.so.2")) {
|
||||
dlopen_helper = "/zip/.dlopen.x86_64.glibc.elf";
|
||||
} else {
|
||||
dlopen_helper = "/zip/.dlopen.x86_64.musl.elf";
|
||||
static char *foreign_alloc_block(void) {
|
||||
char *p = 0;
|
||||
size_t sz = 65536;
|
||||
if (!IsWindows()) {
|
||||
p = __sys_mmap(0, sz, PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0, 0);
|
||||
if (p == MAP_FAILED) {
|
||||
p = 0;
|
||||
}
|
||||
} else {
|
||||
uintptr_t h;
|
||||
if ((h = CreateFileMapping(-1, 0, kNtPageExecuteReadwrite, 0, sz, 0))) {
|
||||
p = MapViewOfFileEx(h, kNtFileMapWrite | kNtFileMapExecute, 0, 0, sz, 0);
|
||||
CloseHandle(h);
|
||||
}
|
||||
}
|
||||
#elif defined(__aarch64__)
|
||||
if (IsLinux()) {
|
||||
dlopen_helper = "/zip/.dlopen.aarch64.glibc.elf";
|
||||
}
|
||||
#endif
|
||||
if (!dlopen_helper) {
|
||||
return; // this platform isn't supported yet
|
||||
}
|
||||
struct CosmoTib *cosmo_tib = __get_tls();
|
||||
if (!setjmp(foreign.jb)) {
|
||||
elf_exec(dlopen_helper, interp, 2,
|
||||
(char *[]){
|
||||
program_invocation_name,
|
||||
(char *)foreign_helper,
|
||||
NULL,
|
||||
},
|
||||
environ);
|
||||
return; // if it returns then it failed
|
||||
}
|
||||
foreign.tib = __get_tls();
|
||||
__set_tls(cosmo_tib);
|
||||
foreign.is_supported = true;
|
||||
}
|
||||
|
||||
static bool foreign_init(void) {
|
||||
bool res;
|
||||
cosmo_once(&foreign.once, foreign_setup);
|
||||
if (!(res = foreign.is_supported)) {
|
||||
last_dlerror = "dlopen() isn't supported on this platform";
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int dlclose_nt(void *handle) {
|
||||
int res;
|
||||
if (FreeLibrary((uintptr_t)handle)) {
|
||||
res = 0;
|
||||
} else {
|
||||
last_dlerror = "FreeLibrary() failed";
|
||||
res = -1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void *dlopen_nt(const char *path, int mode) {
|
||||
int n;
|
||||
uintptr_t handle;
|
||||
char16_t path16[PATH_MAX + 2];
|
||||
if (mode & ~(RTLD_LOCAL | RTLD_LAZY | RTLD_NOW)) {
|
||||
last_dlerror = "invalid mode";
|
||||
return 0;
|
||||
}
|
||||
if ((n = __mkntpath(path, path16)) == -1) {
|
||||
last_dlerror = "path invalid";
|
||||
return 0;
|
||||
}
|
||||
if (n > 3 && //
|
||||
path16[n - 3] == '.' && //
|
||||
path16[n - 2] == 's' && //
|
||||
path16[n - 1] == 'o') {
|
||||
path16[n - 2] = 'd';
|
||||
path16[n - 1] = 'l';
|
||||
path16[n + 0] = 'l';
|
||||
path16[n + 1] = 0;
|
||||
}
|
||||
if (!(handle = LoadLibrary(path16))) {
|
||||
last_dlerror = "library not found";
|
||||
}
|
||||
return (void *)handle;
|
||||
}
|
||||
|
||||
static char *dlsym_nt_alloc_rwx_block(void) {
|
||||
uintptr_t h;
|
||||
char *p = 0;
|
||||
if ((h = CreateFileMapping(-1, 0, kNtPageExecuteReadwrite, 0, 65536, 0)) &&
|
||||
(p = MapViewOfFileEx(h, kNtFileMapWrite | kNtFileMapExecute, 0, 0, 65536,
|
||||
0))) {
|
||||
if (p) {
|
||||
WRITE32LE(p, 4); // store used index
|
||||
} else {
|
||||
last_dlerror = "out of memory";
|
||||
dlerror_set("out of memory");
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static void *dlsym_nt_alloc_rwx(size_t n) {
|
||||
static void *foreign_alloc(size_t n) {
|
||||
void *res;
|
||||
static char *block;
|
||||
pthread_mutex_lock(&foreign.lock);
|
||||
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_mutex_lock(&lock);
|
||||
if (!block || READ32LE(block) + n > 65536) {
|
||||
if (!(block = dlsym_nt_alloc_rwx_block())) {
|
||||
if (!(block = foreign_alloc_block())) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
res = block + READ32LE(block);
|
||||
WRITE32LE(block, READ32LE(block) + n);
|
||||
pthread_mutex_unlock(&foreign.lock);
|
||||
pthread_mutex_unlock(&lock);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void *dlsym_nt_thunk(void *func, void *tramp) {
|
||||
static void *foreign_thunk_sysv(void *func) {
|
||||
unsigned char *code;
|
||||
if (!(code = dlsym_nt_alloc_rwx(27))) return 0;
|
||||
if (!(code = foreign_alloc(23))) return 0;
|
||||
// movabs $func,%r9
|
||||
code[0] = 0x49;
|
||||
code[1] = 0xb9;
|
||||
WRITE64LE(code + 2, (uintptr_t)func);
|
||||
// movabs $tramp,%r10
|
||||
code[10] = 0x49;
|
||||
code[11] = 0xba;
|
||||
WRITE64LE(code + 12, (uintptr_t)foreign_tramp);
|
||||
// jmp *%r10
|
||||
code[20] = 0x41;
|
||||
code[21] = 0xff;
|
||||
code[22] = 0xe2;
|
||||
return code;
|
||||
}
|
||||
|
||||
static void *foreign_thunk_nt(void *func) {
|
||||
unsigned char *code;
|
||||
if (!(code = foreign_alloc(27))) return 0;
|
||||
// push %rbp
|
||||
code[0] = 0x55;
|
||||
// mov %rsp,%rbp
|
||||
|
@ -422,7 +425,7 @@ static void *dlsym_nt_thunk(void *func, void *tramp) {
|
|||
// movabs $tramp,%r10
|
||||
code[14] = 0x49;
|
||||
code[15] = 0xba;
|
||||
WRITE64LE(code + 16, (uintptr_t)tramp);
|
||||
WRITE64LE(code + 16, (uintptr_t)__sysv2nt14);
|
||||
// jmp *%r10
|
||||
code[24] = 0x41;
|
||||
code[25] = 0xff;
|
||||
|
@ -430,14 +433,115 @@ static void *dlsym_nt_thunk(void *func, void *tramp) {
|
|||
return code;
|
||||
}
|
||||
|
||||
static wontreturn dontinstrument void foreign_helper(void **p) {
|
||||
foreign.dlopen = foreign_thunk_sysv(p[0]);
|
||||
foreign.dlsym = foreign_thunk_sysv(p[1]);
|
||||
foreign.dlclose = foreign_thunk_sysv(p[2]);
|
||||
foreign.dlerror = foreign_thunk_sysv(p[3]);
|
||||
longjmp(foreign.jb, 1);
|
||||
}
|
||||
|
||||
static bool foreign_setup(void) {
|
||||
char interp[256] = {0};
|
||||
if (!elf_interp(interp, sizeof(interp), "/usr/bin/env")) {
|
||||
return false;
|
||||
}
|
||||
const char *dlopen_helper = 0;
|
||||
#ifdef __x86_64__
|
||||
if (IsFreebsd()) {
|
||||
dlopen_helper = "/zip/.dlopen.x86_64.freebsd.elf";
|
||||
} else if (IsLinux()) {
|
||||
if (fileexists("/lib64/ld-linux-x86-64.so.2")) {
|
||||
dlopen_helper = "/zip/.dlopen.x86_64.glibc.elf";
|
||||
} else {
|
||||
dlopen_helper = "/zip/.dlopen.x86_64.musl.elf";
|
||||
}
|
||||
}
|
||||
#elif defined(__aarch64__)
|
||||
if (0 && IsLinux()) { // TODO(jart): implement me
|
||||
dlopen_helper = "/zip/.dlopen.aarch64.glibc.elf";
|
||||
}
|
||||
#endif
|
||||
if (!dlopen_helper) {
|
||||
enosys();
|
||||
return false; // this platform isn't supported yet
|
||||
}
|
||||
struct CosmoTib *cosmo_tib = __get_tls();
|
||||
if (!setjmp(foreign.jb)) {
|
||||
elf_exec(dlopen_helper, interp, 2,
|
||||
(char *[]){
|
||||
program_invocation_name,
|
||||
(char *)foreign_helper,
|
||||
NULL,
|
||||
},
|
||||
environ);
|
||||
return false; // if elf_exec() returns, it failed
|
||||
}
|
||||
foreign.tib = __get_tls();
|
||||
__set_tls(cosmo_tib);
|
||||
foreign.is_supported = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void foreign_once(void) {
|
||||
foreign_setup();
|
||||
}
|
||||
|
||||
static bool foreign_init(void) {
|
||||
bool res;
|
||||
cosmo_once(&foreign.once, foreign_once);
|
||||
if (!(res = foreign.is_supported)) {
|
||||
dlerror_set("dlopen() isn't supported on this platform");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int dlclose_nt(void *handle) {
|
||||
int res;
|
||||
if (FreeLibrary((uintptr_t)handle)) {
|
||||
res = 0;
|
||||
} else {
|
||||
dlerror_set("FreeLibrary() failed");
|
||||
res = -1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void *dlopen_nt(const char *path, int mode) {
|
||||
int n;
|
||||
uintptr_t handle;
|
||||
char16_t path16[PATH_MAX + 2];
|
||||
if (mode & ~(RTLD_LOCAL | RTLD_LAZY | RTLD_NOW)) {
|
||||
dlerror_set("invalid mode");
|
||||
return 0;
|
||||
}
|
||||
if ((n = __mkntpath(path, path16)) == -1) {
|
||||
dlerror_set("path invalid");
|
||||
return 0;
|
||||
}
|
||||
if (n > 3 && //
|
||||
path16[n - 3] == '.' && //
|
||||
path16[n - 2] == 's' && //
|
||||
path16[n - 1] == 'o') {
|
||||
path16[n - 2] = 'd';
|
||||
path16[n - 1] = 'l';
|
||||
path16[n + 0] = 'l';
|
||||
path16[n + 1] = 0;
|
||||
}
|
||||
if (!(handle = LoadLibrary(path16))) {
|
||||
dlerror_set("library not found");
|
||||
}
|
||||
return (void *)handle;
|
||||
}
|
||||
|
||||
static void *dlsym_nt(void *handle, const char *name) {
|
||||
long __sysv2nt14();
|
||||
void *x64_abi_func;
|
||||
void *sysv_abi_func = 0;
|
||||
if ((x64_abi_func = GetProcAddress((uintptr_t)handle, name))) {
|
||||
sysv_abi_func = dlsym_nt_thunk(x64_abi_func, __sysv2nt14);
|
||||
sysv_abi_func = foreign_thunk_nt(x64_abi_func);
|
||||
} else {
|
||||
last_dlerror = "symbol not found";
|
||||
dlerror_set("symbol not found: ");
|
||||
strlcat(dlerror_buf, name, sizeof(dlerror_buf));
|
||||
}
|
||||
return sysv_abi_func;
|
||||
}
|
||||
|
@ -516,16 +620,15 @@ void *cosmo_dlopen(const char *path, int mode) {
|
|||
} else if (IsXnuSilicon()) {
|
||||
res = dlopen_silicon(path, mode);
|
||||
} else if (IsXnu()) {
|
||||
last_dlerror = "dlopen() isn't supported on x86-64 MacOS";
|
||||
dlerror_set("dlopen() isn't supported on x86-64 MacOS");
|
||||
res = 0;
|
||||
} else if (foreign_init()) {
|
||||
BEGIN_FOREIGN;
|
||||
STRACE("calling platform dlopen %p tib %p...", foreign.dlopen, foreign.tib);
|
||||
res = foreign.dlopen(path, mode);
|
||||
END_FOREIGN;
|
||||
} else {
|
||||
res = 0;
|
||||
}
|
||||
STRACE("dlopen(%#s, %d) → %p", path, mode, res);
|
||||
STRACE("dlopen(%#s, %d) → %p% m", path, mode, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -539,23 +642,23 @@ void *cosmo_dlopen(const char *path, int mode) {
|
|||
* @return address of symbol, or NULL w/ dlerror()
|
||||
*/
|
||||
void *cosmo_dlsym(void *handle, const char *name) {
|
||||
void *res;
|
||||
void *func;
|
||||
if (IsWindows()) {
|
||||
res = dlsym_nt(handle, name);
|
||||
func = dlsym_nt(handle, name);
|
||||
} else if (IsXnuSilicon()) {
|
||||
res = __syslib->__dlsym(handle, name);
|
||||
func = __syslib->__dlsym(handle, name);
|
||||
} else if (IsXnu()) {
|
||||
last_dlerror = "dlopen() isn't supported on x86-64 MacOS";
|
||||
res = 0;
|
||||
dlerror_set("dlopen() isn't supported on x86-64 MacOS");
|
||||
func = 0;
|
||||
} else if (foreign_init()) {
|
||||
BEGIN_FOREIGN;
|
||||
res = foreign.dlsym(handle, name);
|
||||
END_FOREIGN;
|
||||
if ((func = foreign.dlsym(handle, name))) {
|
||||
func = foreign_thunk_sysv(func);
|
||||
}
|
||||
} else {
|
||||
res = 0;
|
||||
func = 0;
|
||||
}
|
||||
STRACE("dlsym(%p, %#s) → %p", handle, name, res);
|
||||
return res;
|
||||
STRACE("dlsym(%p, %#s) → %p", handle, name, func);
|
||||
return func;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -571,12 +674,10 @@ int cosmo_dlclose(void *handle) {
|
|||
} else if (IsXnuSilicon()) {
|
||||
res = __syslib->__dlclose(handle);
|
||||
} else if (IsXnu()) {
|
||||
last_dlerror = "dlopen() isn't supported on x86-64 MacOS";
|
||||
dlerror_set("dlopen() isn't supported on x86-64 MacOS");
|
||||
res = -1;
|
||||
} else if (foreign_init()) {
|
||||
BEGIN_FOREIGN;
|
||||
res = foreign.dlclose(handle);
|
||||
END_FOREIGN;
|
||||
} else {
|
||||
res = -1;
|
||||
}
|
||||
|
@ -592,13 +693,12 @@ char *cosmo_dlerror(void) {
|
|||
if (IsXnuSilicon()) {
|
||||
res = __syslib->__dlerror();
|
||||
} else if (IsWindows() || IsXnu()) {
|
||||
res = (char *)last_dlerror;
|
||||
res = dlerror_buf;
|
||||
} else if (foreign_init()) {
|
||||
BEGIN_FOREIGN;
|
||||
res = foreign.dlerror();
|
||||
END_FOREIGN;
|
||||
res = dlerror_set(res);
|
||||
} else {
|
||||
res = (char *)last_dlerror;
|
||||
res = dlerror_buf;
|
||||
}
|
||||
STRACE("dlerror() → %#s", res);
|
||||
return res;
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "ape/sections.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/runtime/memtrack.internal.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
@ -33,6 +36,11 @@ privileged long __get_safe_size(long want, long extraspace) {
|
|||
if (!__tls_enabled) return want;
|
||||
struct PosixThread *pt;
|
||||
struct CosmoTib *tib = __get_tls_privileged();
|
||||
if (!IsAutoFrame((uintptr_t)tib >> 16) &&
|
||||
!(__executable_start <= (const unsigned char *)tib &&
|
||||
(const unsigned char *)tib < _end)) {
|
||||
return want;
|
||||
}
|
||||
long bottom, sp = GetStackPointer();
|
||||
if ((char *)sp >= tib->tib_sigstack_addr &&
|
||||
(char *)sp <= tib->tib_sigstack_addr + tib->tib_sigstack_size) {
|
||||
|
|
|
@ -471,7 +471,6 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt,
|
|||
p = b;
|
||||
f = fmt;
|
||||
e = p + n; // assume if n was negative e < p will be the case
|
||||
tib = __tls_enabled ? __get_tls_privileged() : 0;
|
||||
for (;;) {
|
||||
for (;;) {
|
||||
if (!(c = *f++) || c == '%') break;
|
||||
|
@ -582,6 +581,7 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt,
|
|||
goto FormatUnsigned;
|
||||
|
||||
case 'P':
|
||||
tib = __tls_enabled ? __get_tls_privileged() : 0;
|
||||
if (!(tib && (tib->tib_flags & TIB_FLAG_VFORKED))) {
|
||||
x = __pid;
|
||||
#ifdef __x86_64__
|
||||
|
@ -607,6 +607,7 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt,
|
|||
goto FormatDecimal;
|
||||
|
||||
case 'H':
|
||||
tib = __tls_enabled ? __get_tls_privileged() : 0;
|
||||
if (!(tib && (tib->tib_flags & TIB_FLAG_VFORKED))) {
|
||||
if (tib) {
|
||||
x = atomic_load_explicit(&tib->tib_tid, memory_order_relaxed);
|
||||
|
@ -760,6 +761,7 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt,
|
|||
|
||||
case 'm': {
|
||||
int e;
|
||||
tib = __tls_enabled ? __get_tls_privileged() : 0;
|
||||
if (!(e = tib ? tib->tib_errno : __errno) && sign == ' ') {
|
||||
break;
|
||||
} else {
|
||||
|
|
|
@ -147,6 +147,9 @@ relegated static char *ShowGeneralRegisters(char *p, ucontext_t *ctx) {
|
|||
ctx->uc_mcontext.fpregs ? ctx->uc_mcontext.fpregs->swd : 0,
|
||||
ctx->uc_mcontext.fpregs ? ctx->uc_mcontext.fpregs->mxcsr : 0);
|
||||
*p++ = '\n';
|
||||
p = stpcpy(p, "TLS ");
|
||||
p = HexCpy(p, (long)__get_tls(), 64);
|
||||
*p++ = '\n';
|
||||
return p;
|
||||
}
|
||||
|
||||
|
|
|
@ -176,7 +176,7 @@ struct ElfWriter *elfwriter_open(const char *path, int mode, int arch) {
|
|||
#elif defined(__riscv)
|
||||
arch = EM_RISCV;
|
||||
#elif defined(__s390x__)
|
||||
elf->ehdr->e_machine = EM_S390;
|
||||
arch = EM_S390;
|
||||
#else
|
||||
#error "unsupported architecture"
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue