diff --git a/doc/posix-divergence b/doc/posix-divergence index eb54c482..92e03c45 100644 --- a/doc/posix-divergence +++ b/doc/posix-divergence @@ -46,6 +46,16 @@ Obsolescent in POSIX and scheduled for Sortix removal * ctime, ctime_r are scheduled for removal (obsolescent in POSIX). * utime, are scheduled for removal (obsolescent in POSIX). +Signal Stacks +------------- + +Threads are able to set a recursive signal handling stack using sigaltstack(2) +even if SS_ONSTACK is currently set - while POSIX mandates EPERM in this case. +Such a stack will be used for recursive signals (with SA_ONSTACK set) for the +duration of the signal handler. The original signal stack state will be restored +when the signal handler returns, any edit with sigaltstack will be temporary +(unless the saved ucontext is modified). + Timestamps ---------- diff --git a/kernel/include/sortix/__/sigset.h b/kernel/include/sortix/__/sigset.h new file mode 100644 index 00000000..99cbd357 --- /dev/null +++ b/kernel/include/sortix/__/sigset.h @@ -0,0 +1,36 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2014. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + sortix/__/sigset.h + Declaration of the sigset_t structure size. + +*******************************************************************************/ + +#ifndef INCLUDE_SORTIX____SIGSET_H +#define INCLUDE_SORTIX____SIGSET_H + +#include + +__BEGIN_DECLS + +#define __SIGSET_NUM_SIGNALS 128 + +__END_DECLS + +#endif diff --git a/kernel/include/sortix/__/wait.h b/kernel/include/sortix/__/wait.h new file mode 100644 index 00000000..16c90c8f --- /dev/null +++ b/kernel/include/sortix/__/wait.h @@ -0,0 +1,55 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2014. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + sortix/__/wait.h + Declarations for waiting for the events of children. + +*******************************************************************************/ + +#ifndef INCLUDE_SORTIX____WAIT_H +#define INCLUDE_SORTIX____WAIT_H + +#include + +__BEGIN_DECLS + +#define __WNATURE_EXITED 0 +#define __WNATURE_SIGNALED 1 +#define __WNATURE_STOPPED 2 +#define __WNATURE_CONTINUED 3 + +#define __WEXITSTATUS(status) ((status >> 8) & 0xFF) +#define __WTERMSIG(status) ((status >> 0) & 0x7F) +#define __WNATURE(status) ((status >> 16) & 0xFF) + +#define __WIFEXITED(status) (__WNATURE(status) == __WNATURE_EXITED) +#define __WIFSIGNALED(status) (__WNATURE(status) == __WNATURE_SIGNALED) +#define __WIFSTOPPED(status) (__WNATURE(status) == __WNATURE_STOPPED) +#define __WIFCONTINUED(status) (__WNATURE(status) == __WNATURE_CONTINUED) + +#define __WSTOPSIG(status) __WTERMSIG(status) + +#define __WCONSTRUCT(nature, exitcode, signal) \ + (((nature) & 0xFF) << 16 | \ + ((exitcode) & 0xFF) << 8 | \ + ((signal) & 0x7F) << 0) + +__END_DECLS + +#endif diff --git a/kernel/include/sortix/exit.h b/kernel/include/sortix/exit.h index 85ca5535..25fc194f 100644 --- a/kernel/include/sortix/exit.h +++ b/kernel/include/sortix/exit.h @@ -43,12 +43,17 @@ struct exit_thread size_t unmap_size; void* zero_from; size_t zero_size; - unsigned long reserved[4]; + void* tls_unmap_from; + size_t tls_unmap_size; + unsigned long reserved[2]; }; #define EXIT_THREAD_ONLY_IF_OTHERS (1<<0) #define EXIT_THREAD_UNMAP (1<<1) #define EXIT_THREAD_ZERO (1<<2) +#define EXIT_THREAD_TLS_UNMAP (1<<3) +#define EXIT_THREAD_PROCESS (1<<4) +#define EXIT_THREAD_DUMP_CORE (1<<5) __END_DECLS diff --git a/kernel/include/sortix/fork.h b/kernel/include/sortix/fork.h index b0fec766..780277a7 100644 --- a/kernel/include/sortix/fork.h +++ b/kernel/include/sortix/fork.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2012. + Copyright(C) Jonas 'Sortie' Termansen 2012, 2014. This file is part of Sortix. @@ -22,13 +22,15 @@ *******************************************************************************/ -#ifndef SORTIX_FORK_H -#define SORTIX_FORK_H +#ifndef INCLUDE_SORTIX_FORK_H +#define INCLUDE_SORTIX_FORK_H #include -#include -#include +#include + +#include +#include __BEGIN_DECLS @@ -77,13 +79,48 @@ __BEGIN_DECLS own state into such a structure and calling tfork. Note that this structure is highly platform specific, portable code should use the standard threading facilities combined with sfork if possible. */ +struct tfork +{ #if defined(__i386__) -typedef struct tforkregs_x86 tforkregs_t; + uint32_t eip; + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t edi; + uint32_t esi; + uint32_t esp; + uint32_t ebp; + uint32_t eflags; + uint32_t fsbase; + uint32_t gsbase; #elif defined(__x86_64__) -typedef struct tforkregs_x64 tforkregs_t; + uint64_t rip; + uint64_t rax; + uint64_t rbx; + uint64_t rcx; + uint64_t rdx; + uint64_t rdi; + uint64_t rsi; + uint64_t rsp; + uint64_t rbp; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t rflags; + uint64_t fsbase; + uint64_t gsbase; #else -#warning No tforkregs_cpu structure declared +#error "You need to add a struct tfork for your platform" #endif + sigset_t sigmask; + stack_t altstack; +}; __END_DECLS diff --git a/kernel/include/sortix/kernel/kthread.h b/kernel/include/sortix/kernel/kthread.h index 0bb3f9bb..61d08efd 100644 --- a/kernel/include/sortix/kernel/kthread.h +++ b/kernel/include/sortix/kernel/kthread.h @@ -68,9 +68,15 @@ public: } ~ScopedLock() + { + Reset(); + } + + void Reset() { if ( mutex ) kthread_mutex_unlock(mutex); + mutex = NULL; } private: @@ -88,9 +94,15 @@ public: } ~ScopedLockSignal() + { + Reset(); + } + + void Reset() { if ( mutex && acquired ) kthread_mutex_unlock(mutex); + mutex = NULL; } bool IsAcquired() { return acquired; } diff --git a/kernel/include/sortix/kernel/process.h b/kernel/include/sortix/kernel/process.h index c09434a7..8b2849a7 100644 --- a/kernel/include/sortix/kernel/process.h +++ b/kernel/include/sortix/kernel/process.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014. This file is part of Sortix. @@ -27,6 +27,9 @@ #include #include +#include +#include +#include #include #include @@ -97,6 +100,12 @@ public: kthread_mutex_t resource_limits_lock; struct rlimit resource_limits[RLIMIT_NUM_DECLARED]; +public: + kthread_mutex_t signal_lock; + struct sigaction signal_actions[SIG_MAX_NUM]; + sigset_t signal_pending; + void (*sigreturn)(void); + public: void BootstrapTables(Ref dtable, Ref mtable); void BootstrapDirectories(Ref root); @@ -123,7 +132,7 @@ private: size_t zombiewaiting; bool iszombie; bool nozombify; - int exitstatus; + int exit_code; public: Process* group; @@ -138,6 +147,7 @@ public: public: Thread* firstthread; kthread_mutex_t threadlock; + bool threads_exiting; public: struct segment* segments; @@ -160,7 +170,8 @@ public: int envc, const char* const* envp, CPU::InterruptRegisters* regs); void ResetAddressSpace(); - void Exit(int status); + void ExitThroughSignal(int signal); + void ExitWithCode(int exit_code); pid_t Wait(pid_t pid, int* status, int options); bool DeliverSignal(int signum); bool DeliverGroupSignal(int signum); @@ -194,7 +205,6 @@ public: public: static Process* Get(pid_t pid); - static pid_t HackGetForegroundProcess(); private: static bool Put(Process* process); @@ -203,7 +213,7 @@ private: }; void InitializeThreadRegisters(CPU::InterruptRegisters* regs, - const tforkregs_t* requested); + const struct tfork* requested); Process* CurrentProcess(); } // namespace Sortix diff --git a/kernel/include/sortix/kernel/signal.h b/kernel/include/sortix/kernel/signal.h index 98c05fbc..f844e471 100644 --- a/kernel/include/sortix/kernel/signal.h +++ b/kernel/include/sortix/kernel/signal.h @@ -18,7 +18,7 @@ Sortix. If not, see . sortix/kernel/signal.h - Classes and functions making it easier to handle Unix signals. + Asynchronous user-space thread interruption. *******************************************************************************/ @@ -26,6 +26,7 @@ #define INCLUDE_SORTIX_KERNEL_SIGNAL_H #include +#include #include @@ -35,28 +36,10 @@ extern "C" volatile unsigned long asm_signal_is_pending; namespace Signal { -class Queue -{ -public: - Queue(); - -// TODO: This is the wrong data structure for the problem! -private: - bool pending[SIG_MAX_NUM]; - -// TODO: This is not SMP ready: -// To avoid race conditions, these should be called with interrupts off. -public: - void Push(int signum); - int Pop(int cursig); - -}; - void Init(); -int Priority(int signum); -void Dispatch(CPU::InterruptRegisters* regs, void* user = NULL); -void Return(CPU::InterruptRegisters* regs, void* user = NULL); inline bool IsPending() { return asm_signal_is_pending != 0; } +void DispatchHandler(CPU::InterruptRegisters* regs, void* user); +void ReturnHandler(CPU::InterruptRegisters* regs, void* user); } // namespace Signal diff --git a/kernel/include/sortix/kernel/thread.h b/kernel/include/sortix/kernel/thread.h index 39eeead3..e964da17 100644 --- a/kernel/include/sortix/kernel/thread.h +++ b/kernel/include/sortix/kernel/thread.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014. This file is part of Sortix. @@ -25,8 +25,12 @@ #ifndef INCLUDE_SORTIX_KERNEL_THREAD_H #define INCLUDE_SORTIX_KERNEL_THREAD_H +#include #include +#include +#include +#include #include #include @@ -68,17 +72,13 @@ Thread* RunKernelThread(ThreadEntry entry, void* user, size_t stacksize = 0); void SetupKernelThreadRegs(CPU::InterruptRegisters* regs, ThreadEntry entry, void* user, addr_t stack, size_t stacksize); -extern "C" void Thread__OnSigKill(Thread* thread); - -typedef void (*sighandler_t)(int); - class Thread { friend Thread* CreateKernelThread(Process* process, CPU::InterruptRegisters* regs, unsigned long fsbase, unsigned long gsbase); friend void KernelInit(unsigned long magic, multiboot_info_t* bootinfo); -friend void Thread__OnSigKill(Thread* thread); +friend void UpdatePendingSignals(Thread* thread); public: static void Init(); @@ -107,11 +107,14 @@ public: bool fpuinitialized; public: + sigset_t signal_pending; + sigset_t signal_mask; + stack_t signal_stack; addr_t addrspace; - sighandler_t sighandler; addr_t kernelstackpos; size_t kernelstacksize; bool kernelstackmalloced; + bool pledged_destruction; #if defined(__i386__) || defined(__x86_64__) public: @@ -121,11 +124,6 @@ public: private: CPU::InterruptRegisters registers; - Signal::Queue signalqueue; - int currentsignal; - int siglevel; - int signums[SIG_NUM_LEVELS]; - CPU::InterruptRegisters sigregs[SIG_NUM_LEVELS]; public: void SaveRegisters(const CPU::InterruptRegisters* src); @@ -133,16 +131,9 @@ public: void HandleSignal(CPU::InterruptRegisters* regs); void HandleSigreturn(CPU::InterruptRegisters* regs); bool DeliverSignal(int signum); + bool DeliverSignalUnlocked(int signum); addr_t SwitchAddressSpace(addr_t newaddrspace); -private: - void GotoOnSigKill(CPU::InterruptRegisters* regs); - __attribute__((noreturn)) void OnSigKill(); - void LastPrayer(); - void SetHavePendingSignals(); - void HandleSignalFixupRegsCPU(CPU::InterruptRegisters* regs); - void HandleSignalCPU(CPU::InterruptRegisters* regs); - }; Thread* CurrentThread(); diff --git a/kernel/include/sortix/sigaction.h b/kernel/include/sortix/sigaction.h new file mode 100644 index 00000000..a2b03b80 --- /dev/null +++ b/kernel/include/sortix/sigaction.h @@ -0,0 +1,63 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013, 2014. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + sortix/sigaction.h + Declares struct sigaction and associated flags. + +*******************************************************************************/ + +#ifndef INCLUDE_SORTIX_SIGACTION_H +#define INCLUDE_SORTIX_SIGACTION_H + +#include + +#include +#include + +__BEGIN_DECLS + +#define SA_NOCLDSTOP (1<<0) +#define SA_ONSTACK (1<<1) +#define SA_RESETHAND (1<<2) +#define SA_RESTART (1<<3) +#define SA_SIGINFO (1<<4) +#define SA_NOCLDWAIT (1<<5) +#define SA_NODEFER (1<<6) +#define SA_COOKIE (1<<7) + +#define __SA_SUPPORTED_FLAGS (SA_NOCLDSTOP | SA_ONSTACK | SA_RESETHAND | \ + SA_RESTART | SA_SIGINFO | SA_NOCLDWAIT | \ + SA_NODEFER | SA_COOKIE) + +struct sigaction +{ + sigset_t sa_mask; + __extension__ union + { + void (*sa_handler)(int); + void (*sa_sigaction)(int, siginfo_t*, void*); + void (*sa_sigaction_cookie)(int, siginfo_t*, void*, void*); + }; + void* sa_cookie; + int sa_flags; +}; + +__END_DECLS + +#endif diff --git a/kernel/include/sortix/sigevent.h b/kernel/include/sortix/sigevent.h index a84dd3c3..8011a707 100644 --- a/kernel/include/sortix/sigevent.h +++ b/kernel/include/sortix/sigevent.h @@ -31,6 +31,8 @@ #include <__/pthread.h> #endif +#include + __BEGIN_DECLS #if __STDC_HOSTED__ @@ -44,12 +46,6 @@ typedef __pthread_attr_t pthread_attr_t; #define SIGEV_SIGNAL 1 #define SIGEV_THREAD 2 -union sigval -{ - int sival_int; - void* sival_ptr; -}; - struct sigevent { int sigev_notify; diff --git a/kernel/include/sortix/siginfo.h b/kernel/include/sortix/siginfo.h new file mode 100644 index 00000000..fe040ca3 --- /dev/null +++ b/kernel/include/sortix/siginfo.h @@ -0,0 +1,96 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013, 2014. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + sortix/siginfo.h + Declares siginfo_t and associated flags. + +*******************************************************************************/ + +#ifndef INCLUDE_SORTIX_SIGINFO_H +#define INCLUDE_SORTIX_SIGINFO_H + +#include + +#include + +#include + +__BEGIN_DECLS + +#ifndef __pid_t_defined +#define __pid_t_defined +typedef __pid_t pid_t; +#endif + +#ifndef __uid_t_defined +#define __uid_t_defined +typedef __uid_t uid_t; +#endif + +typedef struct +{ + union sigval si_value; + void* si_addr; + pid_t si_pid; + uid_t si_uid; + int si_signo; + int si_code; + int si_errno; + int si_status; +} siginfo_t; + +/* TODO: Decide appropriate values for these constants: */ +#define ILL_ILLOPC 1 +#define ILL_ILLOPN 2 +#define ILL_ILLADR 3 +#define ILL_ILLTRP 4 +#define ILL_PRVOPC 5 +#define ILL_PRVREG 6 +#define ILL_COPROC 7 +#define ILL_BADSTK 8 +#define FPE_INTDIV 9 +#define FPE_INTOVF 10 +#define FPE_FLTDIV 11 +#define FPE_FLTOVF 12 +#define FPE_FLTUND 13 +#define FPE_FLTRES 14 +#define FPE_FLTINV 15 +#define FPE_FLTSUB 16 +#define SEGV_MAPERR 17 +#define SEGV_ACCERR 18 +#define BUS_ADRALN 19 +#define BUS_ADRERR 20 +#define BUS_OBJERR 21 +#define TRAP_BRKPT 22 +#define TRAP_TRACE 23 +#define CLD_EXITED 24 +#define CLD_KILLED 25 +#define CLD_DUMPED 26 +#define CLD_TRAPPED 27 +#define CLD_STOPPED 29 +#define CLD_CONTINUED 30 +#define SI_USER 31 +#define SI_QUEUE 32 +#define SI_TIMER 33 +#define SI_ASYNCIO 34 +#define SI_MSGQ 35 + +__END_DECLS + +#endif diff --git a/kernel/include/sortix/signal.h b/kernel/include/sortix/signal.h index aa4d2d54..d1213e76 100644 --- a/kernel/include/sortix/signal.h +++ b/kernel/include/sortix/signal.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014. This file is part of Sortix. @@ -25,59 +25,52 @@ #ifndef SORTIX_INCLUDE_SIGNAL_H #define SORTIX_INCLUDE_SIGNAL_H -/* TODO: Yes, signals are implemented in a non-standard manner for now. */ - #include __BEGIN_DECLS -#define SIGHUP 1 /* Hangup */ -#define SIGINT 2 /* Interrupt */ -#define SIGQUIT 3 /* Quit */ -#define SIGILL 4 /* Illegal Instruction */ -#define SIGTRAP 5 /* Trace/Breakpoint Trap */ -#define SIGABRT 6 /* Abort */ -#define SIGEMT 7 /* Emulation Trap */ -#define SIGFPE 8 /* Arithmetic Exception */ -#define SIGKILL 9 /* Killed */ -#define SIGBUS 10 /* Bus Error */ -#define SIGSEGV 11 /* Segmentation Fault */ -#define SIGSYS 12 /* Bad System Call */ -#define SIGPIPE 13 /* Broken Pipe */ -#define SIGALRM 14 /* Alarm Clock */ -#define SIGTERM 15 /* Terminated */ -#define SIGUSR1 16 /* User Signal 1 */ -#define SIGUSR2 17 /* User Signal 2 */ -#define SIGCHLD 18 /* Child Status */ -#define SIGPWR 19 /* Power Fail/Restart */ -#define SIGWINCH 20 /* Window Size Change */ -#define SIGURG 21 /* Urgent Socket Condition */ -#define SIGSTOP 23 /* Stopped (signal) */ -#define SIGTSTP 24 /* Stopped (user) */ -#define SIGCONT 25 /* Continued */ -#define SIGTTIN 26 /* Stopped (tty input) */ -#define SIGTTOU 27 /* Stopped (tty output) */ -#define SIGVTALRM 28 /* Virtual Timer Expired */ -#define SIGXCPU 30 /* CPU time limit exceeded */ -#define SIGXFSZ 31 /* File size limit exceeded */ -#define SIGWAITING 32 /* All LWPs blocked */ -#define SIGLWP 33 /* Virtual Interprocessor Interrupt for Threads Library */ -#define SIGAIO 34 /* Asynchronous I/O */ -#define SIGPROF 35 -#define SIG__NUM_DECLARED 36 -#define SIG_MAX_NUM 128 -#define NSIG SIG_MAX_NUM +#define SIGHUP 1 /* Hangup */ +#define SIGINT 2 /* Interrupt */ +#define SIGQUIT 3 /* Quit */ +#define SIGILL 4 /* Illegal instruction */ +#define SIGTRAP 5 /* Trace/breakpoint trap */ +#define SIGABRT 6 /* Aborted */ +#define SIGBUS 7 /* Bus Error */ +#define SIGFPE 8 /* Floating point exception */ +#define SIGKILL 9 /* Killed */ +#define SIGUSR1 10 /* User defined signal 1 */ +#define SIGSEGV 11 /* Segmentation fault */ +#define SIGUSR2 12 /* User defined signal 2 */ +#define SIGPIPE 13 /* Broken pipe */ +#define SIGALRM 14 /* Alarm clock */ +#define SIGTERM 15 /* Terminated */ +#define SIGSYS 16 /* Bad system call */ +#define SIGCHLD 17 /* Child exited */ +#define SIGCONT 18 /* Continued */ +#define SIGSTOP 19 /* Stopped (signal) */ +#define SIGTSTP 20 /* Stopped */ +#define SIGTTIN 21 /* Stopped (tty input) */ +#define SIGTTOU 22 /* Stopped (tty output) */ +#define SIGURG 23 /* Urgent I/O condition */ +#define SIGXCPU 24 /* CPU time limit exceeded */ +#define SIGXFSZ 25 /* File size limit exceeded */ +#define SIGVTALRM 26 /* Virtual timer expired */ +#define SIGPWR 27 /* Power Fail/Restart */ +#define SIGWINCH 28 /* Window changed */ +#define SIGRTMIN 64 /* First user-available real-time signal. */ +#define SIGRTMAX 127 /* Last user-available real-time signal. */ -#define SIG_PRIO_NORMAL 0 -#define SIG_PRIO_HIGH 1 -#define SIG_PRIO_STOP 2 -#define SIG_PRIO_CORE 3 -#define SIG_PRIO_KILL 4 -#define SIG_NUM_LEVELS 5 +#define __SIG_NUM_DECLARED 31 +#define __SIG_MAX_NUM 128 -#define SIG_BLOCK 0 -#define SIG_UNBLOCK 1 -#define SIG_SETMASK 2 +#if defined(__is_sortix_kernel) +#define SIG_NUM_DECLARED __SIG_NUM_DECLARED +#define SIG_MAX_NUM __SIG_MAX_NUM +#endif + +#define SIG_ERR ((void (*)(int)) -1) +#define SIG_DFL ((void (*)(int)) 0) +#define SIG_IGN ((void (*)(int)) 1) __END_DECLS diff --git a/kernel/include/sortix/sigprocmask.h b/kernel/include/sortix/sigprocmask.h new file mode 100644 index 00000000..8260adff --- /dev/null +++ b/kernel/include/sortix/sigprocmask.h @@ -0,0 +1,38 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2014. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + sortix/sigprocmask.h + Declares flags for sigprocmask. + +*******************************************************************************/ + +#ifndef INCLUDE_SORTIX_SIGPROCMASK_H +#define INCLUDE_SORTIX_SIGPROCMASK_H + +#include + +__BEGIN_DECLS + +#define SIG_BLOCK 0 +#define SIG_UNBLOCK 1 +#define SIG_SETMASK 2 + +__END_DECLS + +#endif diff --git a/kernel/include/sortix/sigset.h b/kernel/include/sortix/sigset.h index 2e93a720..80becb8d 100644 --- a/kernel/include/sortix/sigset.h +++ b/kernel/include/sortix/sigset.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2012. + Copyright(C) Jonas 'Sortie' Termansen 2012, 2013, 2014. This file is part of Sortix. @@ -27,11 +27,13 @@ #include +#include + __BEGIN_DECLS typedef struct { - unsigned long __val[(1024 / (8 * sizeof (unsigned long int)))]; + unsigned long __val[__SIGSET_NUM_SIGNALS / (8 * sizeof(unsigned long int))]; } sigset_t; __END_DECLS diff --git a/kernel/include/sortix/x86/fork.h b/kernel/include/sortix/sigval.h similarity index 67% rename from kernel/include/sortix/x86/fork.h rename to kernel/include/sortix/sigval.h index b3c6f545..b2b6adb9 100644 --- a/kernel/include/sortix/x86/fork.h +++ b/kernel/include/sortix/sigval.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2012, 2013. + Copyright(C) Jonas 'Sortie' Termansen 2013. This file is part of Sortix. @@ -17,34 +17,22 @@ You should have received a copy of the GNU General Public License along with Sortix. If not, see . - sortix/x86/fork.h - Declarations related to the fork family of system calls on x86 Sortix. + sortix/sigval.h + Declares union sigval. *******************************************************************************/ -#ifndef SORTIX_X86_FORK_H -#define SORTIX_X86_FORK_H +#ifndef INCLUDE_SORTIX_SIGVAL_H +#define INCLUDE_SORTIX_SIGVAL_H #include -#include - __BEGIN_DECLS -struct tforkregs_x86 +union sigval { - uint32_t eip; - uint32_t eax; - uint32_t ebx; - uint32_t ecx; - uint32_t edx; - uint32_t edi; - uint32_t esi; - uint32_t esp; - uint32_t ebp; - uint32_t eflags; - uint32_t fsbase; - uint32_t gsbase; + int sival_int; + void* sival_ptr; }; __END_DECLS diff --git a/kernel/include/sortix/x64/fork.h b/kernel/include/sortix/stack.h similarity index 61% rename from kernel/include/sortix/x64/fork.h rename to kernel/include/sortix/stack.h index 8a6b6c5a..a4945d27 100644 --- a/kernel/include/sortix/x64/fork.h +++ b/kernel/include/sortix/stack.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2012, 2013. + Copyright(C) Jonas 'Sortie' Termansen 2012, 2013, 2014. This file is part of Sortix. @@ -17,43 +17,37 @@ You should have received a copy of the GNU General Public License along with Sortix. If not, see . - sortix/x64/fork.h - Declarations related to the fork family of system calls on x64 Sortix. + sortix/stack.h + Declares stack_t and associated flags. *******************************************************************************/ -#ifndef SORTIX_X64_FORK_H -#define SORTIX_X64_FORK_H +#ifndef INCLUDE_SORTIX_STACK_H +#define INCLUDE_SORTIX_STACK_H #include -#include +#include __BEGIN_DECLS -struct tforkregs_x64 +#ifndef __size_t_defined +#define __size_t_defined +#define __need_size_t +#include +#endif + +#define SS_ONSTACK (1<<0) +#define SS_DISABLE (1<<1) + +#define __SS_SUPPORTED_FLAGS (SS_ONSTACK | SS_DISABLE) + +typedef struct { - uint64_t rip; - uint64_t rax; - uint64_t rbx; - uint64_t rcx; - uint64_t rdx; - uint64_t rdi; - uint64_t rsi; - uint64_t rsp; - uint64_t rbp; - uint64_t r8; - uint64_t r9; - uint64_t r10; - uint64_t r11; - uint64_t r12; - uint64_t r13; - uint64_t r14; - uint64_t r15; - uint64_t rflags; - uint64_t fsbase; - uint64_t gsbase; -}; + void* ss_sp; + size_t ss_size; + int ss_flags; +} stack_t; __END_DECLS diff --git a/kernel/include/sortix/syscallnum.h b/kernel/include/sortix/syscallnum.h index 3a862f54..01297d3a 100644 --- a/kernel/include/sortix/syscallnum.h +++ b/kernel/include/sortix/syscallnum.h @@ -26,7 +26,7 @@ #define INCLUDE_SORTIX_SYSCALLNUM_H #define SYSCALL_BAD_SYSCALL 0 -#define SYSCALL_EXIT 1 +#define SYSCALL_EXIT 1 /* OBSOLETE */ #define SYSCALL_SLEEP 2 #define SYSCALL_USLEEP 3 #define SYSCALL_PRINT_STRING 4 @@ -154,6 +154,11 @@ #define SYSCALL_WRMSR 130 #define SYSCALL_SCHED_YIELD 131 #define SYSCALL_EXIT_THREAD 132 -#define SYSCALL_MAX_NUM 133 /* index of highest constant + 1 */ +#define SYSCALL_SIGACTION 133 +#define SYSCALL_SIGALTSTACK 134 +#define SYSCALL_SIGPENDING 135 +#define SYSCALL_SIGPROCMASK 136 +#define SYSCALL_SIGSUSPEND 137 +#define SYSCALL_MAX_NUM 138 /* index of highest constant + 1 */ #endif diff --git a/kernel/include/sortix/timespec.h b/kernel/include/sortix/timespec.h index a891b1f3..9cac3dc1 100644 --- a/kernel/include/sortix/timespec.h +++ b/kernel/include/sortix/timespec.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2012. + Copyright(C) Jonas 'Sortie' Termansen 2012, 2014. This file is part of Sortix. @@ -27,8 +27,15 @@ #include +#include + __BEGIN_DECLS +#ifndef __time_t_defined +#define __time_t_defined +typedef __time_t time_t; +#endif + struct timespec { time_t tv_sec; diff --git a/kernel/include/sortix/ucontext.h b/kernel/include/sortix/ucontext.h new file mode 100644 index 00000000..5d36b0d8 --- /dev/null +++ b/kernel/include/sortix/ucontext.h @@ -0,0 +1,155 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013, 2014. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + sortix/ucontext.h + Declares ucontext_t and associated values. + +*******************************************************************************/ + +#ifndef INCLUDE_SORTIX_UCONTEXT_H +#define INCLUDE_SORTIX_UCONTEXT_H + +#include + +#include +#include + +__BEGIN_DECLS + +/* Register declarations for i386 */ +#if defined(__i386__) +typedef int greg_t; +#define NGREG 23 +typedef greg_t gregset_t[NGREG]; +enum +{ + REG_GS = 0, +#define REG_GS REG_GS + REG_FS, +#define REG_FS REG_FS + REG_ES, +#define REG_ES REG_ES + REG_DS, +#define REG_DS REG_DS + REG_EDI, +#define REG_EDI REG_EDI + REG_ESI, +#define REG_ESI REG_ESI + REG_EBP, +#define REG_EBP REG_EBP + REG_ESP, +#define REG_ESP REG_ESP + REG_EBX, +#define REG_EBX REG_EBX + REG_EDX, +#define REG_EDX REG_EDX + REG_ECX, +#define REG_ECX REG_ECX + REG_EAX, +#define REG_EAX REG_EAX + REG_EIP, +#define REG_EIP REG_EIP + REG_CS, +#define REG_CS REG_CS + REG_EFL, +#define REG_EFL REG_EFL + REG_SS, +#define REG_SS REG_SS + REG_CR2, +#define REG_CR2 REG_CR2 + REG_FSBASE, +#define REG_FSBASE REG_FSBASE + REG_GSBASE, +#define REG_GSBASE REG_GSBASE +}; +#endif + +/* Register declarations for x86-64 */ +#if defined(__x86_64__) +typedef long int greg_t; +#define NGREG 23 +typedef greg_t gregset_t[NGREG]; +enum +{ + REG_R8 = 0, +#define REG_R8 REG_R8 + REG_R9, +#define REG_R9 REG_R9 + REG_R10, +#define REG_R10 REG_R10 + REG_R11, +#define REG_R11 REG_R11 + REG_R12, +#define REG_R12 REG_R12 + REG_R13, +#define REG_R13 REG_R13 + REG_R14, +#define REG_R14 REG_R14 + REG_R15, +#define REG_R15 REG_R15 + REG_RDI, +#define REG_RDI REG_RDI + REG_RSI, +#define REG_RSI REG_RSI + REG_RBP, +#define REG_RBP REG_RBP + REG_RBX, +#define REG_RBX REG_RBX + REG_RDX, +#define REG_RDX REG_RDX + REG_RAX, +#define REG_RAX REG_RAX + REG_RCX, +#define REG_RCX REG_RCX + REG_RSP, +#define REG_RSP REG_RSP + REG_RIP, +#define REG_RIP REG_RIP + REG_EFL, +#define REG_EFL REG_EFL + REG_CSGSFS, /* Actually short cs, gs, fs, __pad0. */ +#define REG_CSGSFS REG_CSGSFS + REG_CR2, +#define REG_CR2 REG_CR2 + REG_FSBASE, +#define REG_FSBASE REG_FSBASE + REG_GSBASE, +#define REG_GSBASE REG_GSBASE +}; +#endif + +typedef struct +{ + gregset_t gregs; +#if defined(__i386__) || defined(__x86_64__) + unsigned char fpuenv[512]; +#endif +} mcontext_t; + +typedef struct ucontext +{ + struct ucontext* uc_link; + sigset_t uc_sigmask; + stack_t uc_stack; + mcontext_t uc_mcontext; +} ucontext_t; + +__END_DECLS + +#endif diff --git a/kernel/include/sortix/wait.h b/kernel/include/sortix/wait.h index f7b0727b..749a36cd 100644 --- a/kernel/include/sortix/wait.h +++ b/kernel/include/sortix/wait.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2012. + Copyright(C) Jonas 'Sortie' Termansen 2012, 2014. This file is part of Sortix. @@ -27,21 +27,32 @@ #include +#include + __BEGIN_DECLS -#define WNOHANG (1<<0) +#define WCONTINUED (1<<0) +#define WNOHANG (1<<1) +#define WUNTRACED (1<<2) -#define WEXITSTATUS(status) ((status >> 8) & 0xFF) -#define WTERMSIG(status) ((status >> 0) & 0x7F) -#define WSTOPSIG(status) WTERMSIG(status) -#define WIFEXITED(status) (WTERMSIG(status) == 0) -#define WIFSIGNALED(status) (WTERMSIG(status) != 0) -/*#define WIFCONTINUED(status) (WTERMSIG(status) == TODO)*/ -/*#define WIFSTOPPED(status) (WTERMSIG(status) == TODO)*/ -/*#define WIFCONTINUED(status) (WTERMSIG(status) == TODO)*/ +#define WNATURE_EXITED __WNATURE_EXITED +#define WNATURE_SIGNALED __WNATURE_SIGNALED +#define WNATURE_STOPPED __WNATURE_STOPPED +#define WNATURE_CONTINUED __WNATURE_CONTINUED -#define W_EXITCODE(ret, sig) ((ret) << 8 | (sig)) -#define W_STOPCODE(sig) ((sig) << 8 | 0x7f) +#define WEXITSTATUS(status) __WEXITSTATUS(status) +#define WTERMSIG(status) __WTERMSIG(status) +#define WNATURE(status) __WNATURE(status) + +#define WIFEXITED(status) __WIFEXITED(status) +#define WIFSIGNALED(status) __WIFSIGNALED(status) +#define WIFSTOPPED(status) __WIFSTOPPED(status) +#define WIFCONTINUED(status) __WIFCONTINUED(status) + +#define WSTOPSIG(status) __WSTOPSIG(status) + +#define WCONSTRUCT(nature, exitcode, signal) \ + __WCONSTRUCT(nature, exitcode, signal) __END_DECLS diff --git a/kernel/logterminal.cpp b/kernel/logterminal.cpp index 1da3ae2c..39927e81 100644 --- a/kernel/logterminal.cpp +++ b/kernel/logterminal.cpp @@ -187,17 +187,8 @@ void LogTerminal::ProcessKeystroke(int kbkey) { while ( linebuffer.CanBackspace() ) linebuffer.Backspace(); - if ( foreground_pgid ) - { - if ( Process* process = Process::Get(foreground_pgid) ) - process->DeliverGroupSignal(SIGINT); - } - else // TODO: Backwards compatibility, delete this. - { - pid_t pid = Process::HackGetForegroundProcess(); - if ( Process* process = Process::Get(pid) ) - process->DeliverSignal(SIGINT); - } + if ( Process* process = Process::Get(foreground_pgid) ) + process->DeliverGroupSignal(SIGINT); return; } if ( termmode & TERMMODE_SIGNAL && control && kbkey == KBKEY_D ) diff --git a/kernel/process.cpp b/kernel/process.cpp index a9b7859d..8f9768e5 100644 --- a/kernel/process.cpp +++ b/kernel/process.cpp @@ -22,10 +22,13 @@ *******************************************************************************/ +#include + #include #include #include #include +#include #include #include #include @@ -99,6 +102,7 @@ Process::Process() grouplimbo = false; firstthread = NULL; threadlock = KTHREAD_MUTEX_INITIALIZER; + threads_exiting = false; ptrlock = KTHREAD_MUTEX_INITIALIZER; idlock = KTHREAD_MUTEX_INITIALIZER; user_timers_lock = KTHREAD_MUTEX_INITIALIZER; @@ -107,7 +111,7 @@ Process::Process() segments_used = 0; segments_length = 0; segment_lock = KTHREAD_MUTEX_INITIALIZER; - exitstatus = -1; + exit_code = -1; pid = AllocatePID(); uid = euid = 0; gid = egid = 0; @@ -115,6 +119,16 @@ Process::Process() nicelock = KTHREAD_MUTEX_INITIALIZER; nice = 0; resource_limits_lock = KTHREAD_MUTEX_INITIALIZER; + signal_lock = KTHREAD_MUTEX_INITIALIZER; + sigemptyset(&signal_pending); + memset(&signal_actions, 0, sizeof(signal_actions)); + for ( int i = 0; i < SIG_MAX_NUM; i++ ) + { + sigemptyset(&signal_actions[i].sa_mask); + signal_actions[i].sa_handler = SIG_DFL; + signal_actions[i].sa_cookie = NULL; + signal_actions[i].sa_flags = 0; + } for ( size_t i = 0; i < RLIMIT_NUM_DECLARED; i++ ) { resource_limits[i].rlim_cur = RLIM_INFINITY; @@ -415,7 +429,7 @@ void Process::NotifyChildExit(Process* child, bool zombify) void Process::NotifyNewZombies() { ScopedLock lock(&childlock); - // TODO: Send SIGCHLD here? + DeliverSignal(SIGCHLD); if ( zombiewaiting ) kthread_cond_broadcast(&zombiecond); } @@ -481,9 +495,9 @@ pid_t Process::Wait(pid_t thepid, int* user_status, int options) child_execute_clock.Advance(zombie->child_execute_clock.current_time); child_system_clock.Advance(zombie->child_system_clock.current_time); - int status = zombie->exitstatus; + int status = zombie->exit_code; if ( status < 0 ) - status = W_EXITCODE(128 + SIGKILL, SIGKILL); + status = WCONSTRUCT(WNATURE_SIGNALED, 128 + SIGKILL, SIGKILL); kthread_mutex_lock(&zombie->groupparentlock); bool in_limbo = zombie->groupfirst && (zombie->grouplimbo = true); @@ -504,12 +518,16 @@ static pid_t sys_waitpid(pid_t pid, int* user_status, int options) return CurrentProcess()->Wait(pid, user_status, options); } -void Process::Exit(int status) +void Process::ExitThroughSignal(int signal) +{ + ExitWithCode(WCONSTRUCT(WNATURE_SIGNALED, 128 + signal, signal)); +} + +void Process::ExitWithCode(int requested_exit_code) { ScopedLock lock(&threadlock); - // Status codes can only contain 8 bits according to ISO C and POSIX. - if ( exitstatus == -1 ) - exitstatus = W_EXITCODE(status & 0xFF, 0); + if ( exit_code == -1 ) + exit_code = requested_exit_code; // Broadcast SIGKILL to all our threads which will begin our long path // of process termination. We simply can't stop the threads as they may @@ -519,30 +537,6 @@ void Process::Exit(int status) t->DeliverSignal(SIGKILL); } -static int sys_exit(int status) -{ - CurrentProcess()->Exit(status); - return 0; -} - -bool Process::DeliverSignal(int signum) -{ - // TODO: How to handle signals that kill the process? - if ( firstthread ) - return firstthread->DeliverSignal(signum); - return errno = EINIT, false; -} - -bool Process::DeliverGroupSignal(int signum) -{ - ScopedLock lock(&groupparentlock); - if ( !groupfirst ) - return errno = ESRCH, false; - for ( Process* iter = groupfirst; iter; iter = iter->groupnext ) - iter->DeliverSignal(signum); - return true; -} - void Process::AddChildProcess(Process* child) { ScopedLock mylock(&childlock); @@ -740,6 +734,21 @@ void Process::ResetForExecute() DeleteTimers(); + for ( int i = 0; i < SIG_MAX_NUM; i++ ) + { + signal_actions[i].sa_flags = 0; + if ( signal_actions[i].sa_handler == SIG_DFL ) + continue; + if ( signal_actions[i].sa_handler == SIG_IGN ) + continue; + signal_actions[i].sa_handler = SIG_DFL; + } + + sigreturn = NULL; + stack_t* signal_stack = &CurrentThread()->signal_stack; + memset(signal_stack, 0, sizeof(*signal_stack)); + signal_stack->ss_flags = SS_DISABLE; + ResetAddressSpace(); } @@ -833,6 +842,10 @@ int Process::Execute(const char* programname, const uint8_t* program, int tls_prot = PROT_READ | PROT_WRITE | PROT_KREAD | PROT_KWRITE | PROT_FORK; void* tls_hint = stack_hint; + size_t auxcode_size = Page::Size(); + int auxcode_prot = PROT_EXEC | PROT_READ | PROT_KREAD | PROT_KWRITE | PROT_FORK; + void* auxcode_hint = stack_hint; + size_t arg_size = 0; size_t argv_size = sizeof(char*) * (argc + 1); @@ -850,13 +863,15 @@ int Process::Execute(const char* programname, const uint8_t* program, struct segment stack_segment; struct segment raw_tls_segment; struct segment tls_segment; + struct segment auxcode_segment; kthread_mutex_lock(&segment_lock); if ( !(MapSegment(&arg_segment, stack_hint, arg_size, 0, stack_prot) && MapSegment(&stack_segment, stack_hint, stack_size, 0, stack_prot) && MapSegment(&raw_tls_segment, raw_tls_hint, raw_tls_size, 0, raw_tls_prot) && - MapSegment(&tls_segment, tls_hint, tls_size, 0, tls_prot)) ) + MapSegment(&tls_segment, tls_hint, tls_size, 0, tls_prot) && + MapSegment(&auxcode_segment, auxcode_hint, auxcode_size, 0, auxcode_prot)) ) { kthread_mutex_unlock(&segment_lock); ResetForExecute(); @@ -926,6 +941,20 @@ int Process::Execute(const char* programname, const uint8_t* program, wrmsr(MSRID_FSBASE, (uint64_t) uthread); #endif + uint8_t* auxcode = (uint8_t*) auxcode_segment.addr; +#if defined(__i386__) + sigreturn = (void (*)(void)) &auxcode[0]; + auxcode[0] = 0xCD; /* int .... */ + auxcode[1] = 0x83; /* ... $131 */ +#elif defined(__x86_64__) + sigreturn = (void (*)(void)) &auxcode[0]; + auxcode[0] = 0xCD; /* int .... */ + auxcode[1] = 0x83; /* ... $131 */ +#else + (void) auxcode; + #warning "You need to initialize auxcode with a sigreturn routine" +#endif + dtable->OnExecute(); ExecuteCPU(argc, target_argv, envc, target_envp, stack_segment.addr + stack_segment.size, entry, regs); @@ -1076,9 +1105,9 @@ cleanup_done: return result; } -static pid_t sys_tfork(int flags, tforkregs_t* user_regs) +static pid_t sys_tfork(int flags, struct tfork* user_regs) { - tforkregs_t regs; + struct tfork regs; if ( !CopyFromUser(®s, user_regs, sizeof(regs)) ) return -1; @@ -1092,6 +1121,9 @@ static pid_t sys_tfork(int flags, tforkregs_t* user_regs) if ( !(making_thread || making_process) ) return errno = ENOSYS, -1; + if ( regs.altstack.ss_flags & ~__SS_SUPPORTED_FLAGS ) + return errno = EINVAL, -1; + CPU::InterruptRegisters cpuregs; InitializeThreadRegisters(&cpuregs, ®s); @@ -1124,7 +1156,8 @@ static pid_t sys_tfork(int flags, tforkregs_t* user_regs) thread->kernelstackpos = (addr_t) newkernelstack; thread->kernelstacksize = curthread->kernelstacksize; thread->kernelstackmalloced = true; - thread->sighandler = curthread->sighandler; + memcpy(&thread->signal_mask, ®s.sigmask, sizeof(sigset_t)); + memcpy(&thread->signal_stack, ®s.altstack, sizeof(stack_t)); StartKernelThread(thread); @@ -1233,23 +1266,6 @@ pid_t Process::AllocatePID() return nextpidtoallocate++; } -// TODO: This is not thread safe. -pid_t Process::HackGetForegroundProcess() -{ - for ( pid_t i = nextpidtoallocate; 1 <= i; i-- ) - { - Process* process = Get(i); - if ( !process ) - continue; - if ( process->pid <= 1 ) - continue; - if ( process->iszombie ) - continue; - return i; - } - return 0; -} - int ProcessCompare(Process* a, Process* b) { if ( a->pid < b->pid ) @@ -1375,7 +1391,6 @@ static mode_t sys_getumask(void) void Process::Init() { Syscall::Register(SYSCALL_EXECVE, (void*) sys_execve); - Syscall::Register(SYSCALL_EXIT, (void*) sys_exit); Syscall::Register(SYSCALL_GETPAGESIZE, (void*) sys_getpagesize); Syscall::Register(SYSCALL_GETPGID, (void*) sys_getpgid); Syscall::Register(SYSCALL_GETPID, (void*) sys_getpid); diff --git a/kernel/scheduler.cpp b/kernel/scheduler.cpp index 7b6d6df1..0c6dfb9e 100644 --- a/kernel/scheduler.cpp +++ b/kernel/scheduler.cpp @@ -151,10 +151,13 @@ void Switch(CPU::InterruptRegisters* regs) assert(premagic == SCHED_MAGIC); assert(postmagic == SCHED_MAGIC); DoActualSwitch(regs); - if ( regs->signal_pending && regs->InUserspace() ) - Signal::Dispatch(regs); assert(premagic == SCHED_MAGIC); assert(postmagic == SCHED_MAGIC); + if ( regs->signal_pending && regs->InUserspace() ) + { + Interrupt::Enable(); + Signal::DispatchHandler(regs, NULL); + } } const bool DEBUG_BEGINCTXSWITCH = false; diff --git a/kernel/signal.cpp b/kernel/signal.cpp index bceb15d6..4fff0d74 100644 --- a/kernel/signal.cpp +++ b/kernel/signal.cpp @@ -18,116 +18,833 @@ Sortix. If not, see . signal.cpp - Classes and functions making it easier to handle Unix signals. + Asynchronous user-space thread interruption. *******************************************************************************/ +#include + #include +#include #include +#include +#include +#include #include +#include +#include +#include +#include #include #include -#include +#include #include +#include #include +#if defined(__i386__) || defined(__x86_64__) +#include "x86-family/float.h" +#endif + namespace Sortix { +sigset_t default_ignored_signals; +sigset_t default_stop_signals; +sigset_t unblockable_signals; + // A per-cpu value whether a signal is pending in the running task. extern "C" { volatile unsigned long asm_signal_is_pending = 0; } +void UpdatePendingSignals(Thread* thread) // thread->process->signal_lock held +{ + struct sigaction* signal_actions = thread->process->signal_actions; + + // Determine which signals wouldn't be ignored if received. + sigset_t handled_signals; + sigemptyset(&handled_signals); + for ( int i = 1; i < SIG_MAX_NUM; i++ ) + { + if ( signal_actions[i].sa_handler == SIG_IGN ) + continue; + if ( signal_actions[i].sa_handler == SIG_DFL && + sigismember(&default_ignored_signals, i) ) + continue; + // TODO: A process that is a member of an orphaned process group shall + // not be allowed to stop in response to the SIGTSTP, SIGTTIN, or + // SIGTTOU signals. In cases where delivery of one of these + // signals would stop such a process, the signal shall be + // discarded. + if ( /* is member of an orphaned process group */ false && + signal_actions[i].sa_handler == SIG_DFL && + sigismember(&default_stop_signals, i) ) + continue; + sigaddset(&handled_signals, i); + } + + // TODO: Handle that signals can be pending process-wide! + + // Discard all requested signals that would be ignored if delivered. + sigandset(&thread->signal_pending, &thread->signal_pending, &handled_signals); + + // Determine which signals are not blocked. + sigset_t permitted_signals; + signotset(&permitted_signals, &thread->signal_mask); + sigorset(&permitted_signals, &permitted_signals, &unblockable_signals); + + // Determine which signals can currently be delivered to this thread. + sigset_t deliverable_signals; + sigandset(&deliverable_signals, &permitted_signals, &thread->signal_pending); + + // Determine whether any signals can be delivered. + unsigned long is_pending = !sigisemptyset(&deliverable_signals) ? 1 : 0; + + // Store whether a signal is pending in the virtual register. + if ( thread == CurrentThread() ) + asm_signal_is_pending = is_pending; + else + thread->registers.signal_pending = is_pending; +} + +static +int sys_sigaction(int signum, + const struct sigaction* user_newact, + struct sigaction* user_oldact) +{ + if ( signum < 0 || signum == 0 /* null signal */ || SIG_MAX_NUM <= signum ) + return errno = EINVAL; + + Process* process = CurrentProcess(); + ScopedLock lock(&process->signal_lock); + + struct sigaction* kact = &process->signal_actions[signum]; + + // Let the caller know the previous action. + if ( user_oldact ) + { + if ( !CopyToUser(user_oldact, kact, sizeof(struct sigaction)) ) + return -1; + } + + // Retrieve and validate the new signal action. + if ( user_newact ) + { + struct sigaction newact; + if ( !CopyFromUser(&newact, user_newact, sizeof(struct sigaction)) ) + return -1; + + if ( newact.sa_flags & ~__SA_SUPPORTED_FLAGS ) + return errno = EINVAL, -1; + + if ( newact.sa_handler == SIG_ERR ) + return errno = EINVAL, -1; + + memcpy(kact, &newact, sizeof(struct sigaction)); + + // Signals may become discarded because of the new handler. + ScopedLock threads_lock(&process->threadlock); + for ( Thread* t = process->firstthread; t; t = t->nextsibling ) + UpdatePendingSignals(t); + } + + return 0; +} + +static int sys_sigaltstack(const stack_t* user_newstack, stack_t* user_oldstack) +{ + Thread* thread = CurrentThread(); + + if ( user_oldstack ) + { + if ( !CopyToUser(user_oldstack, &thread->signal_stack, sizeof(stack_t)) ) + return -1; + } + + if ( user_newstack ) + { + stack_t newstack; + if ( !CopyFromUser(&newstack, user_newstack, sizeof(stack_t)) ) + return -1; + + if ( newstack.ss_flags & ~__SS_SUPPORTED_FLAGS ) + return errno = EINVAL, -1; + + memcpy(&thread->signal_stack, &newstack, sizeof(stack_t)); + } + + return 0; +} + +static int sys_sigpending(sigset_t* set) +{ + Process* process = CurrentProcess(); + Thread* thread = CurrentThread(); + + ScopedLock lock(&process->signal_lock); + + // TODO: What about process-wide signals? + + return CopyToUser(set, &thread->signal_pending, sizeof(sigset_t)) ? 0 : -1; +} + +static +int sys_sigprocmask(int how, const sigset_t* user_set, sigset_t* user_oldset) +{ + Process* process = CurrentProcess(); + Thread* thread = CurrentThread(); + + // TODO: Signal masks are a per-thread property, perhaps this should be + // locked in another manner? + ScopedLock lock(&process->signal_lock); + + // Let the caller know the previous signal mask. + if ( user_oldset ) + { + if ( !CopyToUser(user_oldset, &thread->signal_mask, sizeof(sigset_t)) ) + return -1; + } + + // Update the current signal mask according to how. + if ( user_set ) + { + sigset_t set; + if ( !CopyFromUser(&set, user_set, sizeof(sigset_t)) ) + return -1; + + switch ( how ) + { + case SIG_BLOCK: + sigorset(&thread->signal_mask, &thread->signal_mask, &set); + break; + case SIG_UNBLOCK: + signotset(&set, &set); + sigandset(&thread->signal_mask, &thread->signal_mask, &set); + break; + case SIG_SETMASK: + memcpy(&thread->signal_mask, &set, sizeof(sigset_t)); + break; + default: + return errno = EINVAL, -1; + }; + + UpdatePendingSignals(thread); + } + + return 0; +} + +static int sys_sigsuspend(const sigset_t* set) +{ + Process* process = CurrentProcess(); + Thread* thread = CurrentThread(); + + sigset_t old_signal_mask; + sigset_t new_signal_mask; + + ScopedLock lock(&process->signal_lock); + + // Only accept signals from the user-provided set if given. + if ( set ) + { + if ( !CopyFromUser(&new_signal_mask, set, sizeof(sigset_t)) ) + return -1; + memcpy(&old_signal_mask, &thread->signal_mask, sizeof(sigset_t)); + memcpy(&thread->signal_mask, &new_signal_mask, sizeof(sigset_t)); + UpdatePendingSignals(thread); + } + + // Wait for a signal to happen or otherwise never halt. + kthread_cond_t never_triggered = KTHREAD_COND_INITIALIZER; + while ( !Signal::IsPending() ) + kthread_cond_wait_signal(&never_triggered, &process->signal_lock); + + // Restore the previous signal mask if the user gave its own set to wait on. + if ( set ) + { + memcpy(&thread->signal_mask, &old_signal_mask, sizeof(sigset_t)); + UpdatePendingSignals(thread); + } + + // The system call never halts or it halts because a signal interrupted it. + return errno = EINTR, -1; +} + +static int sys_kill(pid_t pid, int signum) +{ + // Protect the kernel process. + if ( !pid ) + return errno = EPERM, -1; + + // TODO: Implement that pid == -1 means all processes! + bool process_group = pid < 0 ? (pid = -pid, true) : false; + + // TODO: Race condition: The process could be deleted while we use it. + Process* process = Process::Get(pid); + if ( !process ) + return errno = ESRCH, -1; + + // TODO: Protect init? + // TODO: Check for permission. + // TODO: Check for zombies. + + if ( process_group ) + { + if ( !process->DeliverGroupSignal(signum) && errno != ESIGPENDING ) + return -1; + return errno = 0, 0; + } + + if ( !process->DeliverSignal(signum) && errno != ESIGPENDING ) + return -1; + return errno = 0, 0; +} + +bool Process::DeliverGroupSignal(int signum) +{ + ScopedLock lock(&groupparentlock); + if ( !groupfirst ) + return errno = ESRCH, false; + for ( Process* iter = groupfirst; iter; iter = iter->groupnext ) + { + int saved_errno = errno; + if ( !iter->DeliverSignal(signum) && errno != ESIGPENDING ) + { + // This is not currently an error condition. + } + errno = saved_errno; + } + return true; +} + +bool Process::DeliverSignal(int signum) +{ + ScopedLock lock(&threadlock); + + if ( !firstthread ) + return errno = EINIT, false; + + // Broadcast particular signals to all the threads in the process. + if ( signum == SIGCONT || signum == SIGSTOP || signum == SIGKILL ) + { + int saved_errno = errno; + for ( Thread* t = firstthread; t; t = t->nextsibling ) + { + if ( !t->DeliverSignal(signum) && errno != ESIGPENDING ) + { + // This is not currently an error condition. + } + } + errno = saved_errno; + return true; + } + + // Route the signal to a suitable thread that accepts it. + // TODO: This isn't how signals should be routed to a particular thread. + if ( CurrentThread()->process == this ) + return CurrentThread()->DeliverSignal(signum); + return firstthread->DeliverSignal(signum); +} + +static int sys_raise(int signum) +{ + if ( !CurrentThread()->DeliverSignal(signum) && errno != ESIGPENDING ) + return -1; + return errno = 0, 0; +} + +bool Thread::DeliverSignal(int signum) +{ + ScopedLock lock(&process->signal_lock); + return DeliverSignalUnlocked(signum); +} + +bool Thread::DeliverSignalUnlocked(int signum) // thread->process->signal_lock held +{ + if ( signum <= 0 || SIG_MAX_NUM <= signum ) + return errno = EINVAL, false; + + // Discard the null signal, which does error checking, but doesn't actually + // deliver a signal to the process or thread. + if ( signum == 0 ) + return true; + + if ( sigismember(&signal_pending, signum) ) + return errno = ESIGPENDING, false; + + sigaddset(&signal_pending, signum); + if ( signum == SIGSTOP || signum == SIGTSTP || + signum == SIGTTIN || signum == SIGTTOU ) + sigdelset(&signal_pending, SIGCONT); + if ( signum == SIGCONT ) + { + sigdelset(&signal_pending, SIGSTOP); + sigdelset(&signal_pending, SIGTSTP); + sigdelset(&signal_pending, SIGTTIN); + sigdelset(&signal_pending, SIGTTOU); + } + UpdatePendingSignals(this); + + return true; +} + +static int PickImportantSignal(const sigset_t* set) +{ + if ( sigismember(set, SIGKILL) ) + return SIGKILL; + if ( sigismember(set, SIGSTOP) ) + return SIGSTOP; + for ( int i = 1; i < SIG_MAX_NUM; i++ ) + if ( sigismember(set, i) ) + return i; + return 0; +} + +static void EncodeMachineContext(mcontext_t* mctx, CPU::InterruptRegisters* regs) +{ + memset(mctx, 0, sizeof(*mctx)); +#if defined(__i386__) + // TODO: REG_GS + // TODO: REG_FS + // TODO: REG_ES + // TODO: REG_DS + mctx->gregs[REG_EDI] = regs->edi; + mctx->gregs[REG_ESI] = regs->esi; + mctx->gregs[REG_EBP] = regs->ebp; + mctx->gregs[REG_ESP] = regs->esp; + mctx->gregs[REG_EBX] = regs->ebx; + mctx->gregs[REG_EDX] = regs->edx; + mctx->gregs[REG_ECX] = regs->ecx; + mctx->gregs[REG_EAX] = regs->eax; + mctx->gregs[REG_EIP] = regs->eip; + // TODO: REG_CS + mctx->gregs[REG_EFL] = regs->eflags & 0x0000FFFF; + mctx->gregs[REG_CR2] = regs->cr2; + // TODO: REG_SS + Float::Yield(); + memcpy(mctx->fpuenv, CurrentThread()->fpuenvaligned, 512); +#elif defined(__x86_64__) + mctx->gregs[REG_R8] = regs->r8; + mctx->gregs[REG_R9] = regs->r9; + mctx->gregs[REG_R10] = regs->r10; + mctx->gregs[REG_R11] = regs->r11; + mctx->gregs[REG_R12] = regs->r12; + mctx->gregs[REG_R13] = regs->r13; + mctx->gregs[REG_R14] = regs->r14; + mctx->gregs[REG_R15] = regs->r15; + mctx->gregs[REG_RDI] = regs->rdi; + mctx->gregs[REG_RSI] = regs->rsi; + mctx->gregs[REG_RBP] = regs->rbp; + mctx->gregs[REG_RBX] = regs->rbx; + mctx->gregs[REG_RDX] = regs->rdx; + mctx->gregs[REG_RAX] = regs->rax; + mctx->gregs[REG_RCX] = regs->rcx; + mctx->gregs[REG_RSP] = regs->rsp; + mctx->gregs[REG_RIP] = regs->rip; + mctx->gregs[REG_EFL] = regs->rflags & 0x000000000000FFFF; + // TODO: REG_CSGSFS. + mctx->gregs[REG_CR2] = regs->cr2; + mctx->gregs[REG_FSBASE] = 0x0; + mctx->gregs[REG_GSBASE] = 0x0; + Float::Yield(); + memcpy(mctx->fpuenv, CurrentThread()->fpuenvaligned, 512); +#else +#error "You need to implement conversion to mcontext" +#endif +} + +static void DecodeMachineContext(mcontext_t* mctx, CPU::InterruptRegisters* regs) +{ +#if defined(__i386__) || defined(__x86_64__) + unsigned long user_flags = FLAGS_CARRY | FLAGS_PARITY | FLAGS_AUX + | FLAGS_ZERO | FLAGS_SIGN | FLAGS_DIRECTION + | FLAGS_OVERFLOW; +#endif +#if defined(__i386__) + regs->edi = mctx->gregs[REG_EDI]; + regs->esi = mctx->gregs[REG_ESI]; + regs->ebp = mctx->gregs[REG_EBP]; + regs->esp = mctx->gregs[REG_ESP]; + regs->ebx = mctx->gregs[REG_EBX]; + regs->edx = mctx->gregs[REG_EDX]; + regs->ecx = mctx->gregs[REG_ECX]; + regs->eax = mctx->gregs[REG_EAX]; + regs->eip = mctx->gregs[REG_EIP]; + regs->eflags &= ~user_flags; + regs->eflags |= mctx->gregs[REG_EFL] & user_flags; + regs->cr2 = mctx->gregs[REG_CR2]; + Float::Yield(); + memcpy(CurrentThread()->fpuenvaligned, mctx->fpuenv, 512); +#elif defined(__x86_64__) + regs->r8 = mctx->gregs[REG_R8]; + regs->r9 = mctx->gregs[REG_R9]; + regs->r10 = mctx->gregs[REG_R10]; + regs->r11 = mctx->gregs[REG_R11]; + regs->r12 = mctx->gregs[REG_R12]; + regs->r13 = mctx->gregs[REG_R13]; + regs->r14 = mctx->gregs[REG_R14]; + regs->r15 = mctx->gregs[REG_R15]; + regs->rdi = mctx->gregs[REG_RDI]; + regs->rsi = mctx->gregs[REG_RSI]; + regs->rbp = mctx->gregs[REG_RBP]; + regs->rbx = mctx->gregs[REG_RBX]; + regs->rdx = mctx->gregs[REG_RDX]; + regs->rax = mctx->gregs[REG_RAX]; + regs->rcx = mctx->gregs[REG_RCX]; + regs->rsp = mctx->gregs[REG_RSP]; + regs->rip = mctx->gregs[REG_RIP]; + regs->rflags &= ~user_flags; + regs->rflags |= mctx->gregs[REG_EFL] & user_flags; + regs->cr2 = mctx->gregs[REG_CR2]; + Float::Yield(); + memcpy(CurrentThread()->fpuenvaligned, mctx->fpuenv, 512); +#else +#error "You need to implement conversion to mcontext" +#endif +} + +#if defined(__i386__) +struct stack_frame +{ + unsigned long sigreturn; + int signum_param; + siginfo_t* siginfo_param; + ucontext_t* ucontext_param; + void* cookie_param; + siginfo_t siginfo; + ucontext_t ucontext; +}; +#elif defined(__x86_64__) +struct stack_frame +{ + unsigned long sigreturn; + siginfo_t siginfo; + ucontext_t ucontext; +}; +#else +#error "You need to implement struct stack_frame" +#endif + +void Thread::HandleSignal(CPU::InterruptRegisters* regs) +{ + assert(Interrupt::IsEnabled()); + assert(this == CurrentThread()); + + ScopedLock lock(&process->signal_lock); + + assert(process->sigreturn); + +retry_another_signal: + + // Determine which signals are not blocked. + sigset_t permitted_signals; + signotset(&permitted_signals, &signal_mask); + sigorset(&permitted_signals, &permitted_signals, &unblockable_signals); + + // Determine which signals can currently be delivered to this thread. + sigset_t deliverable_signals; + sigandset(&deliverable_signals, &permitted_signals, &signal_pending); + + // Decide which signal to deliver to the thread. + int signum = PickImportantSignal(&deliverable_signals); + if ( !signum ) + return; + + // Unmark the selected signal as pending. + sigdelset(&signal_pending, signum); + UpdatePendingSignals(this); + regs->signal_pending = asm_signal_is_pending; + + // Destroy the current thread if the signal is critical. + if ( signum == SIGKILL ) + { + lock.Reset(); + kthread_exit(); + } + + struct sigaction* action = &process->signal_actions[signum]; + + // Stop the current thread upon receipt of a stop signal that isn't handled + // or cannot be handled (SIGSTOP). + if ( (action->sa_handler == SIG_DFL && + sigismember(&default_stop_signals, signum) ) || + signum == SIGSTOP ) + { + Log::PrintF("%s:%u: `%s' FIXME SIGSTOP\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); + // TODO: Stop the current process. + // TODO: Deliver SIGCHLD to the parent except if SA_NOCLDSTOP is set in + // the parent's SIGCHLD sigaction. + // TODO: SIGCHLD should not be delivered until all the threads in the + // process has received SIGSTOP and stopped? + // TODO: SIGKILL must still be deliverable to a stopped process. + } + + // Resume the current thread upon receipt of SIGCONT. + if ( signum == SIGCONT ) + { + Log::PrintF("%s:%u: `%s' FIXME SIGCONT\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); + // TODO: Resume the current process. + // TODO: Can SIGCONT be masked? + // TODO: Can SIGCONT be handled? + // TODO: Can SIGCONT be ignored? + // TODO: Deliver SIGCHLD to the parent except if SA_NOCLDSTOP is set in + // the parent's SIGCHLD sigaction. + } + + // Signals that would be ignored are already filtered away at this point. + assert(action->sa_handler != SIG_IGN); + assert(action->sa_handler != SIG_DFL || !sigismember(&default_ignored_signals, signum)); + + // The default action must be to terminate the process. Signals that are + // ignored by default got discarded earlier. + if ( action->sa_handler == SIG_DFL ) + { + kthread_mutex_unlock(&process->signal_lock); + process->ExitThroughSignal(signum); + kthread_mutex_lock(&process->signal_lock); + goto retry_another_signal; + } + + // At this point we have to attempt to invoke the user-space signal handler, + // which will then return control to us through sigreturn. However, we can't + // save the kernel state because 1) we can't trust the user-space stack 2) + // we can't rely on the kernel stack being intact as the signal handler may + // invoke system calls. For those reasons, we'll have to modify the saved + // registers so they restore a user-space state. We can do this because + // threads in the kernel cannot be delivered signals except when returning + // from a system call, so we'll simply save the state that would have been + // returned to user-space had no signal occured. + if ( !regs->InUserspace() ) + { +#if defined(__i386__) + uint32_t* params = (uint32_t*) regs->ebx; + regs->eip = params[0]; + regs->eflags = params[2]; + regs->esp = params[3]; + regs->cs = UCS | URPL; + regs->ds = UDS | URPL; + regs->ss = UDS | URPL; +#elif defined(__x86_64__) + regs->rip = regs->rdi; + regs->rflags = regs->rsi; + regs->rsp = regs->r8; + regs->cs = UCS | URPL; + regs->ds = UDS | URPL; + regs->ss = UDS | URPL; +#else +#error "You may need to fix the registers" +#endif + } + + sigset_t new_signal_mask; + memcpy(&new_signal_mask, &action->sa_mask, sizeof(sigset_t)); + sigorset(&new_signal_mask, &new_signal_mask, &signal_mask); + + // Prevent signals from interrupting themselves by default. + if ( !(action->sa_flags & SA_NODEFER) ) + sigaddset(&new_signal_mask, signum); + + // Determine whether we use an alternate signal stack. + bool signal_uses_altstack = action->sa_flags & SA_ONSTACK; + bool usable_altstack = !(signal_stack.ss_flags & (SS_DISABLE | SS_ONSTACK)); + bool use_altstack = signal_uses_altstack && usable_altstack; + + // Determine which signal stack to use and what to save. + stack_t old_signal_stack, new_signal_stack; + uintptr_t stack_location; + if ( use_altstack ) + { + old_signal_stack = signal_stack; + new_signal_stack = signal_stack; + new_signal_stack.ss_flags |= SS_ONSTACK; +#if defined(__i386__) || defined(__x86_64__) + stack_location = (uintptr_t) signal_stack.ss_sp + signal_stack.ss_size; +#else +#error "You need to implement getting the alternate stack pointer" +#endif + } + else + { + old_signal_stack.ss_sp = NULL; + old_signal_stack.ss_flags = SS_DISABLE; + old_signal_stack.ss_size = 0; + new_signal_stack = signal_stack; +#if defined(__i386__) + stack_location = (uintptr_t) regs->esp; +#elif defined(__x86_64__) + stack_location = (uintptr_t) regs->rsp; +#else +#error "You need to implement getting the user-space stack pointer" +#endif + } + + CPU::InterruptRegisters new_regs = *regs; + + struct stack_frame stack_frame; + memset(&stack_frame, 0, sizeof(stack_frame)); + + void* handler_ptr = action->sa_flags & SA_COOKIE ? + (void*) action->sa_sigaction_cookie : + action->sa_flags & SA_SIGINFO ? + (void*) action->sa_sigaction : + (void*) action->sa_handler; + +#if defined(__i386__) + stack_location -= sizeof(stack_frame); + stack_location &= ~(4UL-1UL); + struct stack_frame* stack = (struct stack_frame*) stack_location; + + stack_frame.sigreturn = (unsigned long) process->sigreturn; + stack_frame.signum_param = signum; + stack_frame.siginfo_param = &stack->siginfo; + stack_frame.ucontext_param = &stack->ucontext; + stack_frame.cookie_param = action->sa_cookie; + + new_regs.esp = (unsigned long) stack; + new_regs.eip = (unsigned long) handler_ptr; + new_regs.eflags &= ~FLAGS_DIRECTION; +#elif defined(__x86_64__) + stack_location -= 128; /* Red zone. */ + stack_location -= sizeof(stack_frame); + stack_location = ((stack_location - 8) & ~(16UL-1UL)) + 8; + struct stack_frame* stack = (struct stack_frame*) stack_location; + + stack_frame.sigreturn = (unsigned long) process->sigreturn; + new_regs.rdi = (unsigned long) signum; + new_regs.rsi = (unsigned long) &stack->siginfo; + new_regs.rdx = (unsigned long) &stack->ucontext; + new_regs.rcx = (unsigned long) action->sa_cookie; + + new_regs.rsp = (unsigned long) stack; + new_regs.rip = (unsigned long) handler_ptr; + new_regs.rflags &= ~FLAGS_DIRECTION; +#else +#error "You need to format the stack frame" +#endif + + // Format the siginfo into the stack frame. + stack_frame.siginfo.si_signo = signum; +#if defined(__i386__) || defined(__x86_64__) + // TODO: Is this cr2 value trustworthy? I don't think it is. + if ( signum == SIGSEGV ) + stack_frame.siginfo.si_addr = (void*) regs->cr2; +#else +#warning "You need to tell user-space where it crashed" +#endif + + // Format the ucontext into the stack frame. + stack_frame.ucontext.uc_link = NULL; + memcpy(&stack_frame.ucontext.uc_sigmask, &signal_mask, sizeof(signal_mask)); + memcpy(&stack_frame.ucontext.uc_stack, &signal_stack, sizeof(signal_stack)); + EncodeMachineContext(&stack_frame.ucontext.uc_mcontext, regs); + + if ( !CopyToUser(stack, &stack_frame, sizeof(stack_frame)) ) + { + // Self-destruct if we crashed during delivering the crash signal. + if ( signum == SIGSEGV ) + { + kthread_mutex_unlock(&process->signal_lock); + process->ExitThroughSignal(signum); + kthread_mutex_lock(&process->signal_lock); + goto retry_another_signal; + } + + // Deliver SIGSEGV if we could not deliver the signal on the stack. + // TODO: Is it possible to block SIGSEGV here? + kthread_mutex_unlock(&process->signal_lock); + DeliverSignal(SIGSEGV); + kthread_mutex_lock(&process->signal_lock); + goto retry_another_signal; + } + + // Update the current signal mask. + memcpy(&signal_mask, &new_signal_mask, sizeof(sigset_t)); + + // Update the current alternate signal stack. + signal_stack = new_signal_stack; + + // Update the current registers. + *regs = new_regs; + + // TODO: SA_RESETHAND: + // "If set, the disposition of the signal shall be reset to SIG_DFL + // and the SA_SIGINFO flag shall be cleared on entry to the signal + // handler. Note: SIGILL and SIGTRAP cannot be automatically reset + // when delivered; the system silently enforces this restriction." + + // Run the signal handler by returning to user-space. + return; +} + +void Thread::HandleSigreturn(CPU::InterruptRegisters* regs) +{ + assert(Interrupt::IsEnabled()); + assert(this == CurrentThread()); + + ScopedLock lock(&process->signal_lock); + + struct stack_frame stack_frame; + const struct stack_frame* user_stack_frame; +#if defined(__i386__) + user_stack_frame = (const struct stack_frame*) (regs->esp - 4); +#elif defined(__x86_64__) + user_stack_frame = (const struct stack_frame*) (regs->rsp - 8); +#else +#error "You need to locate the stack we passed the signal handler" +#endif + + if ( CopyFromUser(&stack_frame, user_stack_frame, sizeof(stack_frame)) ) + { + memcpy(&signal_mask, &stack_frame.ucontext.uc_sigmask, sizeof(signal_mask)); + memcpy(&signal_stack, &stack_frame.ucontext.uc_stack, sizeof(signal_stack)); + signal_stack.ss_flags &= __SS_SUPPORTED_FLAGS; + DecodeMachineContext(&stack_frame.ucontext.uc_mcontext, regs); + } + + UpdatePendingSignals(this); + regs->signal_pending = asm_signal_is_pending; + + lock.Reset(); + + HandleSignal(regs); +} + namespace Signal { -const int PRIORITIES[SIG__NUM_DECLARED] = -{ - SIG_PRIO_NORMAL, // unused - SIG_PRIO_NORMAL, // SIGHUP - SIG_PRIO_NORMAL, // SIGINT - SIG_PRIO_NORMAL, // SIGQUIT - SIG_PRIO_CORE, // SIGILL - SIG_PRIO_CORE, // SIGTRAP - SIG_PRIO_CORE, // SIGABRT - SIG_PRIO_CORE, // SIGEMT - SIG_PRIO_CORE, // SIGFPE - SIG_PRIO_KILL, // SIGKILL - SIG_PRIO_CORE, // SIGBUS - SIG_PRIO_CORE, // SIGSEGV - SIG_PRIO_CORE, // SIGSYS - SIG_PRIO_NORMAL, // SIGPIPE - SIG_PRIO_NORMAL, // SIGALRM - SIG_PRIO_NORMAL, // SIGTERM - SIG_PRIO_NORMAL, // SIGUSR1 - SIG_PRIO_NORMAL, // SIGUSR2 - SIG_PRIO_NORMAL, // SIGCHLD - SIG_PRIO_HIGH, // SIGPWR - SIG_PRIO_NORMAL, // SIGWINCH - SIG_PRIO_NORMAL, // SIGURG - SIG_PRIO_NORMAL, // obsolete - SIG_PRIO_STOP, // SIGSTOP - SIG_PRIO_STOP, // SIGTSTP - SIG_PRIO_STOP, // SIGCONT - SIG_PRIO_STOP, // SIGTTIN - SIG_PRIO_STOP, // SIGTTOU - SIG_PRIO_NORMAL, // SIGVTALRM - SIG_PRIO_NORMAL, // obsolete - SIG_PRIO_CORE, // SIGXCPU - SIG_PRIO_CORE, // SIGXFSZ - SIG_PRIO_NORMAL, // SIGCORE - SIG_PRIO_NORMAL, // SIGLWP - SIG_PRIO_NORMAL, // SIGAIO -}; - -int Priority(int signum) -{ - assert(0 <= signum && signum < SIG_MAX_NUM); - if ( !signum ) - return -1; - if ( SIG__NUM_DECLARED <= signum ) - return SIG_PRIO_NORMAL; - return PRIORITIES[signum]; -} - -Queue::Queue() -{ - for ( int i = 1; i < SIG_MAX_NUM; i++ ) - pending[i] = false; -} - -void Queue::Push(int signum) -{ - assert(0 < signum && signum < SIG_MAX_NUM); - pending[signum] = true; -} - -int Queue::Pop(int cursig) -{ - int best = 0; - int bestprio = Priority(cursig); - for ( int i = 1; i < SIG_MAX_NUM; i++ ) - if ( pending[i] && bestprio < Priority(i) ) - { - best = i; - bestprio = Priority(i); - } - pending[best] = false; - return best; -} - -void Dispatch(CPU::InterruptRegisters* regs, void* /*user*/) +void DispatchHandler(CPU::InterruptRegisters* regs, void* /*user*/) { return CurrentThread()->HandleSignal(regs); } -void Return(CPU::InterruptRegisters* regs, void* /*user*/) +void ReturnHandler(CPU::InterruptRegisters* regs, void* /*user*/) { return CurrentThread()->HandleSigreturn(regs); } void Init() { + sigemptyset(&default_ignored_signals); + sigaddset(&default_ignored_signals, SIGCHLD); + sigaddset(&default_ignored_signals, SIGURG); + sigaddset(&default_ignored_signals, SIGPWR); + sigaddset(&default_ignored_signals, SIGWINCH); + sigemptyset(&default_stop_signals); + sigaddset(&default_stop_signals, SIGTSTP); + sigaddset(&default_stop_signals, SIGTTIN); + sigaddset(&default_stop_signals, SIGTTOU); + sigemptyset(&unblockable_signals); + sigaddset(&unblockable_signals, SIGKILL); + sigaddset(&unblockable_signals, SIGSTOP); + + Syscall::Register(SYSCALL_KILL, (void*) sys_kill); + Syscall::Register(SYSCALL_RAISE, (void*) sys_raise); + Syscall::Register(SYSCALL_SIGACTION, (void*) sys_sigaction); + Syscall::Register(SYSCALL_SIGALTSTACK, (void*) sys_sigaltstack); + Syscall::Register(SYSCALL_SIGPENDING, (void*) sys_sigpending); + Syscall::Register(SYSCALL_SIGPROCMASK, (void*) sys_sigprocmask); + Syscall::Register(SYSCALL_SIGSUSPEND, (void*) sys_sigsuspend); } } // namespace Signal + } // namespace Sortix diff --git a/kernel/thread.cpp b/kernel/thread.cpp index 1e2090c9..59648f24 100644 --- a/kernel/thread.cpp +++ b/kernel/thread.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2014. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014. This file is part of Sortix. @@ -22,8 +22,11 @@ *******************************************************************************/ +#include + #include #include +#include #include #include @@ -58,15 +61,17 @@ Thread::Thread() kernelstackpos = 0; kernelstacksize = 0; kernelstackmalloced = false; - currentsignal = 0; - siglevel = 0; - sighandler = NULL; + pledged_destruction = false; terminated = false; fpuinitialized = false; // If malloc isn't 16-byte aligned, then we can't rely on offsets in // our own class, so we'll just fix ourselves nicely up. unsigned long fpuaddr = ((unsigned long) fpuenv+16UL) & ~(16UL-1UL); fpuenvaligned = (uint8_t*) fpuaddr; + sigemptyset(&signal_pending); + sigemptyset(&signal_mask); + memset(&signal_stack, 0, sizeof(signal_stack)); + signal_stack.ss_flags = SS_DISABLE; } Thread::~Thread() @@ -89,11 +94,6 @@ addr_t Thread::SwitchAddressSpace(addr_t newaddrspace) return result; } -// Last chance to clean up user-space things before this thread dies. -void Thread::LastPrayer() -{ -} - extern "C" void BootstrapKernelThread(void* user, ThreadEntry entry) { entry(user); @@ -196,98 +196,19 @@ Thread* RunKernelThread(ThreadEntry entry, void* user, size_t stacksize) return thread; } -void Thread::HandleSignal(CPU::InterruptRegisters* regs) -{ - int signum = signalqueue.Pop(currentsignal); - regs->signal_pending = 0; - - if ( !signum ) - return; - - if ( !sighandler ) - return; - - if ( SIG_NUM_LEVELS <= siglevel ) - return; - - // Signals can't return to kernel mode because the kernel stack may have - // been overwritten by a system call during the signal handler. Correct - // the return state so it returns to userspace and not the kernel. - if ( !regs->InUserspace() ) - HandleSignalFixupRegsCPU(regs); - - if ( signum == SIGKILL ) - { - // We need to run the OnSigKill method here with interrupts enabled - // and on our own stack. But this method this may have been called - // from the scheduler on any stack, so we need to do a little - // bootstrap and switch to our own stack. - GotoOnSigKill(regs); - return; - } - - int level = siglevel++; - signums[level] = currentsignal = signum; - memcpy(sigregs + level, regs, sizeof(*regs)); - - HandleSignalCPU(regs); -} - -void Thread::HandleSigreturn(CPU::InterruptRegisters* regs) -{ - if ( !siglevel ) - return; - - siglevel--; - - currentsignal = siglevel ? signums[siglevel-1] : 0; - memcpy(regs, sigregs + siglevel, sizeof(*regs)); - regs->signal_pending = 0; - - // Check if a more important signal is pending. - HandleSignal(regs); -} - -extern "C" void Thread__OnSigKill(Thread* thread) -{ - thread->OnSigKill(); -} - -void Thread::OnSigKill() -{ - LastPrayer(); - kthread_exit(); -} - -void Thread::SetHavePendingSignals() -{ - // TODO: This doesn't really work if Interrupt::IsCPUInterrupted()! - if ( CurrentThread() == this ) - asm_signal_is_pending = 1; - else - registers.signal_pending = 1; -} - -bool Thread::DeliverSignal(int signum) -{ - if ( signum <= 0 || 128 <= signum ) - return errno = EINVAL, false; - - bool wasenabled = Interrupt::SetEnabled(false); - signalqueue.Push(signum); - SetHavePendingSignals(); - Interrupt::SetEnabled(wasenabled); - - return true; -} - -static int sys_exit_thread(int status, +static int sys_exit_thread(int requested_exit_code, int flags, const struct exit_thread* user_extended) { if ( flags & ~(EXIT_THREAD_ONLY_IF_OTHERS | EXIT_THREAD_UNMAP | - EXIT_THREAD_ZERO) ) + EXIT_THREAD_ZERO | + EXIT_THREAD_TLS_UNMAP | + EXIT_THREAD_PROCESS | + EXIT_THREAD_DUMP_CORE) ) + return errno = EINVAL, -1; + + if ( (flags & EXIT_THREAD_ONLY_IF_OTHERS) && (flags & EXIT_THREAD_PROCESS) ) return errno = EINVAL, -1; Thread* thread = CurrentThread(); @@ -309,12 +230,25 @@ static int sys_exit_thread(int status, { if ( iter == thread ) continue; + if ( iter->pledged_destruction ) + continue; if ( iter->terminated ) continue; is_others = true; } + if ( !(flags & EXIT_THREAD_ONLY_IF_OTHERS) || is_others ) + thread->pledged_destruction = true; + bool are_threads_exiting = false; + if ( (flags & EXIT_THREAD_PROCESS) || !is_others ) + process->threads_exiting = true; + else if ( process->threads_exiting ) + are_threads_exiting = true; kthread_mutex_unlock(&thread->process->threadlock); + // Self-destruct if another thread began exiting the process. + if ( are_threads_exiting ) + kthread_exit(); + if ( (flags & EXIT_THREAD_ONLY_IF_OTHERS) && !is_others ) return errno = ESRCH, -1; @@ -326,62 +260,61 @@ static int sys_exit_thread(int status, Memory::UnmapMemory(process, (uintptr_t) extended.unmap_from, extended.unmap_size); Memory::Flush(); + // TODO: The segment is not actually removed! + } + + if ( flags & EXIT_THREAD_TLS_UNMAP && + Page::IsAligned((uintptr_t) extended.tls_unmap_from) && + extended.tls_unmap_size ) + { + ScopedLock lock(&process->segment_lock); + Memory::UnmapMemory(process, (uintptr_t) extended.tls_unmap_from, + extended.tls_unmap_size); + Memory::Flush(); } if ( flags & EXIT_THREAD_ZERO ) ZeroUser(extended.zero_from, extended.zero_size); if ( !is_others ) - thread->process->Exit(status); + { + // Validate the requested exit code such that the process can't exit + // with an impossible exit status or that it wasn't actually terminated. + + int the_nature = WNATURE(requested_exit_code); + int the_status = WEXITSTATUS(requested_exit_code); + int the_signal = WTERMSIG(requested_exit_code); + + if ( the_nature == WNATURE_EXITED ) + the_signal = 0; + else if ( the_nature == WNATURE_SIGNALED ) + { + if ( the_signal == 0 /* null signal */ || + the_signal == SIGSTOP || + the_signal == SIGTSTP || + the_signal == SIGTTIN || + the_signal == SIGTTOU || + the_signal == SIGCONT ) + the_signal = SIGKILL; + the_status = 128 + the_signal; + } + else + { + the_nature = WNATURE_SIGNALED; + the_signal = SIGKILL; + } + + requested_exit_code = WCONSTRUCT(the_nature, the_status, the_signal); + + thread->process->ExitWithCode(requested_exit_code); + } kthread_exit(); } -static int sys_kill(pid_t pid, int signum) -{ - // Protect the system idle process. - if ( !pid ) - return errno = EPERM, -1; - - // TODO: Implement that pid == -1 means all processes! - bool process_group = pid < 0 ? (pid = -pid, true) : false; - - // If we kill our own process, deliver the signal to this thread. - Thread* currentthread = CurrentThread(); - if ( currentthread->process->pid == pid ) - return currentthread->DeliverSignal(signum) ? 0 : -1; - - // TODO: Race condition: The process could be deleted while we use it. - Process* process = Process::Get(pid); - if ( !process ) - return errno = ESRCH, -1; - - // TODO: Protect init? - // TODO: Check for permission. - // TODO: Check for zombies. - - return process_group ? - process->DeliverGroupSignal(signum) ? 0 : -1 : - process->DeliverSignal(signum) ? 0 : -1; -} - -static int sys_raise(int signum) -{ - return CurrentThread()->DeliverSignal(signum) ? 0 : -1; -} - -static int sys_register_signal_handler(sighandler_t sighandler) -{ - CurrentThread()->sighandler = sighandler; - return 0; -} - void Thread::Init() { Syscall::Register(SYSCALL_EXIT_THREAD, (void*) sys_exit_thread); - Syscall::Register(SYSCALL_KILL, (void*) sys_kill); - Syscall::Register(SYSCALL_RAISE, (void*) sys_raise); - Syscall::Register(SYSCALL_REGISTER_SIGNAL_HANDLER, (void*) sys_register_signal_handler); } } // namespace Sortix diff --git a/kernel/user-timer.cpp b/kernel/user-timer.cpp index cc2d0703..c2c55a39 100644 --- a/kernel/user-timer.cpp +++ b/kernel/user-timer.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2013. + Copyright(C) Jonas 'Sortie' Termansen 2013, 2014. This file is part of Sortix. @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -138,7 +139,12 @@ static int sys_timer_getoverrun(timer_t timerid) if ( !timer ) return -1; - // TODO: This is not fully kept track of yet. + if ( (size_t) INT_MAX < timer->num_overrun_events) + return INT_MAX; + + // TODO: How does the caller reset the overrun count back to 0? Should we + // adopt the Linux semantics where it resets back to 0 after INT_MAX? + // How about signed overflow in the kernel and in the user process? return 0; } @@ -164,15 +170,14 @@ static void timer_callback(Clock* /*clock*/, Timer* timer, void* user) Process* process = user_timer->process; ScopedLock lock(&process->user_timers_lock); - size_t current_overrun = timer->num_overrun_events; // TODO: This delivery facility is insufficient! sigevent is much more // powerful than sending a simple old-school signal. - // TODO: If the last signal from last time is still being processed, we need - // to handle the sum of overrun. I'm not sure how to handle overrun - // properly, so we'll just pretend to user-space it never happens when - // it does and we do some of the bookkeeping. - (void) current_overrun; - process->DeliverSignal(user_timer->event.sigev_signo); + if ( !process->DeliverSignal(user_timer->event.sigev_signo) && + errno == ESIGPENDING ) + { + if ( timer->num_overrun_events < SIZE_MAX ) + timer->num_overrun_events++; + } } static int sys_timer_settime(timer_t timerid, int flags, diff --git a/kernel/x64/process.cpp b/kernel/x64/process.cpp index 648e6b98..98cb3885 100644 --- a/kernel/x64/process.cpp +++ b/kernel/x64/process.cpp @@ -38,10 +38,7 @@ void Process::ExecuteCPU(int argc, char** argv, int envc, char** envp, addr_t stackpos, addr_t entry, CPU::InterruptRegisters* regs) { - const uint64_t CS = 0x18; - const uint64_t DS = 0x20; - const uint64_t RPL = 0x3; - + memset(regs, 0, sizeof(*regs)); regs->rdi = argc; regs->rsi = (size_t) argv; regs->rdx = envc; @@ -49,14 +46,15 @@ void Process::ExecuteCPU(int argc, char** argv, int envc, char** envp, regs->rip = entry; regs->rsp = stackpos & ~15UL; regs->rbp = regs->rsp; - regs->cs = CS | RPL; - regs->ds = DS | RPL; - regs->ss = DS | RPL; + regs->cs = UCS | URPL; + regs->ds = UDS | URPL; + regs->ss = UDS | URPL; regs->rflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID; + regs->signal_pending = 0; } void InitializeThreadRegisters(CPU::InterruptRegisters* regs, - const tforkregs_t* requested) + const struct tfork* requested) { memset(regs, 0, sizeof(*regs)); regs->rip = requested->rip; diff --git a/kernel/x64/thread.cpp b/kernel/x64/thread.cpp index 81049c84..689da0c6 100644 --- a/kernel/x64/thread.cpp +++ b/kernel/x64/thread.cpp @@ -117,43 +117,4 @@ void SetupKernelThreadRegs(CPU::InterruptRegisters* regs, ThreadEntry entry, regs->signal_pending = 0; } -void Thread::HandleSignalFixupRegsCPU(CPU::InterruptRegisters* regs) -{ - if ( regs->InUserspace() ) - return; - regs->rip = regs->rdi; - regs->rflags = regs->rsi; - regs->rsp = regs->r8; - regs->cs = UCS | URPL; - regs->ds = UDS | URPL; - regs->ss = UDS | URPL; -} - -void Thread::HandleSignalCPU(CPU::InterruptRegisters* regs) -{ - const size_t STACK_ALIGNMENT = 16UL; - const size_t RED_ZONE_SIZE = 128UL; - regs->rsp -= RED_ZONE_SIZE; - regs->rsp &= ~(STACK_ALIGNMENT-1UL); - regs->rbp = regs->rsp; - regs->rdi = currentsignal; - regs->rip = (size_t) sighandler; - regs->rflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID; - regs->kerrno = 0; - regs->signal_pending = 0; -} - -void Thread::GotoOnSigKill(CPU::InterruptRegisters* regs) -{ - regs->rip = (unsigned long) Thread__OnSigKill; - regs->rdi = (unsigned long) this; - regs->rsp = regs->rbp = kernelstackpos + kernelstacksize; - regs->rflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID; - regs->cs = KCS | KRPL; - regs->ds = KDS | KRPL; - regs->ss = KDS | KRPL; - regs->kerrno = 0; - regs->signal_pending = 0; -} - } // namespace Sortix diff --git a/kernel/x86-family/float.cpp b/kernel/x86-family/float.cpp index cd3a0bcd..17978e80 100644 --- a/kernel/x86-family/float.cpp +++ b/kernel/x86-family/float.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2014. This file is part of Sortix. @@ -34,6 +34,7 @@ namespace Sortix { namespace Float { static Thread* fputhread; +bool fpu_is_enabled = false; static inline void InitFPU() { @@ -52,6 +53,53 @@ static inline void LoadState(const uint8_t* src) asm volatile ("fxrstor (%0)" : : "r"(src)); } +void Yield() +{ + Thread* thread = CurrentThread(); + + Interrupt::Disable(); + + bool fpu_was_enabled = fpu_is_enabled; + + // The FPU contains the registers for this thread. + if ( fputhread == thread ) + { + if ( !fpu_was_enabled ) + EnableFPU(); + SaveState(thread->fpuenvaligned); + fputhread = NULL; + DisableFPU(); + } + + // This thread has used the FPU once. + else if ( thread->fpuinitialized ) + { + // Nothing needs to be done, the FPU is owned by another thread and the + // FPU registers are already stored in the thread structure. + } + + // This thread has never used the FPU and needs its registers initialized. + else + { + if ( !fpu_was_enabled ) + EnableFPU(); + + if ( fputhread ) + SaveState(fputhread->fpuenvaligned); + + InitFPU(); + SaveState(thread->fpuenvaligned); + + if ( fputhread ) + LoadState(fputhread->fpuenvaligned); + + if ( !fpu_was_enabled ) + DisableFPU(); + } + + Interrupt::Enable(); +} + static void OnFPUAccess(CPU::InterruptRegisters* /*regs*/, void* /*user*/) { EnableFPU(); diff --git a/kernel/x86-family/float.h b/kernel/x86-family/float.h index e581dbc5..a8810145 100644 --- a/kernel/x86-family/float.h +++ b/kernel/x86-family/float.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2014. This file is part of Sortix. @@ -31,16 +31,21 @@ class Thread; namespace Float { +extern bool fpu_is_enabled; + void Init(); void NofityTaskExit(Thread* thread); +void Yield(); static inline void EnableFPU() { asm volatile ("clts"); + fpu_is_enabled = true; } static inline void DisableFPU() { + fpu_is_enabled = false; unsigned long cr0; asm volatile ("mov %%cr0, %0" : "=r"(cr0)); cr0 |= 1UL<<3UL; diff --git a/kernel/x86-family/idt.cpp b/kernel/x86-family/idt.cpp index a1e32f98..73117c1e 100644 --- a/kernel/x86-family/idt.cpp +++ b/kernel/x86-family/idt.cpp @@ -30,14 +30,6 @@ namespace Sortix { namespace IDT { -static struct idt_entry idt_entries[256]; - -void Init() -{ - memset(&idt_entries, 0, sizeof(idt_entries)); - Set(idt_entries, 256); -} - void Set(struct idt_entry* table, size_t length) { size_t limit = sizeof(idt_entry) * length - 1; @@ -69,10 +61,5 @@ void SetEntry(struct idt_entry* entry, uintptr_t handler, uint16_t selector, uin #endif } -void SetEntry(uint8_t num, uintptr_t handler, uint16_t selector, uint8_t flags, uint8_t ist) -{ - SetEntry(idt_entries + num, handler, selector, flags, ist); -} - } // namespace IDT } // namespace Sortix diff --git a/kernel/x86-family/interrupt.cpp b/kernel/x86-family/interrupt.cpp index 0e17d88b..8cd8e082 100644 --- a/kernel/x86-family/interrupt.cpp +++ b/kernel/x86-family/interrupt.cpp @@ -230,13 +230,13 @@ void Init() RegisterRawHandler(47, irq15, false, false); RegisterRawHandler(128, syscall_handler, true, true); RegisterRawHandler(129, yield_cpu_handler, true, false); - RegisterRawHandler(130, isr130, true, false); - RegisterRawHandler(131, isr131, true, false); + RegisterRawHandler(130, isr130, true, true); + RegisterRawHandler(131, isr131, true, true); RegisterRawHandler(132, thread_exit_handler, true, false); RegisterHandler(129, Scheduler::InterruptYieldCPU, NULL); - RegisterHandler(130, Signal::Dispatch, NULL); - RegisterHandler(131, Signal::Return, NULL); + RegisterHandler(130, Signal::DispatchHandler, NULL); + RegisterHandler(131, Signal::ReturnHandler, NULL); RegisterHandler(132, Scheduler::ThreadExitCPU, NULL); IDT::Set(interrupt_table, NUM_INTERRUPTS); @@ -313,6 +313,19 @@ void UserCrashHandler(CPU::InterruptRegisters* regs) // Execute this crash handler with preemption on. Interrupt::Enable(); + // TODO: Also send signals for other types of user-space crashes. + if ( regs->int_no == 14 /* Page fault */ ) + { + struct sigaction* act = &CurrentProcess()->signal_actions[SIGSEGV]; + kthread_mutex_lock(&CurrentProcess()->signal_lock); + bool handled = act->sa_handler != SIG_DFL && act->sa_handler != SIG_IGN; + if ( handled ) + CurrentThread()->DeliverSignalUnlocked(SIGSEGV); + kthread_mutex_unlock(&CurrentProcess()->signal_lock); + if ( handled ) + return Signal::DispatchHandler(regs, NULL); + } + // Walk and print the stack frames if this is a debug build. if ( CALLTRACE_USER ) CrashCalltrace(regs); @@ -330,13 +343,10 @@ void UserCrashHandler(CPU::InterruptRegisters* regs) // Exit the process with the right error code. // TODO: Send a SIGINT, SIGBUS, or whatever instead. - CurrentProcess()->Exit(139); - - // TODO: Is it strictly needed or even desirable to disable preemption here? - Interrupt::Disable(); + CurrentProcess()->ExitThroughSignal(SIGSEGV); // Deliver signals to this thread so it can exit correctly. - Signal::Dispatch(regs); + Signal::DispatchHandler(regs, NULL); } extern "C" void interrupt_handler(CPU::InterruptRegisters* regs) diff --git a/kernel/x86/kthread.S b/kernel/x86/kthread.S index 7849e503..336585c2 100644 --- a/kernel/x86/kthread.S +++ b/kernel/x86/kthread.S @@ -99,11 +99,3 @@ asm_call_BootstrapKernelThread: call BootstrapKernelThread # BootstrapKernelThread is noreturn, no need for code here. .size asm_call_BootstrapKernelThread, . - asm_call_BootstrapKernelThread - -.global asm_call_Thread__OnSigKill -.type asm_call_Thread__OnSigKill, @function -asm_call_Thread__OnSigKill: - pushl %edi - call Thread__OnSigKill - # Thread__OnSigKill is noreturn, no need for code here. -.size asm_call_Thread__OnSigKill, . - asm_call_Thread__OnSigKill diff --git a/kernel/x86/process.cpp b/kernel/x86/process.cpp index 18b641f2..5b6b5bd7 100644 --- a/kernel/x86/process.cpp +++ b/kernel/x86/process.cpp @@ -38,6 +38,7 @@ void Process::ExecuteCPU(int argc, char** argv, int envc, char** envp, addr_t stackpos, addr_t entry, CPU::InterruptRegisters* regs) { + memset(regs, 0, sizeof(*regs)); regs->eax = argc; regs->ebx = (size_t) argv; regs->edx = envc; @@ -49,10 +50,11 @@ void Process::ExecuteCPU(int argc, char** argv, int envc, char** envp, regs->ds = UDS | URPL; regs->ss = UDS | URPL; regs->eflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID; + regs->signal_pending = 0; } void InitializeThreadRegisters(CPU::InterruptRegisters* regs, - const tforkregs_t* requested) + const struct tfork* requested) { memset(regs, 0, sizeof(*regs)); regs->eip = requested->eip; diff --git a/kernel/x86/thread.cpp b/kernel/x86/thread.cpp index 2d8b4187..304eecd0 100644 --- a/kernel/x86/thread.cpp +++ b/kernel/x86/thread.cpp @@ -96,50 +96,4 @@ void SetupKernelThreadRegs(CPU::InterruptRegisters* regs, ThreadEntry entry, regs->signal_pending = 0; } -void Thread::HandleSignalFixupRegsCPU(CPU::InterruptRegisters* regs) -{ - if ( regs->InUserspace() ) - return; - uint32_t* params = (uint32_t*) regs->ebx; - regs->eip = params[0]; - regs->eflags = params[2]; - regs->esp = params[3]; - regs->cs = UCS | URPL; - regs->ds = UDS | URPL; - regs->ss = UDS | URPL; -} - -void Thread::HandleSignalCPU(CPU::InterruptRegisters* regs) -{ - const size_t STACK_ALIGNMENT = 16UL; - const size_t RED_ZONE_SIZE = 128UL; - regs->esp -= RED_ZONE_SIZE; - regs->esp &= ~(STACK_ALIGNMENT-1UL); - regs->ebp = regs->esp; - regs->edi = currentsignal; - regs->eip = (size_t) sighandler; - regs->eflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID; - regs->kerrno = 0; - regs->signal_pending = 0; -} - -extern "C" void asm_call_Thread__OnSigKill(void); - -void Thread::GotoOnSigKill(CPU::InterruptRegisters* regs) -{ - regs->eip = (unsigned long) asm_call_Thread__OnSigKill; - regs->edi = (unsigned long) this; - // TODO: HACK: The -256 is because if we are jumping to the safe stack - // we currently are on, this may not be fully supported by interrupt.s - // that is quite aware of this (but isn't perfect). If our destination - // is further down the stack, then we are probably safe. - regs->esp = regs->ebp = kernelstackpos + kernelstacksize - 256; - regs->eflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID; - regs->cs = KCS | KRPL; - regs->ds = KDS | KRPL; - regs->ss = KDS | KRPL; - regs->kerrno = 0; - regs->signal_pending = 0; -} - } // namespace Sortix diff --git a/libc/Makefile b/libc/Makefile index 954163d4..f8631b7f 100644 --- a/libc/Makefile +++ b/libc/Makefile @@ -374,14 +374,15 @@ pwd/setpwent.o \ sched/sched_yield.o \ signal/kill.o \ signal/killpg.o \ +signal/psiginfo.o \ signal/psignal.o \ signal/raise.o \ signal/sigaction.o \ -signal/SIG_DFL.o \ -signal/SIG_ERR.o \ -signal/SIG_IGN.o \ +signal/sigaltstack.o \ signal/signal.o \ +signal/sigpending.o \ signal/sigprocmask.o \ +signal/sigsuspend.o \ stdio/fcloseall.o \ stdio/fdio.o \ stdio/fgetpos.o \ diff --git a/libc/include/setjmp.h b/libc/include/setjmp.h index b356dcc3..dbba193d 100644 --- a/libc/include/setjmp.h +++ b/libc/include/setjmp.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2012. + Copyright(C) Jonas 'Sortie' Termansen 2012, 2014. This file is part of the Sortix C Library. @@ -27,18 +27,24 @@ #include +#include + __BEGIN_DECLS #if defined(__x86_64__) -typedef unsigned long jmp_buf[8]; +typedef unsigned long sigjmp_buf[8 + 1 + __SIGSET_NUM_SIGNALS / (sizeof(unsigned long int) * 8)]; #elif defined(__i386__) -typedef unsigned long jmp_buf[6]; +typedef unsigned long sigjmp_buf[6 + 1 + __SIGSET_NUM_SIGNALS / (sizeof(unsigned long int) * 8)]; #else -#error "You need to implement jmp_buf on your CPU" +#error "You need to implement sigjmp_buf on your CPU" #endif -void longjmp(jmp_buf env, int val); -int setjmp(jmp_buf env); +typedef sigjmp_buf jmp_buf; + +void longjmp(jmp_buf, int); +int setjmp(jmp_buf); +void siglongjmp(sigjmp_buf, int); +int sigsetjmp(sigjmp_buf, int); __END_DECLS diff --git a/libc/include/signal.h b/libc/include/signal.h index eb70a84c..0dc7b1d0 100644 --- a/libc/include/signal.h +++ b/libc/include/signal.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014. This file is part of the Sortix C Library. @@ -18,7 +18,7 @@ along with the Sortix C Library. If not, see . signal.h - Signals. + Signal API. *******************************************************************************/ @@ -33,7 +33,17 @@ #include <__/pthread.h> #endif +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include __BEGIN_DECLS @@ -53,12 +63,6 @@ typedef __pid_t pid_t; #include #endif -/* TODO: POSIX says this header declares struct timespec, but not time_t... */ -#ifndef __time_t_defined -#define __time_t_defined -typedef __time_t time_t; -#endif - #if __STDC_HOSTED__ #ifndef __pthread_attr_t_defined @@ -73,134 +77,12 @@ typedef __pthread_t pthread_t; #endif -__END_DECLS +#define NSIG __SIG_MAX_NUM -#include -#include - -__BEGIN_DECLS - -/* TODO: Should this be volatile? It isn't on Linux. */ typedef int sig_atomic_t; -typedef void (*sighandler_t)(int); - -void SIG_DFL(int); -void SIG_IGN(int); -void SIG_ERR(int); - -#define SIG_DFL SIG_DFL -#define SIG_IGN SIG_IGN -#define SIG_ERR SIG_ERR -/* TODO: POSIX specifies a obsolecent SIG_HOLD here. */ - -union sigval -{ - int sival_int; - void* sival_ptr; -}; - -struct sigevent -{ - int sigev_notify; - int sigev_signo; - union sigval sigev_value; - void (*sigev_notify_function)(union sigval); - /*pthread_attr_t* sigev_notify_attributes;*/ -}; - -#define SIGEV_NONE 0 -#define SIGEV_SIGNAL 1 -#define SIGEV_THREAD 2 - -/* TODO: SIGRTMIN */ -/* TODO: SIGRTMAX */ - -typedef struct -{ - int si_signo; - int si_code; - int si_errno; - pid_t si_pid; - uid_t si_uid; - void* si_addr; - int si_status; - union sigval si_value; -} siginfo_t; - -#define ILL_ILLOPC 1 -#define ILL_ILLOPN 2 -#define ILL_ILLADR 3 -#define ILL_ILLTRP 4 -#define ILL_PRVOPC 5 -#define ILL_PRVREG 6 -#define ILL_COPROC 7 -#define ILL_BADSTK 8 -#define FPE_INTDIV 9 -#define FPE_INTOVF 10 -#define FPE_FLTDIV 11 -#define FPE_FLTOVF 12 -#define FPE_FLTUND 13 -#define FPE_FLTRES 14 -#define FPE_FLTINV 15 -#define FPE_FLTSUB 16 -#define SEGV_MAPERR 17 -#define SEGV_ACCERR 18 -#define BUS_ADRALN 19 -#define BUS_ADRERR 20 -#define BUS_OBJERR 21 -#define TRAP_BRKPT 22 -#define TRAP_TRACE 23 -#define CLD_EXITED 24 -#define CLD_KILLED 25 -#define CLD_DUMPED 26 -#define CLD_TRAPPED 27 -#define CLD_STOPPED 29 -#define CLD_CONTINUED 30 -#define SI_USER 31 -#define SI_QUEUE 32 -#define SI_TIMER 33 -#define SI_ASYNCIO 34 -#define SI_MSGQ 35 - -struct sigaction -{ - void (*sa_handler)(int); - void (*sa_sigaction)(int, siginfo_t*, void*); - sigset_t sa_mask; - int sa_flags; -}; - -#define SA_NOCLDSTOP (1<<0) -#define SA_ONSTACK (1<<1) -#define SA_RESETHAND (1<<2) -#define SA_RESTART (1<<3) -#define SA_SIGINFO (1<<4) -#define SA_NOCLDWAIT (1<<5) -#define SA_NODEFER (1<<6) -#define SS_ONSTACK (1<<7) -#define SS_DISABLE (1<<8) -/* TODO: MINSIGSTKSZ */ -/* TODO: SIGSTKSZ */ - -/* TODO: mcontext_t */ -typedef int mcontext_t; - -typedef struct -{ - void* ss_sp; - size_t ss_size; - int ss_flags; -} stack_t; - -typedef struct __ucontext ucontext_t; -struct __ucontext -{ - ucontext_t* uc_link; - sigset_t uc_sigmask; - stack_t uc_stack; - mcontext_t uc_mcontext; -}; +#define MINSIGSTKSZ 2048 +#define SIGSTKSZ 8192 int kill(pid_t, int); int killpg(pid_t, int); @@ -216,25 +98,19 @@ int sigandset(sigset_t*, const sigset_t*, const sigset_t*); int sigdelset(sigset_t*, int); int sigemptyset(sigset_t*); int sigfillset(sigset_t*); -/* TODO: sighold (obsolescent XSI). */ -/* TODO: sigignore (obsolescent XSI). */ -/* TODO: siginterrupt (obsolescent XSI). */ int sigisemptyset(const sigset_t*); int sigismember(const sigset_t*, int); -sighandler_t signal(int, sighandler_t); +void (*signal(int, void (*)(int)))(int); int signotset(sigset_t*, const sigset_t*); int sigorset(sigset_t*, const sigset_t*, const sigset_t*); -/* TODO: sigpause (obsolescent XSI). */ int sigpending(sigset_t*); int sigprocmask(int, const sigset_t* __restrict, sigset_t* __restrict); -int sigqueue(pid_t, int, const union sigval); -/* TODO: sigrelse (obsolescent XSI). */ -/* TODO: sigset (obsolescent XSI). */ +/* TODO: int sigqueue(pid_t, int, const union sigval); */ int sigsuspend(const sigset_t*); -int sigtimedwait(const sigset_t* __restrict, siginfo_t* __restrict, - const struct timespec* __restrict); -int sigwait(const sigset_t* __restrict, int* __restrict); -int sigwaitinfo(const sigset_t* __restrict, siginfo_t* vrestrict); +/* TODO: int sigtimedwait(const sigset_t* __restrict, siginfo_t* __restrict, + const struct timespec* __restrict); */ +/* TODO: int sigwait(const sigset_t* __restrict, int* __restrict); */ +/* TODO: int sigwaitinfo(const sigset_t* __restrict, siginfo_t* __restrict); */ __END_DECLS diff --git a/libc/include/sys/wait.h b/libc/include/sys/wait.h index 93ca3d44..5ed99414 100644 --- a/libc/include/sys/wait.h +++ b/libc/include/sys/wait.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2014. This file is part of the Sortix C Library. @@ -22,10 +22,8 @@ *******************************************************************************/ -// TODO: Make this header comply with POSIX-1.2008 - -#ifndef _SYS_WAIT_H -#define _SYS_WAIT_H 1 +#ifndef INCLUDE_SYS_WAIT_H +#define INCLUDE_SYS_WAIT_H 1 #include @@ -40,12 +38,8 @@ __BEGIN_DECLS typedef __pid_t pid_t; #endif -/* TODO: These are not implemented in sortix libc yet. */ -#if 0 -int waitid(idtype_t, id_t, siginfo_t*, int); -#endif - pid_t wait(int* stat_loc); +/* TODO: int waitid(idtype_t, id_t, siginfo_t*, int); */ pid_t waitpid(pid_t pid, int *stat_loc, int options); __END_DECLS diff --git a/libc/include/unistd.h b/libc/include/unistd.h index ad667b0e..9627e4fe 100644 --- a/libc/include/unistd.h +++ b/libc/include/unistd.h @@ -406,7 +406,7 @@ int exit_thread(int, int, const struct exit_thread*); int memstat(size_t* memused, size_t* memtotal); int mkpartition(int fd, off_t start, off_t length); pid_t sfork(int flags); -pid_t tfork(int flags, tforkregs_t* regs); +pid_t tfork(int flags, struct tfork* regs); size_t writeall(int fd, const void* buf, size_t count); size_t writeleast(int fd, const void* buf, size_t least, size_t max); #endif diff --git a/libc/init/init.cpp b/libc/init/init.cpp index 685ff7e8..44a7eadc 100644 --- a/libc/init/init.cpp +++ b/libc/init/init.cpp @@ -31,7 +31,6 @@ extern "C" { char* program_invocation_name; } extern "C" { char* program_invocation_short_name; } extern "C" void init_stdio(); -extern "C" void init_signal(); static char* find_last_elem(char* str) { @@ -53,9 +52,6 @@ extern "C" void initialize_standard_library(int argc, char* argv[]) program_invocation_name = (char*) argv0; program_invocation_short_name = find_last_elem((char*) argv0); - // It's probably best to initialize the Unix signals early on. - init_signal(); - // Initialize pthreads and stuff like thread-local storage. pthread_initialize(); diff --git a/libc/signal/SIG_DFL.cpp b/libc/signal/SIG_DFL.cpp deleted file mode 100644 index 7c64a7c4..00000000 --- a/libc/signal/SIG_DFL.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. - - This file is part of the Sortix C Library. - - The Sortix C Library is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or (at your - option) any later version. - - The Sortix C Library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with the Sortix C Library. If not, see . - - signal/SIG_DFL.cpp - Default signal handler. - -*******************************************************************************/ - -#include -#include - -static void Core(int signum) -{ - exit(128 + signum); -} - -extern "C" void SIG_DFL(int signum) -{ - if ( signum == SIGHUP ) { exit(128 + signum); } else - if ( signum == SIGINT ) { exit(128 + signum); } else - if ( signum == SIGQUIT ) { Core(signum); } else - if ( signum == SIGTRAP ) { Core(signum); } else - if ( signum == SIGABRT ) { Core(signum); } else - if ( signum == SIGEMT ) { Core(signum); } else - if ( signum == SIGFPE ) { Core(signum); } else - if ( signum == SIGKILL ) { exit(128 + signum); } else - if ( signum == SIGBUS ) { Core(signum); } else - if ( signum == SIGSEGV ) { Core(signum); } else - if ( signum == SIGSYS ) { Core(signum); } else - if ( signum == SIGPIPE ) { exit(128 + signum); } else - if ( signum == SIGALRM ) { exit(128 + signum); } else - if ( signum == SIGTERM ) { exit(128 + signum); } else - if ( signum == SIGUSR1 ) { exit(128 + signum); } else - if ( signum == SIGUSR2 ) { exit(128 + signum); } else - if ( signum == SIGCHLD ) { /* Ignore this signal. */ } else - if ( signum == SIGPWR ) { /* Ignore this signal. */ } else - if ( signum == SIGWINCH ) { /* Ignore this signal. */ } else - if ( signum == SIGURG ) { /* Ignore this signal. */ } else - if ( signum == SIGCONT ) { /* Ignore this signal. */ } else - if ( signum == SIGVTALRM ) { /* Ignore this signal. */ } else - if ( signum == SIGXCPU ) { Core(signum); } else - if ( signum == SIGXFSZ ) { Core(signum); } else - if ( signum == SIGWAITING ) { /* Ignore this signal. */ } else - if ( signum == SIGLWP ) { /* Ignore this signal. */ } else - if ( signum == SIGAIO ) { /* Ignore this signal. */ } else - { /* Ignore this signal. */ } -} diff --git a/libc/signal/SIG_IGN.cpp b/libc/signal/psiginfo.cpp similarity index 81% rename from libc/signal/SIG_IGN.cpp rename to libc/signal/psiginfo.cpp index c276d83c..465077f7 100644 --- a/libc/signal/SIG_IGN.cpp +++ b/libc/signal/psiginfo.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2014. This file is part of the Sortix C Library. @@ -17,15 +17,14 @@ You should have received a copy of the GNU Lesser General Public License along with the Sortix C Library. If not, see . - signal/SIG_IGN.cpp - Ignore signal. + signal/psiginfo.cpp + Print signal error condition to stderr. *******************************************************************************/ #include -#include -extern "C" void SIG_IGN(int /*signum*/) +extern "C" void psiginfo(const siginfo_t* si, const char* message) { - + psignal(si->si_signo, message); } diff --git a/libc/signal/sigaction.cpp b/libc/signal/sigaction.cpp index 78487a95..087787c9 100644 --- a/libc/signal/sigaction.cpp +++ b/libc/signal/sigaction.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013. + Copyright(C) Jonas 'Sortie' Termansen 2013. This file is part of the Sortix C Library. @@ -22,53 +22,19 @@ *******************************************************************************/ -#include +#include + #include -const int MAX_SIGNALS = 128; -extern sighandler_t handlers[MAX_SIGNALS]; +DEFN_SYSCALL3(int, sys_sigaction, SYSCALL_SIGACTION, + int, + const struct sigaction*, + struct sigaction*); -// TODO: Actually implement the sigaction interface for real. extern "C" -int sigaction(int signum, const struct sigaction* restrict act, +int sigaction(int signum, + const struct sigaction* restrict act, struct sigaction* restrict oldact) { - if ( !act ) - { - // TODO: set oldact->sa_mask here? - oldact->sa_sigaction = NULL; - oldact->sa_handler = handlers[signum]; - oldact->sa_flags = 0; - return 0; - } - int understood_flags = SA_SIGINFO | SA_RESTART; - if ( act->sa_flags & ~understood_flags ) - { - fprintf(stderr, "%s:%u sigaction with unsupported flags 0x%x, ignoring " - "hoping they aren't needed.\n", __FILE__, __LINE__, - act->sa_flags & ~understood_flags); - } - if ( act->sa_flags & SA_RESTART ) - /* TODO: Actually implement this. Signals are a bit rare on Sortix right - now, so it doesn't matter much that system calls don't restart - on Sortix, so pretend that they do. */ {}; - if ( act->sa_flags & SA_SIGINFO ) - { - fprintf(stderr, "%s:%u sigaction with unsupported SA_SIGINFO, ignoring " - "hoping the signal never happens.\n", __FILE__, - __LINE__); - return 0; - } - sighandler_t new_handler = act->sa_handler; - sighandler_t old_handler = signal(signum, new_handler); - if ( old_handler == SIG_ERR ) - return -1; - if ( oldact ) - { - // TODO: set oldact->sa_mask here? - oldact->sa_sigaction = NULL; - oldact->sa_handler = old_handler; - oldact->sa_flags = 0; - } - return 0; + return sys_sigaction(signum, act, oldact); } diff --git a/libc/signal/sigaltstack.cpp b/libc/signal/sigaltstack.cpp new file mode 100644 index 00000000..35e7dfb1 --- /dev/null +++ b/libc/signal/sigaltstack.cpp @@ -0,0 +1,34 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + This file is part of the Sortix C Library. + + The Sortix C Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + The Sortix C Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with the Sortix C Library. If not, see . + + signal/sigaltstack.cpp + Sets the stack used during signal handling. + +*******************************************************************************/ + +#include + +#include + +DEFN_SYSCALL2(int, sys_sigaltstack, SYSCALL_SIGALTSTACK, const stack_t*, stack_t*); + +extern "C" int sigaltstack(const stack_t* restrict ss, stack_t* restrict oss) +{ + return sys_sigaltstack(ss, oss); +} diff --git a/libc/signal/signal.cpp b/libc/signal/signal.cpp index d13a88e5..3bf6c9e2 100644 --- a/libc/signal/signal.cpp +++ b/libc/signal/signal.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2013, 2014. This file is part of the Sortix C Library. @@ -18,38 +18,35 @@ along with the Sortix C Library. If not, see . signal/signal.cpp - Handles the good old unix signals. + Configure and retrieve a signal handler. *******************************************************************************/ -#include -#include - #include +#include -const int MAX_SIGNALS = 128; -sighandler_t handlers[MAX_SIGNALS]; - -extern "C" void SignalHandlerAssembly(int signum); -extern "C" void SignalHandler(int signum) +extern "C" void (*signal(int signum, void (*handler)(int)))(int) { - if ( 0 <= signum && signum < (int) MAX_SIGNALS ) - handlers[signum](signum); -} - -DEFN_SYSCALL1(int, sys_register_signal_handler, SYSCALL_REGISTER_SIGNAL_HANDLER, sighandler_t); - -extern "C" void init_signal() -{ - for ( int i = 0; i < MAX_SIGNALS; i++ ) - handlers[i] = SIG_DFL; - - // Tell the kernel which function we want called upon signals. - sys_register_signal_handler(&SignalHandlerAssembly); -} - -extern "C" sighandler_t signal(int signum, sighandler_t handler) -{ - if ( signum < 0 || MAX_SIGNALS <= signum ) { return SIG_ERR; } - return handlers[signum] = handler; + // Create a structure describing the new handler. + struct sigaction newact; + memset(&newact, 0, sizeof(newact)); + sigemptyset(&newact.sa_mask); + newact.sa_handler = handler; + newact.sa_flags = SA_RESTART; + + // Register the new handler and atomically get the old. + struct sigaction oldact; + if ( sigaction(signum, &newact, &oldact) != 0 ) + return SIG_ERR; + + // We can't return the old handler properly if it's SA_SIGINFO or SA_COOKIE, + // unless it's the common SIG_IGN or SIG_DFL handlers. Let's just say to the + // caller that it's SIG_DFL and assume that they'll be using sigaction + // instead if they wish to restore an old handler. + if ( (oldact.sa_flags & (SA_SIGINFO | SA_COOKIE)) && + oldact.sa_handler != SIG_IGN && + oldact.sa_handler != SIG_DFL ) + return SIG_DFL; + + return oldact.sa_handler; } diff --git a/libc/signal/SIG_ERR.cpp b/libc/signal/sigpending.cpp similarity index 77% rename from libc/signal/SIG_ERR.cpp rename to libc/signal/sigpending.cpp index 67ea1193..2fa42edd 100644 --- a/libc/signal/SIG_ERR.cpp +++ b/libc/signal/sigpending.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2013. This file is part of the Sortix C Library. @@ -17,15 +17,18 @@ You should have received a copy of the GNU Lesser General Public License along with the Sortix C Library. If not, see . - signal/SIG_ERR.cpp - Abort on signal. + signal/sigpending.cpp + Get the set of pending signals. *******************************************************************************/ -#include -#include +#include -extern "C" void SIG_ERR(int /*signum*/) +#include + +DEFN_SYSCALL1(int, sys_sigpending, SYSCALL_SIGPENDING, sigset_t*); + +extern "C" int sigpending(sigset_t* set) { - abort(); + return sys_sigpending(set); } diff --git a/libc/signal/sigprocmask.cpp b/libc/signal/sigprocmask.cpp index 925a98d2..bed6d425 100644 --- a/libc/signal/sigprocmask.cpp +++ b/libc/signal/sigprocmask.cpp @@ -22,13 +22,13 @@ *******************************************************************************/ +#include + #include -#include + +DEFN_SYSCALL3(int, sys_sigprocmask, SYSCALL_SIGPROCMASK, int, const sigset_t*, sigset_t*); extern "C" int sigprocmask(int how, const sigset_t* set, sigset_t* oldset) { - (void) how; - (void) set; - (void) oldset; - return 0; + return sys_sigprocmask(how, set, oldset); } diff --git a/libc/signal/sigsuspend.cpp b/libc/signal/sigsuspend.cpp new file mode 100644 index 00000000..e3d76db5 --- /dev/null +++ b/libc/signal/sigsuspend.cpp @@ -0,0 +1,34 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + This file is part of the Sortix C Library. + + The Sortix C Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + The Sortix C Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with the Sortix C Library. If not, see . + + signal/sigsuspend.cpp + Wait until a signal occurs. + +*******************************************************************************/ + +#include + +#include + +DEFN_SYSCALL1(int, sys_sigsuspend, SYSCALL_SIGSUSPEND, const sigset_t*); + +extern "C" int sigsuspend(const sigset_t* set) +{ + return sys_sigsuspend(set); +} diff --git a/libc/stdlib/abort.cpp b/libc/stdlib/abort.cpp index afac3ead..0524d778 100644 --- a/libc/stdlib/abort.cpp +++ b/libc/stdlib/abort.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014. This file is part of the Sortix C Library. @@ -23,9 +23,12 @@ *******************************************************************************/ #include +#include #include +#include #include +#include #if defined(__is_sortix_kernel) @@ -42,19 +45,30 @@ extern "C" void abort(void) extern "C" void abort(void) { struct stat st; - if ( getenv("LIBC_DEBUG_CALLTRACE") || stat("/etc/calltrace", &st) == 0 ) + if ( stat("/etc/calltrace", &st) == 0 ) calltrace(); - if ( getenv("LIBC_DEBUG_LOOP") || stat("/etc/calltrace_loop", &st) == 0 ) + if ( stat("/etc/calltrace_loop", &st) == 0 ) while ( true ); - // TODO: Send SIGABRT instead! - _Exit(128 + 6); + + sigset_t set_of_sigabrt; + sigemptyset(&set_of_sigabrt); + sigaddset(&set_of_sigabrt, SIGABRT); + sigprocmask(SIG_UNBLOCK, &set_of_sigabrt, NULL); + + raise(SIGABRT); + + int exit_code = WCONSTRUCT(WNATURE_SIGNALED, 128 + SIGABRT, SIGABRT); + int exit_flags = EXIT_THREAD_PROCESS | EXIT_THREAD_DUMP_CORE; + exit_thread(exit_code, exit_flags, NULL); + + __builtin_unreachable(); } #else extern "C" void abort(void) { - while ( true ) { }; + while ( true ) { } __builtin_unreachable(); } diff --git a/libc/stdlib/exit.cpp b/libc/stdlib/exit.cpp index 5e9bdec2..a72a3780 100644 --- a/libc/stdlib/exit.cpp +++ b/libc/stdlib/exit.cpp @@ -30,15 +30,20 @@ extern "C" { struct exit_handler* __exit_handler_stack = NULL; } -static pthread_mutex_t exit_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t exit_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; +static bool currently_exiting = false; extern "C" void exit(int status) { - // Only allow a single thread to do the exit cleanup. If somehow the cleanup - // code calls exit, then we'll self-destruct. If multiple threads attempt to - // call exit, then we'll destroy the ones that got here too late. - if ( pthread_mutex_trylock(&exit_lock) != 0 ) - exit_thread(status, 0, NULL); + // It's undefined behavior to call this function more than once: If more + // than one thread calls the function we'll wait until the process dies. + pthread_mutex_lock(&exit_lock); + + // It's undefined behavior to call this function more than once: If a + // cleanup function calls this function we'll self-destruct immediately. + if ( currently_exiting ) + _Exit(status); + currently_exiting = true; while ( __exit_handler_stack ) { diff --git a/libc/string/strsignal.cpp b/libc/string/strsignal.cpp index dba917f8..255c5a6a 100644 --- a/libc/string/strsignal.cpp +++ b/libc/string/strsignal.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2012. + Copyright(C) Jonas 'Sortie' Termansen 2012, 2014. This file is part of the Sortix C Library. @@ -30,40 +30,41 @@ extern "C" const char* sortix_strsignal(int signum) { switch ( signum ) { - case SIGHUP: return "SIGHUP"; - case SIGINT: return "SIGINT"; - case SIGQUIT: return "SIGQUIT"; - case SIGILL: return "SIGILL"; - case SIGTRAP: return "SIGTRAP"; - case SIGABRT: return "SIGABRT"; - case SIGEMT: return "SIGEMT"; - case SIGFPE: return "SIGFPE"; - case SIGKILL: return "SIGKILL"; - case SIGBUS: return "SIGBUS"; - case SIGSEGV: return "SYSSEGV"; - case SIGSYS: return "SIGSYS"; - case SIGPIPE: return "SIGPIPE"; - case SIGALRM: return "SIGALRM"; - case SIGTERM: return "SIGTERM"; - case SIGUSR1: return "SIGUSR1"; - case SIGUSR2: return "SIGUSR2"; - case SIGCHLD: return "SIGCHLD"; - case SIGPWR: return "SIGPWR"; - case SIGWINCH: return "SIGWINCH"; - case SIGURG: return "SIGURG"; - case SIGSTOP: return "SIGSTOP"; - case SIGTSTP: return "SIGTSTP"; - case SIGCONT: return "SIGCONT"; - case SIGTTIN: return "SIGTTIN"; - case SIGTTOU: return "SIGTTOU"; - case SIGVTALRM: return "SIGVTALRM"; - case SIGXCPU: return "SIGXCPU"; - case SIGXFSZ: return "SIGXFSZ"; - case SIGWAITING: return "SIGWAITING"; - case SIGLWP: return "SIGLWP"; - case SIGAIO: return "SIGAIO"; - default: return "Unknown signal value"; + case SIGHUP: return "Hangup"; + case SIGINT: return "Interrupt"; + case SIGQUIT: return "Quit"; + case SIGILL: return "Illegal instruction"; + case SIGTRAP: return "Trace/breakpoint trap"; + case SIGABRT: return "Aborted"; + case SIGBUS: return "Bus Error"; + case SIGFPE: return "Floating point exception"; + case SIGKILL: return "Killed"; + case SIGUSR1: return "User defined signal 1"; + case SIGSEGV: return "Segmentation fault"; + case SIGUSR2: return "User defined signal 2"; + case SIGPIPE: return "Broken pipe"; + case SIGALRM: return "Alarm clock"; + case SIGTERM: return "Terminated"; + case SIGSYS: return "Bad system call"; + case SIGCHLD: return "Child exited"; + case SIGCONT: return "Continued"; + case SIGSTOP: return "Stopped (signal)"; + case SIGTSTP: return "Stopped"; + case SIGTTIN: return "Stopped (tty input)"; + case SIGTTOU: return "Stopped (tty output)"; + case SIGURG: return "Urgent I/O condition"; + case SIGXCPU: return "CPU time limit exceeded"; + case SIGXFSZ: return "File size limit exceeded"; + case SIGVTALRM: return "Virtual timer expired"; + case SIGPWR: return "Power Fail/Restart"; + case SIGWINCH: return "Window changed"; + default: break; } + + if ( SIGRTMIN <= signum && signum <= SIGRTMAX ) + return "Real-time signal"; + + return "Unknown signal value"; } extern "C" char* strsignal(int signum) diff --git a/libc/unistd/_exit.cpp b/libc/unistd/_exit.cpp index 09eef7da..69414c3b 100644 --- a/libc/unistd/_exit.cpp +++ b/libc/unistd/_exit.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2014 This file is part of the Sortix C Library. @@ -22,13 +22,13 @@ *******************************************************************************/ -#include -#include +#include -DEFN_SYSCALL1(int, sys_exit, SYSCALL_EXIT, int); +#include extern "C" void _exit(int status) { - sys_exit(status); + int exit_code = WCONSTRUCT(WNATURE_EXITED, status, 0); + exit_thread(exit_code, EXIT_THREAD_PROCESS, NULL); __builtin_unreachable(); } diff --git a/libc/unistd/sfork.cpp b/libc/unistd/sfork.cpp index c8615377..4161cdf5 100644 --- a/libc/unistd/sfork.cpp +++ b/libc/unistd/sfork.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2014. This file is part of the Sortix C Library. @@ -22,11 +22,18 @@ *******************************************************************************/ +#include +#include #include -extern "C" pid_t __call_tfork_with_regs(int flags); +extern "C" pid_t __sfork(int flags, struct tfork* regs); extern "C" pid_t sfork(int flags) { - return __call_tfork_with_regs(flags); + struct tfork regs; + memset(®s, 0, sizeof(regs)); + regs.altstack.ss_flags = SS_DISABLE; + sigprocmask(SIG_SETMASK, NULL, ®s.sigmask); + regs.altstack.ss_flags = SS_DISABLE; + return __sfork(flags, ®s); } diff --git a/libc/unistd/sysconf.cpp b/libc/unistd/sysconf.cpp index 83abff0d..c37694b0 100644 --- a/libc/unistd/sysconf.cpp +++ b/libc/unistd/sysconf.cpp @@ -24,6 +24,7 @@ #include #include +#include #include extern "C" long sysconf(int name) @@ -34,6 +35,7 @@ extern "C" long sysconf(int name) case _SC_PAGESIZE: case _SC_PAGE_SIZE: return getpagesize(); case _SC_OPEN_MAX: return 0x10000; + case _SC_RTSIG_MAX: return (SIGRTMAX+1) - SIGRTMIN; default: fprintf(stderr, "%s:%u warning: %s(%i) is unsupported\n", __FILE__, __LINE__, __func__, name); diff --git a/libc/unistd/tfork.cpp b/libc/unistd/tfork.cpp index 5723ea95..96d26cd3 100644 --- a/libc/unistd/tfork.cpp +++ b/libc/unistd/tfork.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2014. This file is part of the Sortix C Library. @@ -25,9 +25,9 @@ #include #include -DEFN_SYSCALL2(pid_t, sys_tfork, SYSCALL_TFORK, int, tforkregs_t*); +DEFN_SYSCALL2(pid_t, sys_tfork, SYSCALL_TFORK, int, struct tfork*); -extern "C" pid_t tfork(int flags, tforkregs_t* regs) +extern "C" pid_t tfork(int flags, struct tfork* regs) { return sys_tfork(flags, regs); } diff --git a/libc/x64/fork.S b/libc/x64/fork.S index 1bbbba75..8ecff3ee 100644 --- a/libc/x64/fork.S +++ b/libc/x64/fork.S @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2012. + Copyright(C) Jonas 'Sortie' Termansen 2012, 2014. This file is part of the Sortix C Library. @@ -27,48 +27,45 @@ .section .text -.globl __call_tfork_with_regs -.type __call_tfork_with_regs, @function -__call_tfork_with_regs: +.globl __sfork +.type __sfork, @function +__sfork: pushq %rbp movq %rsp, %rbp - # Save the flags parameter so rdmsr won't trash it and align stack. pushq %rdi - sub $8, %rsp + pushq %rsi - # The actual system call expects a struct tforkregs_x64 containing the state - # of each register in the child. Since we create an identical copy, we - # simply set each member of the structure to our own state. Note that since - # the stack goes downwards, we create it in the reverse order. - movl $MSRID_GSBASE, %edi - call rdmsr - pushq %rax + movq $.Lafter_fork, (0 * 8)(%rsi) # rip + movq $0, (1 * 8)(%rsi) # rax, result is 0 for child + movq %rbx, (2 * 8)(%rsi) + movq %rcx, (3 * 8)(%rsi) + movq %rdx, (4 * 8)(%rsi) + movq %rdi, (5 * 8)(%rsi) + movq %rsi, (6 * 8)(%rsi) + movq %rsp, (7 * 8)(%rsi) + movq %rbp, (8 * 8)(%rsi) + movq %r8, (9 * 8)(%rsi) + movq %r9, (10 * 8)(%rsi) + movq %r10, (11 * 8)(%rsi) + movq %r11, (12 * 8)(%rsi) + movq %r12, (13 * 8)(%rsi) + movq %r13, (14 * 8)(%rsi) + movq %r14, (15 * 8)(%rsi) + movq %r15, (16 * 8)(%rsi) + pushfq + popq %rax + movq %rax, (17 * 8)(%rsi) # rflags movl $MSRID_FSBASE, %edi call rdmsr - pushq %rax - movq -8(%rbp), %rdi - pushfq - pushq %r15 - pushq %r14 - pushq %r13 - pushq %r12 - pushq %r11 - pushq %r10 - pushq %r9 - pushq %r8 - pushq %rbp - pushq %rsp - pushq %rsi - pushq %rdi - pushq %rdx - pushq %rcx - pushq %rbx - pushq $0 # rax, result of sfork is 0 for the child. - pushq $.Lafter_fork # rip, child will start execution from here. + movq 0(%rsp), %rsi + movq %rax, (18 * 8)(%rsi) # fsbase + movl $MSRID_GSBASE, %edi + call rdmsr + movq 0(%rsp), %rsi + movq %rax, (19 * 8)(%rsi) # gsbase + movq 8(%rsp), %rdi - # Call tfork with a nice pointer to our structure. - movq %rsp, %rsi call tfork .Lafter_fork: @@ -77,4 +74,4 @@ __call_tfork_with_regs: # which does that for us. leaveq retq -.size __call_tfork_with_regs, . - __call_tfork_with_regs +.size __sfork, . - __sfork diff --git a/libc/x64/setjmp.S b/libc/x64/setjmp.S index f3a653f2..d8fd0786 100644 --- a/libc/x64/setjmp.S +++ b/libc/x64/setjmp.S @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2013. + Copyright(C) Jonas 'Sortie' Termansen 2013, 2014. This file is part of the Sortix C Library. @@ -22,41 +22,88 @@ *******************************************************************************/ +#define SIG_SETMASK 2 + +/* + * sigjmp_buf[0] = %rbx + * sigjmp_buf[1] = %rsp + * sigjmp_buf[2] = %rbp + * sigjmp_buf[3] = %r12 + * sigjmp_buf[4] = %r13 + * sigjmp_buf[5] = %r14 + * sigjmp_buf[6] = %r15 + * sigjmp_buf[7] = + * sigjmp_buf[8] = + * sigjmp_buf[9...] = + */ + .global setjmp .type setjmp, @function setjmp: - # TODO: Floating point stuff! - mov %rbx, 0x00(%rdi) - mov %rsp, 0x08(%rdi) - mov %rbp, 0x10(%rdi) - mov %r12, 0x18(%rdi) - mov %r13, 0x20(%rdi) - mov %r14, 0x28(%rdi) - mov %r15, 0x30(%rdi) - mov 0(%rsp), %rax - mov %rax, 0x38(%rdi) - xorl %eax, %eax -.Lsetjmp_return: - ret + movl $1, %esi # setjmp saves the signal mask on Sortix + jmp 1f .size setjmp, . - setjmp +.global sigsetjmp +.type sigsetjmp, @function +sigsetjmp: + mov %esi, %esi # clear upper 32-bit bits + testl %esi, %esi + jz 2f +1: push %rdi + push %rsi + lea (9 * 8)(%rdi), %rdx # oldset + xor %esi, %esi # set + xor %edi, %edi # how (ignored because set is NULL) + call sigprocmask # assumes sigprocmask is per-thread on Sortix + pop %rsi + pop %rdi +2: mov 0(%rsp), %rax + mov %rbx, (0 * 8)(%rdi) + mov %rsp, (1 * 8)(%rdi) + mov %rbp, (2 * 8)(%rdi) + mov %r12, (3 * 8)(%rdi) + mov %r13, (4 * 8)(%rdi) + mov %r14, (5 * 8)(%rdi) + mov %r15, (6 * 8)(%rdi) + mov %rax, (7 * 8)(%rdi) + mov %rsi, (8 * 8)(%rdi) + xorl %eax, %eax + ret +.size sigsetjmp, . - sigsetjmp + .global longjmp .type longjmp, @function longjmp: + jmp siglongjmp +.size longjmp, . - longjmp + +.global siglongjmp +.type siglongjmp, @function +siglongjmp: testl %esi, %esi jnz 1f - mov $1, %esi -1: - # TODO: Floating point stuff! - mov 0x00(%rdi), %rbx - mov 0x08(%rdi), %rsp - mov 0x10(%rdi), %rbp - mov 0x18(%rdi), %r12 - mov 0x20(%rdi), %r13 - mov 0x28(%rdi), %r14 - mov 0x30(%rdi), %r15 - mov 0x38(%rdi), %rax - mov %rax, 0(%rsp) - mov %esi, %eax - jmp .Lsetjmp_return -.size longjmp, . - longjmp + movl $1, %esi +1: movq (8 * 8)(%rdi), %rdx + testq %rdx, %rdx + jz 2f + pushq %rdi + pushq %rsi + leaq (9 * 8)(%rdi), %rsi # set + movl $SIG_SETMASK, %edi # how + xorl %edx, %edx # oldset + call sigprocmask + popq %rsi + popq %rdi +2: movq (0 * 8)(%rdi), %rbx + movq (1 * 8)(%rdi), %rsp + movq (2 * 8)(%rdi), %rbp + movq (3 * 8)(%rdi), %r12 + movq (4 * 8)(%rdi), %r13 + movq (5 * 8)(%rdi), %r14 + movq (6 * 8)(%rdi), %r15 + movq (7 * 8)(%rdi), %rax + movq %rax, 0(%rsp) + movl %esi, %eax + ret +.size siglongjmp, . - siglongjmp diff --git a/libc/x86/fork.S b/libc/x86/fork.S index 3b31e7cb..53cfec34 100644 --- a/libc/x86/fork.S +++ b/libc/x86/fork.S @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2012. + Copyright(C) Jonas 'Sortie' Termansen 2012, 2014. This file is part of the Sortix C Library. @@ -27,38 +27,45 @@ .section .text -.globl __call_tfork_with_regs -.type __call_tfork_with_regs, @function -__call_tfork_with_regs: +.globl __sfork +.type __sfork, @function +__sfork: pushl %ebp movl %esp, %ebp - # The actual system call expects a struct tforkregs_x86 containing the state - # of each register in the child. Since we create an identical copy, we - # simply set each member of the structure to our own state. Note that since - # the stack goes downwards, we create it in the reverse order. - pushl $MSRID_GSBASE - call rdmsr - movl %eax, (%esp) + movl 12(%ebp), %ecx + push %ecx + movl 8(%ebp), %edx + push %edx + # -- stack is 16-byte aligned -- # + + movl $.Lafter_fork, (0 * 4)(%ecx) # eip + movl $0, (1 * 4)(%ecx) # rax, result is 0 for child + movl %ebx, (2 * 4)(%ecx) + movl %ecx, (3 * 4)(%ecx) + movl %edx, (4 * 4)(%ecx) + movl %edi, (5 * 4)(%ecx) + movl %esi, (6 * 4)(%ecx) + movl %esp, (7 * 4)(%ecx) + movl %ebp, (8 * 4)(%ecx) + pushfl + popl %eax + movl %eax, (9 * 4)(%ecx) # eflags + + subl $12, %esp pushl $MSRID_FSBASE call rdmsr - movl %eax, (%esp) - pushfl - pushl %ebp - pushl %esp - pushl %esi - pushl %edi - pushl %edx - pushl %ecx - pushl %ebx - pushl $0 # eax, result of sfork (0 for the child). - pushl $.Lafter_fork # rip, child will start execution from here. + movl 12(%ebp), %ecx + movl %eax, (10 * 4)(%ecx) # fsbase + addl $16, %esp + + subl $12, %esp + pushl $MSRID_GSBASE + call rdmsr + movl 12(%ebp), %ecx + movl %eax, (11 * 4)(%ecx) # gsbase + addl $16, %esp - # Call tfork with a nice pointer to our structure. Note that %edi contains - # the flag parameter that this function accepted. - movl 8(%ebp), %edx # flags parameter, edx need not be preserved. - pushl %esp - pushl %edx call tfork .Lafter_fork: @@ -67,4 +74,4 @@ __call_tfork_with_regs: # which does that for us. leavel retl -.size __call_tfork_with_regs, . - __call_tfork_with_regs +.size __sfork, . - __sfork diff --git a/libc/x86/setjmp.S b/libc/x86/setjmp.S index 6a7fb8a3..7531d33c 100644 --- a/libc/x86/setjmp.S +++ b/libc/x86/setjmp.S @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2013. + Copyright(C) Jonas 'Sortie' Termansen 2013, 2014. This file is part of the Sortix C Library. @@ -22,40 +22,86 @@ *******************************************************************************/ +#define SIG_SETMASK 2 + +/* + * sigjmp_buf[0] = %ebx + * sigjmp_buf[1] = %esi + * sigjmp_buf[2] = %edi + * sigjmp_buf[3] = %ebp + * sigjmp_buf[4] = %esp + * sigjmp_buf[5] = + * sigjmp_buf[6] = + * sigjmp_buf[7...] = + */ + .global setjmp .type setjmp, @function setjmp: - mov 4(%esp), %ecx - # TODO: Floating point stuff! - mov %ebx, 0x00(%ecx) - mov %esi, 0x04(%ecx) - mov %edi, 0x08(%ecx) - mov %ebp, 0x0C(%ecx) - mov %esp, 0x10(%ecx) - mov 0(%esp), %eax - mov %eax, 0x14(%ecx) - xorl %eax, %eax -.Lsetjmp_return: - ret + # setjmp saves the signal mask on Sortix + jmp 1f .size setjmp, . - setjmp +.global sigsetjmp +.type sigsetjmp, @function +sigsetjmp: + movl 8(%esp), %edx + testl %edx, %edx + jz 2f +1: movl 4(%esp), %ecx + leal (7 * 4)(%ecx), %edx + pushl %edx # oldset + pushl $0 # set + pushl $0 # how (ignored because set is NULL) + call sigprocmask + addl $(3 * 4), %esp + movl $1, %edx +2: movl 4(%esp), %ecx + movl 0(%esp), %eax + movl %ebx, (0 * 4)(%ecx) + movl %esi, (1 * 4)(%ecx) + movl %edi, (2 * 4)(%ecx) + movl %ebp, (3 * 4)(%ecx) + movl %esp, (4 * 4)(%ecx) + movl %eax, (5 * 4)(%ecx) + movl %edx, (6 * 4)(%ecx) + xorl %eax, %eax + ret +.size sigsetjmp, . - sigsetjmp + .global longjmp .type longjmp, @function longjmp: - mov 4(%esp), %ecx - mov 8(%esp), %edx - testl %edx, %edx - jnz 1f - mov $1, %edx -1: - # TODO: Floating point stuff! - mov 0x00(%ecx), %ebx - mov 0x04(%ecx), %esi - mov 0x08(%ecx), %edi - mov 0x0C(%ecx), %ebp - mov 0x10(%ecx), %esp - mov 0x14(%ecx), %eax - mov %eax, 0(%esp) - mov %edx, %eax - jmp .Lsetjmp_return + jmp siglongjmp .size longjmp, . - longjmp + +.global siglongjmp +.type siglongjmp, @function +siglongjmp: + movl 4(%esp), %ecx + movl 8(%esp), %eax + testl %eax, %eax + jnz 1f + movl $1, %eax +1: movl (6 * 4)(%ecx), %edx + testl %edx, %edx + jz 2f + pushl %eax + pushl %ecx + pushl $0 # oldset + leal (7 * 4)(%ecx), %edx + pushl %edx # set + pushl $SIG_SETMASK # how + call sigprocmask + addl $(3 * 4), %esp + popl %ecx + popl %eax +2: movl (0 * 4)(%ecx), %ebx + movl (1 * 4)(%ecx), %esi + movl (2 * 4)(%ecx), %edi + movl (3 * 4)(%ecx), %ebp + movl (4 * 4)(%ecx), %esp + movl (5 * 4)(%ecx), %edx + movl %edx, 0(%esp) + ret +.size siglongjmp, . - siglongjmp diff --git a/libpthread/include/pthread.h b/libpthread/include/pthread.h index 50107a2c..2bd40ab1 100644 --- a/libpthread/include/pthread.h +++ b/libpthread/include/pthread.h @@ -181,8 +181,6 @@ extern pthread_mutex_t __pthread_keys_lock; extern struct pthread_key* __pthread_keys; extern size_t __pthread_keys_used; extern size_t __pthread_keys_length; -extern pthread_mutex_t __pthread_num_threads_lock; -extern size_t __pthread_num_threads; struct pthread* pthread_allocate_tls(void); diff --git a/libpthread/pthread_create.c++ b/libpthread/pthread_create.c++ index 50faf3f9..ea7a79c3 100644 --- a/libpthread/pthread_create.c++ +++ b/libpthread/pthread_create.c++ @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -71,7 +72,7 @@ const unsigned long FLAGS_ID = 1 << 21; // 0x200000 #if defined(__i386__) static const unsigned long MINIMUM_STACK_SIZE = 4 * sizeof(unsigned long); -static void setup_thread_state(struct pthread* thread, tforkregs_t* regs) +static void setup_thread_state(struct pthread* thread, struct tfork* regs) { assert(MINIMUM_STACK_SIZE <= thread->uthread.stack_size); @@ -99,7 +100,7 @@ static void setup_thread_state(struct pthread* thread, tforkregs_t* regs) #if defined(__x86_64__) static const unsigned long MINIMUM_STACK_SIZE = 2 * sizeof(unsigned long); -static void setup_thread_state(struct pthread* thread, tforkregs_t* regs) +static void setup_thread_state(struct pthread* thread, struct tfork* regs) { assert(MINIMUM_STACK_SIZE <= thread->uthread.stack_size); @@ -121,12 +122,6 @@ static void setup_thread_state(struct pthread* thread, tforkregs_t* regs) } #endif -extern "C" -{ - pthread_mutex_t __pthread_num_threads_lock = PTHREAD_MUTEX_INITIALIZER; - size_t __pthread_num_threads = 1; -} - extern "C" int pthread_create(pthread_t* restrict thread_ptr, const pthread_attr_t* restrict attr, @@ -225,20 +220,19 @@ int pthread_create(pthread_t* restrict thread_ptr, } // Prepare the registers and initial stack for the new thread. - tforkregs_t regs; + struct tfork regs; setup_thread_state(thread, ®s); + memset(®s.altstack, 0, sizeof(regs.altstack)); + regs.altstack.ss_flags = SS_DISABLE; + sigprocmask(SIG_SETMASK, NULL, ®s.sigmask); // Create a new thread with the requested state. - pthread_mutex_lock(&__pthread_num_threads_lock); if ( tfork(SFTHREAD, ®s) < 0 ) { - pthread_mutex_unlock(&__pthread_num_threads_lock); munmap(thread->uthread.stack_mmap, thread->uthread.stack_size); munmap(thread->uthread.tls_mmap, thread->uthread.tls_size); return errno; } - __pthread_num_threads++; - pthread_mutex_unlock(&__pthread_num_threads_lock); *thread_ptr = thread; diff --git a/libpthread/pthread_exit.c++ b/libpthread/pthread_exit.c++ index ee916ba6..8ae159e2 100644 --- a/libpthread/pthread_exit.c++ +++ b/libpthread/pthread_exit.c++ @@ -56,13 +56,9 @@ void pthread_exit(void* return_value) thread->keys_length = 0; pthread_mutex_unlock(&__pthread_keys_lock); - pthread_mutex_lock(&__pthread_num_threads_lock); - size_t num_threads = __pthread_num_threads--; - pthread_mutex_unlock(&__pthread_num_threads_lock); - if ( num_threads == 1 ) - exit(0); pthread_mutex_lock(&thread->detach_lock); thread->exit_result = return_value; + int exit_flags = EXIT_THREAD_UNMAP; struct exit_thread extended; memset(&extended, 0, sizeof(extended)); extended.unmap_from = thread->uthread.stack_mmap; @@ -71,13 +67,15 @@ void pthread_exit(void* return_value) { extended.zero_from = &thread->join_lock.lock; extended.zero_size = sizeof(thread->join_lock.lock); - exit_thread(0, EXIT_THREAD_UNMAP | EXIT_THREAD_ZERO, &extended); - __builtin_unreachable(); + exit_flags |= EXIT_THREAD_ZERO; } else { - munmap(thread->uthread.tls_mmap, thread->uthread.tls_size); - exit_thread(0, EXIT_THREAD_UNMAP, &extended); - __builtin_unreachable(); + extended.tls_unmap_from = thread->uthread.tls_mmap; + extended.tls_unmap_size = thread->uthread.tls_size; + exit_flags |= EXIT_THREAD_TLS_UNMAP; } + + exit_thread(0, EXIT_THREAD_ONLY_IF_OTHERS | exit_flags, &extended); + exit(0); }