Implement signals.

Note: This is an incompatible ABI change.
This commit is contained in:
Jonas 'Sortie' Termansen 2013-08-04 20:24:59 +02:00
parent 316ed84e60
commit 30cd318c17
64 changed files with 2188 additions and 1121 deletions

View File

@ -46,6 +46,16 @@ Obsolescent in POSIX and scheduled for Sortix removal
* ctime, ctime_r are scheduled for removal (obsolescent in POSIX). * ctime, ctime_r are scheduled for removal (obsolescent in POSIX).
* utime, <utime.h> are scheduled for removal (obsolescent in POSIX). * utime, <utime.h> 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 Timestamps
---------- ----------

View File

@ -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 <http://www.gnu.org/licenses/>.
sortix/__/sigset.h
Declaration of the sigset_t structure size.
*******************************************************************************/
#ifndef INCLUDE_SORTIX____SIGSET_H
#define INCLUDE_SORTIX____SIGSET_H
#include <sys/cdefs.h>
__BEGIN_DECLS
#define __SIGSET_NUM_SIGNALS 128
__END_DECLS
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
sortix/__/wait.h
Declarations for waiting for the events of children.
*******************************************************************************/
#ifndef INCLUDE_SORTIX____WAIT_H
#define INCLUDE_SORTIX____WAIT_H
#include <sys/cdefs.h>
__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

View File

@ -43,12 +43,17 @@ struct exit_thread
size_t unmap_size; size_t unmap_size;
void* zero_from; void* zero_from;
size_t zero_size; 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_ONLY_IF_OTHERS (1<<0)
#define EXIT_THREAD_UNMAP (1<<1) #define EXIT_THREAD_UNMAP (1<<1)
#define EXIT_THREAD_ZERO (1<<2) #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 __END_DECLS

View File

@ -1,6 +1,6 @@
/******************************************************************************* /*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2012. Copyright(C) Jonas 'Sortie' Termansen 2012, 2014.
This file is part of Sortix. This file is part of Sortix.
@ -22,13 +22,15 @@
*******************************************************************************/ *******************************************************************************/
#ifndef SORTIX_FORK_H #ifndef INCLUDE_SORTIX_FORK_H
#define SORTIX_FORK_H #define INCLUDE_SORTIX_FORK_H
#include <sys/cdefs.h> #include <sys/cdefs.h>
#include <sortix/x86/fork.h> #include <stdint.h>
#include <sortix/x64/fork.h>
#include <sortix/sigset.h>
#include <sortix/stack.h>
__BEGIN_DECLS __BEGIN_DECLS
@ -77,13 +79,48 @@ __BEGIN_DECLS
own state into such a structure and calling tfork. Note that this structure own state into such a structure and calling tfork. Note that this structure
is highly platform specific, portable code should use the standard threading is highly platform specific, portable code should use the standard threading
facilities combined with sfork if possible. */ facilities combined with sfork if possible. */
struct tfork
{
#if defined(__i386__) #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__) #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 #else
#warning No tforkregs_cpu structure declared #error "You need to add a struct tfork for your platform"
#endif #endif
sigset_t sigmask;
stack_t altstack;
};
__END_DECLS __END_DECLS

View File

@ -68,9 +68,15 @@ public:
} }
~ScopedLock() ~ScopedLock()
{
Reset();
}
void Reset()
{ {
if ( mutex ) if ( mutex )
kthread_mutex_unlock(mutex); kthread_mutex_unlock(mutex);
mutex = NULL;
} }
private: private:
@ -88,9 +94,15 @@ public:
} }
~ScopedLockSignal() ~ScopedLockSignal()
{
Reset();
}
void Reset()
{ {
if ( mutex && acquired ) if ( mutex && acquired )
kthread_mutex_unlock(mutex); kthread_mutex_unlock(mutex);
mutex = NULL;
} }
bool IsAcquired() { return acquired; } bool IsAcquired() { return acquired; }

View File

@ -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. This file is part of Sortix.
@ -27,6 +27,9 @@
#include <sortix/fork.h> #include <sortix/fork.h>
#include <sortix/resource.h> #include <sortix/resource.h>
#include <sortix/sigaction.h>
#include <sortix/signal.h>
#include <sortix/sigset.h>
#include <sortix/kernel/clock.h> #include <sortix/kernel/clock.h>
#include <sortix/kernel/kthread.h> #include <sortix/kernel/kthread.h>
@ -97,6 +100,12 @@ public:
kthread_mutex_t resource_limits_lock; kthread_mutex_t resource_limits_lock;
struct rlimit resource_limits[RLIMIT_NUM_DECLARED]; 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: public:
void BootstrapTables(Ref<DescriptorTable> dtable, Ref<MountTable> mtable); void BootstrapTables(Ref<DescriptorTable> dtable, Ref<MountTable> mtable);
void BootstrapDirectories(Ref<Descriptor> root); void BootstrapDirectories(Ref<Descriptor> root);
@ -123,7 +132,7 @@ private:
size_t zombiewaiting; size_t zombiewaiting;
bool iszombie; bool iszombie;
bool nozombify; bool nozombify;
int exitstatus; int exit_code;
public: public:
Process* group; Process* group;
@ -138,6 +147,7 @@ public:
public: public:
Thread* firstthread; Thread* firstthread;
kthread_mutex_t threadlock; kthread_mutex_t threadlock;
bool threads_exiting;
public: public:
struct segment* segments; struct segment* segments;
@ -160,7 +170,8 @@ public:
int envc, const char* const* envp, int envc, const char* const* envp,
CPU::InterruptRegisters* regs); CPU::InterruptRegisters* regs);
void ResetAddressSpace(); 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); pid_t Wait(pid_t pid, int* status, int options);
bool DeliverSignal(int signum); bool DeliverSignal(int signum);
bool DeliverGroupSignal(int signum); bool DeliverGroupSignal(int signum);
@ -194,7 +205,6 @@ public:
public: public:
static Process* Get(pid_t pid); static Process* Get(pid_t pid);
static pid_t HackGetForegroundProcess();
private: private:
static bool Put(Process* process); static bool Put(Process* process);
@ -203,7 +213,7 @@ private:
}; };
void InitializeThreadRegisters(CPU::InterruptRegisters* regs, void InitializeThreadRegisters(CPU::InterruptRegisters* regs,
const tforkregs_t* requested); const struct tfork* requested);
Process* CurrentProcess(); Process* CurrentProcess();
} // namespace Sortix } // namespace Sortix

View File

@ -18,7 +18,7 @@
Sortix. If not, see <http://www.gnu.org/licenses/>. Sortix. If not, see <http://www.gnu.org/licenses/>.
sortix/kernel/signal.h 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 #define INCLUDE_SORTIX_KERNEL_SIGNAL_H
#include <sortix/signal.h> #include <sortix/signal.h>
#include <sortix/sigset.h>
#include <sortix/kernel/cpu.h> #include <sortix/kernel/cpu.h>
@ -35,28 +36,10 @@ extern "C" volatile unsigned long asm_signal_is_pending;
namespace Signal { 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(); 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; } 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 } // namespace Signal

View File

@ -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. This file is part of Sortix.
@ -25,8 +25,12 @@
#ifndef INCLUDE_SORTIX_KERNEL_THREAD_H #ifndef INCLUDE_SORTIX_KERNEL_THREAD_H
#define INCLUDE_SORTIX_KERNEL_THREAD_H #define INCLUDE_SORTIX_KERNEL_THREAD_H
#include <sortix/sigaction.h>
#include <sortix/signal.h> #include <sortix/signal.h>
#include <sortix/sigset.h>
#include <sortix/stack.h>
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/scheduler.h> #include <sortix/kernel/scheduler.h>
#include <sortix/kernel/signal.h> #include <sortix/kernel/signal.h>
@ -68,17 +72,13 @@ Thread* RunKernelThread(ThreadEntry entry, void* user, size_t stacksize = 0);
void SetupKernelThreadRegs(CPU::InterruptRegisters* regs, ThreadEntry entry, void SetupKernelThreadRegs(CPU::InterruptRegisters* regs, ThreadEntry entry,
void* user, addr_t stack, size_t stacksize); void* user, addr_t stack, size_t stacksize);
extern "C" void Thread__OnSigKill(Thread* thread);
typedef void (*sighandler_t)(int);
class Thread class Thread
{ {
friend Thread* CreateKernelThread(Process* process, friend Thread* CreateKernelThread(Process* process,
CPU::InterruptRegisters* regs, CPU::InterruptRegisters* regs,
unsigned long fsbase, unsigned long gsbase); unsigned long fsbase, unsigned long gsbase);
friend void KernelInit(unsigned long magic, multiboot_info_t* bootinfo); friend void KernelInit(unsigned long magic, multiboot_info_t* bootinfo);
friend void Thread__OnSigKill(Thread* thread); friend void UpdatePendingSignals(Thread* thread);
public: public:
static void Init(); static void Init();
@ -107,11 +107,14 @@ public:
bool fpuinitialized; bool fpuinitialized;
public: public:
sigset_t signal_pending;
sigset_t signal_mask;
stack_t signal_stack;
addr_t addrspace; addr_t addrspace;
sighandler_t sighandler;
addr_t kernelstackpos; addr_t kernelstackpos;
size_t kernelstacksize; size_t kernelstacksize;
bool kernelstackmalloced; bool kernelstackmalloced;
bool pledged_destruction;
#if defined(__i386__) || defined(__x86_64__) #if defined(__i386__) || defined(__x86_64__)
public: public:
@ -121,11 +124,6 @@ public:
private: private:
CPU::InterruptRegisters registers; CPU::InterruptRegisters registers;
Signal::Queue signalqueue;
int currentsignal;
int siglevel;
int signums[SIG_NUM_LEVELS];
CPU::InterruptRegisters sigregs[SIG_NUM_LEVELS];
public: public:
void SaveRegisters(const CPU::InterruptRegisters* src); void SaveRegisters(const CPU::InterruptRegisters* src);
@ -133,16 +131,9 @@ public:
void HandleSignal(CPU::InterruptRegisters* regs); void HandleSignal(CPU::InterruptRegisters* regs);
void HandleSigreturn(CPU::InterruptRegisters* regs); void HandleSigreturn(CPU::InterruptRegisters* regs);
bool DeliverSignal(int signum); bool DeliverSignal(int signum);
bool DeliverSignalUnlocked(int signum);
addr_t SwitchAddressSpace(addr_t newaddrspace); 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(); Thread* CurrentThread();

View File

@ -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 <http://www.gnu.org/licenses/>.
sortix/sigaction.h
Declares struct sigaction and associated flags.
*******************************************************************************/
#ifndef INCLUDE_SORTIX_SIGACTION_H
#define INCLUDE_SORTIX_SIGACTION_H
#include <sys/cdefs.h>
#include <sortix/siginfo.h>
#include <sortix/sigset.h>
__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

View File

@ -31,6 +31,8 @@
#include <__/pthread.h> #include <__/pthread.h>
#endif #endif
#include <sortix/sigval.h>
__BEGIN_DECLS __BEGIN_DECLS
#if __STDC_HOSTED__ #if __STDC_HOSTED__
@ -44,12 +46,6 @@ typedef __pthread_attr_t pthread_attr_t;
#define SIGEV_SIGNAL 1 #define SIGEV_SIGNAL 1
#define SIGEV_THREAD 2 #define SIGEV_THREAD 2
union sigval
{
int sival_int;
void* sival_ptr;
};
struct sigevent struct sigevent
{ {
int sigev_notify; int sigev_notify;

View File

@ -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 <http://www.gnu.org/licenses/>.
sortix/siginfo.h
Declares siginfo_t and associated flags.
*******************************************************************************/
#ifndef INCLUDE_SORTIX_SIGINFO_H
#define INCLUDE_SORTIX_SIGINFO_H
#include <sys/cdefs.h>
#include <sys/__/types.h>
#include <sortix/sigval.h>
__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

View File

@ -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. This file is part of Sortix.
@ -25,59 +25,52 @@
#ifndef SORTIX_INCLUDE_SIGNAL_H #ifndef SORTIX_INCLUDE_SIGNAL_H
#define SORTIX_INCLUDE_SIGNAL_H #define SORTIX_INCLUDE_SIGNAL_H
/* TODO: Yes, signals are implemented in a non-standard manner for now. */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__BEGIN_DECLS __BEGIN_DECLS
#define SIGHUP 1 /* Hangup */ #define SIGHUP 1 /* Hangup */
#define SIGINT 2 /* Interrupt */ #define SIGINT 2 /* Interrupt */
#define SIGQUIT 3 /* Quit */ #define SIGQUIT 3 /* Quit */
#define SIGILL 4 /* Illegal Instruction */ #define SIGILL 4 /* Illegal instruction */
#define SIGTRAP 5 /* Trace/Breakpoint Trap */ #define SIGTRAP 5 /* Trace/breakpoint trap */
#define SIGABRT 6 /* Abort */ #define SIGABRT 6 /* Aborted */
#define SIGEMT 7 /* Emulation Trap */ #define SIGBUS 7 /* Bus Error */
#define SIGFPE 8 /* Arithmetic Exception */ #define SIGFPE 8 /* Floating point exception */
#define SIGKILL 9 /* Killed */ #define SIGKILL 9 /* Killed */
#define SIGBUS 10 /* Bus Error */ #define SIGUSR1 10 /* User defined signal 1 */
#define SIGSEGV 11 /* Segmentation Fault */ #define SIGSEGV 11 /* Segmentation fault */
#define SIGSYS 12 /* Bad System Call */ #define SIGUSR2 12 /* User defined signal 2 */
#define SIGPIPE 13 /* Broken Pipe */ #define SIGPIPE 13 /* Broken pipe */
#define SIGALRM 14 /* Alarm Clock */ #define SIGALRM 14 /* Alarm clock */
#define SIGTERM 15 /* Terminated */ #define SIGTERM 15 /* Terminated */
#define SIGUSR1 16 /* User Signal 1 */ #define SIGSYS 16 /* Bad system call */
#define SIGUSR2 17 /* User Signal 2 */ #define SIGCHLD 17 /* Child exited */
#define SIGCHLD 18 /* Child Status */ #define SIGCONT 18 /* Continued */
#define SIGPWR 19 /* Power Fail/Restart */ #define SIGSTOP 19 /* Stopped (signal) */
#define SIGWINCH 20 /* Window Size Change */ #define SIGTSTP 20 /* Stopped */
#define SIGURG 21 /* Urgent Socket Condition */ #define SIGTTIN 21 /* Stopped (tty input) */
#define SIGSTOP 23 /* Stopped (signal) */ #define SIGTTOU 22 /* Stopped (tty output) */
#define SIGTSTP 24 /* Stopped (user) */ #define SIGURG 23 /* Urgent I/O condition */
#define SIGCONT 25 /* Continued */ #define SIGXCPU 24 /* CPU time limit exceeded */
#define SIGTTIN 26 /* Stopped (tty input) */ #define SIGXFSZ 25 /* File size limit exceeded */
#define SIGTTOU 27 /* Stopped (tty output) */ #define SIGVTALRM 26 /* Virtual timer expired */
#define SIGVTALRM 28 /* Virtual Timer Expired */ #define SIGPWR 27 /* Power Fail/Restart */
#define SIGXCPU 30 /* CPU time limit exceeded */ #define SIGWINCH 28 /* Window changed */
#define SIGXFSZ 31 /* File size limit exceeded */ #define SIGRTMIN 64 /* First user-available real-time signal. */
#define SIGWAITING 32 /* All LWPs blocked */ #define SIGRTMAX 127 /* Last user-available real-time signal. */
#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 SIG_PRIO_NORMAL 0 #define __SIG_NUM_DECLARED 31
#define SIG_PRIO_HIGH 1 #define __SIG_MAX_NUM 128
#define SIG_PRIO_STOP 2
#define SIG_PRIO_CORE 3
#define SIG_PRIO_KILL 4
#define SIG_NUM_LEVELS 5
#define SIG_BLOCK 0 #if defined(__is_sortix_kernel)
#define SIG_UNBLOCK 1 #define SIG_NUM_DECLARED __SIG_NUM_DECLARED
#define SIG_SETMASK 2 #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 __END_DECLS

View File

@ -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 <http://www.gnu.org/licenses/>.
sortix/sigprocmask.h
Declares flags for sigprocmask.
*******************************************************************************/
#ifndef INCLUDE_SORTIX_SIGPROCMASK_H
#define INCLUDE_SORTIX_SIGPROCMASK_H
#include <sys/cdefs.h>
__BEGIN_DECLS
#define SIG_BLOCK 0
#define SIG_UNBLOCK 1
#define SIG_SETMASK 2
__END_DECLS
#endif

View File

@ -1,6 +1,6 @@
/******************************************************************************* /*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2012. Copyright(C) Jonas 'Sortie' Termansen 2012, 2013, 2014.
This file is part of Sortix. This file is part of Sortix.
@ -27,11 +27,13 @@
#include <sys/cdefs.h> #include <sys/cdefs.h>
#include <sortix/__/sigset.h>
__BEGIN_DECLS __BEGIN_DECLS
typedef struct typedef struct
{ {
unsigned long __val[(1024 / (8 * sizeof (unsigned long int)))]; unsigned long __val[__SIGSET_NUM_SIGNALS / (8 * sizeof(unsigned long int))];
} sigset_t; } sigset_t;
__END_DECLS __END_DECLS

View File

@ -1,6 +1,6 @@
/******************************************************************************* /*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2012, 2013. Copyright(C) Jonas 'Sortie' Termansen 2013.
This file is part of Sortix. This file is part of Sortix.
@ -17,34 +17,22 @@
You should have received a copy of the GNU General Public License along with You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>. Sortix. If not, see <http://www.gnu.org/licenses/>.
sortix/x86/fork.h sortix/sigval.h
Declarations related to the fork family of system calls on x86 Sortix. Declares union sigval.
*******************************************************************************/ *******************************************************************************/
#ifndef SORTIX_X86_FORK_H #ifndef INCLUDE_SORTIX_SIGVAL_H
#define SORTIX_X86_FORK_H #define INCLUDE_SORTIX_SIGVAL_H
#include <sys/cdefs.h> #include <sys/cdefs.h>
#include <stdint.h>
__BEGIN_DECLS __BEGIN_DECLS
struct tforkregs_x86 union sigval
{ {
uint32_t eip; int sival_int;
uint32_t eax; void* sival_ptr;
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;
}; };
__END_DECLS __END_DECLS

View File

@ -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. This file is part of Sortix.
@ -17,43 +17,37 @@
You should have received a copy of the GNU General Public License along with You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>. Sortix. If not, see <http://www.gnu.org/licenses/>.
sortix/x64/fork.h sortix/stack.h
Declarations related to the fork family of system calls on x64 Sortix. Declares stack_t and associated flags.
*******************************************************************************/ *******************************************************************************/
#ifndef SORTIX_X64_FORK_H #ifndef INCLUDE_SORTIX_STACK_H
#define SORTIX_X64_FORK_H #define INCLUDE_SORTIX_STACK_H
#include <sys/cdefs.h> #include <sys/cdefs.h>
#include <stdint.h> #include <sys/__/types.h>
__BEGIN_DECLS __BEGIN_DECLS
struct tforkregs_x64 #ifndef __size_t_defined
#define __size_t_defined
#define __need_size_t
#include <stddef.h>
#endif
#define SS_ONSTACK (1<<0)
#define SS_DISABLE (1<<1)
#define __SS_SUPPORTED_FLAGS (SS_ONSTACK | SS_DISABLE)
typedef struct
{ {
uint64_t rip; void* ss_sp;
uint64_t rax; size_t ss_size;
uint64_t rbx; int ss_flags;
uint64_t rcx; } stack_t;
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;
};
__END_DECLS __END_DECLS

View File

@ -26,7 +26,7 @@
#define INCLUDE_SORTIX_SYSCALLNUM_H #define INCLUDE_SORTIX_SYSCALLNUM_H
#define SYSCALL_BAD_SYSCALL 0 #define SYSCALL_BAD_SYSCALL 0
#define SYSCALL_EXIT 1 #define SYSCALL_EXIT 1 /* OBSOLETE */
#define SYSCALL_SLEEP 2 #define SYSCALL_SLEEP 2
#define SYSCALL_USLEEP 3 #define SYSCALL_USLEEP 3
#define SYSCALL_PRINT_STRING 4 #define SYSCALL_PRINT_STRING 4
@ -154,6 +154,11 @@
#define SYSCALL_WRMSR 130 #define SYSCALL_WRMSR 130
#define SYSCALL_SCHED_YIELD 131 #define SYSCALL_SCHED_YIELD 131
#define SYSCALL_EXIT_THREAD 132 #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 #endif

View File

@ -1,6 +1,6 @@
/******************************************************************************* /*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2012. Copyright(C) Jonas 'Sortie' Termansen 2012, 2014.
This file is part of Sortix. This file is part of Sortix.
@ -27,8 +27,15 @@
#include <sys/cdefs.h> #include <sys/cdefs.h>
#include <sys/__/types.h>
__BEGIN_DECLS __BEGIN_DECLS
#ifndef __time_t_defined
#define __time_t_defined
typedef __time_t time_t;
#endif
struct timespec struct timespec
{ {
time_t tv_sec; time_t tv_sec;

View File

@ -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 <http://www.gnu.org/licenses/>.
sortix/ucontext.h
Declares ucontext_t and associated values.
*******************************************************************************/
#ifndef INCLUDE_SORTIX_UCONTEXT_H
#define INCLUDE_SORTIX_UCONTEXT_H
#include <sys/cdefs.h>
#include <sortix/sigset.h>
#include <sortix/stack.h>
__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

View File

@ -1,6 +1,6 @@
/******************************************************************************* /*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2012. Copyright(C) Jonas 'Sortie' Termansen 2012, 2014.
This file is part of Sortix. This file is part of Sortix.
@ -27,21 +27,32 @@
#include <sys/cdefs.h> #include <sys/cdefs.h>
#include <sortix/__/wait.h>
__BEGIN_DECLS __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 WNATURE_EXITED __WNATURE_EXITED
#define WTERMSIG(status) ((status >> 0) & 0x7F) #define WNATURE_SIGNALED __WNATURE_SIGNALED
#define WSTOPSIG(status) WTERMSIG(status) #define WNATURE_STOPPED __WNATURE_STOPPED
#define WIFEXITED(status) (WTERMSIG(status) == 0) #define WNATURE_CONTINUED __WNATURE_CONTINUED
#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 W_EXITCODE(ret, sig) ((ret) << 8 | (sig)) #define WEXITSTATUS(status) __WEXITSTATUS(status)
#define W_STOPCODE(sig) ((sig) << 8 | 0x7f) #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 __END_DECLS

View File

@ -187,17 +187,8 @@ void LogTerminal::ProcessKeystroke(int kbkey)
{ {
while ( linebuffer.CanBackspace() ) while ( linebuffer.CanBackspace() )
linebuffer.Backspace(); linebuffer.Backspace();
if ( foreground_pgid ) if ( Process* process = Process::Get(foreground_pgid) )
{ process->DeliverGroupSignal(SIGINT);
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);
}
return; return;
} }
if ( termmode & TERMMODE_SIGNAL && control && kbkey == KBKEY_D ) if ( termmode & TERMMODE_SIGNAL && control && kbkey == KBKEY_D )

View File

@ -22,10 +22,13 @@
*******************************************************************************/ *******************************************************************************/
#include <sys/wait.h>
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <limits.h> #include <limits.h>
#include <msr.h> #include <msr.h>
#include <signal.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -99,6 +102,7 @@ Process::Process()
grouplimbo = false; grouplimbo = false;
firstthread = NULL; firstthread = NULL;
threadlock = KTHREAD_MUTEX_INITIALIZER; threadlock = KTHREAD_MUTEX_INITIALIZER;
threads_exiting = false;
ptrlock = KTHREAD_MUTEX_INITIALIZER; ptrlock = KTHREAD_MUTEX_INITIALIZER;
idlock = KTHREAD_MUTEX_INITIALIZER; idlock = KTHREAD_MUTEX_INITIALIZER;
user_timers_lock = KTHREAD_MUTEX_INITIALIZER; user_timers_lock = KTHREAD_MUTEX_INITIALIZER;
@ -107,7 +111,7 @@ Process::Process()
segments_used = 0; segments_used = 0;
segments_length = 0; segments_length = 0;
segment_lock = KTHREAD_MUTEX_INITIALIZER; segment_lock = KTHREAD_MUTEX_INITIALIZER;
exitstatus = -1; exit_code = -1;
pid = AllocatePID(); pid = AllocatePID();
uid = euid = 0; uid = euid = 0;
gid = egid = 0; gid = egid = 0;
@ -115,6 +119,16 @@ Process::Process()
nicelock = KTHREAD_MUTEX_INITIALIZER; nicelock = KTHREAD_MUTEX_INITIALIZER;
nice = 0; nice = 0;
resource_limits_lock = KTHREAD_MUTEX_INITIALIZER; 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++ ) for ( size_t i = 0; i < RLIMIT_NUM_DECLARED; i++ )
{ {
resource_limits[i].rlim_cur = RLIM_INFINITY; resource_limits[i].rlim_cur = RLIM_INFINITY;
@ -415,7 +429,7 @@ void Process::NotifyChildExit(Process* child, bool zombify)
void Process::NotifyNewZombies() void Process::NotifyNewZombies()
{ {
ScopedLock lock(&childlock); ScopedLock lock(&childlock);
// TODO: Send SIGCHLD here? DeliverSignal(SIGCHLD);
if ( zombiewaiting ) if ( zombiewaiting )
kthread_cond_broadcast(&zombiecond); 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_execute_clock.Advance(zombie->child_execute_clock.current_time);
child_system_clock.Advance(zombie->child_system_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 ) if ( status < 0 )
status = W_EXITCODE(128 + SIGKILL, SIGKILL); status = WCONSTRUCT(WNATURE_SIGNALED, 128 + SIGKILL, SIGKILL);
kthread_mutex_lock(&zombie->groupparentlock); kthread_mutex_lock(&zombie->groupparentlock);
bool in_limbo = zombie->groupfirst && (zombie->grouplimbo = true); 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); 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); ScopedLock lock(&threadlock);
// Status codes can only contain 8 bits according to ISO C and POSIX. if ( exit_code == -1 )
if ( exitstatus == -1 ) exit_code = requested_exit_code;
exitstatus = W_EXITCODE(status & 0xFF, 0);
// Broadcast SIGKILL to all our threads which will begin our long path // 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 // 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); 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) void Process::AddChildProcess(Process* child)
{ {
ScopedLock mylock(&childlock); ScopedLock mylock(&childlock);
@ -740,6 +734,21 @@ void Process::ResetForExecute()
DeleteTimers(); 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(); 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; int tls_prot = PROT_READ | PROT_WRITE | PROT_KREAD | PROT_KWRITE | PROT_FORK;
void* tls_hint = stack_hint; 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 arg_size = 0;
size_t argv_size = sizeof(char*) * (argc + 1); 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 stack_segment;
struct segment raw_tls_segment; struct segment raw_tls_segment;
struct segment tls_segment; struct segment tls_segment;
struct segment auxcode_segment;
kthread_mutex_lock(&segment_lock); kthread_mutex_lock(&segment_lock);
if ( !(MapSegment(&arg_segment, stack_hint, arg_size, 0, stack_prot) && if ( !(MapSegment(&arg_segment, stack_hint, arg_size, 0, stack_prot) &&
MapSegment(&stack_segment, stack_hint, stack_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(&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); kthread_mutex_unlock(&segment_lock);
ResetForExecute(); ResetForExecute();
@ -926,6 +941,20 @@ int Process::Execute(const char* programname, const uint8_t* program,
wrmsr(MSRID_FSBASE, (uint64_t) uthread); wrmsr(MSRID_FSBASE, (uint64_t) uthread);
#endif #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(); dtable->OnExecute();
ExecuteCPU(argc, target_argv, envc, target_envp, stack_segment.addr + stack_segment.size, entry, regs); ExecuteCPU(argc, target_argv, envc, target_envp, stack_segment.addr + stack_segment.size, entry, regs);
@ -1076,9 +1105,9 @@ cleanup_done:
return result; 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(&regs, user_regs, sizeof(regs)) ) if ( !CopyFromUser(&regs, user_regs, sizeof(regs)) )
return -1; return -1;
@ -1092,6 +1121,9 @@ static pid_t sys_tfork(int flags, tforkregs_t* user_regs)
if ( !(making_thread || making_process) ) if ( !(making_thread || making_process) )
return errno = ENOSYS, -1; return errno = ENOSYS, -1;
if ( regs.altstack.ss_flags & ~__SS_SUPPORTED_FLAGS )
return errno = EINVAL, -1;
CPU::InterruptRegisters cpuregs; CPU::InterruptRegisters cpuregs;
InitializeThreadRegisters(&cpuregs, &regs); InitializeThreadRegisters(&cpuregs, &regs);
@ -1124,7 +1156,8 @@ static pid_t sys_tfork(int flags, tforkregs_t* user_regs)
thread->kernelstackpos = (addr_t) newkernelstack; thread->kernelstackpos = (addr_t) newkernelstack;
thread->kernelstacksize = curthread->kernelstacksize; thread->kernelstacksize = curthread->kernelstacksize;
thread->kernelstackmalloced = true; thread->kernelstackmalloced = true;
thread->sighandler = curthread->sighandler; memcpy(&thread->signal_mask, &regs.sigmask, sizeof(sigset_t));
memcpy(&thread->signal_stack, &regs.altstack, sizeof(stack_t));
StartKernelThread(thread); StartKernelThread(thread);
@ -1233,23 +1266,6 @@ pid_t Process::AllocatePID()
return nextpidtoallocate++; 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) int ProcessCompare(Process* a, Process* b)
{ {
if ( a->pid < b->pid ) if ( a->pid < b->pid )
@ -1375,7 +1391,6 @@ static mode_t sys_getumask(void)
void Process::Init() void Process::Init()
{ {
Syscall::Register(SYSCALL_EXECVE, (void*) sys_execve); Syscall::Register(SYSCALL_EXECVE, (void*) sys_execve);
Syscall::Register(SYSCALL_EXIT, (void*) sys_exit);
Syscall::Register(SYSCALL_GETPAGESIZE, (void*) sys_getpagesize); Syscall::Register(SYSCALL_GETPAGESIZE, (void*) sys_getpagesize);
Syscall::Register(SYSCALL_GETPGID, (void*) sys_getpgid); Syscall::Register(SYSCALL_GETPGID, (void*) sys_getpgid);
Syscall::Register(SYSCALL_GETPID, (void*) sys_getpid); Syscall::Register(SYSCALL_GETPID, (void*) sys_getpid);

View File

@ -151,10 +151,13 @@ void Switch(CPU::InterruptRegisters* regs)
assert(premagic == SCHED_MAGIC); assert(premagic == SCHED_MAGIC);
assert(postmagic == SCHED_MAGIC); assert(postmagic == SCHED_MAGIC);
DoActualSwitch(regs); DoActualSwitch(regs);
if ( regs->signal_pending && regs->InUserspace() )
Signal::Dispatch(regs);
assert(premagic == SCHED_MAGIC); assert(premagic == SCHED_MAGIC);
assert(postmagic == SCHED_MAGIC); assert(postmagic == SCHED_MAGIC);
if ( regs->signal_pending && regs->InUserspace() )
{
Interrupt::Enable();
Signal::DispatchHandler(regs, NULL);
}
} }
const bool DEBUG_BEGINCTXSWITCH = false; const bool DEBUG_BEGINCTXSWITCH = false;

View File

@ -18,116 +18,833 @@
Sortix. If not, see <http://www.gnu.org/licenses/>. Sortix. If not, see <http://www.gnu.org/licenses/>.
signal.cpp signal.cpp
Classes and functions making it easier to handle Unix signals. Asynchronous user-space thread interruption.
*******************************************************************************/ *******************************************************************************/
#include <sys/types.h>
#include <assert.h> #include <assert.h>
#include <errno.h>
#include <string.h> #include <string.h>
#include <stdint.h>
#include <signal.h>
#include <sortix/sigaction.h>
#include <sortix/signal.h> #include <sortix/signal.h>
#include <sortix/sigset.h>
#include <sortix/stack.h>
#include <sortix/ucontext.h>
#include <sortix/kernel/copy.h>
#include <sortix/kernel/interrupt.h> #include <sortix/kernel/interrupt.h>
#include <sortix/kernel/kernel.h> #include <sortix/kernel/kernel.h>
#include <sortix/kernel/panic.h> #include <sortix/kernel/process.h>
#include <sortix/kernel/signal.h> #include <sortix/kernel/signal.h>
#include <sortix/kernel/syscall.h>
#include <sortix/kernel/thread.h> #include <sortix/kernel/thread.h>
#if defined(__i386__) || defined(__x86_64__)
#include "x86-family/float.h"
#endif
namespace Sortix { 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. // A per-cpu value whether a signal is pending in the running task.
extern "C" { volatile unsigned long asm_signal_is_pending = 0; } 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 { namespace Signal {
const int PRIORITIES[SIG__NUM_DECLARED] = void DispatchHandler(CPU::InterruptRegisters* regs, void* /*user*/)
{
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*/)
{ {
return CurrentThread()->HandleSignal(regs); return CurrentThread()->HandleSignal(regs);
} }
void Return(CPU::InterruptRegisters* regs, void* /*user*/) void ReturnHandler(CPU::InterruptRegisters* regs, void* /*user*/)
{ {
return CurrentThread()->HandleSigreturn(regs); return CurrentThread()->HandleSigreturn(regs);
} }
void Init() 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 Signal
} // namespace Sortix } // namespace Sortix

View File

@ -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. This file is part of Sortix.
@ -22,8 +22,11 @@
*******************************************************************************/ *******************************************************************************/
#include <sys/wait.h>
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <signal.h>
#include <string.h> #include <string.h>
#include <sortix/exit.h> #include <sortix/exit.h>
@ -58,15 +61,17 @@ Thread::Thread()
kernelstackpos = 0; kernelstackpos = 0;
kernelstacksize = 0; kernelstacksize = 0;
kernelstackmalloced = false; kernelstackmalloced = false;
currentsignal = 0; pledged_destruction = false;
siglevel = 0;
sighandler = NULL;
terminated = false; terminated = false;
fpuinitialized = false; fpuinitialized = false;
// If malloc isn't 16-byte aligned, then we can't rely on offsets in // 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. // our own class, so we'll just fix ourselves nicely up.
unsigned long fpuaddr = ((unsigned long) fpuenv+16UL) & ~(16UL-1UL); unsigned long fpuaddr = ((unsigned long) fpuenv+16UL) & ~(16UL-1UL);
fpuenvaligned = (uint8_t*) fpuaddr; 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() Thread::~Thread()
@ -89,11 +94,6 @@ addr_t Thread::SwitchAddressSpace(addr_t newaddrspace)
return result; 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) extern "C" void BootstrapKernelThread(void* user, ThreadEntry entry)
{ {
entry(user); entry(user);
@ -196,98 +196,19 @@ Thread* RunKernelThread(ThreadEntry entry, void* user, size_t stacksize)
return thread; return thread;
} }
void Thread::HandleSignal(CPU::InterruptRegisters* regs) static int sys_exit_thread(int requested_exit_code,
{
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,
int flags, int flags,
const struct exit_thread* user_extended) const struct exit_thread* user_extended)
{ {
if ( flags & ~(EXIT_THREAD_ONLY_IF_OTHERS | if ( flags & ~(EXIT_THREAD_ONLY_IF_OTHERS |
EXIT_THREAD_UNMAP | 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; return errno = EINVAL, -1;
Thread* thread = CurrentThread(); Thread* thread = CurrentThread();
@ -309,12 +230,25 @@ static int sys_exit_thread(int status,
{ {
if ( iter == thread ) if ( iter == thread )
continue; continue;
if ( iter->pledged_destruction )
continue;
if ( iter->terminated ) if ( iter->terminated )
continue; continue;
is_others = true; 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); 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 ) if ( (flags & EXIT_THREAD_ONLY_IF_OTHERS) && !is_others )
return errno = ESRCH, -1; return errno = ESRCH, -1;
@ -326,62 +260,61 @@ static int sys_exit_thread(int status,
Memory::UnmapMemory(process, (uintptr_t) extended.unmap_from, Memory::UnmapMemory(process, (uintptr_t) extended.unmap_from,
extended.unmap_size); extended.unmap_size);
Memory::Flush(); 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 ) if ( flags & EXIT_THREAD_ZERO )
ZeroUser(extended.zero_from, extended.zero_size); ZeroUser(extended.zero_from, extended.zero_size);
if ( !is_others ) 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(); 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() void Thread::Init()
{ {
Syscall::Register(SYSCALL_EXIT_THREAD, (void*) sys_exit_thread); 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 } // namespace Sortix

View File

@ -1,6 +1,6 @@
/******************************************************************************* /*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013. Copyright(C) Jonas 'Sortie' Termansen 2013, 2014.
This file is part of Sortix. This file is part of Sortix.
@ -25,6 +25,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <errno.h> #include <errno.h>
#include <limits.h>
#include <timespec.h> #include <timespec.h>
#include <sortix/clock.h> #include <sortix/clock.h>
@ -138,7 +139,12 @@ static int sys_timer_getoverrun(timer_t timerid)
if ( !timer ) if ( !timer )
return -1; 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; return 0;
} }
@ -164,15 +170,14 @@ static void timer_callback(Clock* /*clock*/, Timer* timer, void* user)
Process* process = user_timer->process; Process* process = user_timer->process;
ScopedLock lock(&process->user_timers_lock); ScopedLock lock(&process->user_timers_lock);
size_t current_overrun = timer->num_overrun_events;
// TODO: This delivery facility is insufficient! sigevent is much more // TODO: This delivery facility is insufficient! sigevent is much more
// powerful than sending a simple old-school signal. // powerful than sending a simple old-school signal.
// TODO: If the last signal from last time is still being processed, we need if ( !process->DeliverSignal(user_timer->event.sigev_signo) &&
// to handle the sum of overrun. I'm not sure how to handle overrun errno == ESIGPENDING )
// properly, so we'll just pretend to user-space it never happens when {
// it does and we do some of the bookkeeping. if ( timer->num_overrun_events < SIZE_MAX )
(void) current_overrun; timer->num_overrun_events++;
process->DeliverSignal(user_timer->event.sigev_signo); }
} }
static int sys_timer_settime(timer_t timerid, int flags, static int sys_timer_settime(timer_t timerid, int flags,

View File

@ -38,10 +38,7 @@ void Process::ExecuteCPU(int argc, char** argv, int envc, char** envp,
addr_t stackpos, addr_t entry, addr_t stackpos, addr_t entry,
CPU::InterruptRegisters* regs) CPU::InterruptRegisters* regs)
{ {
const uint64_t CS = 0x18; memset(regs, 0, sizeof(*regs));
const uint64_t DS = 0x20;
const uint64_t RPL = 0x3;
regs->rdi = argc; regs->rdi = argc;
regs->rsi = (size_t) argv; regs->rsi = (size_t) argv;
regs->rdx = envc; regs->rdx = envc;
@ -49,14 +46,15 @@ void Process::ExecuteCPU(int argc, char** argv, int envc, char** envp,
regs->rip = entry; regs->rip = entry;
regs->rsp = stackpos & ~15UL; regs->rsp = stackpos & ~15UL;
regs->rbp = regs->rsp; regs->rbp = regs->rsp;
regs->cs = CS | RPL; regs->cs = UCS | URPL;
regs->ds = DS | RPL; regs->ds = UDS | URPL;
regs->ss = DS | RPL; regs->ss = UDS | URPL;
regs->rflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID; regs->rflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
regs->signal_pending = 0;
} }
void InitializeThreadRegisters(CPU::InterruptRegisters* regs, void InitializeThreadRegisters(CPU::InterruptRegisters* regs,
const tforkregs_t* requested) const struct tfork* requested)
{ {
memset(regs, 0, sizeof(*regs)); memset(regs, 0, sizeof(*regs));
regs->rip = requested->rip; regs->rip = requested->rip;

View File

@ -117,43 +117,4 @@ void SetupKernelThreadRegs(CPU::InterruptRegisters* regs, ThreadEntry entry,
regs->signal_pending = 0; 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 } // namespace Sortix

View File

@ -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. This file is part of Sortix.
@ -34,6 +34,7 @@ namespace Sortix {
namespace Float { namespace Float {
static Thread* fputhread; static Thread* fputhread;
bool fpu_is_enabled = false;
static inline void InitFPU() static inline void InitFPU()
{ {
@ -52,6 +53,53 @@ static inline void LoadState(const uint8_t* src)
asm volatile ("fxrstor (%0)" : : "r"(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*/) static void OnFPUAccess(CPU::InterruptRegisters* /*regs*/, void* /*user*/)
{ {
EnableFPU(); EnableFPU();

View File

@ -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. This file is part of Sortix.
@ -31,16 +31,21 @@ class Thread;
namespace Float { namespace Float {
extern bool fpu_is_enabled;
void Init(); void Init();
void NofityTaskExit(Thread* thread); void NofityTaskExit(Thread* thread);
void Yield();
static inline void EnableFPU() static inline void EnableFPU()
{ {
asm volatile ("clts"); asm volatile ("clts");
fpu_is_enabled = true;
} }
static inline void DisableFPU() static inline void DisableFPU()
{ {
fpu_is_enabled = false;
unsigned long cr0; unsigned long cr0;
asm volatile ("mov %%cr0, %0" : "=r"(cr0)); asm volatile ("mov %%cr0, %0" : "=r"(cr0));
cr0 |= 1UL<<3UL; cr0 |= 1UL<<3UL;

View File

@ -30,14 +30,6 @@
namespace Sortix { namespace Sortix {
namespace IDT { 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) void Set(struct idt_entry* table, size_t length)
{ {
size_t limit = sizeof(idt_entry) * length - 1; 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 #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 IDT
} // namespace Sortix } // namespace Sortix

View File

@ -230,13 +230,13 @@ void Init()
RegisterRawHandler(47, irq15, false, false); RegisterRawHandler(47, irq15, false, false);
RegisterRawHandler(128, syscall_handler, true, true); RegisterRawHandler(128, syscall_handler, true, true);
RegisterRawHandler(129, yield_cpu_handler, true, false); RegisterRawHandler(129, yield_cpu_handler, true, false);
RegisterRawHandler(130, isr130, true, false); RegisterRawHandler(130, isr130, true, true);
RegisterRawHandler(131, isr131, true, false); RegisterRawHandler(131, isr131, true, true);
RegisterRawHandler(132, thread_exit_handler, true, false); RegisterRawHandler(132, thread_exit_handler, true, false);
RegisterHandler(129, Scheduler::InterruptYieldCPU, NULL); RegisterHandler(129, Scheduler::InterruptYieldCPU, NULL);
RegisterHandler(130, Signal::Dispatch, NULL); RegisterHandler(130, Signal::DispatchHandler, NULL);
RegisterHandler(131, Signal::Return, NULL); RegisterHandler(131, Signal::ReturnHandler, NULL);
RegisterHandler(132, Scheduler::ThreadExitCPU, NULL); RegisterHandler(132, Scheduler::ThreadExitCPU, NULL);
IDT::Set(interrupt_table, NUM_INTERRUPTS); IDT::Set(interrupt_table, NUM_INTERRUPTS);
@ -313,6 +313,19 @@ void UserCrashHandler(CPU::InterruptRegisters* regs)
// Execute this crash handler with preemption on. // Execute this crash handler with preemption on.
Interrupt::Enable(); 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. // Walk and print the stack frames if this is a debug build.
if ( CALLTRACE_USER ) if ( CALLTRACE_USER )
CrashCalltrace(regs); CrashCalltrace(regs);
@ -330,13 +343,10 @@ void UserCrashHandler(CPU::InterruptRegisters* regs)
// Exit the process with the right error code. // Exit the process with the right error code.
// TODO: Send a SIGINT, SIGBUS, or whatever instead. // TODO: Send a SIGINT, SIGBUS, or whatever instead.
CurrentProcess()->Exit(139); CurrentProcess()->ExitThroughSignal(SIGSEGV);
// TODO: Is it strictly needed or even desirable to disable preemption here?
Interrupt::Disable();
// Deliver signals to this thread so it can exit correctly. // 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) extern "C" void interrupt_handler(CPU::InterruptRegisters* regs)

View File

@ -99,11 +99,3 @@ asm_call_BootstrapKernelThread:
call BootstrapKernelThread call BootstrapKernelThread
# BootstrapKernelThread is noreturn, no need for code here. # BootstrapKernelThread is noreturn, no need for code here.
.size asm_call_BootstrapKernelThread, . - asm_call_BootstrapKernelThread .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

View File

@ -38,6 +38,7 @@ void Process::ExecuteCPU(int argc, char** argv, int envc, char** envp,
addr_t stackpos, addr_t entry, addr_t stackpos, addr_t entry,
CPU::InterruptRegisters* regs) CPU::InterruptRegisters* regs)
{ {
memset(regs, 0, sizeof(*regs));
regs->eax = argc; regs->eax = argc;
regs->ebx = (size_t) argv; regs->ebx = (size_t) argv;
regs->edx = envc; regs->edx = envc;
@ -49,10 +50,11 @@ void Process::ExecuteCPU(int argc, char** argv, int envc, char** envp,
regs->ds = UDS | URPL; regs->ds = UDS | URPL;
regs->ss = UDS | URPL; regs->ss = UDS | URPL;
regs->eflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID; regs->eflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
regs->signal_pending = 0;
} }
void InitializeThreadRegisters(CPU::InterruptRegisters* regs, void InitializeThreadRegisters(CPU::InterruptRegisters* regs,
const tforkregs_t* requested) const struct tfork* requested)
{ {
memset(regs, 0, sizeof(*regs)); memset(regs, 0, sizeof(*regs));
regs->eip = requested->eip; regs->eip = requested->eip;

View File

@ -96,50 +96,4 @@ void SetupKernelThreadRegs(CPU::InterruptRegisters* regs, ThreadEntry entry,
regs->signal_pending = 0; 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 } // namespace Sortix

View File

@ -374,14 +374,15 @@ pwd/setpwent.o \
sched/sched_yield.o \ sched/sched_yield.o \
signal/kill.o \ signal/kill.o \
signal/killpg.o \ signal/killpg.o \
signal/psiginfo.o \
signal/psignal.o \ signal/psignal.o \
signal/raise.o \ signal/raise.o \
signal/sigaction.o \ signal/sigaction.o \
signal/SIG_DFL.o \ signal/sigaltstack.o \
signal/SIG_ERR.o \
signal/SIG_IGN.o \
signal/signal.o \ signal/signal.o \
signal/sigpending.o \
signal/sigprocmask.o \ signal/sigprocmask.o \
signal/sigsuspend.o \
stdio/fcloseall.o \ stdio/fcloseall.o \
stdio/fdio.o \ stdio/fdio.o \
stdio/fgetpos.o \ stdio/fgetpos.o \

View File

@ -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. This file is part of the Sortix C Library.
@ -27,18 +27,24 @@
#include <sys/cdefs.h> #include <sys/cdefs.h>
#include <sortix/__/sigset.h>
__BEGIN_DECLS __BEGIN_DECLS
#if defined(__x86_64__) #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__) #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 #else
#error "You need to implement jmp_buf on your CPU" #error "You need to implement sigjmp_buf on your CPU"
#endif #endif
void longjmp(jmp_buf env, int val); typedef sigjmp_buf jmp_buf;
int setjmp(jmp_buf env);
void longjmp(jmp_buf, int);
int setjmp(jmp_buf);
void siglongjmp(sigjmp_buf, int);
int sigsetjmp(sigjmp_buf, int);
__END_DECLS __END_DECLS

View File

@ -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. This file is part of the Sortix C Library.
@ -18,7 +18,7 @@
along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>. along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>.
signal.h signal.h
Signals. Signal API.
*******************************************************************************/ *******************************************************************************/
@ -33,7 +33,17 @@
#include <__/pthread.h> #include <__/pthread.h>
#endif #endif
#include <sortix/sigaction.h>
#include <sortix/sigevent.h>
#include <sortix/siginfo.h>
#include <sortix/signal.h> #include <sortix/signal.h>
#include <sortix/signal.h>
#include <sortix/sigprocmask.h>
#include <sortix/sigset.h>
#include <sortix/sigval.h>
#include <sortix/stack.h>
#include <sortix/timespec.h>
#include <sortix/ucontext.h>
__BEGIN_DECLS __BEGIN_DECLS
@ -53,12 +63,6 @@ typedef __pid_t pid_t;
#include <stddef.h> #include <stddef.h>
#endif #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__ #if __STDC_HOSTED__
#ifndef __pthread_attr_t_defined #ifndef __pthread_attr_t_defined
@ -73,134 +77,12 @@ typedef __pthread_t pthread_t;
#endif #endif
__END_DECLS #define NSIG __SIG_MAX_NUM
#include <sortix/sigset.h>
#include <sortix/timespec.h>
__BEGIN_DECLS
/* TODO: Should this be volatile? It isn't on Linux. */
typedef int sig_atomic_t; typedef int sig_atomic_t;
typedef void (*sighandler_t)(int); #define MINSIGSTKSZ 2048
#define SIGSTKSZ 8192
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;
};
int kill(pid_t, int); int kill(pid_t, int);
int killpg(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 sigdelset(sigset_t*, int);
int sigemptyset(sigset_t*); int sigemptyset(sigset_t*);
int sigfillset(sigset_t*); int sigfillset(sigset_t*);
/* TODO: sighold (obsolescent XSI). */
/* TODO: sigignore (obsolescent XSI). */
/* TODO: siginterrupt (obsolescent XSI). */
int sigisemptyset(const sigset_t*); int sigisemptyset(const sigset_t*);
int sigismember(const sigset_t*, int); 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 signotset(sigset_t*, const sigset_t*);
int sigorset(sigset_t*, const sigset_t*, const sigset_t*); int sigorset(sigset_t*, const sigset_t*, const sigset_t*);
/* TODO: sigpause (obsolescent XSI). */
int sigpending(sigset_t*); int sigpending(sigset_t*);
int sigprocmask(int, const sigset_t* __restrict, sigset_t* __restrict); int sigprocmask(int, const sigset_t* __restrict, sigset_t* __restrict);
int sigqueue(pid_t, int, const union sigval); /* TODO: int sigqueue(pid_t, int, const union sigval); */
/* TODO: sigrelse (obsolescent XSI). */
/* TODO: sigset (obsolescent XSI). */
int sigsuspend(const sigset_t*); int sigsuspend(const sigset_t*);
int sigtimedwait(const sigset_t* __restrict, siginfo_t* __restrict, /* TODO: int sigtimedwait(const sigset_t* __restrict, siginfo_t* __restrict,
const struct timespec* __restrict); const struct timespec* __restrict); */
int sigwait(const sigset_t* __restrict, int* __restrict); /* TODO: int sigwait(const sigset_t* __restrict, int* __restrict); */
int sigwaitinfo(const sigset_t* __restrict, siginfo_t* vrestrict); /* TODO: int sigwaitinfo(const sigset_t* __restrict, siginfo_t* __restrict); */
__END_DECLS __END_DECLS

View File

@ -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. This file is part of the Sortix C Library.
@ -22,10 +22,8 @@
*******************************************************************************/ *******************************************************************************/
// TODO: Make this header comply with POSIX-1.2008 #ifndef INCLUDE_SYS_WAIT_H
#define INCLUDE_SYS_WAIT_H 1
#ifndef _SYS_WAIT_H
#define _SYS_WAIT_H 1
#include <sys/cdefs.h> #include <sys/cdefs.h>
@ -40,12 +38,8 @@ __BEGIN_DECLS
typedef __pid_t pid_t; typedef __pid_t pid_t;
#endif #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); 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); pid_t waitpid(pid_t pid, int *stat_loc, int options);
__END_DECLS __END_DECLS

View File

@ -406,7 +406,7 @@ int exit_thread(int, int, const struct exit_thread*);
int memstat(size_t* memused, size_t* memtotal); int memstat(size_t* memused, size_t* memtotal);
int mkpartition(int fd, off_t start, off_t length); int mkpartition(int fd, off_t start, off_t length);
pid_t sfork(int flags); 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 writeall(int fd, const void* buf, size_t count);
size_t writeleast(int fd, const void* buf, size_t least, size_t max); size_t writeleast(int fd, const void* buf, size_t least, size_t max);
#endif #endif

View File

@ -31,7 +31,6 @@ extern "C" { char* program_invocation_name; }
extern "C" { char* program_invocation_short_name; } extern "C" { char* program_invocation_short_name; }
extern "C" void init_stdio(); extern "C" void init_stdio();
extern "C" void init_signal();
static char* find_last_elem(char* str) 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_name = (char*) argv0;
program_invocation_short_name = find_last_elem((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. // Initialize pthreads and stuff like thread-local storage.
pthread_initialize(); pthread_initialize();

View File

@ -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 <http://www.gnu.org/licenses/>.
signal/SIG_DFL.cpp
Default signal handler.
*******************************************************************************/
#include <signal.h>
#include <stdlib.h>
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. */ }
}

View File

@ -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. 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 You should have received a copy of the GNU Lesser General Public License
along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>. along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>.
signal/SIG_IGN.cpp signal/psiginfo.cpp
Ignore signal. Print signal error condition to stderr.
*******************************************************************************/ *******************************************************************************/
#include <signal.h> #include <signal.h>
#include <stdlib.h>
extern "C" void SIG_IGN(int /*signum*/) extern "C" void psiginfo(const siginfo_t* si, const char* message)
{ {
psignal(si->si_signo, message);
} }

View File

@ -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. This file is part of the Sortix C Library.
@ -22,53 +22,19 @@
*******************************************************************************/ *******************************************************************************/
#include <stdio.h> #include <sys/syscall.h>
#include <signal.h> #include <signal.h>
const int MAX_SIGNALS = 128; DEFN_SYSCALL3(int, sys_sigaction, SYSCALL_SIGACTION,
extern sighandler_t handlers[MAX_SIGNALS]; int,
const struct sigaction*,
struct sigaction*);
// TODO: Actually implement the sigaction interface for real.
extern "C" extern "C"
int sigaction(int signum, const struct sigaction* restrict act, int sigaction(int signum,
const struct sigaction* restrict act,
struct sigaction* restrict oldact) struct sigaction* restrict oldact)
{ {
if ( !act ) return sys_sigaction(signum, act, oldact);
{
// 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;
} }

View File

@ -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 <http://www.gnu.org/licenses/>.
signal/sigaltstack.cpp
Sets the stack used during signal handling.
*******************************************************************************/
#include <sys/syscall.h>
#include <signal.h>
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);
}

View File

@ -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. This file is part of the Sortix C Library.
@ -18,38 +18,35 @@
along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>. along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>.
signal/signal.cpp signal/signal.cpp
Handles the good old unix signals. Configure and retrieve a signal handler.
*******************************************************************************/ *******************************************************************************/
#include <sys/types.h>
#include <sys/syscall.h>
#include <signal.h> #include <signal.h>
#include <string.h>
const int MAX_SIGNALS = 128; extern "C" void (*signal(int signum, void (*handler)(int)))(int)
sighandler_t handlers[MAX_SIGNALS];
extern "C" void SignalHandlerAssembly(int signum);
extern "C" void SignalHandler(int signum)
{ {
if ( 0 <= signum && signum < (int) MAX_SIGNALS ) // Create a structure describing the new handler.
handlers[signum](signum); struct sigaction newact;
} memset(&newact, 0, sizeof(newact));
sigemptyset(&newact.sa_mask);
DEFN_SYSCALL1(int, sys_register_signal_handler, SYSCALL_REGISTER_SIGNAL_HANDLER, sighandler_t); newact.sa_handler = handler;
newact.sa_flags = SA_RESTART;
extern "C" void init_signal()
{ // Register the new handler and atomically get the old.
for ( int i = 0; i < MAX_SIGNALS; i++ ) struct sigaction oldact;
handlers[i] = SIG_DFL; if ( sigaction(signum, &newact, &oldact) != 0 )
return SIG_ERR;
// Tell the kernel which function we want called upon signals.
sys_register_signal_handler(&SignalHandlerAssembly); // 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
extern "C" sighandler_t signal(int signum, sighandler_t handler) // instead if they wish to restore an old handler.
{ if ( (oldact.sa_flags & (SA_SIGINFO | SA_COOKIE)) &&
if ( signum < 0 || MAX_SIGNALS <= signum ) { return SIG_ERR; } oldact.sa_handler != SIG_IGN &&
return handlers[signum] = handler; oldact.sa_handler != SIG_DFL )
return SIG_DFL;
return oldact.sa_handler;
} }

View File

@ -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. 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 You should have received a copy of the GNU Lesser General Public License
along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>. along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>.
signal/SIG_ERR.cpp signal/sigpending.cpp
Abort on signal. Get the set of pending signals.
*******************************************************************************/ *******************************************************************************/
#include <signal.h> #include <sys/syscall.h>
#include <stdlib.h>
extern "C" void SIG_ERR(int /*signum*/) #include <signal.h>
DEFN_SYSCALL1(int, sys_sigpending, SYSCALL_SIGPENDING, sigset_t*);
extern "C" int sigpending(sigset_t* set)
{ {
abort(); return sys_sigpending(set);
} }

View File

@ -22,13 +22,13 @@
*******************************************************************************/ *******************************************************************************/
#include <sys/syscall.h>
#include <signal.h> #include <signal.h>
#include <stdio.h>
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) extern "C" int sigprocmask(int how, const sigset_t* set, sigset_t* oldset)
{ {
(void) how; return sys_sigprocmask(how, set, oldset);
(void) set;
(void) oldset;
return 0;
} }

View File

@ -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 <http://www.gnu.org/licenses/>.
signal/sigsuspend.cpp
Wait until a signal occurs.
*******************************************************************************/
#include <sys/syscall.h>
#include <signal.h>
DEFN_SYSCALL1(int, sys_sigsuspend, SYSCALL_SIGSUSPEND, const sigset_t*);
extern "C" int sigsuspend(const sigset_t* set)
{
return sys_sigsuspend(set);
}

View File

@ -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. This file is part of the Sortix C Library.
@ -23,9 +23,12 @@
*******************************************************************************/ *******************************************************************************/
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/wait.h>
#include <calltrace.h> #include <calltrace.h>
#include <signal.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#if defined(__is_sortix_kernel) #if defined(__is_sortix_kernel)
@ -42,19 +45,30 @@ extern "C" void abort(void)
extern "C" void abort(void) extern "C" void abort(void)
{ {
struct stat st; struct stat st;
if ( getenv("LIBC_DEBUG_CALLTRACE") || stat("/etc/calltrace", &st) == 0 ) if ( stat("/etc/calltrace", &st) == 0 )
calltrace(); calltrace();
if ( getenv("LIBC_DEBUG_LOOP") || stat("/etc/calltrace_loop", &st) == 0 ) if ( stat("/etc/calltrace_loop", &st) == 0 )
while ( true ); 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 #else
extern "C" void abort(void) extern "C" void abort(void)
{ {
while ( true ) { }; while ( true ) { }
__builtin_unreachable(); __builtin_unreachable();
} }

View File

@ -30,15 +30,20 @@
extern "C" { struct exit_handler* __exit_handler_stack = NULL; } 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) extern "C" void exit(int status)
{ {
// Only allow a single thread to do the exit cleanup. If somehow the cleanup // It's undefined behavior to call this function more than once: If more
// code calls exit, then we'll self-destruct. If multiple threads attempt to // than one thread calls the function we'll wait until the process dies.
// call exit, then we'll destroy the ones that got here too late. pthread_mutex_lock(&exit_lock);
if ( pthread_mutex_trylock(&exit_lock) != 0 )
exit_thread(status, 0, NULL); // 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 ) while ( __exit_handler_stack )
{ {

View File

@ -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. This file is part of the Sortix C Library.
@ -30,40 +30,41 @@ extern "C" const char* sortix_strsignal(int signum)
{ {
switch ( signum ) switch ( signum )
{ {
case SIGHUP: return "SIGHUP"; case SIGHUP: return "Hangup";
case SIGINT: return "SIGINT"; case SIGINT: return "Interrupt";
case SIGQUIT: return "SIGQUIT"; case SIGQUIT: return "Quit";
case SIGILL: return "SIGILL"; case SIGILL: return "Illegal instruction";
case SIGTRAP: return "SIGTRAP"; case SIGTRAP: return "Trace/breakpoint trap";
case SIGABRT: return "SIGABRT"; case SIGABRT: return "Aborted";
case SIGEMT: return "SIGEMT"; case SIGBUS: return "Bus Error";
case SIGFPE: return "SIGFPE"; case SIGFPE: return "Floating point exception";
case SIGKILL: return "SIGKILL"; case SIGKILL: return "Killed";
case SIGBUS: return "SIGBUS"; case SIGUSR1: return "User defined signal 1";
case SIGSEGV: return "SYSSEGV"; case SIGSEGV: return "Segmentation fault";
case SIGSYS: return "SIGSYS"; case SIGUSR2: return "User defined signal 2";
case SIGPIPE: return "SIGPIPE"; case SIGPIPE: return "Broken pipe";
case SIGALRM: return "SIGALRM"; case SIGALRM: return "Alarm clock";
case SIGTERM: return "SIGTERM"; case SIGTERM: return "Terminated";
case SIGUSR1: return "SIGUSR1"; case SIGSYS: return "Bad system call";
case SIGUSR2: return "SIGUSR2"; case SIGCHLD: return "Child exited";
case SIGCHLD: return "SIGCHLD"; case SIGCONT: return "Continued";
case SIGPWR: return "SIGPWR"; case SIGSTOP: return "Stopped (signal)";
case SIGWINCH: return "SIGWINCH"; case SIGTSTP: return "Stopped";
case SIGURG: return "SIGURG"; case SIGTTIN: return "Stopped (tty input)";
case SIGSTOP: return "SIGSTOP"; case SIGTTOU: return "Stopped (tty output)";
case SIGTSTP: return "SIGTSTP"; case SIGURG: return "Urgent I/O condition";
case SIGCONT: return "SIGCONT"; case SIGXCPU: return "CPU time limit exceeded";
case SIGTTIN: return "SIGTTIN"; case SIGXFSZ: return "File size limit exceeded";
case SIGTTOU: return "SIGTTOU"; case SIGVTALRM: return "Virtual timer expired";
case SIGVTALRM: return "SIGVTALRM"; case SIGPWR: return "Power Fail/Restart";
case SIGXCPU: return "SIGXCPU"; case SIGWINCH: return "Window changed";
case SIGXFSZ: return "SIGXFSZ"; default: break;
case SIGWAITING: return "SIGWAITING";
case SIGLWP: return "SIGLWP";
case SIGAIO: return "SIGAIO";
default: return "Unknown signal value";
} }
if ( SIGRTMIN <= signum && signum <= SIGRTMAX )
return "Real-time signal";
return "Unknown signal value";
} }
extern "C" char* strsignal(int signum) extern "C" char* strsignal(int signum)

View File

@ -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. This file is part of the Sortix C Library.
@ -22,13 +22,13 @@
*******************************************************************************/ *******************************************************************************/
#include <sys/syscall.h> #include <sys/wait.h>
#include <unistd.h>
DEFN_SYSCALL1(int, sys_exit, SYSCALL_EXIT, int); #include <unistd.h>
extern "C" void _exit(int status) 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(); __builtin_unreachable();
} }

View File

@ -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. This file is part of the Sortix C Library.
@ -22,11 +22,18 @@
*******************************************************************************/ *******************************************************************************/
#include <signal.h>
#include <string.h>
#include <unistd.h> #include <unistd.h>
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) extern "C" pid_t sfork(int flags)
{ {
return __call_tfork_with_regs(flags); struct tfork regs;
memset(&regs, 0, sizeof(regs));
regs.altstack.ss_flags = SS_DISABLE;
sigprocmask(SIG_SETMASK, NULL, &regs.sigmask);
regs.altstack.ss_flags = SS_DISABLE;
return __sfork(flags, &regs);
} }

View File

@ -24,6 +24,7 @@
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <signal.h>
#include <unistd.h> #include <unistd.h>
extern "C" long sysconf(int name) extern "C" long sysconf(int name)
@ -34,6 +35,7 @@ extern "C" long sysconf(int name)
case _SC_PAGESIZE: case _SC_PAGE_SIZE: case _SC_PAGESIZE: case _SC_PAGE_SIZE:
return getpagesize(); return getpagesize();
case _SC_OPEN_MAX: return 0x10000; case _SC_OPEN_MAX: return 0x10000;
case _SC_RTSIG_MAX: return (SIGRTMAX+1) - SIGRTMIN;
default: default:
fprintf(stderr, "%s:%u warning: %s(%i) is unsupported\n", fprintf(stderr, "%s:%u warning: %s(%i) is unsupported\n",
__FILE__, __LINE__, __func__, name); __FILE__, __LINE__, __func__, name);

View File

@ -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. This file is part of the Sortix C Library.
@ -25,9 +25,9 @@
#include <sys/syscall.h> #include <sys/syscall.h>
#include <unistd.h> #include <unistd.h>
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); return sys_tfork(flags, regs);
} }

View File

@ -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. This file is part of the Sortix C Library.
@ -27,48 +27,45 @@
.section .text .section .text
.globl __call_tfork_with_regs .globl __sfork
.type __call_tfork_with_regs, @function .type __sfork, @function
__call_tfork_with_regs: __sfork:
pushq %rbp pushq %rbp
movq %rsp, %rbp movq %rsp, %rbp
# Save the flags parameter so rdmsr won't trash it and align stack.
pushq %rdi pushq %rdi
sub $8, %rsp pushq %rsi
# The actual system call expects a struct tforkregs_x64 containing the state movq $.Lafter_fork, (0 * 8)(%rsi) # rip
# of each register in the child. Since we create an identical copy, we movq $0, (1 * 8)(%rsi) # rax, result is 0 for child
# simply set each member of the structure to our own state. Note that since movq %rbx, (2 * 8)(%rsi)
# the stack goes downwards, we create it in the reverse order. movq %rcx, (3 * 8)(%rsi)
movl $MSRID_GSBASE, %edi movq %rdx, (4 * 8)(%rsi)
call rdmsr movq %rdi, (5 * 8)(%rsi)
pushq %rax 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 movl $MSRID_FSBASE, %edi
call rdmsr call rdmsr
pushq %rax movq 0(%rsp), %rsi
movq -8(%rbp), %rdi movq %rax, (18 * 8)(%rsi) # fsbase
pushfq movl $MSRID_GSBASE, %edi
pushq %r15 call rdmsr
pushq %r14 movq 0(%rsp), %rsi
pushq %r13 movq %rax, (19 * 8)(%rsi) # gsbase
pushq %r12 movq 8(%rsp), %rdi
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.
# Call tfork with a nice pointer to our structure.
movq %rsp, %rsi
call tfork call tfork
.Lafter_fork: .Lafter_fork:
@ -77,4 +74,4 @@ __call_tfork_with_regs:
# which does that for us. # which does that for us.
leaveq leaveq
retq retq
.size __call_tfork_with_regs, . - __call_tfork_with_regs .size __sfork, . - __sfork

View File

@ -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. 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] = <return-address>
* sigjmp_buf[8] = <non-zero if sigmask saved>
* sigjmp_buf[9...] = <saved sigmask if sigjmp_buf[8] != 0>
*/
.global setjmp .global setjmp
.type setjmp, @function .type setjmp, @function
setjmp: setjmp:
# TODO: Floating point stuff! movl $1, %esi # setjmp saves the signal mask on Sortix
mov %rbx, 0x00(%rdi) jmp 1f
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
.size setjmp, . - setjmp .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 .global longjmp
.type longjmp, @function .type longjmp, @function
longjmp: longjmp:
jmp siglongjmp
.size longjmp, . - longjmp
.global siglongjmp
.type siglongjmp, @function
siglongjmp:
testl %esi, %esi testl %esi, %esi
jnz 1f jnz 1f
mov $1, %esi movl $1, %esi
1: 1: movq (8 * 8)(%rdi), %rdx
# TODO: Floating point stuff! testq %rdx, %rdx
mov 0x00(%rdi), %rbx jz 2f
mov 0x08(%rdi), %rsp pushq %rdi
mov 0x10(%rdi), %rbp pushq %rsi
mov 0x18(%rdi), %r12 leaq (9 * 8)(%rdi), %rsi # set
mov 0x20(%rdi), %r13 movl $SIG_SETMASK, %edi # how
mov 0x28(%rdi), %r14 xorl %edx, %edx # oldset
mov 0x30(%rdi), %r15 call sigprocmask
mov 0x38(%rdi), %rax popq %rsi
mov %rax, 0(%rsp) popq %rdi
mov %esi, %eax 2: movq (0 * 8)(%rdi), %rbx
jmp .Lsetjmp_return movq (1 * 8)(%rdi), %rsp
.size longjmp, . - longjmp 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

View File

@ -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. This file is part of the Sortix C Library.
@ -27,38 +27,45 @@
.section .text .section .text
.globl __call_tfork_with_regs .globl __sfork
.type __call_tfork_with_regs, @function .type __sfork, @function
__call_tfork_with_regs: __sfork:
pushl %ebp pushl %ebp
movl %esp, %ebp movl %esp, %ebp
# The actual system call expects a struct tforkregs_x86 containing the state movl 12(%ebp), %ecx
# of each register in the child. Since we create an identical copy, we push %ecx
# simply set each member of the structure to our own state. Note that since movl 8(%ebp), %edx
# the stack goes downwards, we create it in the reverse order. push %edx
pushl $MSRID_GSBASE # -- stack is 16-byte aligned -- #
call rdmsr
movl %eax, (%esp) 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 pushl $MSRID_FSBASE
call rdmsr call rdmsr
movl %eax, (%esp) movl 12(%ebp), %ecx
pushfl movl %eax, (10 * 4)(%ecx) # fsbase
pushl %ebp addl $16, %esp
pushl %esp
pushl %esi subl $12, %esp
pushl %edi pushl $MSRID_GSBASE
pushl %edx call rdmsr
pushl %ecx movl 12(%ebp), %ecx
pushl %ebx movl %eax, (11 * 4)(%ecx) # gsbase
pushl $0 # eax, result of sfork (0 for the child). addl $16, %esp
pushl $.Lafter_fork # rip, child will start execution from here.
# 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 call tfork
.Lafter_fork: .Lafter_fork:
@ -67,4 +74,4 @@ __call_tfork_with_regs:
# which does that for us. # which does that for us.
leavel leavel
retl retl
.size __call_tfork_with_regs, . - __call_tfork_with_regs .size __sfork, . - __sfork

View File

@ -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. 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] = <return-address>
* sigjmp_buf[6] = <non-zero if sigmask saved>
* sigjmp_buf[7...] = <saved sigmask if sigjmp_buf[6] != 0>
*/
.global setjmp .global setjmp
.type setjmp, @function .type setjmp, @function
setjmp: setjmp:
mov 4(%esp), %ecx # setjmp saves the signal mask on Sortix
# TODO: Floating point stuff! jmp 1f
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
.size setjmp, . - setjmp .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 .global longjmp
.type longjmp, @function .type longjmp, @function
longjmp: longjmp:
mov 4(%esp), %ecx jmp siglongjmp
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
.size longjmp, . - longjmp .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

View File

@ -181,8 +181,6 @@ extern pthread_mutex_t __pthread_keys_lock;
extern struct pthread_key* __pthread_keys; extern struct pthread_key* __pthread_keys;
extern size_t __pthread_keys_used; extern size_t __pthread_keys_used;
extern size_t __pthread_keys_length; 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); struct pthread* pthread_allocate_tls(void);

View File

@ -26,6 +26,7 @@
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <signal.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
@ -71,7 +72,7 @@ const unsigned long FLAGS_ID = 1 << 21; // 0x200000
#if defined(__i386__) #if defined(__i386__)
static const unsigned long MINIMUM_STACK_SIZE = 4 * sizeof(unsigned long); 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); 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__) #if defined(__x86_64__)
static const unsigned long MINIMUM_STACK_SIZE = 2 * sizeof(unsigned long); 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); assert(MINIMUM_STACK_SIZE <= thread->uthread.stack_size);
@ -121,12 +122,6 @@ static void setup_thread_state(struct pthread* thread, tforkregs_t* regs)
} }
#endif #endif
extern "C"
{
pthread_mutex_t __pthread_num_threads_lock = PTHREAD_MUTEX_INITIALIZER;
size_t __pthread_num_threads = 1;
}
extern "C" extern "C"
int pthread_create(pthread_t* restrict thread_ptr, int pthread_create(pthread_t* restrict thread_ptr,
const pthread_attr_t* restrict attr, 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. // Prepare the registers and initial stack for the new thread.
tforkregs_t regs; struct tfork regs;
setup_thread_state(thread, &regs); setup_thread_state(thread, &regs);
memset(&regs.altstack, 0, sizeof(regs.altstack));
regs.altstack.ss_flags = SS_DISABLE;
sigprocmask(SIG_SETMASK, NULL, &regs.sigmask);
// Create a new thread with the requested state. // Create a new thread with the requested state.
pthread_mutex_lock(&__pthread_num_threads_lock);
if ( tfork(SFTHREAD, &regs) < 0 ) if ( tfork(SFTHREAD, &regs) < 0 )
{ {
pthread_mutex_unlock(&__pthread_num_threads_lock);
munmap(thread->uthread.stack_mmap, thread->uthread.stack_size); munmap(thread->uthread.stack_mmap, thread->uthread.stack_size);
munmap(thread->uthread.tls_mmap, thread->uthread.tls_size); munmap(thread->uthread.tls_mmap, thread->uthread.tls_size);
return errno; return errno;
} }
__pthread_num_threads++;
pthread_mutex_unlock(&__pthread_num_threads_lock);
*thread_ptr = thread; *thread_ptr = thread;

View File

@ -56,13 +56,9 @@ void pthread_exit(void* return_value)
thread->keys_length = 0; thread->keys_length = 0;
pthread_mutex_unlock(&__pthread_keys_lock); 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); pthread_mutex_lock(&thread->detach_lock);
thread->exit_result = return_value; thread->exit_result = return_value;
int exit_flags = EXIT_THREAD_UNMAP;
struct exit_thread extended; struct exit_thread extended;
memset(&extended, 0, sizeof(extended)); memset(&extended, 0, sizeof(extended));
extended.unmap_from = thread->uthread.stack_mmap; 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_from = &thread->join_lock.lock;
extended.zero_size = sizeof(thread->join_lock.lock); extended.zero_size = sizeof(thread->join_lock.lock);
exit_thread(0, EXIT_THREAD_UNMAP | EXIT_THREAD_ZERO, &extended); exit_flags |= EXIT_THREAD_ZERO;
__builtin_unreachable();
} }
else else
{ {
munmap(thread->uthread.tls_mmap, thread->uthread.tls_size); extended.tls_unmap_from = thread->uthread.tls_mmap;
exit_thread(0, EXIT_THREAD_UNMAP, &extended); extended.tls_unmap_size = thread->uthread.tls_size;
__builtin_unreachable(); exit_flags |= EXIT_THREAD_TLS_UNMAP;
} }
exit_thread(0, EXIT_THREAD_ONLY_IF_OTHERS | exit_flags, &extended);
exit(0);
} }