sysdeps/linux: implement threading support

This commit is contained in:
Geert Custers 2021-01-04 08:46:57 +01:00
parent 02f2a8ed17
commit af2761a390
11 changed files with 166 additions and 5 deletions

View file

@ -5,6 +5,7 @@
#include <bits/ensure.h>
#include <mlibc/debug.hpp>
#include <mlibc/all-sysdeps.hpp>
#include <mlibc/thread-entry.hpp>
#include "cxx-syscall.hpp"
#define STUB_ONLY { __ensure(!"STUB_ONLY function was called"); __builtin_unreachable(); }
@ -17,15 +18,18 @@
#define NR_fstat 5
#define NR_lseek 8
#define NR_mmap 9
#define NR_mprotect 10
#define NR_sigaction 13
#define NR_rt_sigprocmask 14
#define NR_ioctl 16
#define NR_pipe 22
#define NR_select 23
#define NR_nanosleep 35
#define NR_socket 41
#define NR_connect 42
#define NR_sendmsg 46
#define NR_recvmsg 47
#define NR_clone 56
#define NR_fork 57
#define NR_execve 59
#define NR_exit 60
@ -35,6 +39,7 @@
#define NR_arch_prctl 158
#define NR_sys_futex 202
#define NR_clock_gettime 228
#define NR_exit_group 231
#define NR_pselect6 270
#define NR_pipe2 293
@ -196,9 +201,26 @@ int sys_unlink(const char *path) {
return 0;
}
int sys_sleep(time_t *secs, long *nanos) {
struct timespec req = {
.tv_sec = *secs,
.tv_nsec = *nanos
};
struct timespec rem = {0};
auto ret = do_syscall(NR_nanosleep, &req, &rem);
if (int e = sc_error(ret); e)
return e;
*secs = rem.tv_sec;
*nanos = rem.tv_nsec;
return 0;
}
#if __MLIBC_POSIX_OPTION
#include <sys/ioctl.h>
#include <sched.h>
int sys_isatty(int fd) {
struct winsize ws;
@ -278,13 +300,39 @@ int sys_sigprocmask(int how, const sigset_t *set, sigset_t *old) {
auto ret = do_syscall(NR_rt_sigprocmask, how, set, old, NSIG / 8);
if (int e = sc_error(ret); e)
return e;
return 0;
}
int sys_clone(void *entry, void *user_arg, void *tcb, pid_t *pid_out) {
void *stack = prepare_stack(entry, user_arg);
unsigned long flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND
| CLONE_THREAD | CLONE_SYSVSEM | CLONE_SETTLS | CLONE_SETTLS
| CLONE_PARENT_SETTID;
auto ret = __mlibc_spawn_thread(flags, stack, pid_out, NULL, tcb);
if (ret < 0)
return ret;
return 0;
}
#endif // __MLIBC_POSIX_OPTION
int sys_vm_protect(void *pointer, size_t size, int prot) {
auto ret = do_syscall(NR_mprotect, pointer, size, prot);
if (int e = sc_error(ret); e)
return e;
return 0;
}
void sys_thread_exit() {
do_syscall(NR_exit, 0);
__builtin_trap();
}
void sys_exit(int status) {
do_syscall(NR_exit, status);
do_syscall(NR_exit_group, status);
__builtin_trap();
}

View file

@ -0,0 +1,12 @@
#ifndef MLIBC_THREAD_ENTRY
#include <mlibc/tcb.hpp>
extern "C" int __mlibc_spawn_thread(int flags, void *stack, void *pid_out, void *child_tid, void *tcb);
extern "C" void __mlibc_enter_thread(void *entry, void *user_arg);
namespace mlibc {
void *prepare_stack(void *entry, void *user_arg);
}
#endif // MLIBC_THREAD_ENTRY

View file

@ -9,6 +9,13 @@ libc_sources += files(
'generic/sysdeps.cpp',
)
if not disable_posix_option
libc_sources += files(
'x86_64/thread.cpp',
'x86_64/thread_entry.S'
)
endif
if not no_headers
install_headers(
'include/abi-bits/abi.h',
@ -42,7 +49,7 @@ if not headers_only
crt = custom_target('crt1',
build_by_default: true,
command: c_compiler.cmd_array() + ['-c', '-o', '@OUTPUT@', '@INPUT@'],
input: 'x86_64-crt/crt1.S',
input: 'x86_64/crt-src/crt1.S',
output: 'crt1.o',
install: true,
install_dir: get_option('libdir')
@ -51,7 +58,7 @@ if not headers_only
custom_target('Scrt1',
build_by_default: true,
command: c_compiler.cmd_array() + ['-c', '-o', '@OUTPUT@', '@INPUT@'],
input: 'x86_64-crt/Scrt1.S',
input: 'x86_64/crt-src/Scrt1.S',
output: 'Scrt1.o',
install: true,
install_dir: get_option('libdir')
@ -60,7 +67,7 @@ if not headers_only
custom_target('crti',
build_by_default: true,
command: c_compiler.cmd_array() + ['-c', '-o', '@OUTPUT@', '@INPUT@'],
input: 'x86_64-crt/crti.S',
input: 'x86_64/crt-src/crti.S',
output: 'crti.o',
install: true,
install_dir: get_option('libdir')
@ -69,7 +76,7 @@ if not headers_only
custom_target('crtn',
build_by_default: true,
command: c_compiler.cmd_array() + ['-c', '-o', '@OUTPUT@', '@INPUT@'],
input: 'x86_64-crt/crtn.S',
input: 'x86_64/crt-src/crtn.S',
output: 'crtn.o',
install: true,
install_dir: get_option('libdir')

View file

@ -0,0 +1,50 @@
#include <mlibc/thread-entry.hpp>
#include <mlibc/all-sysdeps.hpp>
#include <mlibc/thread.hpp>
#include <bits/ensure.h>
#include <sys/mman.h>
#include <stdint.h>
#include <stddef.h>
namespace {
constexpr unsigned int STACK_SIZE = 0x200000;
constexpr unsigned int GUARD_SIZE = 0x2000;
}
extern "C" void __mlibc_enter_thread(void *entry, void *user_arg) {
// The linux kernel already sets the TCB in sys_clone().
auto tcb = mlibc::get_current_tcb();
// Wait until our parent sets up the TID.
while(!__atomic_load_n(&tcb->tid, __ATOMIC_RELAXED))
mlibc::sys_futex_wait(&tcb->tid, 0);
void *(*func)(void *) = reinterpret_cast<void *(*)(void *)>(entry);
auto result = func(user_arg);
tcb->returnValue = result;
__atomic_store_n(&tcb->didExit, 1, __ATOMIC_RELEASE);
mlibc::sys_futex_wake(&tcb->didExit);
mlibc::sys_thread_exit();
}
namespace mlibc {
void *prepare_stack(void *entry, void *user_arg) {
uintptr_t map = reinterpret_cast<uintptr_t>(
mmap(nullptr, STACK_SIZE + GUARD_SIZE,
PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)
);
__ensure(reinterpret_cast<void*>(map) != MAP_FAILED);
int ret = mprotect(reinterpret_cast<void*>(map + GUARD_SIZE), STACK_SIZE,
PROT_READ | PROT_WRITE);
__ensure(!ret);
auto sp = reinterpret_cast<uintptr_t*>(map + STACK_SIZE + GUARD_SIZE);
*--sp = reinterpret_cast<uintptr_t>(user_arg);
*--sp = reinterpret_cast<uintptr_t>(entry);
return sp;
}
} // namespace mlibc

View file

@ -0,0 +1,21 @@
.section .text
.global __mlibc_spawn_thread
.type __mlibc_spawn_thread, "function"
__mlibc_spawn_thread:
xor %eax, %eax
/* The rest of the args are already in the right registers,
* only need to fixup rcx to r10
*/
mov %rcx, %r10
mov $56, %al
syscall
test %eax, %eax
jnz 1f
xor %ebp, %ebp
pop %rdi
pop %rsi
call __mlibc_enter_thread
hlt
1:
ret

View file

@ -15,6 +15,7 @@ posix_test_cases = [
'inet_ntop',
'inet_pton',
'pthread_rwlock',
'pthread_create',
'getaddrinfo',
'dprintf',
'posix_spawn',

View file

@ -0,0 +1,22 @@
#include <assert.h>
#include <time.h>
#include <pthread.h>
int variable = 0;
static void *worker(void *arg) {
(void) arg;
variable = 1;
return NULL;
}
int main() {
pthread_t thread;
int ret = pthread_create(&thread, NULL, &worker, NULL);
assert(!ret);
ret = pthread_join(thread, NULL);
assert(!ret);
assert(variable == 1);
return 0;
}