diff --git a/sysdeps/linux/generic/sysdeps.cpp b/sysdeps/linux/generic/sysdeps.cpp index 6dbbd4df..4fe15efd 100644 --- a/sysdeps/linux/generic/sysdeps.cpp +++ b/sysdeps/linux/generic/sysdeps.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #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 +#include 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(); } diff --git a/sysdeps/linux/include/mlibc/thread-entry.hpp b/sysdeps/linux/include/mlibc/thread-entry.hpp new file mode 100644 index 00000000..a20cab58 --- /dev/null +++ b/sysdeps/linux/include/mlibc/thread-entry.hpp @@ -0,0 +1,12 @@ +#ifndef MLIBC_THREAD_ENTRY + +#include + +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 diff --git a/sysdeps/linux/meson.build b/sysdeps/linux/meson.build index 322131f7..36cae582 100644 --- a/sysdeps/linux/meson.build +++ b/sysdeps/linux/meson.build @@ -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') diff --git a/sysdeps/linux/x86_64-crt/Scrt1.S b/sysdeps/linux/x86_64/crt-src/Scrt1.S similarity index 100% rename from sysdeps/linux/x86_64-crt/Scrt1.S rename to sysdeps/linux/x86_64/crt-src/Scrt1.S diff --git a/sysdeps/linux/x86_64-crt/crt1.S b/sysdeps/linux/x86_64/crt-src/crt1.S similarity index 100% rename from sysdeps/linux/x86_64-crt/crt1.S rename to sysdeps/linux/x86_64/crt-src/crt1.S diff --git a/sysdeps/linux/x86_64-crt/crti.S b/sysdeps/linux/x86_64/crt-src/crti.S similarity index 100% rename from sysdeps/linux/x86_64-crt/crti.S rename to sysdeps/linux/x86_64/crt-src/crti.S diff --git a/sysdeps/linux/x86_64-crt/crtn.S b/sysdeps/linux/x86_64/crt-src/crtn.S similarity index 100% rename from sysdeps/linux/x86_64-crt/crtn.S rename to sysdeps/linux/x86_64/crt-src/crtn.S diff --git a/sysdeps/linux/x86_64/thread.cpp b/sysdeps/linux/x86_64/thread.cpp new file mode 100644 index 00000000..9dd1ce57 --- /dev/null +++ b/sysdeps/linux/x86_64/thread.cpp @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include +#include +#include + +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(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( + mmap(nullptr, STACK_SIZE + GUARD_SIZE, + PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) + ); + __ensure(reinterpret_cast(map) != MAP_FAILED); + int ret = mprotect(reinterpret_cast(map + GUARD_SIZE), STACK_SIZE, + PROT_READ | PROT_WRITE); + __ensure(!ret); + + auto sp = reinterpret_cast(map + STACK_SIZE + GUARD_SIZE); + *--sp = reinterpret_cast(user_arg); + *--sp = reinterpret_cast(entry); + return sp; +} +} // namespace mlibc diff --git a/sysdeps/linux/x86_64/thread_entry.S b/sysdeps/linux/x86_64/thread_entry.S new file mode 100644 index 00000000..3cad0199 --- /dev/null +++ b/sysdeps/linux/x86_64/thread_entry.S @@ -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 diff --git a/tests/meson.build b/tests/meson.build index 5764bdbd..12578c64 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -15,6 +15,7 @@ posix_test_cases = [ 'inet_ntop', 'inet_pton', 'pthread_rwlock', + 'pthread_create', 'getaddrinfo', 'dprintf', 'posix_spawn', diff --git a/tests/posix/pthread_create.c b/tests/posix/pthread_create.c new file mode 100644 index 00000000..b78674fa --- /dev/null +++ b/tests/posix/pthread_create.c @@ -0,0 +1,22 @@ +#include +#include +#include + +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; +}