sysdeps/linux: implement threading support
This commit is contained in:
parent
02f2a8ed17
commit
af2761a390
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
12
sysdeps/linux/include/mlibc/thread-entry.hpp
Normal file
12
sysdeps/linux/include/mlibc/thread-entry.hpp
Normal 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
|
|
@ -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')
|
||||
|
|
50
sysdeps/linux/x86_64/thread.cpp
Normal file
50
sysdeps/linux/x86_64/thread.cpp
Normal 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
|
21
sysdeps/linux/x86_64/thread_entry.S
Normal file
21
sysdeps/linux/x86_64/thread_entry.S
Normal 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
|
|
@ -15,6 +15,7 @@ posix_test_cases = [
|
|||
'inet_ntop',
|
||||
'inet_pton',
|
||||
'pthread_rwlock',
|
||||
'pthread_create',
|
||||
'getaddrinfo',
|
||||
'dprintf',
|
||||
'posix_spawn',
|
||||
|
|
22
tests/posix/pthread_create.c
Normal file
22
tests/posix/pthread_create.c
Normal 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;
|
||||
}
|
Loading…
Reference in a new issue