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).
* 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
----------

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;
void* zero_from;
size_t zero_size;
unsigned long reserved[4];
void* tls_unmap_from;
size_t tls_unmap_size;
unsigned long reserved[2];
};
#define EXIT_THREAD_ONLY_IF_OTHERS (1<<0)
#define EXIT_THREAD_UNMAP (1<<1)
#define EXIT_THREAD_ZERO (1<<2)
#define EXIT_THREAD_TLS_UNMAP (1<<3)
#define EXIT_THREAD_PROCESS (1<<4)
#define EXIT_THREAD_DUMP_CORE (1<<5)
__END_DECLS

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.
@ -22,13 +22,15 @@
*******************************************************************************/
#ifndef SORTIX_FORK_H
#define SORTIX_FORK_H
#ifndef INCLUDE_SORTIX_FORK_H
#define INCLUDE_SORTIX_FORK_H
#include <sys/cdefs.h>
#include <sortix/x86/fork.h>
#include <sortix/x64/fork.h>
#include <stdint.h>
#include <sortix/sigset.h>
#include <sortix/stack.h>
__BEGIN_DECLS
@ -77,13 +79,48 @@ __BEGIN_DECLS
own state into such a structure and calling tfork. Note that this structure
is highly platform specific, portable code should use the standard threading
facilities combined with sfork if possible. */
struct tfork
{
#if defined(__i386__)
typedef struct tforkregs_x86 tforkregs_t;
uint32_t eip;
uint32_t eax;
uint32_t ebx;
uint32_t ecx;
uint32_t edx;
uint32_t edi;
uint32_t esi;
uint32_t esp;
uint32_t ebp;
uint32_t eflags;
uint32_t fsbase;
uint32_t gsbase;
#elif defined(__x86_64__)
typedef struct tforkregs_x64 tforkregs_t;
uint64_t rip;
uint64_t rax;
uint64_t rbx;
uint64_t rcx;
uint64_t rdx;
uint64_t rdi;
uint64_t rsi;
uint64_t rsp;
uint64_t rbp;
uint64_t r8;
uint64_t r9;
uint64_t r10;
uint64_t r11;
uint64_t r12;
uint64_t r13;
uint64_t r14;
uint64_t r15;
uint64_t rflags;
uint64_t fsbase;
uint64_t gsbase;
#else
#warning No tforkregs_cpu structure declared
#error "You need to add a struct tfork for your platform"
#endif
sigset_t sigmask;
stack_t altstack;
};
__END_DECLS

View File

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

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

View File

@ -18,7 +18,7 @@
Sortix. If not, see <http://www.gnu.org/licenses/>.
sortix/kernel/signal.h
Classes and functions making it easier to handle Unix signals.
Asynchronous user-space thread interruption.
*******************************************************************************/
@ -26,6 +26,7 @@
#define INCLUDE_SORTIX_KERNEL_SIGNAL_H
#include <sortix/signal.h>
#include <sortix/sigset.h>
#include <sortix/kernel/cpu.h>
@ -35,28 +36,10 @@ extern "C" volatile unsigned long asm_signal_is_pending;
namespace Signal {
class Queue
{
public:
Queue();
// TODO: This is the wrong data structure for the problem!
private:
bool pending[SIG_MAX_NUM];
// TODO: This is not SMP ready:
// To avoid race conditions, these should be called with interrupts off.
public:
void Push(int signum);
int Pop(int cursig);
};
void Init();
int Priority(int signum);
void Dispatch(CPU::InterruptRegisters* regs, void* user = NULL);
void Return(CPU::InterruptRegisters* regs, void* user = NULL);
inline bool IsPending() { return asm_signal_is_pending != 0; }
void DispatchHandler(CPU::InterruptRegisters* regs, void* user);
void ReturnHandler(CPU::InterruptRegisters* regs, void* user);
} // namespace Signal

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

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

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

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.
@ -27,11 +27,13 @@
#include <sys/cdefs.h>
#include <sortix/__/sigset.h>
__BEGIN_DECLS
typedef struct
{
unsigned long __val[(1024 / (8 * sizeof (unsigned long int)))];
unsigned long __val[__SIGSET_NUM_SIGNALS / (8 * sizeof(unsigned long int))];
} sigset_t;
__END_DECLS

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.
@ -17,34 +17,22 @@
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
sortix/x86/fork.h
Declarations related to the fork family of system calls on x86 Sortix.
sortix/sigval.h
Declares union sigval.
*******************************************************************************/
#ifndef SORTIX_X86_FORK_H
#define SORTIX_X86_FORK_H
#ifndef INCLUDE_SORTIX_SIGVAL_H
#define INCLUDE_SORTIX_SIGVAL_H
#include <sys/cdefs.h>
#include <stdint.h>
__BEGIN_DECLS
struct tforkregs_x86
union sigval
{
uint32_t eip;
uint32_t eax;
uint32_t ebx;
uint32_t ecx;
uint32_t edx;
uint32_t edi;
uint32_t esi;
uint32_t esp;
uint32_t ebp;
uint32_t eflags;
uint32_t fsbase;
uint32_t gsbase;
int sival_int;
void* sival_ptr;
};
__END_DECLS

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.
@ -17,43 +17,37 @@
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
sortix/x64/fork.h
Declarations related to the fork family of system calls on x64 Sortix.
sortix/stack.h
Declares stack_t and associated flags.
*******************************************************************************/
#ifndef SORTIX_X64_FORK_H
#define SORTIX_X64_FORK_H
#ifndef INCLUDE_SORTIX_STACK_H
#define INCLUDE_SORTIX_STACK_H
#include <sys/cdefs.h>
#include <stdint.h>
#include <sys/__/types.h>
__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;
uint64_t rax;
uint64_t rbx;
uint64_t rcx;
uint64_t rdx;
uint64_t rdi;
uint64_t rsi;
uint64_t rsp;
uint64_t rbp;
uint64_t r8;
uint64_t r9;
uint64_t r10;
uint64_t r11;
uint64_t r12;
uint64_t r13;
uint64_t r14;
uint64_t r15;
uint64_t rflags;
uint64_t fsbase;
uint64_t gsbase;
};
void* ss_sp;
size_t ss_size;
int ss_flags;
} stack_t;
__END_DECLS

View File

@ -26,7 +26,7 @@
#define INCLUDE_SORTIX_SYSCALLNUM_H
#define SYSCALL_BAD_SYSCALL 0
#define SYSCALL_EXIT 1
#define SYSCALL_EXIT 1 /* OBSOLETE */
#define SYSCALL_SLEEP 2
#define SYSCALL_USLEEP 3
#define SYSCALL_PRINT_STRING 4
@ -154,6 +154,11 @@
#define SYSCALL_WRMSR 130
#define SYSCALL_SCHED_YIELD 131
#define SYSCALL_EXIT_THREAD 132
#define SYSCALL_MAX_NUM 133 /* index of highest constant + 1 */
#define SYSCALL_SIGACTION 133
#define SYSCALL_SIGALTSTACK 134
#define SYSCALL_SIGPENDING 135
#define SYSCALL_SIGPROCMASK 136
#define SYSCALL_SIGSUSPEND 137
#define SYSCALL_MAX_NUM 138 /* index of highest constant + 1 */
#endif

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.
@ -27,8 +27,15 @@
#include <sys/cdefs.h>
#include <sys/__/types.h>
__BEGIN_DECLS
#ifndef __time_t_defined
#define __time_t_defined
typedef __time_t time_t;
#endif
struct timespec
{
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.
@ -27,21 +27,32 @@
#include <sys/cdefs.h>
#include <sortix/__/wait.h>
__BEGIN_DECLS
#define WNOHANG (1<<0)
#define WCONTINUED (1<<0)
#define WNOHANG (1<<1)
#define WUNTRACED (1<<2)
#define WEXITSTATUS(status) ((status >> 8) & 0xFF)
#define WTERMSIG(status) ((status >> 0) & 0x7F)
#define WSTOPSIG(status) WTERMSIG(status)
#define WIFEXITED(status) (WTERMSIG(status) == 0)
#define WIFSIGNALED(status) (WTERMSIG(status) != 0)
/*#define WIFCONTINUED(status) (WTERMSIG(status) == TODO)*/
/*#define WIFSTOPPED(status) (WTERMSIG(status) == TODO)*/
/*#define WIFCONTINUED(status) (WTERMSIG(status) == TODO)*/
#define WNATURE_EXITED __WNATURE_EXITED
#define WNATURE_SIGNALED __WNATURE_SIGNALED
#define WNATURE_STOPPED __WNATURE_STOPPED
#define WNATURE_CONTINUED __WNATURE_CONTINUED
#define W_EXITCODE(ret, sig) ((ret) << 8 | (sig))
#define W_STOPCODE(sig) ((sig) << 8 | 0x7f)
#define WEXITSTATUS(status) __WEXITSTATUS(status)
#define WTERMSIG(status) __WTERMSIG(status)
#define WNATURE(status) __WNATURE(status)
#define WIFEXITED(status) __WIFEXITED(status)
#define WIFSIGNALED(status) __WIFSIGNALED(status)
#define WIFSTOPPED(status) __WIFSTOPPED(status)
#define WIFCONTINUED(status) __WIFCONTINUED(status)
#define WSTOPSIG(status) __WSTOPSIG(status)
#define WCONSTRUCT(nature, exitcode, signal) \
__WCONSTRUCT(nature, exitcode, signal)
__END_DECLS

View File

@ -187,17 +187,8 @@ void LogTerminal::ProcessKeystroke(int kbkey)
{
while ( linebuffer.CanBackspace() )
linebuffer.Backspace();
if ( foreground_pgid )
{
if ( Process* process = Process::Get(foreground_pgid) )
process->DeliverGroupSignal(SIGINT);
}
else // TODO: Backwards compatibility, delete this.
{
pid_t pid = Process::HackGetForegroundProcess();
if ( Process* process = Process::Get(pid) )
process->DeliverSignal(SIGINT);
}
if ( Process* process = Process::Get(foreground_pgid) )
process->DeliverGroupSignal(SIGINT);
return;
}
if ( termmode & TERMMODE_SIGNAL && control && kbkey == KBKEY_D )

View File

@ -22,10 +22,13 @@
*******************************************************************************/
#include <sys/wait.h>
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <msr.h>
#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@ -99,6 +102,7 @@ Process::Process()
grouplimbo = false;
firstthread = NULL;
threadlock = KTHREAD_MUTEX_INITIALIZER;
threads_exiting = false;
ptrlock = KTHREAD_MUTEX_INITIALIZER;
idlock = KTHREAD_MUTEX_INITIALIZER;
user_timers_lock = KTHREAD_MUTEX_INITIALIZER;
@ -107,7 +111,7 @@ Process::Process()
segments_used = 0;
segments_length = 0;
segment_lock = KTHREAD_MUTEX_INITIALIZER;
exitstatus = -1;
exit_code = -1;
pid = AllocatePID();
uid = euid = 0;
gid = egid = 0;
@ -115,6 +119,16 @@ Process::Process()
nicelock = KTHREAD_MUTEX_INITIALIZER;
nice = 0;
resource_limits_lock = KTHREAD_MUTEX_INITIALIZER;
signal_lock = KTHREAD_MUTEX_INITIALIZER;
sigemptyset(&signal_pending);
memset(&signal_actions, 0, sizeof(signal_actions));
for ( int i = 0; i < SIG_MAX_NUM; i++ )
{
sigemptyset(&signal_actions[i].sa_mask);
signal_actions[i].sa_handler = SIG_DFL;
signal_actions[i].sa_cookie = NULL;
signal_actions[i].sa_flags = 0;
}
for ( size_t i = 0; i < RLIMIT_NUM_DECLARED; i++ )
{
resource_limits[i].rlim_cur = RLIM_INFINITY;
@ -415,7 +429,7 @@ void Process::NotifyChildExit(Process* child, bool zombify)
void Process::NotifyNewZombies()
{
ScopedLock lock(&childlock);
// TODO: Send SIGCHLD here?
DeliverSignal(SIGCHLD);
if ( zombiewaiting )
kthread_cond_broadcast(&zombiecond);
}
@ -481,9 +495,9 @@ pid_t Process::Wait(pid_t thepid, int* user_status, int options)
child_execute_clock.Advance(zombie->child_execute_clock.current_time);
child_system_clock.Advance(zombie->child_system_clock.current_time);
int status = zombie->exitstatus;
int status = zombie->exit_code;
if ( status < 0 )
status = W_EXITCODE(128 + SIGKILL, SIGKILL);
status = WCONSTRUCT(WNATURE_SIGNALED, 128 + SIGKILL, SIGKILL);
kthread_mutex_lock(&zombie->groupparentlock);
bool in_limbo = zombie->groupfirst && (zombie->grouplimbo = true);
@ -504,12 +518,16 @@ static pid_t sys_waitpid(pid_t pid, int* user_status, int options)
return CurrentProcess()->Wait(pid, user_status, options);
}
void Process::Exit(int status)
void Process::ExitThroughSignal(int signal)
{
ExitWithCode(WCONSTRUCT(WNATURE_SIGNALED, 128 + signal, signal));
}
void Process::ExitWithCode(int requested_exit_code)
{
ScopedLock lock(&threadlock);
// Status codes can only contain 8 bits according to ISO C and POSIX.
if ( exitstatus == -1 )
exitstatus = W_EXITCODE(status & 0xFF, 0);
if ( exit_code == -1 )
exit_code = requested_exit_code;
// Broadcast SIGKILL to all our threads which will begin our long path
// of process termination. We simply can't stop the threads as they may
@ -519,30 +537,6 @@ void Process::Exit(int status)
t->DeliverSignal(SIGKILL);
}
static int sys_exit(int status)
{
CurrentProcess()->Exit(status);
return 0;
}
bool Process::DeliverSignal(int signum)
{
// TODO: How to handle signals that kill the process?
if ( firstthread )
return firstthread->DeliverSignal(signum);
return errno = EINIT, false;
}
bool Process::DeliverGroupSignal(int signum)
{
ScopedLock lock(&groupparentlock);
if ( !groupfirst )
return errno = ESRCH, false;
for ( Process* iter = groupfirst; iter; iter = iter->groupnext )
iter->DeliverSignal(signum);
return true;
}
void Process::AddChildProcess(Process* child)
{
ScopedLock mylock(&childlock);
@ -740,6 +734,21 @@ void Process::ResetForExecute()
DeleteTimers();
for ( int i = 0; i < SIG_MAX_NUM; i++ )
{
signal_actions[i].sa_flags = 0;
if ( signal_actions[i].sa_handler == SIG_DFL )
continue;
if ( signal_actions[i].sa_handler == SIG_IGN )
continue;
signal_actions[i].sa_handler = SIG_DFL;
}
sigreturn = NULL;
stack_t* signal_stack = &CurrentThread()->signal_stack;
memset(signal_stack, 0, sizeof(*signal_stack));
signal_stack->ss_flags = SS_DISABLE;
ResetAddressSpace();
}
@ -833,6 +842,10 @@ int Process::Execute(const char* programname, const uint8_t* program,
int tls_prot = PROT_READ | PROT_WRITE | PROT_KREAD | PROT_KWRITE | PROT_FORK;
void* tls_hint = stack_hint;
size_t auxcode_size = Page::Size();
int auxcode_prot = PROT_EXEC | PROT_READ | PROT_KREAD | PROT_KWRITE | PROT_FORK;
void* auxcode_hint = stack_hint;
size_t arg_size = 0;
size_t argv_size = sizeof(char*) * (argc + 1);
@ -850,13 +863,15 @@ int Process::Execute(const char* programname, const uint8_t* program,
struct segment stack_segment;
struct segment raw_tls_segment;
struct segment tls_segment;
struct segment auxcode_segment;
kthread_mutex_lock(&segment_lock);
if ( !(MapSegment(&arg_segment, stack_hint, arg_size, 0, stack_prot) &&
MapSegment(&stack_segment, stack_hint, stack_size, 0, stack_prot) &&
MapSegment(&raw_tls_segment, raw_tls_hint, raw_tls_size, 0, raw_tls_prot) &&
MapSegment(&tls_segment, tls_hint, tls_size, 0, tls_prot)) )
MapSegment(&tls_segment, tls_hint, tls_size, 0, tls_prot) &&
MapSegment(&auxcode_segment, auxcode_hint, auxcode_size, 0, auxcode_prot)) )
{
kthread_mutex_unlock(&segment_lock);
ResetForExecute();
@ -926,6 +941,20 @@ int Process::Execute(const char* programname, const uint8_t* program,
wrmsr(MSRID_FSBASE, (uint64_t) uthread);
#endif
uint8_t* auxcode = (uint8_t*) auxcode_segment.addr;
#if defined(__i386__)
sigreturn = (void (*)(void)) &auxcode[0];
auxcode[0] = 0xCD; /* int .... */
auxcode[1] = 0x83; /* ... $131 */
#elif defined(__x86_64__)
sigreturn = (void (*)(void)) &auxcode[0];
auxcode[0] = 0xCD; /* int .... */
auxcode[1] = 0x83; /* ... $131 */
#else
(void) auxcode;
#warning "You need to initialize auxcode with a sigreturn routine"
#endif
dtable->OnExecute();
ExecuteCPU(argc, target_argv, envc, target_envp, stack_segment.addr + stack_segment.size, entry, regs);
@ -1076,9 +1105,9 @@ cleanup_done:
return result;
}
static pid_t sys_tfork(int flags, tforkregs_t* user_regs)
static pid_t sys_tfork(int flags, struct tfork* user_regs)
{
tforkregs_t regs;
struct tfork regs;
if ( !CopyFromUser(&regs, user_regs, sizeof(regs)) )
return -1;
@ -1092,6 +1121,9 @@ static pid_t sys_tfork(int flags, tforkregs_t* user_regs)
if ( !(making_thread || making_process) )
return errno = ENOSYS, -1;
if ( regs.altstack.ss_flags & ~__SS_SUPPORTED_FLAGS )
return errno = EINVAL, -1;
CPU::InterruptRegisters cpuregs;
InitializeThreadRegisters(&cpuregs, &regs);
@ -1124,7 +1156,8 @@ static pid_t sys_tfork(int flags, tforkregs_t* user_regs)
thread->kernelstackpos = (addr_t) newkernelstack;
thread->kernelstacksize = curthread->kernelstacksize;
thread->kernelstackmalloced = true;
thread->sighandler = curthread->sighandler;
memcpy(&thread->signal_mask, &regs.sigmask, sizeof(sigset_t));
memcpy(&thread->signal_stack, &regs.altstack, sizeof(stack_t));
StartKernelThread(thread);
@ -1233,23 +1266,6 @@ pid_t Process::AllocatePID()
return nextpidtoallocate++;
}
// TODO: This is not thread safe.
pid_t Process::HackGetForegroundProcess()
{
for ( pid_t i = nextpidtoallocate; 1 <= i; i-- )
{
Process* process = Get(i);
if ( !process )
continue;
if ( process->pid <= 1 )
continue;
if ( process->iszombie )
continue;
return i;
}
return 0;
}
int ProcessCompare(Process* a, Process* b)
{
if ( a->pid < b->pid )
@ -1375,7 +1391,6 @@ static mode_t sys_getumask(void)
void Process::Init()
{
Syscall::Register(SYSCALL_EXECVE, (void*) sys_execve);
Syscall::Register(SYSCALL_EXIT, (void*) sys_exit);
Syscall::Register(SYSCALL_GETPAGESIZE, (void*) sys_getpagesize);
Syscall::Register(SYSCALL_GETPGID, (void*) sys_getpgid);
Syscall::Register(SYSCALL_GETPID, (void*) sys_getpid);

View File

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

View File

@ -18,116 +18,833 @@
Sortix. If not, see <http://www.gnu.org/licenses/>.
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 <errno.h>
#include <string.h>
#include <stdint.h>
#include <signal.h>
#include <sortix/sigaction.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/kernel.h>
#include <sortix/kernel/panic.h>
#include <sortix/kernel/process.h>
#include <sortix/kernel/signal.h>
#include <sortix/kernel/syscall.h>
#include <sortix/kernel/thread.h>
#if defined(__i386__) || defined(__x86_64__)
#include "x86-family/float.h"
#endif
namespace Sortix {
sigset_t default_ignored_signals;
sigset_t default_stop_signals;
sigset_t unblockable_signals;
// A per-cpu value whether a signal is pending in the running task.
extern "C" { volatile unsigned long asm_signal_is_pending = 0; }
void UpdatePendingSignals(Thread* thread) // thread->process->signal_lock held
{
struct sigaction* signal_actions = thread->process->signal_actions;
// Determine which signals wouldn't be ignored if received.
sigset_t handled_signals;
sigemptyset(&handled_signals);
for ( int i = 1; i < SIG_MAX_NUM; i++ )
{
if ( signal_actions[i].sa_handler == SIG_IGN )
continue;
if ( signal_actions[i].sa_handler == SIG_DFL &&
sigismember(&default_ignored_signals, i) )
continue;
// TODO: A process that is a member of an orphaned process group shall
// not be allowed to stop in response to the SIGTSTP, SIGTTIN, or
// SIGTTOU signals. In cases where delivery of one of these
// signals would stop such a process, the signal shall be
// discarded.
if ( /* is member of an orphaned process group */ false &&
signal_actions[i].sa_handler == SIG_DFL &&
sigismember(&default_stop_signals, i) )
continue;
sigaddset(&handled_signals, i);
}
// TODO: Handle that signals can be pending process-wide!
// Discard all requested signals that would be ignored if delivered.
sigandset(&thread->signal_pending, &thread->signal_pending, &handled_signals);
// Determine which signals are not blocked.
sigset_t permitted_signals;
signotset(&permitted_signals, &thread->signal_mask);
sigorset(&permitted_signals, &permitted_signals, &unblockable_signals);
// Determine which signals can currently be delivered to this thread.
sigset_t deliverable_signals;
sigandset(&deliverable_signals, &permitted_signals, &thread->signal_pending);
// Determine whether any signals can be delivered.
unsigned long is_pending = !sigisemptyset(&deliverable_signals) ? 1 : 0;
// Store whether a signal is pending in the virtual register.
if ( thread == CurrentThread() )
asm_signal_is_pending = is_pending;
else
thread->registers.signal_pending = is_pending;
}
static
int sys_sigaction(int signum,
const struct sigaction* user_newact,
struct sigaction* user_oldact)
{
if ( signum < 0 || signum == 0 /* null signal */ || SIG_MAX_NUM <= signum )
return errno = EINVAL;
Process* process = CurrentProcess();
ScopedLock lock(&process->signal_lock);
struct sigaction* kact = &process->signal_actions[signum];
// Let the caller know the previous action.
if ( user_oldact )
{
if ( !CopyToUser(user_oldact, kact, sizeof(struct sigaction)) )
return -1;
}
// Retrieve and validate the new signal action.
if ( user_newact )
{
struct sigaction newact;
if ( !CopyFromUser(&newact, user_newact, sizeof(struct sigaction)) )
return -1;
if ( newact.sa_flags & ~__SA_SUPPORTED_FLAGS )
return errno = EINVAL, -1;
if ( newact.sa_handler == SIG_ERR )
return errno = EINVAL, -1;
memcpy(kact, &newact, sizeof(struct sigaction));
// Signals may become discarded because of the new handler.
ScopedLock threads_lock(&process->threadlock);
for ( Thread* t = process->firstthread; t; t = t->nextsibling )
UpdatePendingSignals(t);
}
return 0;
}
static int sys_sigaltstack(const stack_t* user_newstack, stack_t* user_oldstack)
{
Thread* thread = CurrentThread();
if ( user_oldstack )
{
if ( !CopyToUser(user_oldstack, &thread->signal_stack, sizeof(stack_t)) )
return -1;
}
if ( user_newstack )
{
stack_t newstack;
if ( !CopyFromUser(&newstack, user_newstack, sizeof(stack_t)) )
return -1;
if ( newstack.ss_flags & ~__SS_SUPPORTED_FLAGS )
return errno = EINVAL, -1;
memcpy(&thread->signal_stack, &newstack, sizeof(stack_t));
}
return 0;
}
static int sys_sigpending(sigset_t* set)
{
Process* process = CurrentProcess();
Thread* thread = CurrentThread();
ScopedLock lock(&process->signal_lock);
// TODO: What about process-wide signals?
return CopyToUser(set, &thread->signal_pending, sizeof(sigset_t)) ? 0 : -1;
}
static
int sys_sigprocmask(int how, const sigset_t* user_set, sigset_t* user_oldset)
{
Process* process = CurrentProcess();
Thread* thread = CurrentThread();
// TODO: Signal masks are a per-thread property, perhaps this should be
// locked in another manner?
ScopedLock lock(&process->signal_lock);
// Let the caller know the previous signal mask.
if ( user_oldset )
{
if ( !CopyToUser(user_oldset, &thread->signal_mask, sizeof(sigset_t)) )
return -1;
}
// Update the current signal mask according to how.
if ( user_set )
{
sigset_t set;
if ( !CopyFromUser(&set, user_set, sizeof(sigset_t)) )
return -1;
switch ( how )
{
case SIG_BLOCK:
sigorset(&thread->signal_mask, &thread->signal_mask, &set);
break;
case SIG_UNBLOCK:
signotset(&set, &set);
sigandset(&thread->signal_mask, &thread->signal_mask, &set);
break;
case SIG_SETMASK:
memcpy(&thread->signal_mask, &set, sizeof(sigset_t));
break;
default:
return errno = EINVAL, -1;
};
UpdatePendingSignals(thread);
}
return 0;
}
static int sys_sigsuspend(const sigset_t* set)
{
Process* process = CurrentProcess();
Thread* thread = CurrentThread();
sigset_t old_signal_mask;
sigset_t new_signal_mask;
ScopedLock lock(&process->signal_lock);
// Only accept signals from the user-provided set if given.
if ( set )
{
if ( !CopyFromUser(&new_signal_mask, set, sizeof(sigset_t)) )
return -1;
memcpy(&old_signal_mask, &thread->signal_mask, sizeof(sigset_t));
memcpy(&thread->signal_mask, &new_signal_mask, sizeof(sigset_t));
UpdatePendingSignals(thread);
}
// Wait for a signal to happen or otherwise never halt.
kthread_cond_t never_triggered = KTHREAD_COND_INITIALIZER;
while ( !Signal::IsPending() )
kthread_cond_wait_signal(&never_triggered, &process->signal_lock);
// Restore the previous signal mask if the user gave its own set to wait on.
if ( set )
{
memcpy(&thread->signal_mask, &old_signal_mask, sizeof(sigset_t));
UpdatePendingSignals(thread);
}
// The system call never halts or it halts because a signal interrupted it.
return errno = EINTR, -1;
}
static int sys_kill(pid_t pid, int signum)
{
// Protect the kernel process.
if ( !pid )
return errno = EPERM, -1;
// TODO: Implement that pid == -1 means all processes!
bool process_group = pid < 0 ? (pid = -pid, true) : false;
// TODO: Race condition: The process could be deleted while we use it.
Process* process = Process::Get(pid);
if ( !process )
return errno = ESRCH, -1;
// TODO: Protect init?
// TODO: Check for permission.
// TODO: Check for zombies.
if ( process_group )
{
if ( !process->DeliverGroupSignal(signum) && errno != ESIGPENDING )
return -1;
return errno = 0, 0;
}
if ( !process->DeliverSignal(signum) && errno != ESIGPENDING )
return -1;
return errno = 0, 0;
}
bool Process::DeliverGroupSignal(int signum)
{
ScopedLock lock(&groupparentlock);
if ( !groupfirst )
return errno = ESRCH, false;
for ( Process* iter = groupfirst; iter; iter = iter->groupnext )
{
int saved_errno = errno;
if ( !iter->DeliverSignal(signum) && errno != ESIGPENDING )
{
// This is not currently an error condition.
}
errno = saved_errno;
}
return true;
}
bool Process::DeliverSignal(int signum)
{
ScopedLock lock(&threadlock);
if ( !firstthread )
return errno = EINIT, false;
// Broadcast particular signals to all the threads in the process.
if ( signum == SIGCONT || signum == SIGSTOP || signum == SIGKILL )
{
int saved_errno = errno;
for ( Thread* t = firstthread; t; t = t->nextsibling )
{
if ( !t->DeliverSignal(signum) && errno != ESIGPENDING )
{
// This is not currently an error condition.
}
}
errno = saved_errno;
return true;
}
// Route the signal to a suitable thread that accepts it.
// TODO: This isn't how signals should be routed to a particular thread.
if ( CurrentThread()->process == this )
return CurrentThread()->DeliverSignal(signum);
return firstthread->DeliverSignal(signum);
}
static int sys_raise(int signum)
{
if ( !CurrentThread()->DeliverSignal(signum) && errno != ESIGPENDING )
return -1;
return errno = 0, 0;
}
bool Thread::DeliverSignal(int signum)
{
ScopedLock lock(&process->signal_lock);
return DeliverSignalUnlocked(signum);
}
bool Thread::DeliverSignalUnlocked(int signum) // thread->process->signal_lock held
{
if ( signum <= 0 || SIG_MAX_NUM <= signum )
return errno = EINVAL, false;
// Discard the null signal, which does error checking, but doesn't actually
// deliver a signal to the process or thread.
if ( signum == 0 )
return true;
if ( sigismember(&signal_pending, signum) )
return errno = ESIGPENDING, false;
sigaddset(&signal_pending, signum);
if ( signum == SIGSTOP || signum == SIGTSTP ||
signum == SIGTTIN || signum == SIGTTOU )
sigdelset(&signal_pending, SIGCONT);
if ( signum == SIGCONT )
{
sigdelset(&signal_pending, SIGSTOP);
sigdelset(&signal_pending, SIGTSTP);
sigdelset(&signal_pending, SIGTTIN);
sigdelset(&signal_pending, SIGTTOU);
}
UpdatePendingSignals(this);
return true;
}
static int PickImportantSignal(const sigset_t* set)
{
if ( sigismember(set, SIGKILL) )
return SIGKILL;
if ( sigismember(set, SIGSTOP) )
return SIGSTOP;
for ( int i = 1; i < SIG_MAX_NUM; i++ )
if ( sigismember(set, i) )
return i;
return 0;
}
static void EncodeMachineContext(mcontext_t* mctx, CPU::InterruptRegisters* regs)
{
memset(mctx, 0, sizeof(*mctx));
#if defined(__i386__)
// TODO: REG_GS
// TODO: REG_FS
// TODO: REG_ES
// TODO: REG_DS
mctx->gregs[REG_EDI] = regs->edi;
mctx->gregs[REG_ESI] = regs->esi;
mctx->gregs[REG_EBP] = regs->ebp;
mctx->gregs[REG_ESP] = regs->esp;
mctx->gregs[REG_EBX] = regs->ebx;
mctx->gregs[REG_EDX] = regs->edx;
mctx->gregs[REG_ECX] = regs->ecx;
mctx->gregs[REG_EAX] = regs->eax;
mctx->gregs[REG_EIP] = regs->eip;
// TODO: REG_CS
mctx->gregs[REG_EFL] = regs->eflags & 0x0000FFFF;
mctx->gregs[REG_CR2] = regs->cr2;
// TODO: REG_SS
Float::Yield();
memcpy(mctx->fpuenv, CurrentThread()->fpuenvaligned, 512);
#elif defined(__x86_64__)
mctx->gregs[REG_R8] = regs->r8;
mctx->gregs[REG_R9] = regs->r9;
mctx->gregs[REG_R10] = regs->r10;
mctx->gregs[REG_R11] = regs->r11;
mctx->gregs[REG_R12] = regs->r12;
mctx->gregs[REG_R13] = regs->r13;
mctx->gregs[REG_R14] = regs->r14;
mctx->gregs[REG_R15] = regs->r15;
mctx->gregs[REG_RDI] = regs->rdi;
mctx->gregs[REG_RSI] = regs->rsi;
mctx->gregs[REG_RBP] = regs->rbp;
mctx->gregs[REG_RBX] = regs->rbx;
mctx->gregs[REG_RDX] = regs->rdx;
mctx->gregs[REG_RAX] = regs->rax;
mctx->gregs[REG_RCX] = regs->rcx;
mctx->gregs[REG_RSP] = regs->rsp;
mctx->gregs[REG_RIP] = regs->rip;
mctx->gregs[REG_EFL] = regs->rflags & 0x000000000000FFFF;
// TODO: REG_CSGSFS.
mctx->gregs[REG_CR2] = regs->cr2;
mctx->gregs[REG_FSBASE] = 0x0;
mctx->gregs[REG_GSBASE] = 0x0;
Float::Yield();
memcpy(mctx->fpuenv, CurrentThread()->fpuenvaligned, 512);
#else
#error "You need to implement conversion to mcontext"
#endif
}
static void DecodeMachineContext(mcontext_t* mctx, CPU::InterruptRegisters* regs)
{
#if defined(__i386__) || defined(__x86_64__)
unsigned long user_flags = FLAGS_CARRY | FLAGS_PARITY | FLAGS_AUX
| FLAGS_ZERO | FLAGS_SIGN | FLAGS_DIRECTION
| FLAGS_OVERFLOW;
#endif
#if defined(__i386__)
regs->edi = mctx->gregs[REG_EDI];
regs->esi = mctx->gregs[REG_ESI];
regs->ebp = mctx->gregs[REG_EBP];
regs->esp = mctx->gregs[REG_ESP];
regs->ebx = mctx->gregs[REG_EBX];
regs->edx = mctx->gregs[REG_EDX];
regs->ecx = mctx->gregs[REG_ECX];
regs->eax = mctx->gregs[REG_EAX];
regs->eip = mctx->gregs[REG_EIP];
regs->eflags &= ~user_flags;
regs->eflags |= mctx->gregs[REG_EFL] & user_flags;
regs->cr2 = mctx->gregs[REG_CR2];
Float::Yield();
memcpy(CurrentThread()->fpuenvaligned, mctx->fpuenv, 512);
#elif defined(__x86_64__)
regs->r8 = mctx->gregs[REG_R8];
regs->r9 = mctx->gregs[REG_R9];
regs->r10 = mctx->gregs[REG_R10];
regs->r11 = mctx->gregs[REG_R11];
regs->r12 = mctx->gregs[REG_R12];
regs->r13 = mctx->gregs[REG_R13];
regs->r14 = mctx->gregs[REG_R14];
regs->r15 = mctx->gregs[REG_R15];
regs->rdi = mctx->gregs[REG_RDI];
regs->rsi = mctx->gregs[REG_RSI];
regs->rbp = mctx->gregs[REG_RBP];
regs->rbx = mctx->gregs[REG_RBX];
regs->rdx = mctx->gregs[REG_RDX];
regs->rax = mctx->gregs[REG_RAX];
regs->rcx = mctx->gregs[REG_RCX];
regs->rsp = mctx->gregs[REG_RSP];
regs->rip = mctx->gregs[REG_RIP];
regs->rflags &= ~user_flags;
regs->rflags |= mctx->gregs[REG_EFL] & user_flags;
regs->cr2 = mctx->gregs[REG_CR2];
Float::Yield();
memcpy(CurrentThread()->fpuenvaligned, mctx->fpuenv, 512);
#else
#error "You need to implement conversion to mcontext"
#endif
}
#if defined(__i386__)
struct stack_frame
{
unsigned long sigreturn;
int signum_param;
siginfo_t* siginfo_param;
ucontext_t* ucontext_param;
void* cookie_param;
siginfo_t siginfo;
ucontext_t ucontext;
};
#elif defined(__x86_64__)
struct stack_frame
{
unsigned long sigreturn;
siginfo_t siginfo;
ucontext_t ucontext;
};
#else
#error "You need to implement struct stack_frame"
#endif
void Thread::HandleSignal(CPU::InterruptRegisters* regs)
{
assert(Interrupt::IsEnabled());
assert(this == CurrentThread());
ScopedLock lock(&process->signal_lock);
assert(process->sigreturn);
retry_another_signal:
// Determine which signals are not blocked.
sigset_t permitted_signals;
signotset(&permitted_signals, &signal_mask);
sigorset(&permitted_signals, &permitted_signals, &unblockable_signals);
// Determine which signals can currently be delivered to this thread.
sigset_t deliverable_signals;
sigandset(&deliverable_signals, &permitted_signals, &signal_pending);
// Decide which signal to deliver to the thread.
int signum = PickImportantSignal(&deliverable_signals);
if ( !signum )
return;
// Unmark the selected signal as pending.
sigdelset(&signal_pending, signum);
UpdatePendingSignals(this);
regs->signal_pending = asm_signal_is_pending;
// Destroy the current thread if the signal is critical.
if ( signum == SIGKILL )
{
lock.Reset();
kthread_exit();
}
struct sigaction* action = &process->signal_actions[signum];
// Stop the current thread upon receipt of a stop signal that isn't handled
// or cannot be handled (SIGSTOP).
if ( (action->sa_handler == SIG_DFL &&
sigismember(&default_stop_signals, signum) ) ||
signum == SIGSTOP )
{
Log::PrintF("%s:%u: `%s' FIXME SIGSTOP\n", __FILE__, __LINE__, __PRETTY_FUNCTION__);
// TODO: Stop the current process.
// TODO: Deliver SIGCHLD to the parent except if SA_NOCLDSTOP is set in
// the parent's SIGCHLD sigaction.
// TODO: SIGCHLD should not be delivered until all the threads in the
// process has received SIGSTOP and stopped?
// TODO: SIGKILL must still be deliverable to a stopped process.
}
// Resume the current thread upon receipt of SIGCONT.
if ( signum == SIGCONT )
{
Log::PrintF("%s:%u: `%s' FIXME SIGCONT\n", __FILE__, __LINE__, __PRETTY_FUNCTION__);
// TODO: Resume the current process.
// TODO: Can SIGCONT be masked?
// TODO: Can SIGCONT be handled?
// TODO: Can SIGCONT be ignored?
// TODO: Deliver SIGCHLD to the parent except if SA_NOCLDSTOP is set in
// the parent's SIGCHLD sigaction.
}
// Signals that would be ignored are already filtered away at this point.
assert(action->sa_handler != SIG_IGN);
assert(action->sa_handler != SIG_DFL || !sigismember(&default_ignored_signals, signum));
// The default action must be to terminate the process. Signals that are
// ignored by default got discarded earlier.
if ( action->sa_handler == SIG_DFL )
{
kthread_mutex_unlock(&process->signal_lock);
process->ExitThroughSignal(signum);
kthread_mutex_lock(&process->signal_lock);
goto retry_another_signal;
}
// At this point we have to attempt to invoke the user-space signal handler,
// which will then return control to us through sigreturn. However, we can't
// save the kernel state because 1) we can't trust the user-space stack 2)
// we can't rely on the kernel stack being intact as the signal handler may
// invoke system calls. For those reasons, we'll have to modify the saved
// registers so they restore a user-space state. We can do this because
// threads in the kernel cannot be delivered signals except when returning
// from a system call, so we'll simply save the state that would have been
// returned to user-space had no signal occured.
if ( !regs->InUserspace() )
{
#if defined(__i386__)
uint32_t* params = (uint32_t*) regs->ebx;
regs->eip = params[0];
regs->eflags = params[2];
regs->esp = params[3];
regs->cs = UCS | URPL;
regs->ds = UDS | URPL;
regs->ss = UDS | URPL;
#elif defined(__x86_64__)
regs->rip = regs->rdi;
regs->rflags = regs->rsi;
regs->rsp = regs->r8;
regs->cs = UCS | URPL;
regs->ds = UDS | URPL;
regs->ss = UDS | URPL;
#else
#error "You may need to fix the registers"
#endif
}
sigset_t new_signal_mask;
memcpy(&new_signal_mask, &action->sa_mask, sizeof(sigset_t));
sigorset(&new_signal_mask, &new_signal_mask, &signal_mask);
// Prevent signals from interrupting themselves by default.
if ( !(action->sa_flags & SA_NODEFER) )
sigaddset(&new_signal_mask, signum);
// Determine whether we use an alternate signal stack.
bool signal_uses_altstack = action->sa_flags & SA_ONSTACK;
bool usable_altstack = !(signal_stack.ss_flags & (SS_DISABLE | SS_ONSTACK));
bool use_altstack = signal_uses_altstack && usable_altstack;
// Determine which signal stack to use and what to save.
stack_t old_signal_stack, new_signal_stack;
uintptr_t stack_location;
if ( use_altstack )
{
old_signal_stack = signal_stack;
new_signal_stack = signal_stack;
new_signal_stack.ss_flags |= SS_ONSTACK;
#if defined(__i386__) || defined(__x86_64__)
stack_location = (uintptr_t) signal_stack.ss_sp + signal_stack.ss_size;
#else
#error "You need to implement getting the alternate stack pointer"
#endif
}
else
{
old_signal_stack.ss_sp = NULL;
old_signal_stack.ss_flags = SS_DISABLE;
old_signal_stack.ss_size = 0;
new_signal_stack = signal_stack;
#if defined(__i386__)
stack_location = (uintptr_t) regs->esp;
#elif defined(__x86_64__)
stack_location = (uintptr_t) regs->rsp;
#else
#error "You need to implement getting the user-space stack pointer"
#endif
}
CPU::InterruptRegisters new_regs = *regs;
struct stack_frame stack_frame;
memset(&stack_frame, 0, sizeof(stack_frame));
void* handler_ptr = action->sa_flags & SA_COOKIE ?
(void*) action->sa_sigaction_cookie :
action->sa_flags & SA_SIGINFO ?
(void*) action->sa_sigaction :
(void*) action->sa_handler;
#if defined(__i386__)
stack_location -= sizeof(stack_frame);
stack_location &= ~(4UL-1UL);
struct stack_frame* stack = (struct stack_frame*) stack_location;
stack_frame.sigreturn = (unsigned long) process->sigreturn;
stack_frame.signum_param = signum;
stack_frame.siginfo_param = &stack->siginfo;
stack_frame.ucontext_param = &stack->ucontext;
stack_frame.cookie_param = action->sa_cookie;
new_regs.esp = (unsigned long) stack;
new_regs.eip = (unsigned long) handler_ptr;
new_regs.eflags &= ~FLAGS_DIRECTION;
#elif defined(__x86_64__)
stack_location -= 128; /* Red zone. */
stack_location -= sizeof(stack_frame);
stack_location = ((stack_location - 8) & ~(16UL-1UL)) + 8;
struct stack_frame* stack = (struct stack_frame*) stack_location;
stack_frame.sigreturn = (unsigned long) process->sigreturn;
new_regs.rdi = (unsigned long) signum;
new_regs.rsi = (unsigned long) &stack->siginfo;
new_regs.rdx = (unsigned long) &stack->ucontext;
new_regs.rcx = (unsigned long) action->sa_cookie;
new_regs.rsp = (unsigned long) stack;
new_regs.rip = (unsigned long) handler_ptr;
new_regs.rflags &= ~FLAGS_DIRECTION;
#else
#error "You need to format the stack frame"
#endif
// Format the siginfo into the stack frame.
stack_frame.siginfo.si_signo = signum;
#if defined(__i386__) || defined(__x86_64__)
// TODO: Is this cr2 value trustworthy? I don't think it is.
if ( signum == SIGSEGV )
stack_frame.siginfo.si_addr = (void*) regs->cr2;
#else
#warning "You need to tell user-space where it crashed"
#endif
// Format the ucontext into the stack frame.
stack_frame.ucontext.uc_link = NULL;
memcpy(&stack_frame.ucontext.uc_sigmask, &signal_mask, sizeof(signal_mask));
memcpy(&stack_frame.ucontext.uc_stack, &signal_stack, sizeof(signal_stack));
EncodeMachineContext(&stack_frame.ucontext.uc_mcontext, regs);
if ( !CopyToUser(stack, &stack_frame, sizeof(stack_frame)) )
{
// Self-destruct if we crashed during delivering the crash signal.
if ( signum == SIGSEGV )
{
kthread_mutex_unlock(&process->signal_lock);
process->ExitThroughSignal(signum);
kthread_mutex_lock(&process->signal_lock);
goto retry_another_signal;
}
// Deliver SIGSEGV if we could not deliver the signal on the stack.
// TODO: Is it possible to block SIGSEGV here?
kthread_mutex_unlock(&process->signal_lock);
DeliverSignal(SIGSEGV);
kthread_mutex_lock(&process->signal_lock);
goto retry_another_signal;
}
// Update the current signal mask.
memcpy(&signal_mask, &new_signal_mask, sizeof(sigset_t));
// Update the current alternate signal stack.
signal_stack = new_signal_stack;
// Update the current registers.
*regs = new_regs;
// TODO: SA_RESETHAND:
// "If set, the disposition of the signal shall be reset to SIG_DFL
// and the SA_SIGINFO flag shall be cleared on entry to the signal
// handler. Note: SIGILL and SIGTRAP cannot be automatically reset
// when delivered; the system silently enforces this restriction."
// Run the signal handler by returning to user-space.
return;
}
void Thread::HandleSigreturn(CPU::InterruptRegisters* regs)
{
assert(Interrupt::IsEnabled());
assert(this == CurrentThread());
ScopedLock lock(&process->signal_lock);
struct stack_frame stack_frame;
const struct stack_frame* user_stack_frame;
#if defined(__i386__)
user_stack_frame = (const struct stack_frame*) (regs->esp - 4);
#elif defined(__x86_64__)
user_stack_frame = (const struct stack_frame*) (regs->rsp - 8);
#else
#error "You need to locate the stack we passed the signal handler"
#endif
if ( CopyFromUser(&stack_frame, user_stack_frame, sizeof(stack_frame)) )
{
memcpy(&signal_mask, &stack_frame.ucontext.uc_sigmask, sizeof(signal_mask));
memcpy(&signal_stack, &stack_frame.ucontext.uc_stack, sizeof(signal_stack));
signal_stack.ss_flags &= __SS_SUPPORTED_FLAGS;
DecodeMachineContext(&stack_frame.ucontext.uc_mcontext, regs);
}
UpdatePendingSignals(this);
regs->signal_pending = asm_signal_is_pending;
lock.Reset();
HandleSignal(regs);
}
namespace Signal {
const int PRIORITIES[SIG__NUM_DECLARED] =
{
SIG_PRIO_NORMAL, // unused
SIG_PRIO_NORMAL, // SIGHUP
SIG_PRIO_NORMAL, // SIGINT
SIG_PRIO_NORMAL, // SIGQUIT
SIG_PRIO_CORE, // SIGILL
SIG_PRIO_CORE, // SIGTRAP
SIG_PRIO_CORE, // SIGABRT
SIG_PRIO_CORE, // SIGEMT
SIG_PRIO_CORE, // SIGFPE
SIG_PRIO_KILL, // SIGKILL
SIG_PRIO_CORE, // SIGBUS
SIG_PRIO_CORE, // SIGSEGV
SIG_PRIO_CORE, // SIGSYS
SIG_PRIO_NORMAL, // SIGPIPE
SIG_PRIO_NORMAL, // SIGALRM
SIG_PRIO_NORMAL, // SIGTERM
SIG_PRIO_NORMAL, // SIGUSR1
SIG_PRIO_NORMAL, // SIGUSR2
SIG_PRIO_NORMAL, // SIGCHLD
SIG_PRIO_HIGH, // SIGPWR
SIG_PRIO_NORMAL, // SIGWINCH
SIG_PRIO_NORMAL, // SIGURG
SIG_PRIO_NORMAL, // obsolete
SIG_PRIO_STOP, // SIGSTOP
SIG_PRIO_STOP, // SIGTSTP
SIG_PRIO_STOP, // SIGCONT
SIG_PRIO_STOP, // SIGTTIN
SIG_PRIO_STOP, // SIGTTOU
SIG_PRIO_NORMAL, // SIGVTALRM
SIG_PRIO_NORMAL, // obsolete
SIG_PRIO_CORE, // SIGXCPU
SIG_PRIO_CORE, // SIGXFSZ
SIG_PRIO_NORMAL, // SIGCORE
SIG_PRIO_NORMAL, // SIGLWP
SIG_PRIO_NORMAL, // SIGAIO
};
int Priority(int signum)
{
assert(0 <= signum && signum < SIG_MAX_NUM);
if ( !signum )
return -1;
if ( SIG__NUM_DECLARED <= signum )
return SIG_PRIO_NORMAL;
return PRIORITIES[signum];
}
Queue::Queue()
{
for ( int i = 1; i < SIG_MAX_NUM; i++ )
pending[i] = false;
}
void Queue::Push(int signum)
{
assert(0 < signum && signum < SIG_MAX_NUM);
pending[signum] = true;
}
int Queue::Pop(int cursig)
{
int best = 0;
int bestprio = Priority(cursig);
for ( int i = 1; i < SIG_MAX_NUM; i++ )
if ( pending[i] && bestprio < Priority(i) )
{
best = i;
bestprio = Priority(i);
}
pending[best] = false;
return best;
}
void Dispatch(CPU::InterruptRegisters* regs, void* /*user*/)
void DispatchHandler(CPU::InterruptRegisters* regs, void* /*user*/)
{
return CurrentThread()->HandleSignal(regs);
}
void Return(CPU::InterruptRegisters* regs, void* /*user*/)
void ReturnHandler(CPU::InterruptRegisters* regs, void* /*user*/)
{
return CurrentThread()->HandleSigreturn(regs);
}
void Init()
{
sigemptyset(&default_ignored_signals);
sigaddset(&default_ignored_signals, SIGCHLD);
sigaddset(&default_ignored_signals, SIGURG);
sigaddset(&default_ignored_signals, SIGPWR);
sigaddset(&default_ignored_signals, SIGWINCH);
sigemptyset(&default_stop_signals);
sigaddset(&default_stop_signals, SIGTSTP);
sigaddset(&default_stop_signals, SIGTTIN);
sigaddset(&default_stop_signals, SIGTTOU);
sigemptyset(&unblockable_signals);
sigaddset(&unblockable_signals, SIGKILL);
sigaddset(&unblockable_signals, SIGSTOP);
Syscall::Register(SYSCALL_KILL, (void*) sys_kill);
Syscall::Register(SYSCALL_RAISE, (void*) sys_raise);
Syscall::Register(SYSCALL_SIGACTION, (void*) sys_sigaction);
Syscall::Register(SYSCALL_SIGALTSTACK, (void*) sys_sigaltstack);
Syscall::Register(SYSCALL_SIGPENDING, (void*) sys_sigpending);
Syscall::Register(SYSCALL_SIGPROCMASK, (void*) sys_sigprocmask);
Syscall::Register(SYSCALL_SIGSUSPEND, (void*) sys_sigsuspend);
}
} // namespace Signal
} // namespace Sortix

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.
@ -22,8 +22,11 @@
*******************************************************************************/
#include <sys/wait.h>
#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <sortix/exit.h>
@ -58,15 +61,17 @@ Thread::Thread()
kernelstackpos = 0;
kernelstacksize = 0;
kernelstackmalloced = false;
currentsignal = 0;
siglevel = 0;
sighandler = NULL;
pledged_destruction = false;
terminated = false;
fpuinitialized = false;
// If malloc isn't 16-byte aligned, then we can't rely on offsets in
// our own class, so we'll just fix ourselves nicely up.
unsigned long fpuaddr = ((unsigned long) fpuenv+16UL) & ~(16UL-1UL);
fpuenvaligned = (uint8_t*) fpuaddr;
sigemptyset(&signal_pending);
sigemptyset(&signal_mask);
memset(&signal_stack, 0, sizeof(signal_stack));
signal_stack.ss_flags = SS_DISABLE;
}
Thread::~Thread()
@ -89,11 +94,6 @@ addr_t Thread::SwitchAddressSpace(addr_t newaddrspace)
return result;
}
// Last chance to clean up user-space things before this thread dies.
void Thread::LastPrayer()
{
}
extern "C" void BootstrapKernelThread(void* user, ThreadEntry entry)
{
entry(user);
@ -196,98 +196,19 @@ Thread* RunKernelThread(ThreadEntry entry, void* user, size_t stacksize)
return thread;
}
void Thread::HandleSignal(CPU::InterruptRegisters* regs)
{
int signum = signalqueue.Pop(currentsignal);
regs->signal_pending = 0;
if ( !signum )
return;
if ( !sighandler )
return;
if ( SIG_NUM_LEVELS <= siglevel )
return;
// Signals can't return to kernel mode because the kernel stack may have
// been overwritten by a system call during the signal handler. Correct
// the return state so it returns to userspace and not the kernel.
if ( !regs->InUserspace() )
HandleSignalFixupRegsCPU(regs);
if ( signum == SIGKILL )
{
// We need to run the OnSigKill method here with interrupts enabled
// and on our own stack. But this method this may have been called
// from the scheduler on any stack, so we need to do a little
// bootstrap and switch to our own stack.
GotoOnSigKill(regs);
return;
}
int level = siglevel++;
signums[level] = currentsignal = signum;
memcpy(sigregs + level, regs, sizeof(*regs));
HandleSignalCPU(regs);
}
void Thread::HandleSigreturn(CPU::InterruptRegisters* regs)
{
if ( !siglevel )
return;
siglevel--;
currentsignal = siglevel ? signums[siglevel-1] : 0;
memcpy(regs, sigregs + siglevel, sizeof(*regs));
regs->signal_pending = 0;
// Check if a more important signal is pending.
HandleSignal(regs);
}
extern "C" void Thread__OnSigKill(Thread* thread)
{
thread->OnSigKill();
}
void Thread::OnSigKill()
{
LastPrayer();
kthread_exit();
}
void Thread::SetHavePendingSignals()
{
// TODO: This doesn't really work if Interrupt::IsCPUInterrupted()!
if ( CurrentThread() == this )
asm_signal_is_pending = 1;
else
registers.signal_pending = 1;
}
bool Thread::DeliverSignal(int signum)
{
if ( signum <= 0 || 128 <= signum )
return errno = EINVAL, false;
bool wasenabled = Interrupt::SetEnabled(false);
signalqueue.Push(signum);
SetHavePendingSignals();
Interrupt::SetEnabled(wasenabled);
return true;
}
static int sys_exit_thread(int status,
static int sys_exit_thread(int requested_exit_code,
int flags,
const struct exit_thread* user_extended)
{
if ( flags & ~(EXIT_THREAD_ONLY_IF_OTHERS |
EXIT_THREAD_UNMAP |
EXIT_THREAD_ZERO) )
EXIT_THREAD_ZERO |
EXIT_THREAD_TLS_UNMAP |
EXIT_THREAD_PROCESS |
EXIT_THREAD_DUMP_CORE) )
return errno = EINVAL, -1;
if ( (flags & EXIT_THREAD_ONLY_IF_OTHERS) && (flags & EXIT_THREAD_PROCESS) )
return errno = EINVAL, -1;
Thread* thread = CurrentThread();
@ -309,12 +230,25 @@ static int sys_exit_thread(int status,
{
if ( iter == thread )
continue;
if ( iter->pledged_destruction )
continue;
if ( iter->terminated )
continue;
is_others = true;
}
if ( !(flags & EXIT_THREAD_ONLY_IF_OTHERS) || is_others )
thread->pledged_destruction = true;
bool are_threads_exiting = false;
if ( (flags & EXIT_THREAD_PROCESS) || !is_others )
process->threads_exiting = true;
else if ( process->threads_exiting )
are_threads_exiting = true;
kthread_mutex_unlock(&thread->process->threadlock);
// Self-destruct if another thread began exiting the process.
if ( are_threads_exiting )
kthread_exit();
if ( (flags & EXIT_THREAD_ONLY_IF_OTHERS) && !is_others )
return errno = ESRCH, -1;
@ -326,62 +260,61 @@ static int sys_exit_thread(int status,
Memory::UnmapMemory(process, (uintptr_t) extended.unmap_from,
extended.unmap_size);
Memory::Flush();
// TODO: The segment is not actually removed!
}
if ( flags & EXIT_THREAD_TLS_UNMAP &&
Page::IsAligned((uintptr_t) extended.tls_unmap_from) &&
extended.tls_unmap_size )
{
ScopedLock lock(&process->segment_lock);
Memory::UnmapMemory(process, (uintptr_t) extended.tls_unmap_from,
extended.tls_unmap_size);
Memory::Flush();
}
if ( flags & EXIT_THREAD_ZERO )
ZeroUser(extended.zero_from, extended.zero_size);
if ( !is_others )
thread->process->Exit(status);
{
// Validate the requested exit code such that the process can't exit
// with an impossible exit status or that it wasn't actually terminated.
int the_nature = WNATURE(requested_exit_code);
int the_status = WEXITSTATUS(requested_exit_code);
int the_signal = WTERMSIG(requested_exit_code);
if ( the_nature == WNATURE_EXITED )
the_signal = 0;
else if ( the_nature == WNATURE_SIGNALED )
{
if ( the_signal == 0 /* null signal */ ||
the_signal == SIGSTOP ||
the_signal == SIGTSTP ||
the_signal == SIGTTIN ||
the_signal == SIGTTOU ||
the_signal == SIGCONT )
the_signal = SIGKILL;
the_status = 128 + the_signal;
}
else
{
the_nature = WNATURE_SIGNALED;
the_signal = SIGKILL;
}
requested_exit_code = WCONSTRUCT(the_nature, the_status, the_signal);
thread->process->ExitWithCode(requested_exit_code);
}
kthread_exit();
}
static int sys_kill(pid_t pid, int signum)
{
// Protect the system idle process.
if ( !pid )
return errno = EPERM, -1;
// TODO: Implement that pid == -1 means all processes!
bool process_group = pid < 0 ? (pid = -pid, true) : false;
// If we kill our own process, deliver the signal to this thread.
Thread* currentthread = CurrentThread();
if ( currentthread->process->pid == pid )
return currentthread->DeliverSignal(signum) ? 0 : -1;
// TODO: Race condition: The process could be deleted while we use it.
Process* process = Process::Get(pid);
if ( !process )
return errno = ESRCH, -1;
// TODO: Protect init?
// TODO: Check for permission.
// TODO: Check for zombies.
return process_group ?
process->DeliverGroupSignal(signum) ? 0 : -1 :
process->DeliverSignal(signum) ? 0 : -1;
}
static int sys_raise(int signum)
{
return CurrentThread()->DeliverSignal(signum) ? 0 : -1;
}
static int sys_register_signal_handler(sighandler_t sighandler)
{
CurrentThread()->sighandler = sighandler;
return 0;
}
void Thread::Init()
{
Syscall::Register(SYSCALL_EXIT_THREAD, (void*) sys_exit_thread);
Syscall::Register(SYSCALL_KILL, (void*) sys_kill);
Syscall::Register(SYSCALL_RAISE, (void*) sys_raise);
Syscall::Register(SYSCALL_REGISTER_SIGNAL_HANDLER, (void*) sys_register_signal_handler);
}
} // namespace Sortix

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.
@ -25,6 +25,7 @@
#include <sys/types.h>
#include <errno.h>
#include <limits.h>
#include <timespec.h>
#include <sortix/clock.h>
@ -138,7 +139,12 @@ static int sys_timer_getoverrun(timer_t timerid)
if ( !timer )
return -1;
// TODO: This is not fully kept track of yet.
if ( (size_t) INT_MAX < timer->num_overrun_events)
return INT_MAX;
// TODO: How does the caller reset the overrun count back to 0? Should we
// adopt the Linux semantics where it resets back to 0 after INT_MAX?
// How about signed overflow in the kernel and in the user process?
return 0;
}
@ -164,15 +170,14 @@ static void timer_callback(Clock* /*clock*/, Timer* timer, void* user)
Process* process = user_timer->process;
ScopedLock lock(&process->user_timers_lock);
size_t current_overrun = timer->num_overrun_events;
// TODO: This delivery facility is insufficient! sigevent is much more
// powerful than sending a simple old-school signal.
// TODO: If the last signal from last time is still being processed, we need
// to handle the sum of overrun. I'm not sure how to handle overrun
// properly, so we'll just pretend to user-space it never happens when
// it does and we do some of the bookkeeping.
(void) current_overrun;
process->DeliverSignal(user_timer->event.sigev_signo);
if ( !process->DeliverSignal(user_timer->event.sigev_signo) &&
errno == ESIGPENDING )
{
if ( timer->num_overrun_events < SIZE_MAX )
timer->num_overrun_events++;
}
}
static int sys_timer_settime(timer_t timerid, int flags,

View File

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

View File

@ -117,43 +117,4 @@ void SetupKernelThreadRegs(CPU::InterruptRegisters* regs, ThreadEntry entry,
regs->signal_pending = 0;
}
void Thread::HandleSignalFixupRegsCPU(CPU::InterruptRegisters* regs)
{
if ( regs->InUserspace() )
return;
regs->rip = regs->rdi;
regs->rflags = regs->rsi;
regs->rsp = regs->r8;
regs->cs = UCS | URPL;
regs->ds = UDS | URPL;
regs->ss = UDS | URPL;
}
void Thread::HandleSignalCPU(CPU::InterruptRegisters* regs)
{
const size_t STACK_ALIGNMENT = 16UL;
const size_t RED_ZONE_SIZE = 128UL;
regs->rsp -= RED_ZONE_SIZE;
regs->rsp &= ~(STACK_ALIGNMENT-1UL);
regs->rbp = regs->rsp;
regs->rdi = currentsignal;
regs->rip = (size_t) sighandler;
regs->rflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
regs->kerrno = 0;
regs->signal_pending = 0;
}
void Thread::GotoOnSigKill(CPU::InterruptRegisters* regs)
{
regs->rip = (unsigned long) Thread__OnSigKill;
regs->rdi = (unsigned long) this;
regs->rsp = regs->rbp = kernelstackpos + kernelstacksize;
regs->rflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
regs->cs = KCS | KRPL;
regs->ds = KDS | KRPL;
regs->ss = KDS | KRPL;
regs->kerrno = 0;
regs->signal_pending = 0;
}
} // namespace Sortix

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.
@ -34,6 +34,7 @@ namespace Sortix {
namespace Float {
static Thread* fputhread;
bool fpu_is_enabled = false;
static inline void InitFPU()
{
@ -52,6 +53,53 @@ static inline void LoadState(const uint8_t* src)
asm volatile ("fxrstor (%0)" : : "r"(src));
}
void Yield()
{
Thread* thread = CurrentThread();
Interrupt::Disable();
bool fpu_was_enabled = fpu_is_enabled;
// The FPU contains the registers for this thread.
if ( fputhread == thread )
{
if ( !fpu_was_enabled )
EnableFPU();
SaveState(thread->fpuenvaligned);
fputhread = NULL;
DisableFPU();
}
// This thread has used the FPU once.
else if ( thread->fpuinitialized )
{
// Nothing needs to be done, the FPU is owned by another thread and the
// FPU registers are already stored in the thread structure.
}
// This thread has never used the FPU and needs its registers initialized.
else
{
if ( !fpu_was_enabled )
EnableFPU();
if ( fputhread )
SaveState(fputhread->fpuenvaligned);
InitFPU();
SaveState(thread->fpuenvaligned);
if ( fputhread )
LoadState(fputhread->fpuenvaligned);
if ( !fpu_was_enabled )
DisableFPU();
}
Interrupt::Enable();
}
static void OnFPUAccess(CPU::InterruptRegisters* /*regs*/, void* /*user*/)
{
EnableFPU();

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

View File

@ -30,14 +30,6 @@
namespace Sortix {
namespace IDT {
static struct idt_entry idt_entries[256];
void Init()
{
memset(&idt_entries, 0, sizeof(idt_entries));
Set(idt_entries, 256);
}
void Set(struct idt_entry* table, size_t length)
{
size_t limit = sizeof(idt_entry) * length - 1;
@ -69,10 +61,5 @@ void SetEntry(struct idt_entry* entry, uintptr_t handler, uint16_t selector, uin
#endif
}
void SetEntry(uint8_t num, uintptr_t handler, uint16_t selector, uint8_t flags, uint8_t ist)
{
SetEntry(idt_entries + num, handler, selector, flags, ist);
}
} // namespace IDT
} // namespace Sortix

View File

@ -230,13 +230,13 @@ void Init()
RegisterRawHandler(47, irq15, false, false);
RegisterRawHandler(128, syscall_handler, true, true);
RegisterRawHandler(129, yield_cpu_handler, true, false);
RegisterRawHandler(130, isr130, true, false);
RegisterRawHandler(131, isr131, true, false);
RegisterRawHandler(130, isr130, true, true);
RegisterRawHandler(131, isr131, true, true);
RegisterRawHandler(132, thread_exit_handler, true, false);
RegisterHandler(129, Scheduler::InterruptYieldCPU, NULL);
RegisterHandler(130, Signal::Dispatch, NULL);
RegisterHandler(131, Signal::Return, NULL);
RegisterHandler(130, Signal::DispatchHandler, NULL);
RegisterHandler(131, Signal::ReturnHandler, NULL);
RegisterHandler(132, Scheduler::ThreadExitCPU, NULL);
IDT::Set(interrupt_table, NUM_INTERRUPTS);
@ -313,6 +313,19 @@ void UserCrashHandler(CPU::InterruptRegisters* regs)
// Execute this crash handler with preemption on.
Interrupt::Enable();
// TODO: Also send signals for other types of user-space crashes.
if ( regs->int_no == 14 /* Page fault */ )
{
struct sigaction* act = &CurrentProcess()->signal_actions[SIGSEGV];
kthread_mutex_lock(&CurrentProcess()->signal_lock);
bool handled = act->sa_handler != SIG_DFL && act->sa_handler != SIG_IGN;
if ( handled )
CurrentThread()->DeliverSignalUnlocked(SIGSEGV);
kthread_mutex_unlock(&CurrentProcess()->signal_lock);
if ( handled )
return Signal::DispatchHandler(regs, NULL);
}
// Walk and print the stack frames if this is a debug build.
if ( CALLTRACE_USER )
CrashCalltrace(regs);
@ -330,13 +343,10 @@ void UserCrashHandler(CPU::InterruptRegisters* regs)
// Exit the process with the right error code.
// TODO: Send a SIGINT, SIGBUS, or whatever instead.
CurrentProcess()->Exit(139);
// TODO: Is it strictly needed or even desirable to disable preemption here?
Interrupt::Disable();
CurrentProcess()->ExitThroughSignal(SIGSEGV);
// Deliver signals to this thread so it can exit correctly.
Signal::Dispatch(regs);
Signal::DispatchHandler(regs, NULL);
}
extern "C" void interrupt_handler(CPU::InterruptRegisters* regs)

View File

@ -99,11 +99,3 @@ asm_call_BootstrapKernelThread:
call BootstrapKernelThread
# BootstrapKernelThread is noreturn, no need for code here.
.size asm_call_BootstrapKernelThread, . - asm_call_BootstrapKernelThread
.global asm_call_Thread__OnSigKill
.type asm_call_Thread__OnSigKill, @function
asm_call_Thread__OnSigKill:
pushl %edi
call Thread__OnSigKill
# Thread__OnSigKill is noreturn, no need for code here.
.size asm_call_Thread__OnSigKill, . - asm_call_Thread__OnSigKill

View File

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

View File

@ -96,50 +96,4 @@ void SetupKernelThreadRegs(CPU::InterruptRegisters* regs, ThreadEntry entry,
regs->signal_pending = 0;
}
void Thread::HandleSignalFixupRegsCPU(CPU::InterruptRegisters* regs)
{
if ( regs->InUserspace() )
return;
uint32_t* params = (uint32_t*) regs->ebx;
regs->eip = params[0];
regs->eflags = params[2];
regs->esp = params[3];
regs->cs = UCS | URPL;
regs->ds = UDS | URPL;
regs->ss = UDS | URPL;
}
void Thread::HandleSignalCPU(CPU::InterruptRegisters* regs)
{
const size_t STACK_ALIGNMENT = 16UL;
const size_t RED_ZONE_SIZE = 128UL;
regs->esp -= RED_ZONE_SIZE;
regs->esp &= ~(STACK_ALIGNMENT-1UL);
regs->ebp = regs->esp;
regs->edi = currentsignal;
regs->eip = (size_t) sighandler;
regs->eflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
regs->kerrno = 0;
regs->signal_pending = 0;
}
extern "C" void asm_call_Thread__OnSigKill(void);
void Thread::GotoOnSigKill(CPU::InterruptRegisters* regs)
{
regs->eip = (unsigned long) asm_call_Thread__OnSigKill;
regs->edi = (unsigned long) this;
// TODO: HACK: The -256 is because if we are jumping to the safe stack
// we currently are on, this may not be fully supported by interrupt.s
// that is quite aware of this (but isn't perfect). If our destination
// is further down the stack, then we are probably safe.
regs->esp = regs->ebp = kernelstackpos + kernelstacksize - 256;
regs->eflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
regs->cs = KCS | KRPL;
regs->ds = KDS | KRPL;
regs->ss = KDS | KRPL;
regs->kerrno = 0;
regs->signal_pending = 0;
}
} // namespace Sortix

View File

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

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.
@ -27,18 +27,24 @@
#include <sys/cdefs.h>
#include <sortix/__/sigset.h>
__BEGIN_DECLS
#if defined(__x86_64__)
typedef unsigned long jmp_buf[8];
typedef unsigned long sigjmp_buf[8 + 1 + __SIGSET_NUM_SIGNALS / (sizeof(unsigned long int) * 8)];
#elif defined(__i386__)
typedef unsigned long jmp_buf[6];
typedef unsigned long sigjmp_buf[6 + 1 + __SIGSET_NUM_SIGNALS / (sizeof(unsigned long int) * 8)];
#else
#error "You need to implement jmp_buf on your CPU"
#error "You need to implement sigjmp_buf on your CPU"
#endif
void longjmp(jmp_buf env, int val);
int setjmp(jmp_buf env);
typedef sigjmp_buf jmp_buf;
void longjmp(jmp_buf, int);
int setjmp(jmp_buf);
void siglongjmp(sigjmp_buf, int);
int sigsetjmp(sigjmp_buf, int);
__END_DECLS

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.
@ -18,7 +18,7 @@
along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>.
signal.h
Signals.
Signal API.
*******************************************************************************/
@ -33,7 +33,17 @@
#include <__/pthread.h>
#endif
#include <sortix/sigaction.h>
#include <sortix/sigevent.h>
#include <sortix/siginfo.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
@ -53,12 +63,6 @@ typedef __pid_t pid_t;
#include <stddef.h>
#endif
/* TODO: POSIX says this header declares struct timespec, but not time_t... */
#ifndef __time_t_defined
#define __time_t_defined
typedef __time_t time_t;
#endif
#if __STDC_HOSTED__
#ifndef __pthread_attr_t_defined
@ -73,134 +77,12 @@ typedef __pthread_t pthread_t;
#endif
__END_DECLS
#define NSIG __SIG_MAX_NUM
#include <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 void (*sighandler_t)(int);
void SIG_DFL(int);
void SIG_IGN(int);
void SIG_ERR(int);
#define SIG_DFL SIG_DFL
#define SIG_IGN SIG_IGN
#define SIG_ERR SIG_ERR
/* TODO: POSIX specifies a obsolecent SIG_HOLD here. */
union sigval
{
int sival_int;
void* sival_ptr;
};
struct sigevent
{
int sigev_notify;
int sigev_signo;
union sigval sigev_value;
void (*sigev_notify_function)(union sigval);
/*pthread_attr_t* sigev_notify_attributes;*/
};
#define SIGEV_NONE 0
#define SIGEV_SIGNAL 1
#define SIGEV_THREAD 2
/* TODO: SIGRTMIN */
/* TODO: SIGRTMAX */
typedef struct
{
int si_signo;
int si_code;
int si_errno;
pid_t si_pid;
uid_t si_uid;
void* si_addr;
int si_status;
union sigval si_value;
} siginfo_t;
#define ILL_ILLOPC 1
#define ILL_ILLOPN 2
#define ILL_ILLADR 3
#define ILL_ILLTRP 4
#define ILL_PRVOPC 5
#define ILL_PRVREG 6
#define ILL_COPROC 7
#define ILL_BADSTK 8
#define FPE_INTDIV 9
#define FPE_INTOVF 10
#define FPE_FLTDIV 11
#define FPE_FLTOVF 12
#define FPE_FLTUND 13
#define FPE_FLTRES 14
#define FPE_FLTINV 15
#define FPE_FLTSUB 16
#define SEGV_MAPERR 17
#define SEGV_ACCERR 18
#define BUS_ADRALN 19
#define BUS_ADRERR 20
#define BUS_OBJERR 21
#define TRAP_BRKPT 22
#define TRAP_TRACE 23
#define CLD_EXITED 24
#define CLD_KILLED 25
#define CLD_DUMPED 26
#define CLD_TRAPPED 27
#define CLD_STOPPED 29
#define CLD_CONTINUED 30
#define SI_USER 31
#define SI_QUEUE 32
#define SI_TIMER 33
#define SI_ASYNCIO 34
#define SI_MSGQ 35
struct sigaction
{
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t*, void*);
sigset_t sa_mask;
int sa_flags;
};
#define SA_NOCLDSTOP (1<<0)
#define SA_ONSTACK (1<<1)
#define SA_RESETHAND (1<<2)
#define SA_RESTART (1<<3)
#define SA_SIGINFO (1<<4)
#define SA_NOCLDWAIT (1<<5)
#define SA_NODEFER (1<<6)
#define SS_ONSTACK (1<<7)
#define SS_DISABLE (1<<8)
/* TODO: MINSIGSTKSZ */
/* TODO: SIGSTKSZ */
/* TODO: mcontext_t */
typedef int mcontext_t;
typedef struct
{
void* ss_sp;
size_t ss_size;
int ss_flags;
} stack_t;
typedef struct __ucontext ucontext_t;
struct __ucontext
{
ucontext_t* uc_link;
sigset_t uc_sigmask;
stack_t uc_stack;
mcontext_t uc_mcontext;
};
#define MINSIGSTKSZ 2048
#define SIGSTKSZ 8192
int kill(pid_t, int);
int killpg(pid_t, int);
@ -216,25 +98,19 @@ int sigandset(sigset_t*, const sigset_t*, const sigset_t*);
int sigdelset(sigset_t*, int);
int sigemptyset(sigset_t*);
int sigfillset(sigset_t*);
/* TODO: sighold (obsolescent XSI). */
/* TODO: sigignore (obsolescent XSI). */
/* TODO: siginterrupt (obsolescent XSI). */
int sigisemptyset(const sigset_t*);
int sigismember(const sigset_t*, int);
sighandler_t signal(int, sighandler_t);
void (*signal(int, void (*)(int)))(int);
int signotset(sigset_t*, const sigset_t*);
int sigorset(sigset_t*, const sigset_t*, const sigset_t*);
/* TODO: sigpause (obsolescent XSI). */
int sigpending(sigset_t*);
int sigprocmask(int, const sigset_t* __restrict, sigset_t* __restrict);
int sigqueue(pid_t, int, const union sigval);
/* TODO: sigrelse (obsolescent XSI). */
/* TODO: sigset (obsolescent XSI). */
/* TODO: int sigqueue(pid_t, int, const union sigval); */
int sigsuspend(const sigset_t*);
int sigtimedwait(const sigset_t* __restrict, siginfo_t* __restrict,
const struct timespec* __restrict);
int sigwait(const sigset_t* __restrict, int* __restrict);
int sigwaitinfo(const sigset_t* __restrict, siginfo_t* vrestrict);
/* TODO: int sigtimedwait(const sigset_t* __restrict, siginfo_t* __restrict,
const struct timespec* __restrict); */
/* TODO: int sigwait(const sigset_t* __restrict, int* __restrict); */
/* TODO: int sigwaitinfo(const sigset_t* __restrict, siginfo_t* __restrict); */
__END_DECLS

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.
@ -22,10 +22,8 @@
*******************************************************************************/
// TODO: Make this header comply with POSIX-1.2008
#ifndef _SYS_WAIT_H
#define _SYS_WAIT_H 1
#ifndef INCLUDE_SYS_WAIT_H
#define INCLUDE_SYS_WAIT_H 1
#include <sys/cdefs.h>
@ -40,12 +38,8 @@ __BEGIN_DECLS
typedef __pid_t pid_t;
#endif
/* TODO: These are not implemented in sortix libc yet. */
#if 0
int waitid(idtype_t, id_t, siginfo_t*, int);
#endif
pid_t wait(int* stat_loc);
/* TODO: int waitid(idtype_t, id_t, siginfo_t*, int); */
pid_t waitpid(pid_t pid, int *stat_loc, int options);
__END_DECLS

View File

@ -406,7 +406,7 @@ int exit_thread(int, int, const struct exit_thread*);
int memstat(size_t* memused, size_t* memtotal);
int mkpartition(int fd, off_t start, off_t length);
pid_t sfork(int flags);
pid_t tfork(int flags, tforkregs_t* regs);
pid_t tfork(int flags, struct tfork* regs);
size_t writeall(int fd, const void* buf, size_t count);
size_t writeleast(int fd, const void* buf, size_t least, size_t max);
#endif

View File

@ -31,7 +31,6 @@ extern "C" { char* program_invocation_name; }
extern "C" { char* program_invocation_short_name; }
extern "C" void init_stdio();
extern "C" void init_signal();
static char* find_last_elem(char* str)
{
@ -53,9 +52,6 @@ extern "C" void initialize_standard_library(int argc, char* argv[])
program_invocation_name = (char*) argv0;
program_invocation_short_name = find_last_elem((char*) argv0);
// It's probably best to initialize the Unix signals early on.
init_signal();
// Initialize pthreads and stuff like thread-local storage.
pthread_initialize();

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.
@ -17,15 +17,14 @@
You should have received a copy of the GNU Lesser General Public License
along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>.
signal/SIG_IGN.cpp
Ignore signal.
signal/psiginfo.cpp
Print signal error condition to stderr.
*******************************************************************************/
#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.
@ -22,53 +22,19 @@
*******************************************************************************/
#include <stdio.h>
#include <sys/syscall.h>
#include <signal.h>
const int MAX_SIGNALS = 128;
extern sighandler_t handlers[MAX_SIGNALS];
DEFN_SYSCALL3(int, sys_sigaction, SYSCALL_SIGACTION,
int,
const struct sigaction*,
struct sigaction*);
// TODO: Actually implement the sigaction interface for real.
extern "C"
int sigaction(int signum, const struct sigaction* restrict act,
int sigaction(int signum,
const struct sigaction* restrict act,
struct sigaction* restrict oldact)
{
if ( !act )
{
// TODO: set oldact->sa_mask here?
oldact->sa_sigaction = NULL;
oldact->sa_handler = handlers[signum];
oldact->sa_flags = 0;
return 0;
}
int understood_flags = SA_SIGINFO | SA_RESTART;
if ( act->sa_flags & ~understood_flags )
{
fprintf(stderr, "%s:%u sigaction with unsupported flags 0x%x, ignoring "
"hoping they aren't needed.\n", __FILE__, __LINE__,
act->sa_flags & ~understood_flags);
}
if ( act->sa_flags & SA_RESTART )
/* TODO: Actually implement this. Signals are a bit rare on Sortix right
now, so it doesn't matter much that system calls don't restart
on Sortix, so pretend that they do. */ {};
if ( act->sa_flags & SA_SIGINFO )
{
fprintf(stderr, "%s:%u sigaction with unsupported SA_SIGINFO, ignoring "
"hoping the signal never happens.\n", __FILE__,
__LINE__);
return 0;
}
sighandler_t new_handler = act->sa_handler;
sighandler_t old_handler = signal(signum, new_handler);
if ( old_handler == SIG_ERR )
return -1;
if ( oldact )
{
// TODO: set oldact->sa_mask here?
oldact->sa_sigaction = NULL;
oldact->sa_handler = old_handler;
oldact->sa_flags = 0;
}
return 0;
return sys_sigaction(signum, act, oldact);
}

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.
@ -18,38 +18,35 @@
along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>.
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 <string.h>
const int MAX_SIGNALS = 128;
sighandler_t handlers[MAX_SIGNALS];
extern "C" void SignalHandlerAssembly(int signum);
extern "C" void SignalHandler(int signum)
extern "C" void (*signal(int signum, void (*handler)(int)))(int)
{
if ( 0 <= signum && signum < (int) MAX_SIGNALS )
handlers[signum](signum);
}
DEFN_SYSCALL1(int, sys_register_signal_handler, SYSCALL_REGISTER_SIGNAL_HANDLER, sighandler_t);
extern "C" void init_signal()
{
for ( int i = 0; i < MAX_SIGNALS; i++ )
handlers[i] = SIG_DFL;
// Tell the kernel which function we want called upon signals.
sys_register_signal_handler(&SignalHandlerAssembly);
}
extern "C" sighandler_t signal(int signum, sighandler_t handler)
{
if ( signum < 0 || MAX_SIGNALS <= signum ) { return SIG_ERR; }
return handlers[signum] = handler;
// Create a structure describing the new handler.
struct sigaction newact;
memset(&newact, 0, sizeof(newact));
sigemptyset(&newact.sa_mask);
newact.sa_handler = handler;
newact.sa_flags = SA_RESTART;
// Register the new handler and atomically get the old.
struct sigaction oldact;
if ( sigaction(signum, &newact, &oldact) != 0 )
return SIG_ERR;
// We can't return the old handler properly if it's SA_SIGINFO or SA_COOKIE,
// unless it's the common SIG_IGN or SIG_DFL handlers. Let's just say to the
// caller that it's SIG_DFL and assume that they'll be using sigaction
// instead if they wish to restore an old handler.
if ( (oldact.sa_flags & (SA_SIGINFO | SA_COOKIE)) &&
oldact.sa_handler != SIG_IGN &&
oldact.sa_handler != SIG_DFL )
return SIG_DFL;
return oldact.sa_handler;
}

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.
@ -17,15 +17,18 @@
You should have received a copy of the GNU Lesser General Public License
along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>.
signal/SIG_ERR.cpp
Abort on signal.
signal/sigpending.cpp
Get the set of pending signals.
*******************************************************************************/
#include <signal.h>
#include <stdlib.h>
#include <sys/syscall.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 <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)
{
(void) how;
(void) set;
(void) oldset;
return 0;
return sys_sigprocmask(how, set, oldset);
}

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.
@ -23,9 +23,12 @@
*******************************************************************************/
#include <sys/stat.h>
#include <sys/wait.h>
#include <calltrace.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#if defined(__is_sortix_kernel)
@ -42,19 +45,30 @@ extern "C" void abort(void)
extern "C" void abort(void)
{
struct stat st;
if ( getenv("LIBC_DEBUG_CALLTRACE") || stat("/etc/calltrace", &st) == 0 )
if ( stat("/etc/calltrace", &st) == 0 )
calltrace();
if ( getenv("LIBC_DEBUG_LOOP") || stat("/etc/calltrace_loop", &st) == 0 )
if ( stat("/etc/calltrace_loop", &st) == 0 )
while ( true );
// TODO: Send SIGABRT instead!
_Exit(128 + 6);
sigset_t set_of_sigabrt;
sigemptyset(&set_of_sigabrt);
sigaddset(&set_of_sigabrt, SIGABRT);
sigprocmask(SIG_UNBLOCK, &set_of_sigabrt, NULL);
raise(SIGABRT);
int exit_code = WCONSTRUCT(WNATURE_SIGNALED, 128 + SIGABRT, SIGABRT);
int exit_flags = EXIT_THREAD_PROCESS | EXIT_THREAD_DUMP_CORE;
exit_thread(exit_code, exit_flags, NULL);
__builtin_unreachable();
}
#else
extern "C" void abort(void)
{
while ( true ) { };
while ( true ) { }
__builtin_unreachable();
}

View File

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

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

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.
@ -22,13 +22,13 @@
*******************************************************************************/
#include <sys/syscall.h>
#include <unistd.h>
#include <sys/wait.h>
DEFN_SYSCALL1(int, sys_exit, SYSCALL_EXIT, int);
#include <unistd.h>
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();
}

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

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.
@ -25,9 +25,9 @@
#include <sys/syscall.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);
}

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

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.
@ -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
.type setjmp, @function
setjmp:
# TODO: Floating point stuff!
mov %rbx, 0x00(%rdi)
mov %rsp, 0x08(%rdi)
mov %rbp, 0x10(%rdi)
mov %r12, 0x18(%rdi)
mov %r13, 0x20(%rdi)
mov %r14, 0x28(%rdi)
mov %r15, 0x30(%rdi)
mov 0(%rsp), %rax
mov %rax, 0x38(%rdi)
xorl %eax, %eax
.Lsetjmp_return:
ret
movl $1, %esi # setjmp saves the signal mask on Sortix
jmp 1f
.size setjmp, . - setjmp
.global sigsetjmp
.type sigsetjmp, @function
sigsetjmp:
mov %esi, %esi # clear upper 32-bit bits
testl %esi, %esi
jz 2f
1: push %rdi
push %rsi
lea (9 * 8)(%rdi), %rdx # oldset
xor %esi, %esi # set
xor %edi, %edi # how (ignored because set is NULL)
call sigprocmask # assumes sigprocmask is per-thread on Sortix
pop %rsi
pop %rdi
2: mov 0(%rsp), %rax
mov %rbx, (0 * 8)(%rdi)
mov %rsp, (1 * 8)(%rdi)
mov %rbp, (2 * 8)(%rdi)
mov %r12, (3 * 8)(%rdi)
mov %r13, (4 * 8)(%rdi)
mov %r14, (5 * 8)(%rdi)
mov %r15, (6 * 8)(%rdi)
mov %rax, (7 * 8)(%rdi)
mov %rsi, (8 * 8)(%rdi)
xorl %eax, %eax
ret
.size sigsetjmp, . - sigsetjmp
.global longjmp
.type longjmp, @function
longjmp:
jmp siglongjmp
.size longjmp, . - longjmp
.global siglongjmp
.type siglongjmp, @function
siglongjmp:
testl %esi, %esi
jnz 1f
mov $1, %esi
1:
# TODO: Floating point stuff!
mov 0x00(%rdi), %rbx
mov 0x08(%rdi), %rsp
mov 0x10(%rdi), %rbp
mov 0x18(%rdi), %r12
mov 0x20(%rdi), %r13
mov 0x28(%rdi), %r14
mov 0x30(%rdi), %r15
mov 0x38(%rdi), %rax
mov %rax, 0(%rsp)
mov %esi, %eax
jmp .Lsetjmp_return
.size longjmp, . - longjmp
movl $1, %esi
1: movq (8 * 8)(%rdi), %rdx
testq %rdx, %rdx
jz 2f
pushq %rdi
pushq %rsi
leaq (9 * 8)(%rdi), %rsi # set
movl $SIG_SETMASK, %edi # how
xorl %edx, %edx # oldset
call sigprocmask
popq %rsi
popq %rdi
2: movq (0 * 8)(%rdi), %rbx
movq (1 * 8)(%rdi), %rsp
movq (2 * 8)(%rdi), %rbp
movq (3 * 8)(%rdi), %r12
movq (4 * 8)(%rdi), %r13
movq (5 * 8)(%rdi), %r14
movq (6 * 8)(%rdi), %r15
movq (7 * 8)(%rdi), %rax
movq %rax, 0(%rsp)
movl %esi, %eax
ret
.size siglongjmp, . - siglongjmp

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.
@ -27,38 +27,45 @@
.section .text
.globl __call_tfork_with_regs
.type __call_tfork_with_regs, @function
__call_tfork_with_regs:
.globl __sfork
.type __sfork, @function
__sfork:
pushl %ebp
movl %esp, %ebp
# The actual system call expects a struct tforkregs_x86 containing the state
# of each register in the child. Since we create an identical copy, we
# simply set each member of the structure to our own state. Note that since
# the stack goes downwards, we create it in the reverse order.
pushl $MSRID_GSBASE
call rdmsr
movl %eax, (%esp)
movl 12(%ebp), %ecx
push %ecx
movl 8(%ebp), %edx
push %edx
# -- stack is 16-byte aligned -- #
movl $.Lafter_fork, (0 * 4)(%ecx) # eip
movl $0, (1 * 4)(%ecx) # rax, result is 0 for child
movl %ebx, (2 * 4)(%ecx)
movl %ecx, (3 * 4)(%ecx)
movl %edx, (4 * 4)(%ecx)
movl %edi, (5 * 4)(%ecx)
movl %esi, (6 * 4)(%ecx)
movl %esp, (7 * 4)(%ecx)
movl %ebp, (8 * 4)(%ecx)
pushfl
popl %eax
movl %eax, (9 * 4)(%ecx) # eflags
subl $12, %esp
pushl $MSRID_FSBASE
call rdmsr
movl %eax, (%esp)
pushfl
pushl %ebp
pushl %esp
pushl %esi
pushl %edi
pushl %edx
pushl %ecx
pushl %ebx
pushl $0 # eax, result of sfork (0 for the child).
pushl $.Lafter_fork # rip, child will start execution from here.
movl 12(%ebp), %ecx
movl %eax, (10 * 4)(%ecx) # fsbase
addl $16, %esp
subl $12, %esp
pushl $MSRID_GSBASE
call rdmsr
movl 12(%ebp), %ecx
movl %eax, (11 * 4)(%ecx) # gsbase
addl $16, %esp
# Call tfork with a nice pointer to our structure. Note that %edi contains
# the flag parameter that this function accepted.
movl 8(%ebp), %edx # flags parameter, edx need not be preserved.
pushl %esp
pushl %edx
call tfork
.Lafter_fork:
@ -67,4 +74,4 @@ __call_tfork_with_regs:
# which does that for us.
leavel
retl
.size __call_tfork_with_regs, . - __call_tfork_with_regs
.size __sfork, . - __sfork

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.
@ -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
.type setjmp, @function
setjmp:
mov 4(%esp), %ecx
# TODO: Floating point stuff!
mov %ebx, 0x00(%ecx)
mov %esi, 0x04(%ecx)
mov %edi, 0x08(%ecx)
mov %ebp, 0x0C(%ecx)
mov %esp, 0x10(%ecx)
mov 0(%esp), %eax
mov %eax, 0x14(%ecx)
xorl %eax, %eax
.Lsetjmp_return:
ret
# setjmp saves the signal mask on Sortix
jmp 1f
.size setjmp, . - setjmp
.global sigsetjmp
.type sigsetjmp, @function
sigsetjmp:
movl 8(%esp), %edx
testl %edx, %edx
jz 2f
1: movl 4(%esp), %ecx
leal (7 * 4)(%ecx), %edx
pushl %edx # oldset
pushl $0 # set
pushl $0 # how (ignored because set is NULL)
call sigprocmask
addl $(3 * 4), %esp
movl $1, %edx
2: movl 4(%esp), %ecx
movl 0(%esp), %eax
movl %ebx, (0 * 4)(%ecx)
movl %esi, (1 * 4)(%ecx)
movl %edi, (2 * 4)(%ecx)
movl %ebp, (3 * 4)(%ecx)
movl %esp, (4 * 4)(%ecx)
movl %eax, (5 * 4)(%ecx)
movl %edx, (6 * 4)(%ecx)
xorl %eax, %eax
ret
.size sigsetjmp, . - sigsetjmp
.global longjmp
.type longjmp, @function
longjmp:
mov 4(%esp), %ecx
mov 8(%esp), %edx
testl %edx, %edx
jnz 1f
mov $1, %edx
1:
# TODO: Floating point stuff!
mov 0x00(%ecx), %ebx
mov 0x04(%ecx), %esi
mov 0x08(%ecx), %edi
mov 0x0C(%ecx), %ebp
mov 0x10(%ecx), %esp
mov 0x14(%ecx), %eax
mov %eax, 0(%esp)
mov %edx, %eax
jmp .Lsetjmp_return
jmp siglongjmp
.size longjmp, . - longjmp
.global siglongjmp
.type siglongjmp, @function
siglongjmp:
movl 4(%esp), %ecx
movl 8(%esp), %eax
testl %eax, %eax
jnz 1f
movl $1, %eax
1: movl (6 * 4)(%ecx), %edx
testl %edx, %edx
jz 2f
pushl %eax
pushl %ecx
pushl $0 # oldset
leal (7 * 4)(%ecx), %edx
pushl %edx # set
pushl $SIG_SETMASK # how
call sigprocmask
addl $(3 * 4), %esp
popl %ecx
popl %eax
2: movl (0 * 4)(%ecx), %ebx
movl (1 * 4)(%ecx), %esi
movl (2 * 4)(%ecx), %edi
movl (3 * 4)(%ecx), %ebp
movl (4 * 4)(%ecx), %esp
movl (5 * 4)(%ecx), %edx
movl %edx, 0(%esp)
ret
.size siglongjmp, . - siglongjmp

View File

@ -181,8 +181,6 @@ extern pthread_mutex_t __pthread_keys_lock;
extern struct pthread_key* __pthread_keys;
extern size_t __pthread_keys_used;
extern size_t __pthread_keys_length;
extern pthread_mutex_t __pthread_num_threads_lock;
extern size_t __pthread_num_threads;
struct pthread* pthread_allocate_tls(void);

View File

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

View File

@ -56,13 +56,9 @@ void pthread_exit(void* return_value)
thread->keys_length = 0;
pthread_mutex_unlock(&__pthread_keys_lock);
pthread_mutex_lock(&__pthread_num_threads_lock);
size_t num_threads = __pthread_num_threads--;
pthread_mutex_unlock(&__pthread_num_threads_lock);
if ( num_threads == 1 )
exit(0);
pthread_mutex_lock(&thread->detach_lock);
thread->exit_result = return_value;
int exit_flags = EXIT_THREAD_UNMAP;
struct exit_thread extended;
memset(&extended, 0, sizeof(extended));
extended.unmap_from = thread->uthread.stack_mmap;
@ -71,13 +67,15 @@ void pthread_exit(void* return_value)
{
extended.zero_from = &thread->join_lock.lock;
extended.zero_size = sizeof(thread->join_lock.lock);
exit_thread(0, EXIT_THREAD_UNMAP | EXIT_THREAD_ZERO, &extended);
__builtin_unreachable();
exit_flags |= EXIT_THREAD_ZERO;
}
else
{
munmap(thread->uthread.tls_mmap, thread->uthread.tls_size);
exit_thread(0, EXIT_THREAD_UNMAP, &extended);
__builtin_unreachable();
extended.tls_unmap_from = thread->uthread.tls_mmap;
extended.tls_unmap_size = thread->uthread.tls_size;
exit_flags |= EXIT_THREAD_TLS_UNMAP;
}
exit_thread(0, EXIT_THREAD_ONLY_IF_OTHERS | exit_flags, &extended);
exit(0);
}