Working threading via libelfloader, got rid of the reaper thread

This commit is contained in:
Lubos Dolezel 2018-01-08 22:13:05 +01:00
parent 0d9e0bc82b
commit e5b19f0da3
19 changed files with 98 additions and 151 deletions

View File

@ -6,7 +6,7 @@ function(wrap_elf name elfname)
OUTPUT
${CMAKE_CURRENT_BINARY_DIR}/${name}.c
COMMAND
${CMAKE_BINARY_DIR}/src/libelfloader/wrapgen
${CMAKE_BINARY_DIR}/src/libelfloader/wrapgen/wrapgen
${elfname}
${CMAKE_CURRENT_BINARY_DIR}/${name}.c
DEPENDS

View File

@ -9,6 +9,7 @@
#include "../../../../../platform-include/sys/errno.h"
#include "../mman/mman.h"
#include "../simple.h"
#include "../elfcalls_wrapper.h"
extern void *memset(void *s, int c, size_t n);

View File

@ -7,6 +7,7 @@
#include <linux-syscalls/linux.h>
#include <stddef.h>
#include <stdint.h>
#include "../elfcalls_wrapper.h"
int bsdthread_terminate_trap(
uintptr_t stackaddr,

View File

@ -1,45 +1,68 @@
#include "elfcalls_wrapper.h"
#include <elfcalls.h>
#include <dlfcn.h>
extern struct elf_calls* _elfcalls;
static struct elf_calls* _elfcalls;
struct elf_calls* elfcalls(void)
{
if (!_elfcalls)
{
void* module = dlopen("/usr/lib/libelfloader.dylib", RTLD_NOW);
// if (!module)
// __simple_printf("Load error: %s\n", dlerror());
// struct elf_calls** ptr = (struct elf_calls**) dlsym(module, "_elfcalls");
// __simple_printf("_elfcalls is at %p\n", ptr);
// __simple_printf("*_elfcalls = %p\n", *ptr);
_elfcalls = *(struct elf_calls**) dlsym(module, "_elfcalls");
}
return _elfcalls;
}
void native_exit(int ec)
{
if (_elfcalls)
_elfcalls->exit(ec);
}
void* __darling_thread_create(unsigned long stack_size, unsigned long pthobj_size,
void* entry_point, uintptr_t arg3,
uintptr_t arg4, uintptr_t arg5, uintptr_t arg6,
int (*thread_self_trap)())
{
return _elfcalls->darling_thread_create(stack_size, pthobj_size, entry_point,
return elfcalls()->darling_thread_create(stack_size, pthobj_size, entry_point,
arg3, arg4, arg5, arg6, thread_self_trap);
}
int __darling_thread_terminate(void* stackaddr,
unsigned long freesize, unsigned long pthobj_size)
{
return _elfcalls->darling_thread_terminate(stackaddr, freesize, pthobj_size);
return elfcalls()->darling_thread_terminate(stackaddr, freesize, pthobj_size);
}
void* __darling_thread_get_stack(void)
{
return _elfcalls->darling_thread_get_stack();
return elfcalls()->darling_thread_get_stack();
}
void* native_dlopen(const char* path)
{
return _elfcalls->dlopen(path);
return elfcalls()->dlopen(path);
}
char* native_dlerror(void)
{
return _elfcalls->dlerror();
return elfcalls()->dlerror();
}
void* native_dlsym(void* module, const char* name)
{
return _elfcalls->dlsym(module, name);
return elfcalls()->dlsym(module, name);
}
int native_dlclose(void* module)
{
return _elfcalls->dlclose(module);
return elfcalls()->dlclose(module);
}

View File

@ -12,6 +12,8 @@ char* native_dlerror(void);
void* native_dlsym(void* module, const char* name);
int native_dlclose(void* module);
void native_exit(int ec);
// Native thread wrapping
void* __darling_thread_create(unsigned long stack_size, unsigned long pthobj_size,
void* entry_point, uintptr_t arg3,

View File

@ -2,11 +2,14 @@
#include "../base.h"
#include "../errno.h"
#include <linux-syscalls/linux.h>
#include "../elfcalls_wrapper.h"
long sys_exit(int status)
{
int ret;
native_exit(status);
ret = LINUX_SYSCALL1(__NR_exit_group, status);
if (ret < 0)
ret = errno_linux_to_bsd(ret);

View File

@ -5,16 +5,16 @@
#include <linux-syscalls/linux.h>
#include <elfcalls.h>
extern struct elf_calls* _elfcalls;
extern struct elf_calls* elfcalls(void);
long sys_sem_close(int sem)
{
#ifndef VARIANT_DYLD
int ret;
ret = _elfcalls->sem_close(sem);
ret = elfcalls()->sem_close(sem);
if (ret == -1)
ret = -errno_linux_to_bsd(_elfcalls->get_errno());
ret = -errno_linux_to_bsd(elfcalls()->get_errno());
return ret;
#else

View File

@ -6,7 +6,7 @@
#include <linux-syscalls/linux.h>
#include <elfcalls.h>
extern struct elf_calls* _elfcalls;
extern struct elf_calls* elfcalls(void);
long sys_sem_open(const char* name, int oflag, int mode, int value)
{
@ -16,13 +16,13 @@ long sys_sem_open(const char* name, int oflag, int mode, int value)
// __simple_printf("sem_open %s, %d, %d, %d\n", name, oflag, mode, value);
ptr = _elfcalls->sem_open(name, oflags_bsd_to_linux(oflag), mode, value);
ptr = elfcalls()->sem_open(name, oflags_bsd_to_linux(oflag), mode, value);
//__simple_printf("sem_open -> %p\n", ptr);
if (!ptr)
{
// __simple_printf("errno: %d\n", _elfcalls->get_errno());
return -errno_linux_to_bsd(_elfcalls->get_errno());
// __simple_printf("errno: %d\n", elfcalls()->get_errno());
return -errno_linux_to_bsd(elfcalls()->get_errno());
}
return (long) ptr;

View File

@ -5,7 +5,7 @@
#include <linux-syscalls/linux.h>
#include <elfcalls.h>
extern struct elf_calls* _elfcalls;
extern struct elf_calls* elfcalls(void);
long sys_sem_post(int* sem)
{
@ -13,9 +13,9 @@ long sys_sem_post(int* sem)
int ret;
// __simple_printf("sem_post(%p)\n", sem);
ret = _elfcalls->sem_post(sem);
ret = elfcalls()->sem_post(sem);
if (ret == -1)
ret = -errno_linux_to_bsd(_elfcalls->get_errno());
ret = -errno_linux_to_bsd(elfcalls()->get_errno());
return ret;
#else

View File

@ -5,16 +5,16 @@
#include <linux-syscalls/linux.h>
#include <elfcalls.h>
extern struct elf_calls* _elfcalls;
extern struct elf_calls* elfcalls(void);
long sys_sem_trywait(int* sem)
{
#ifndef VARIANT_DYLD
int ret;
ret = _elfcalls->sem_trywait(sem);
ret = elfcalls()->sem_trywait(sem);
if (ret == -1)
ret = -errno_linux_to_bsd(_elfcalls->get_errno());
ret = -errno_linux_to_bsd(elfcalls()->get_errno());
return ret;
#else

View File

@ -5,16 +5,16 @@
#include <linux-syscalls/linux.h>
#include <elfcalls.h>
extern struct elf_calls* _elfcalls;
extern struct elf_calls* elfcalls(void);
long sys_sem_unlink(const char* name)
{
#ifndef VARIANT_DYLD
int ret;
ret = _elfcalls->sem_unlink(name);
ret = elfcalls()->sem_unlink(name);
if (ret == -1)
ret = -errno_linux_to_bsd(_elfcalls->get_errno());
ret = -errno_linux_to_bsd(elfcalls()->get_errno());
return ret;
#else

View File

@ -6,7 +6,7 @@
#include <elfcalls.h>
#include "../bsdthread/cancelable.h"
extern struct elf_calls* _elfcalls;
extern struct elf_calls* elfcalls(void);
long sys_sem_wait(int* sem)
{
@ -19,9 +19,9 @@ long sys_sem_wait_nocancel(int* sem)
#ifndef VARIANT_DYLD
int ret;
ret = _elfcalls->sem_wait(sem);
ret = elfcalls()->sem_wait(sem);
if (ret == -1)
ret = -errno_linux_to_bsd(_elfcalls->get_errno());
ret = -errno_linux_to_bsd(elfcalls()->get_errno());
return ret;
#else

View File

@ -6,16 +6,16 @@
#include <linux-syscalls/linux.h>
#include <elfcalls.h>
extern struct elf_calls* _elfcalls;
extern struct elf_calls* elfcalls(void);
long sys_shm_open(const char* name, int oflag, int mode)
{
#ifndef VARIANT_DYLD
int ret;
ret = _elfcalls->shm_open(name, oflags_bsd_to_linux(oflag), mode);
ret = elfcalls()->shm_open(name, oflags_bsd_to_linux(oflag), mode);
if (ret == -1)
ret = -errno_linux_to_bsd(_elfcalls->get_errno());
ret = -errno_linux_to_bsd(elfcalls()->get_errno());
return ret;
#else

View File

@ -5,16 +5,16 @@
#include <linux-syscalls/linux.h>
#include <elfcalls.h>
extern struct elf_calls* _elfcalls;
extern struct elf_calls* elfcalls(void);
long sys_shm_unlink(const char* name)
{
#ifndef VARIANT_DYLD
int ret;
ret = _elfcalls->shm_unlink(name);
ret = elfcalls()->shm_unlink(name);
if (ret == -1)
ret = -errno_linux_to_bsd(_elfcalls->get_errno());
ret = -errno_linux_to_bsd(elfcalls()->get_errno());
return ret;
#else

View File

@ -179,7 +179,7 @@ void run(const char* path)
JUMPX(stack, lc.interp_entry);
else
{
puts("Back from loaded binary");
// puts("Back from loaded binary");
}
}

View File

@ -65,6 +65,7 @@ void elfcalls_make(struct elf_calls* calls)
calls->darling_thread_get_stack = __darling_thread_get_stack;
calls->get_errno = get_errno;
calls->exit = exit;
*((void**)&calls->sem_open) = sem_open;
*((void**)&calls->sem_wait) = sem_wait;
*((void**)&calls->sem_trywait) = sem_trywait;
@ -80,20 +81,22 @@ int main(int argc, const char** argv)
{
typedef void (*retfunc)(void);
pthread_once(&once_control, once_test);
struct elf_calls* calls;
retfunc ret;
for (int i = 0; i < argc; i++)
printf("arg %d: %s\n", i, argv[i]);
// for (int i = 0; i < argc; i++)
// printf("arg %d: %s\n", i, argv[i]);
calls = (struct elf_calls*) strtoul(argv[1], NULL, 16);
ret = (retfunc) strtoul(argv[2], NULL, 16);
puts("before elfcalls_make");
// puts("before elfcalls_make");
elfcalls_make(calls);
puts("after elfcalls_make");
printf("Will call %p\n", ret);
// puts("after elfcalls_make");
// printf("Will call %p\n", ret);
ret();
__builtin_unreachable();

View File

@ -36,6 +36,8 @@ struct elf_calls
// POSIX SHM APIs
int (*shm_open)(const char* name, int oflag, unsigned short mode);
int (*shm_unlink)(const char* name);
void (*exit)(int ec);
};
#endif

View File

@ -1,7 +1,7 @@
/*
This file is part of Darling.
Copyright (C) 2015 Lubos Dolezel
Copyright (C) 2015-2018 Lubos Dolezel
Darling is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -27,6 +27,7 @@ along with Darling. If not, see <http://www.gnu.org/licenses/>.
#include <signal.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <setjmp.h>
// The point of this file is build macOS threads on top of native libc's threads,
// otherwise it would not be possible to make native calls from these threads.
@ -46,23 +47,10 @@ struct arg_struct
};
unsigned long pth_obj_size;
void* pth;
};
struct reaper_item
{
struct reaper_item* next;
pthread_t thread;
void* stack;
size_t stacksize;
jmp_buf* jmpbuf;
};
static void* darling_thread_entry(void* p);
static void start_reaper();
static sem_t reaper_sem;
static pthread_mutex_t reaper_mutex = PTHREAD_MUTEX_INITIALIZER;
static struct reaper_item *reaper_items_front = NULL, *reaper_items_end = NULL;
static void reaper_item_push(struct reaper_item* item);
static struct reaper_item* reaper_item_pop(void);
#ifndef PTHREAD_STACK_MIN
# define PTHREAD_STACK_MIN 16384
@ -73,21 +61,17 @@ void* __darling_thread_create(unsigned long stack_size, unsigned long pth_obj_si
uintptr_t arg4, uintptr_t arg5, uintptr_t arg6,
int (*thread_self_trap)())
{
static pthread_once_t reaper_once = PTHREAD_ONCE_INIT;
struct arg_struct args = { (thread_ep) entry_point, arg3,
arg4, arg5, arg6, thread_self_trap, pth_obj_size, NULL };
pthread_attr_t attr;
pthread_t nativeLibcThread;
void* pth;
pthread_once(&reaper_once, start_reaper);
pthread_attr_init(&attr);
//pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// pthread_attr_setstacksize(&attr, stack_size);
pth = mmap(NULL, stack_size + pth_obj_size + 0x1000, PROT_READ | PROT_WRITE,
pth = mmap(NULL, stack_size + pth_obj_size + 0x1000 + 0x1000, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
// pthread_attr_setstack is buggy. The documentation states we should provide the lowest
@ -97,7 +81,10 @@ void* __darling_thread_create(unsigned long stack_size, unsigned long pth_obj_si
//pthread_attr_setstack(&attr, ((char*)pth) + pth_obj_size, stack_size - pth_obj_size - 0x1000);
// std::cout << "Allocated stack at " << pth << ", size " << stack_size << std::endl;
pth = ((char*) pth) + stack_size + 0x1000;
// We allocated an extra page for jmpbuf
args.jmpbuf = (jmp_buf*) pth;
pth = ((char*) pth) + stack_size + 0x2000;
pthread_attr_setstacksize(&attr, 4096);
args.pth = pth;
@ -120,6 +107,15 @@ static void* darling_thread_entry(void* p)
args.port = args.thread_self_trap();
in_args->pth = NULL;
int freesize;
if ((freesize = setjmp(*args.jmpbuf)) != 0)
{
// Terminate the Linux thread
// +0x1000 is an extra page we allocated for the jmp_buf
munmap(args.jmpbuf, freesize + 0x1000);
return NULL;
}
#ifdef __x86_64__
__asm__ __volatile__ (
"movq %1, %%rdi\n"
@ -159,7 +155,7 @@ static void* darling_thread_entry(void* p)
"ret\n" // Jump to the address pushed at the beginning
:: "c" (&args), "d" (args.pth));
#endif
return NULL;
__builtin_unreachable();
}
int __darling_thread_terminate(void* stackaddr,
@ -168,7 +164,7 @@ int __darling_thread_terminate(void* stackaddr,
if (getpid() == syscall(SYS_gettid))
{
// dispatch_main() calls pthread_exit(NULL) on the main thread,
// which turns the our process into a zombie.
// which turns our process into a zombie on Linux.
// Let's just hang around forever.
sigset_t mask;
memset(&mask, 0, sizeof(mask));
@ -176,16 +172,10 @@ int __darling_thread_terminate(void* stackaddr,
while (1)
sigsuspend(&mask);
}
struct reaper_item* item = (struct reaper_item*) malloc(sizeof(struct reaper_item));
item->thread = pthread_self();
item->stack = stackaddr;
item->stacksize = freesize;
reaper_item_push(item);
sem_post(&reaper_sem);
pthread_exit(NULL);
// Jump back into darling_thread_entry()
jmp_buf* jmpbuf = (jmp_buf*) (((char*) stackaddr) - 0x1000);
longjmp(*jmpbuf, freesize);
__builtin_unreachable();
}
@ -201,81 +191,3 @@ void* __darling_thread_get_stack(void)
return ((char*)stackaddr) + stacksize - 0x2000;
}
static void* reaper_entry(void* unused)
{
while (true)
{
struct reaper_item* item;
sem_wait(&reaper_sem);
item = reaper_item_pop();
if (!item)
continue; // Should not happen!
// std::cout << "Reaping thread " << (void*)item.thread << "; Free stack at " << item.stack << ", " << item.stacksize << " bytes\n";
// Wait for thread to terminate
pthread_join(item->thread, NULL);
// Free its stack in the extended range requested by Darwin's libc
munmap(item->stack, item->stacksize);
free(item);
}
}
static void start_reaper()
{
pthread_attr_t attr;
pthread_t thread;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
sem_init(&reaper_sem, 0, 0);
pthread_create(&thread, &attr, reaper_entry, NULL);
pthread_attr_destroy(&attr);
}
static void reaper_item_push(struct reaper_item* item)
{
pthread_mutex_lock(&reaper_mutex);
item->next = NULL;
if (reaper_items_end != NULL)
{
reaper_items_end->next = item;
reaper_items_end = item;
}
else
{
reaper_items_front = reaper_items_end = item;
}
pthread_mutex_unlock(&reaper_mutex);
}
static struct reaper_item* reaper_item_pop(void)
{
struct reaper_item* e;
pthread_mutex_lock(&reaper_mutex);
if (reaper_items_front != NULL)
{
e = reaper_items_front;
if (reaper_items_front == reaper_items_end)
reaper_items_front = reaper_items_end = NULL; // The list is now empty
else
reaper_items_front = e->next;
}
else
e = NULL;
pthread_mutex_unlock(&reaper_mutex);
return e;
}

@ -1 +1 @@
Subproject commit dbede9ecaa789b8a8b869d8e2f38deb0d6262140
Subproject commit 56f0a2d84783a26ef27e8dde0ba75f4d802be8cc