mirror of
https://github.com/red-prig/fpPS4.git
synced 2024-11-23 06:19:57 +00:00
1221 lines
25 KiB
ObjectPascal
1221 lines
25 KiB
ObjectPascal
unit sys_signal;
|
|
|
|
{$mode ObjFPC}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Windows,
|
|
sys_types,
|
|
atomic,
|
|
LFQueue,
|
|
sys_context;
|
|
|
|
{$I signal.inc}
|
|
|
|
type
|
|
p_ksiginfo_t=^ksiginfo_t;
|
|
ksiginfo_t=packed record
|
|
pNext:p_ksiginfo_t;
|
|
//
|
|
ksi_info:siginfo_t;
|
|
end;
|
|
|
|
ksiginfo_list=object
|
|
pHead,pTail:p_ksiginfo_t;
|
|
procedure Insert(Node:p_ksiginfo_t);
|
|
procedure Remove(pPrev,node:p_ksiginfo_t);
|
|
end;
|
|
|
|
p_sigqueue_t=^sigqueue_t;
|
|
sigqueue_t=packed record
|
|
_mask:sigset_t;
|
|
|
|
_lock:Integer; //signal blocking
|
|
_flag:DWORD; //signal flags
|
|
|
|
_queue_lock:DWORD;
|
|
_event_lock:DWORD; //context send lock
|
|
_event:DWORD; //context event
|
|
|
|
_signals:sigset_t;
|
|
|
|
_queue:ksiginfo_list;
|
|
|
|
_contexts:TIntrusiveMPSCQueue;
|
|
end;
|
|
|
|
p_ksig_context=^ksig_context;
|
|
ksig_context=packed record
|
|
next:p_ksig_context;
|
|
ectx:TCONTEXT_EXTENDED;
|
|
_rsp:QWORD;
|
|
end;
|
|
|
|
Const
|
|
SL_ALERTABLE=1;
|
|
SL_NOINTRRUP=2;
|
|
|
|
function _SIG_IDX(sig:Integer):DWORD; inline;
|
|
function _SIG_VALID(sig:Integer):Boolean; inline;
|
|
function _SIG_VALID_32(sig:Integer):Boolean; inline;
|
|
|
|
Procedure sigqueue_init(sq:p_sigqueue_t);
|
|
|
|
function _sig_pending(sq:p_sigqueue_t):DWORD; inline;
|
|
function _sig_is_pending(sq:p_sigqueue_t):Boolean; inline;
|
|
|
|
function _sigaddset(_set:p_sigset_t;signum:Integer):Integer;
|
|
function _sigdelset(_set:p_sigset_t;signum:Integer):Integer;
|
|
function _sigismember(_set:p_sigset_t;signum:Integer):Integer;
|
|
function __sigprocmask(how:Integer;_set,oldset:p_sigset_t):Integer;
|
|
|
|
function __sigaction(signum:Integer;act,oldact:p_sigaction_t):Integer;
|
|
|
|
procedure _sig_lock(flags:integer=0);
|
|
procedure __sig_lock(pt:Pointer;flags:integer=0);
|
|
procedure _sig_unlock(flags:integer=0);
|
|
procedure __sig_unlock(pt:Pointer;flags:integer=0);
|
|
|
|
function _pthread_kill(t:Pointer;sig:Integer):Integer;
|
|
|
|
procedure _copy_ctx_from_sys(src:PCONTEXT;dst:p_ucontext_t);
|
|
procedure _copy_ctx_to_sys(src:p_ucontext_t;dst:PCONTEXT);
|
|
|
|
implementation
|
|
|
|
uses
|
|
ntapi,
|
|
spinlock,
|
|
sys_kernel,
|
|
sys_pthread;
|
|
|
|
Var
|
|
ps_sigact:array[0..31] of sigaction_t;
|
|
|
|
pf_deadlock:QWORD;
|
|
|
|
|
|
function _SIG_IDX(sig:Integer):DWORD; inline;
|
|
begin
|
|
Result:=sig-1;
|
|
end;
|
|
|
|
function _SIG_VALID(sig:Integer):Boolean; inline;
|
|
begin
|
|
Result:=(sig<=_SIG_MAXSIG) and (sig>0);
|
|
end;
|
|
|
|
function _SIG_VALID_32(sig:Integer):Boolean; inline;
|
|
begin
|
|
Result:=(sig<=32) and (sig>0);
|
|
end;
|
|
|
|
procedure ksiginfo_list.Insert(Node:p_ksiginfo_t);
|
|
begin
|
|
if (pTail=nil) then
|
|
begin
|
|
pHead:=node;
|
|
node^.pNext:=nil;
|
|
end else
|
|
begin
|
|
pTail^.pNext:=node;
|
|
end;
|
|
pTail:=node;
|
|
end;
|
|
|
|
procedure ksiginfo_list.Remove(pPrev,node:p_ksiginfo_t);
|
|
begin
|
|
if (pPrev=nil) then
|
|
begin
|
|
if (pHead=node) then
|
|
begin
|
|
pHead:=node^.pNext;
|
|
end;
|
|
end else
|
|
begin
|
|
pPrev^.pNext:=node^.pNext;
|
|
end;
|
|
if (node^.pNext=nil) then
|
|
begin
|
|
if (pTail=node) then
|
|
begin
|
|
pTail:=pPrev;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
Procedure sigqueue_init(sq:p_sigqueue_t);
|
|
var
|
|
i:Byte;
|
|
begin
|
|
if (sq=nil) then Exit;
|
|
sq^:=Default(sigqueue_t);
|
|
sq^._contexts.Create;
|
|
end;
|
|
|
|
function sigqueue_add(sq:p_sigqueue_t;si:p_siginfo_t):Integer;
|
|
var
|
|
node:p_ksiginfo_t;
|
|
begin
|
|
Result:=0;
|
|
if (sq=nil) or (si=nil) then Exit;
|
|
|
|
if (si^.si_signo=SIGKILL) or
|
|
(si^.si_signo=SIGSTOP) then
|
|
begin
|
|
_sigaddset(@sq^._signals,si^.si_signo);
|
|
Exit;
|
|
end;
|
|
|
|
node:=AllocMem(SizeOf(ksiginfo_t));
|
|
if (node=nil) then
|
|
begin
|
|
_sigaddset(@sq^._signals,si^.si_signo);
|
|
Exit(EAGAIN);
|
|
end;
|
|
|
|
node^.ksi_info:=si^;
|
|
|
|
spin_lock(sq^._queue_lock);
|
|
|
|
sq^._queue.Insert(node);
|
|
_sigaddset(@sq^._signals,si^.si_signo);
|
|
|
|
spin_unlock(sq^._queue_lock);
|
|
|
|
end;
|
|
|
|
function sigqueue_get(sq:p_sigqueue_t;signo:Integer;si:p_siginfo_t):Integer;
|
|
var
|
|
node:p_ksiginfo_t;
|
|
prev:p_ksiginfo_t;
|
|
item:p_ksiginfo_t;
|
|
count:DWORD;
|
|
begin
|
|
if (sq=nil) or (si=nil) then Exit(0);
|
|
|
|
if (_sigismember(@sq^._signals,signo)<>1) then Exit(0);
|
|
|
|
count:=0;
|
|
node:=nil;
|
|
prev:=nil;
|
|
|
|
spin_lock(sq^._queue_lock);
|
|
|
|
item:=sq^._queue.pHead;
|
|
While (item<>nil) do
|
|
begin
|
|
if (item^.ksi_info.si_signo=signo) then
|
|
begin
|
|
if (node=nil) then
|
|
begin
|
|
node:=item;
|
|
sq^._queue.Remove(prev,item);
|
|
end else
|
|
begin
|
|
Inc(count);
|
|
end;
|
|
end;
|
|
|
|
prev:=item;
|
|
item:=item^.pNext;
|
|
end;
|
|
|
|
if (node=nil) or (count=0) then
|
|
begin
|
|
_sigdelset(@sq^._signals,signo);
|
|
end;
|
|
|
|
spin_unlock(sq^._queue_lock);
|
|
|
|
if (node=nil) then Exit(0);
|
|
|
|
si^:=node^.ksi_info;
|
|
FreeMem(node);
|
|
|
|
Result:=signo;
|
|
end;
|
|
|
|
function sigqueue_push_ctx(sq:p_sigqueue_t;ectx:PCONTEXT_EXTENDED;_rsp:QWORD):Integer;
|
|
var
|
|
node:p_ksig_context;
|
|
begin
|
|
Result:=0;
|
|
if (sq=nil) or (ectx=nil) then Exit;
|
|
|
|
node:=AllocMem(SizeOf(ksig_context));
|
|
if (node=nil) then Exit(EAGAIN);
|
|
|
|
node^._rsp:=_rsp;
|
|
|
|
if not CopyContextExtended(ectx,@node^.ectx) then
|
|
begin
|
|
FreeMem(node);
|
|
Exit(EINVAL);
|
|
end;
|
|
|
|
sq^._contexts.Push(node);
|
|
end;
|
|
|
|
function sigqueue_pop_ctx(sq:p_sigqueue_t;ectx:PCONTEXT_EXTENDED;_rsp:QWORD):Integer;
|
|
var
|
|
node:p_ksig_context;
|
|
begin
|
|
Result:=0;
|
|
if (sq=nil) or (ectx=nil) then Exit;
|
|
|
|
node:=nil;
|
|
sq^._contexts.Pop(node);
|
|
if (node=nil) then Exit(EAGAIN);
|
|
|
|
if (node^._rsp<>_rsp) then
|
|
begin
|
|
Result:=EFAULT;
|
|
end;
|
|
|
|
if not CopyContextExtended(@node^.ectx,ectx) then
|
|
begin
|
|
Result:=EINVAL;
|
|
end;
|
|
FreeMem(node);
|
|
end;
|
|
|
|
function _sig_pending(sq:p_sigqueue_t):DWORD; inline;
|
|
begin
|
|
Result:=( load_acq_rel(sq^._signals.bits[0]) and (not load_acq_rel(sq^._mask.bits[0])) );
|
|
end;
|
|
|
|
function _sig_is_pending(sq:p_sigqueue_t):Boolean; inline;
|
|
begin
|
|
Result:=_sig_pending(sq)<>0;
|
|
end;
|
|
|
|
function _sigaddset(_set:p_sigset_t;signum:Integer):Integer;
|
|
begin
|
|
if (_set=nil) or
|
|
(not _SIG_VALID(signum)) then Exit(EINVAL);
|
|
signum:=signum-1;
|
|
fetch_or(_set^.qwords[signum shr 6],1 shl (signum and 63));
|
|
Result:=0;
|
|
end;
|
|
|
|
function _sigdelset(_set:p_sigset_t;signum:Integer):Integer;
|
|
begin
|
|
if (_set=nil) or
|
|
(not _SIG_VALID(signum)) then Exit(EINVAL);
|
|
signum:=signum-1;
|
|
fetch_and(_set^.qwords[signum shr 6],not(1 shl (signum and 63)));
|
|
Result:=0;
|
|
end;
|
|
|
|
function _sigismember(_set:p_sigset_t;signum:Integer):Integer;
|
|
begin
|
|
if (_set=nil) or
|
|
(not _SIG_VALID(signum)) then Exit(EINVAL);
|
|
signum:=signum-1;
|
|
Result:=Integer((_set^.qwords[signum shr 6] and (1 shl (signum and 63)))<>0);
|
|
end;
|
|
|
|
function __sigprocmask(how:Integer;_set,oldset:p_sigset_t):Integer;
|
|
var
|
|
t:pthread;
|
|
begin
|
|
t:=tcb_thread;
|
|
if (t=nil) then Exit(EINVAL);
|
|
|
|
if (_set=nil) then
|
|
begin
|
|
if (oldset<>nil) then oldset^:=t^.sig._mask;
|
|
Exit(0);
|
|
end;
|
|
|
|
Case how of
|
|
SIG_BLOCK:
|
|
begin
|
|
if (oldset<>nil) then oldset^:=t^.sig._mask;
|
|
t^.sig._mask.qwords[0]:=t^.sig._mask.qwords[0] or _set^.qwords[0];
|
|
t^.sig._mask.qwords[1]:=t^.sig._mask.qwords[1] or _set^.qwords[1];
|
|
end;
|
|
SIG_UNBLOCK:
|
|
begin
|
|
if (oldset<>nil) then oldset^:=t^.sig._mask;
|
|
t^.sig._mask.qwords[0]:=t^.sig._mask.qwords[0] and (not _set^.qwords[0]);
|
|
t^.sig._mask.qwords[1]:=t^.sig._mask.qwords[1] and (not _set^.qwords[1]);
|
|
end;
|
|
SIG_SETMASK:
|
|
begin
|
|
if (oldset<>nil) then oldset^:=t^.sig._mask;
|
|
t^.sig._mask:=_set^;
|
|
end;
|
|
else
|
|
Exit(EINVAL);
|
|
end;
|
|
|
|
Result:=0;
|
|
end;
|
|
|
|
function _get_sig_str(signum:Integer):RawByteString;
|
|
begin
|
|
case signum of
|
|
SIGHUP :Result:='SIGHUP';
|
|
SIGINT :Result:='SIGINT';
|
|
SIGQUIT :Result:='SIGQUIT';
|
|
SIGILL :Result:='SIGILL';
|
|
SIGTRAP :Result:='SIGTRAP';
|
|
SIGABRT :Result:='SIGABRT';
|
|
SIGEMT :Result:='SIGEMT';
|
|
SIGFPE :Result:='SIGFPE';
|
|
SIGKILL :Result:='SIGKILL';
|
|
SIGBUS :Result:='SIGBUS';
|
|
SIGSEGV :Result:='SIGSEGV';
|
|
SIGSYS :Result:='SIGSYS';
|
|
SIGPIPE :Result:='SIGPIPE';
|
|
SIGALRM :Result:='SIGALRM';
|
|
SIGTERM :Result:='SIGTERM';
|
|
SIGURG :Result:='SIGURG';
|
|
SIGSTOP :Result:='SIGSTOP';
|
|
SIGTSTP :Result:='SIGTSTP';
|
|
SIGCONT :Result:='SIGCONT';
|
|
SIGCHLD :Result:='SIGCHLD';
|
|
SIGTTIN :Result:='SIGTTIN';
|
|
SIGTTOU :Result:='SIGTTOU';
|
|
SIGIO :Result:='SIGIO';
|
|
SIGXCPU :Result:='SIGXCPU';
|
|
SIGXFSZ :Result:='SIGXFSZ';
|
|
SIGVTALRM:Result:='SIGVTALRM';
|
|
SIGPROF :Result:='SIGPROF';
|
|
SIGWINCH :Result:='SIGWINCH';
|
|
SIGINFO :Result:='SIGINFO';
|
|
SIGUSR1 :Result:='SIGUSR1';
|
|
SIGUSR2 :Result:='SIGUSR2';
|
|
SIGTHR :Result:='SIGTHR';
|
|
else
|
|
Str(signum,Result);
|
|
end;
|
|
end;
|
|
|
|
function __sigaction(signum:Integer;act,oldact:p_sigaction_t):Integer;
|
|
var
|
|
idx:DWORD;
|
|
tmp:sigaction_t;
|
|
begin
|
|
if not _SIG_VALID_32(signum) then Exit(EINVAL);
|
|
|
|
Writeln('sigaction:',_get_sig_str(signum));
|
|
|
|
if (act=nil) then
|
|
begin
|
|
tmp:=Default(sigaction_t);
|
|
end else
|
|
begin
|
|
tmp:=act^;
|
|
end;
|
|
|
|
Case signum of
|
|
SIGKILL,
|
|
SIGSTOP:
|
|
begin
|
|
case tmp.__sigaction_u.__code of
|
|
SIG_DFL:;
|
|
else
|
|
Exit(EINVAL);
|
|
end;
|
|
end;
|
|
else;
|
|
end;
|
|
|
|
idx:=_SIG_IDX(signum);
|
|
|
|
if (oldact<>nil) then
|
|
begin
|
|
oldact^:=ps_sigact[idx];
|
|
end;
|
|
|
|
ps_sigact[idx]:=tmp;
|
|
|
|
Result:=0;
|
|
end;
|
|
|
|
function _sig_dfl(sig:Integer):Integer;
|
|
begin
|
|
Case sig of
|
|
SIGURG ,
|
|
SIGCHLD ,
|
|
SIGWINCH,
|
|
SIGINFO :Result:=SIG_IGN;
|
|
else Result:=SIG_ERR;
|
|
end;
|
|
end;
|
|
|
|
function _sig_act_type(sig:Integer;var act:sigaction_t):Integer;
|
|
begin
|
|
Result:=0;
|
|
Case act.__sigaction_u.__code of
|
|
SIG_DFL:Case _sig_dfl(sig) of
|
|
SIG_IGN:Result:=SIG_IGN;
|
|
SIG_ERR:Result:=SIG_ERR;
|
|
end;
|
|
SIG_IGN :Result:=SIG_IGN;
|
|
SIG_ERR :Result:=SIG_ERR;
|
|
SIG_CATCH:Result:=SIG_ERR;
|
|
SIG_HOLD :Result:=SIG_ERR;
|
|
end;
|
|
end;
|
|
|
|
const
|
|
ALERTABLE_FLAG=1;
|
|
SPIN_WAIT_FLAG=2;
|
|
|
|
function __sig_self_interrupt(t:pthread):Integer; forward;
|
|
|
|
procedure _sig_lock(flags:integer=0);
|
|
begin
|
|
__sig_lock(tcb_thread,flags);
|
|
end;
|
|
|
|
procedure __sig_lock(pt:Pointer;flags:integer=0);
|
|
label
|
|
tryagain;
|
|
var
|
|
t:pthread;
|
|
i:Integer;
|
|
pc:QWORD;
|
|
begin
|
|
if (pt=nil) then Exit;
|
|
t:=pt;
|
|
|
|
if ((flags and SL_ALERTABLE)<>0) then
|
|
begin
|
|
fetch_or(t^.sig._flag,ALERTABLE_FLAG);
|
|
end;
|
|
|
|
i:=fetch_add(t^.sig._lock,1);
|
|
|
|
//need to interrupt
|
|
if ((flags and SL_NOINTRRUP)=0) and ((i=0) or ((flags and SL_ALERTABLE)<>0)) then
|
|
begin
|
|
tryagain:
|
|
|
|
fetch_or(t^.sig._flag,SPIN_WAIT_FLAG);
|
|
|
|
pc:=0;
|
|
While (_sig_is_pending(@t^.sig)) do
|
|
begin
|
|
spin_pause;
|
|
Inc(pc);
|
|
if (pc>pf_deadlock) then
|
|
begin
|
|
//deadlock
|
|
__sig_self_interrupt(t);
|
|
end;
|
|
end;
|
|
|
|
fetch_and(t^.sig._flag,DWORD(not SPIN_WAIT_FLAG));
|
|
|
|
if (_sig_is_pending(@t^.sig)) then goto tryagain;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure _sig_unlock(flags:integer=0);
|
|
begin
|
|
__sig_unlock(tcb_thread,flags);
|
|
end;
|
|
|
|
procedure __sig_unlock(pt:Pointer;flags:integer=0);
|
|
label
|
|
tryagain;
|
|
var
|
|
t:pthread;
|
|
i:Integer;
|
|
Alertable:Boolean;
|
|
pc:QWORD;
|
|
begin
|
|
if (pt=nil) then Exit;
|
|
t:=pt;
|
|
|
|
i:=load_acq_rel(t^.sig._lock);
|
|
Alertable:=((t^.sig._flag and ALERTABLE_FLAG)<>0);
|
|
|
|
i:=fetch_sub(t^.sig._lock,1);
|
|
|
|
//need to interrupt
|
|
if ((flags and SL_NOINTRRUP)=0) and ((i=1) or Alertable) then
|
|
begin
|
|
tryagain:
|
|
|
|
fetch_or(t^.sig._flag,SPIN_WAIT_FLAG);
|
|
|
|
pc:=0;
|
|
While (_sig_is_pending(@t^.sig)) do
|
|
begin
|
|
spin_pause;
|
|
Inc(pc);
|
|
if (pc>pf_deadlock) then
|
|
begin
|
|
//deadlock
|
|
__sig_self_interrupt(t);
|
|
end;
|
|
end;
|
|
|
|
fetch_and(t^.sig._flag,DWORD(not (ALERTABLE_FLAG or SPIN_WAIT_FLAG)));
|
|
|
|
if (_sig_is_pending(@t^.sig)) then goto tryagain;
|
|
|
|
end;
|
|
|
|
if ((flags and SL_NOINTRRUP)=0) then
|
|
begin
|
|
fetch_and(t^.sig._flag,DWORD(not ALERTABLE_FLAG));
|
|
end;
|
|
end;
|
|
|
|
//var
|
|
// _test_counter:DWORD;
|
|
|
|
procedure __sig_test(t:pthread;ectx:PCONTEXT_EXTENDED); MS_ABI_Default;
|
|
var
|
|
signo:Integer;
|
|
sact:sigaction_t;
|
|
info:siginfo_t;
|
|
ucontext:_ucontext_t;
|
|
old:sigset_t;
|
|
errno:QWORD;
|
|
//_rsp:QWORD;
|
|
_rbp:QWORD;
|
|
sigpending:DWORD;
|
|
win_err:DWORD;
|
|
_lock:Integer;
|
|
setmask:Boolean;
|
|
begin
|
|
if (t=nil) then Exit;
|
|
|
|
win_err:=GetLastError;
|
|
errno:=t^.errno;
|
|
|
|
While true do
|
|
begin
|
|
|
|
sigpending:=_sig_pending(@t^.sig);
|
|
if (sigpending=0) then Break;
|
|
|
|
signo:=BsfDWord(sigpending)+1;
|
|
|
|
While (sigqueue_get(@t^.sig,signo,@info)<>0) do
|
|
begin
|
|
|
|
//Writeln('>__sig_test:'{,system.InterlockedIncrement(_test_counter)},':',t^.ThreadId);
|
|
|
|
sact:=ps_sigact[_SIG_IDX(signo)];
|
|
|
|
if ((sact.sa_flags and SA_RESETHAND)<>0) then
|
|
begin
|
|
ps_sigact[_SIG_IDX(signo)]:=Default(sigaction_t);
|
|
end;
|
|
|
|
Case _sig_act_type(info.si_signo,sact) of
|
|
SIG_IGN:;
|
|
SIG_ERR:;
|
|
else
|
|
begin
|
|
//errno:=t^.errno;
|
|
setmask:=((sact.sa_flags and SA_NODEFER)=0);
|
|
|
|
if setmask then
|
|
begin
|
|
old:=t^.sig._mask;
|
|
t^.sig._mask:=sact.sa_mask;
|
|
end;
|
|
|
|
ucontext:=Default(_ucontext_t);
|
|
_copy_ctx_from_sys(ectx^.CONTEXT,@ucontext);
|
|
ucontext.uc_sigmask:=t^.sig._mask;
|
|
ucontext.uc_mcontext.mc_err:=errno;
|
|
|
|
//asm
|
|
// //Mov %rsp,_rsp
|
|
// Mov %rbp,_rbp
|
|
//end;
|
|
|
|
//link break from???
|
|
//ucontext.uc_mcontext.mc_lbrfrom :=_rsp;
|
|
//link break to???
|
|
//ucontext.uc_mcontext.mc_lbrto :=_rbp; //bottom in GC_push_all_stack
|
|
|
|
//ucontext.uc_mcontext.mc_lbrfrom :=ucontext.uc_mcontext.mc_rsp;
|
|
//ucontext.uc_mcontext.mc_lbrto :=ucontext.uc_mcontext.mc_rsp;
|
|
|
|
_lock:=t^.sig._lock;
|
|
|
|
if ((sact.sa_flags and SA_SIGINFO)=0) then
|
|
begin
|
|
//sa_handler
|
|
sact.__sigaction_u.__sa_handler(info.si_signo,info.si_code,@ucontext);
|
|
end else
|
|
begin
|
|
//sa_sigaction
|
|
sact.__sigaction_u.__sa_sigaction(info.si_signo,@info,@ucontext);
|
|
end;
|
|
|
|
if (_lock<>t^.sig._lock) then
|
|
begin
|
|
Assert(false);
|
|
end;
|
|
|
|
if setmask then
|
|
begin
|
|
t^.sig._mask:=old;
|
|
end;
|
|
//t^.errno:=errno;
|
|
|
|
end;
|
|
end;
|
|
|
|
//Writeln('<__sig_test:'{,_test_counter,':'},t^.ThreadId);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
t^.errno:=errno;
|
|
SetLastError(win_err);
|
|
end;
|
|
|
|
//compiler i hate you
|
|
procedure __sig_test_align(t:pthread;ectx:PCONTEXT_EXTENDED); MS_ABI_Default; assembler; nostackframe;
|
|
asm
|
|
pushq %rbp
|
|
movq %rsp, %rbp
|
|
andq $-32, %rsp //align stack by AVX
|
|
call __sig_test
|
|
lea 0x0(%rbp),%rsp
|
|
popq %rbp
|
|
ret
|
|
end;
|
|
|
|
procedure __sig_interrupt(t:pthread); MS_ABI_Default;
|
|
var
|
|
ctx:TCONTEXT_EXTENDED;
|
|
rsp:QWORD;
|
|
begin
|
|
if (t=nil) then Exit;
|
|
|
|
asm
|
|
Movq %rbp,rsp
|
|
end;
|
|
|
|
if (sigqueue_pop_ctx(@t^.sig,@ctx,rsp+8)<>0) then
|
|
begin
|
|
asm
|
|
ud2
|
|
end;
|
|
end;
|
|
|
|
//Writeln('>__sig_interrupt:',t^.ThreadId,' ',t^.sig._lock);
|
|
|
|
repeat
|
|
__sig_test_align(t,@ctx);
|
|
until event_try_disable(t^.sig._event);
|
|
|
|
NtContinue(ctx.CONTEXT,False);
|
|
asm
|
|
ud2
|
|
end;
|
|
end;
|
|
|
|
procedure _update_ctx(ctx:PCONTEXT); MS_ABI_Default; assembler; nostackframe;
|
|
asm
|
|
movq %rbx,TCONTEXT.rbx(%rcx)
|
|
movq %rbp,TCONTEXT.rbp(%rcx)
|
|
movq %r12,TCONTEXT.r12(%rcx)
|
|
movq %r13,TCONTEXT.r13(%rcx)
|
|
movq %r14,TCONTEXT.r14(%rcx)
|
|
movq %r15,TCONTEXT.r15(%rcx)
|
|
movq %rsi,TCONTEXT.rsi(%rcx)
|
|
movq %rdi,TCONTEXT.rdi(%rcx)
|
|
leaq 8(%rsp),%rax
|
|
movq %rax,TCONTEXT.rsp(%rcx)
|
|
end;
|
|
|
|
function __sig_self_interrupt(t:pthread):Integer;
|
|
var
|
|
ctx:TCONTEXT_EXTENDED;
|
|
rsp:QWORD;
|
|
label
|
|
eoi;
|
|
begin
|
|
|
|
event_try_enable(t^.sig._event); //mark change
|
|
|
|
//Writeln('__sig_self_interrupt');
|
|
|
|
if not InitializeContextExtended(@ctx) then Exit(ESRCH);
|
|
if (NtGetContextThread(t^.handle,ctx.CONTEXT)<>STATUS_SUCCESS) then Exit(ESRCH);
|
|
|
|
_update_ctx(ctx.CONTEXT);
|
|
ctx.CONTEXT^.Rip:=qword(@eoi);
|
|
|
|
rsp:=AlignDw(ctx.CONTEXT^.Rsp-SizeOf(Pointer),SizeOf(Pointer));
|
|
|
|
Result:=sigqueue_push_ctx(@t^.sig,@ctx,rsp);
|
|
if (Result<>0) then Exit;
|
|
|
|
ctx.CONTEXT^.Rip:=qword(@__sig_interrupt);
|
|
ctx.CONTEXT^.Rcx:=qword(t);
|
|
ctx.CONTEXT^.Rsp:=rsp;
|
|
|
|
//Writeln('beg Sptr=',HexStr(Sptr));
|
|
|
|
//Writeln('>NtContinue:',HexStr(ctx.CONTEXT^.Rip,16));
|
|
NtContinue(ctx.CONTEXT,False);
|
|
|
|
eoi:
|
|
|
|
//Writeln('end Sptr=',HexStr(Sptr));
|
|
|
|
Result:=0;
|
|
end;
|
|
|
|
procedure _apc_null(dwParam:PTRUINT); stdcall;
|
|
begin
|
|
end;
|
|
|
|
//var
|
|
// _send_counter:DWORD;
|
|
|
|
function nt_send_interrupt(t:pthread;signo:Integer):Integer;
|
|
label
|
|
tryagain;
|
|
var
|
|
ctx:TCONTEXT_EXTENDED;
|
|
rsp:QWORD;
|
|
w:LARGE_INTEGER;
|
|
begin
|
|
Result:=0;
|
|
tryagain:
|
|
|
|
if (NtSuspendThread(t^.handle,nil)<>STATUS_SUCCESS) then
|
|
begin
|
|
Exit(ESRCH);
|
|
end;
|
|
|
|
w.QuadPart:=0;
|
|
if (NtWaitForSingleObject(t^.handle,False,@w)<>STATUS_TIMEOUT) then
|
|
begin
|
|
NtResumeThread(t^.handle,nil);
|
|
Exit(ESRCH);
|
|
end;
|
|
|
|
if (_sigismember(@t^.sig._signals,signo)<>1) then
|
|
begin
|
|
//signal is gone
|
|
NtResumeThread(t^.handle,nil);
|
|
Exit(0);
|
|
end;
|
|
|
|
if (t^.sig._lock=0) or ((t^.sig._flag and SPIN_WAIT_FLAG)<>0) then //(not locked) or wait
|
|
begin
|
|
|
|
if not InitializeContextExtended(@ctx) then
|
|
begin
|
|
NtResumeThread(t^.handle,nil);
|
|
Exit(ESRCH);
|
|
end;
|
|
|
|
if (NtGetContextThread(t^.handle,ctx.CONTEXT)<>STATUS_SUCCESS) then
|
|
begin
|
|
NtResumeThread(t^.handle,nil);
|
|
Exit(ESRCH);
|
|
end;
|
|
|
|
if IS_SYSCALL(ctx.CONTEXT^.Rip) then //system call in code without blocking
|
|
begin
|
|
//skip
|
|
//Writeln('Warn syscall:0x',HexStr(ctx.CONTEXT^.Rax,4));
|
|
|
|
//store_release(t^.sig._wait,1);
|
|
|
|
if ((ps_sigact[_SIG_IDX(signo)].sa_flags and SA_RESTART)<>0) then
|
|
begin
|
|
NtResumeThread(t^.handle,nil);
|
|
w.QuadPart:=-10000;
|
|
SwDelayExecution(False,@w); //100ms
|
|
goto tryagain;
|
|
end else
|
|
begin
|
|
NtResumeThread(t^.handle,nil);
|
|
Exit(EAGAIN);
|
|
end;
|
|
|
|
end else
|
|
begin
|
|
|
|
rsp:=AlignDw(ctx.CONTEXT^.Rsp-SizeOf(Pointer),SizeOf(Pointer));
|
|
|
|
Result:=sigqueue_push_ctx(@t^.sig,@ctx,rsp);
|
|
if (Result<>0) then
|
|
begin
|
|
NtResumeThread(t^.handle,nil);
|
|
Exit;
|
|
end;
|
|
|
|
ctx.CONTEXT^.Rip:=qword(@__sig_interrupt);
|
|
ctx.CONTEXT^.Rcx:=qword(t);
|
|
ctx.CONTEXT^.Rsp:=rsp;
|
|
|
|
if (NtSetContextThread(t^.handle,ctx.CONTEXT)<>STATUS_SUCCESS) then
|
|
begin
|
|
NtResumeThread(t^.handle,nil);
|
|
Exit(ESRCH);
|
|
end;
|
|
|
|
end;
|
|
|
|
NtResumeThread(t^.handle,nil);
|
|
end else
|
|
if ((t^.sig._flag and ALERTABLE_FLAG)<>0) then //Alertable
|
|
begin
|
|
NtQueueApcThread(t^.handle,@_apc_null,0,nil,0);
|
|
|
|
NtResumeThread(t^.handle,nil);
|
|
|
|
if ((ps_sigact[_SIG_IDX(signo)].sa_flags and SA_RESTART)<>0) then
|
|
begin
|
|
w.QuadPart:=-10000;
|
|
SwDelayExecution(False,@w); //100ms
|
|
goto tryagain;
|
|
end else
|
|
begin
|
|
Exit(EAGAIN);
|
|
end;
|
|
|
|
end else
|
|
begin
|
|
//locked
|
|
|
|
NtResumeThread(t^.handle,nil);
|
|
|
|
if ((ps_sigact[_SIG_IDX(signo)].sa_flags and SA_RESTART)<>0) then
|
|
begin
|
|
w.QuadPart:=-10000;
|
|
SwDelayExecution(False,@w); //100ms
|
|
goto tryagain;
|
|
end else
|
|
begin
|
|
Exit(EAGAIN);
|
|
end;
|
|
|
|
end;
|
|
end;
|
|
|
|
function __sig_send_interrupt(t:pthread;signo:Integer):Integer;
|
|
begin
|
|
Result:=0;
|
|
|
|
Writeln('__sig_send_interrupt:'{,system.InterlockedIncrement(_send_counter),':'},t^.ThreadId);
|
|
|
|
event_try_enable(t^.sig._event); //mark change
|
|
|
|
if spin_trylock(t^.sig._event_lock) then
|
|
begin
|
|
Result:=nt_send_interrupt(t,signo);
|
|
spin_unlock(t^.sig._event_lock);
|
|
end;
|
|
|
|
end;
|
|
|
|
function _pthread_kill(t:Pointer;sig:Integer):Integer;
|
|
var
|
|
idx:DWORD;
|
|
act:sigaction_t;
|
|
sinfo:siginfo_t;
|
|
begin
|
|
if (t=nil) then Exit(EINVAL);
|
|
|
|
Writeln('_pthread_kill:',sig,':',pthread(t)^.ThreadId);
|
|
|
|
if (sig=0) then Exit(0); //check pthread
|
|
|
|
if not _SIG_VALID_32(sig) then Exit(EINVAL);
|
|
|
|
idx:=_SIG_IDX(sig);
|
|
|
|
act:=ps_sigact[idx];
|
|
|
|
Case _sig_act_type(sig,act) of
|
|
SIG_IGN:
|
|
begin
|
|
Exit(0);
|
|
end;
|
|
SIG_ERR:
|
|
begin
|
|
DebugBreak;
|
|
Halt(0);
|
|
end;
|
|
else;
|
|
end;
|
|
|
|
sinfo:=Default(siginfo_t);
|
|
sinfo.si_signo:=sig;
|
|
sinfo.si_code :=SI_USER;
|
|
sinfo.si_pid :=GetCurrentProcessId;
|
|
|
|
Result:=sigqueue_add(@pthread(t)^.sig,@sinfo);
|
|
if (Result<>0) then Exit;
|
|
|
|
if (t=tcb_thread) then
|
|
begin
|
|
_sig_lock;
|
|
Result:=__sig_self_interrupt(t);
|
|
_sig_unlock;
|
|
end else
|
|
begin
|
|
_sig_lock;
|
|
Result:=__sig_send_interrupt(t,sig);
|
|
_sig_unlock;
|
|
end;
|
|
end;
|
|
|
|
procedure _copy_ctx_from_sys(src:PCONTEXT;dst:p_ucontext_t);
|
|
var
|
|
flags:DWORD;
|
|
context_ex:PCONTEXT_EX;
|
|
xs:PXSTATE;
|
|
uc_xsave:PXmmSaveArea;
|
|
uc_xstate:PXSTATE;
|
|
begin
|
|
if (src=nil) or (dst=nil) then Exit;
|
|
|
|
flags:=src^.ContextFlags and (not CONTEXT_AMD64);
|
|
|
|
context_ex:=PCONTEXT_EX(src+1);
|
|
xs:=PXSTATE(PByte(context_ex)+context_ex^.XState.Offset);
|
|
|
|
uc_xsave:=PXmmSaveArea(@dst^.uc_mcontext.mc_fpstate);
|
|
uc_xstate:=PXSTATE(uc_xsave+1);
|
|
|
|
if ((flags and CONTEXT_INTEGER)<>0) then
|
|
begin
|
|
dst^.uc_flags:=dst^.uc_flags or _UC_CPU;
|
|
|
|
dst^.uc_mcontext.mc_rax:=src^.Rax;
|
|
dst^.uc_mcontext.mc_rbx:=src^.Rbx;
|
|
dst^.uc_mcontext.mc_rcx:=src^.Rcx;
|
|
dst^.uc_mcontext.mc_rdx:=src^.Rdx;
|
|
dst^.uc_mcontext.mc_rsi:=src^.Rsi;
|
|
dst^.uc_mcontext.mc_rdi:=src^.Rdi;
|
|
dst^.uc_mcontext.mc_r8 :=src^.R8;
|
|
dst^.uc_mcontext.mc_r9 :=src^.R9;
|
|
dst^.uc_mcontext.mc_r10:=src^.R10;
|
|
dst^.uc_mcontext.mc_r11:=src^.R11;
|
|
dst^.uc_mcontext.mc_r12:=src^.R12;
|
|
dst^.uc_mcontext.mc_r13:=src^.R13;
|
|
dst^.uc_mcontext.mc_r14:=src^.R14;
|
|
dst^.uc_mcontext.mc_r15:=src^.R15;
|
|
end;
|
|
|
|
if ((flags and CONTEXT_CONTROL)<>0) then
|
|
begin
|
|
dst^.uc_flags:=dst^.uc_flags or _UC_CPU;
|
|
|
|
dst^.uc_mcontext.mc_rsp :=src^.Rsp;
|
|
dst^.uc_mcontext.mc_rbp :=src^.Rbp;
|
|
dst^.uc_mcontext.mc_rip :=src^.Rip;
|
|
dst^.uc_mcontext.mc_rflags:=src^.EFlags;
|
|
dst^.uc_mcontext.mc_cs :=src^.Segcs;
|
|
dst^.uc_mcontext.mc_ss :=src^.Segss;
|
|
end;
|
|
|
|
if ((flags and CONTEXT_SEGMENTS)<>0) then
|
|
begin
|
|
dst^.uc_flags:=dst^.uc_flags or _UC_CPU;
|
|
|
|
dst^.uc_mcontext.mc_ds:=src^.SegDs;
|
|
dst^.uc_mcontext.mc_es:=src^.SegEs;
|
|
dst^.uc_mcontext.mc_fs:=src^.SegFs;
|
|
dst^.uc_mcontext.mc_gs:=src^.SegGs;
|
|
end;
|
|
|
|
if ((flags and CONTEXT_FLOATING_POINT)<>0) then
|
|
begin
|
|
dst^.uc_flags:=dst^.uc_flags or _UC_FPU;
|
|
|
|
uc_xsave^:=src^.FltSave;
|
|
uc_xstate^.Mask:=uc_xstate^.Mask or XSTATE_MASK_LEGACY;
|
|
end;
|
|
|
|
if ((flags and CONTEXT_XSTATE)<>0) then
|
|
begin
|
|
if ((xs^.Mask and XSTATE_MASK_AVX)<>0) then
|
|
begin
|
|
uc_xstate^.Mask:=uc_xstate^.Mask or XSTATE_MASK_AVX;
|
|
Move(xs^.YmmContext,uc_xstate^.YmmContext,SizeOf(TYMMCONTEXT));
|
|
end;
|
|
end;
|
|
|
|
if ((flags and CONTEXT_DEBUG_REGISTERS)<>0) then
|
|
begin
|
|
//save it somewhere
|
|
dst^.uc_mcontext.mc_spare[0]:=src^.Dr0;
|
|
dst^.uc_mcontext.mc_spare[1]:=src^.Dr1;
|
|
dst^.uc_mcontext.mc_spare[2]:=src^.Dr2;
|
|
dst^.uc_mcontext.mc_spare[3]:=src^.Dr3;
|
|
dst^.uc_mcontext.mc_spare[4]:=src^.Dr6;
|
|
dst^.uc_mcontext.mc_spare[5]:=src^.Dr7;
|
|
end;
|
|
|
|
//fix me
|
|
dst^.uc_mcontext.mc_lbrfrom:=src^.Rsp; //LastBranchFromRip
|
|
dst^.uc_mcontext.mc_lbrto :=src^.Rbp; //LastBranchToRip
|
|
|
|
dst^.uc_flags:=dst^.uc_flags or (flags shl 8); //set as extended
|
|
|
|
dst^.uc_mcontext.mc_addr :=dst^.uc_mcontext.mc_rip;
|
|
dst^.uc_mcontext.mc_len :=SizeOf(mcontext_t);
|
|
dst^.uc_mcontext.mc_flags :=_MC_HASSEGS;
|
|
dst^.uc_mcontext.mc_fpformat:=_MC_FPFMT_XMM;
|
|
dst^.uc_mcontext.mc_ownedfp :=_MC_FPOWNED_FPU;
|
|
end;
|
|
|
|
procedure _copy_ctx_to_sys(src:p_ucontext_t;dst:PCONTEXT);
|
|
var
|
|
flags:DWORD;
|
|
context_ex:PCONTEXT_EX;
|
|
xs:PXSTATE;
|
|
uc_xsave:PXmmSaveArea;
|
|
uc_xstate:PXSTATE;
|
|
begin
|
|
if (src=nil) or (dst=nil) then Exit;
|
|
|
|
context_ex:=PCONTEXT_EX(dst+1);
|
|
xs:=PXSTATE(PByte(context_ex)+context_ex^.XState.Offset);
|
|
|
|
uc_xsave:=PXmmSaveArea(@src^.uc_mcontext.mc_fpstate);
|
|
uc_xstate:=PXSTATE(uc_xsave+1);
|
|
|
|
flags:=(src^.uc_flags shr 8); //get extended
|
|
|
|
if (flags=0) then //nop, get by other
|
|
begin
|
|
if ((src^.uc_flags and _UC_CPU)<>0) then
|
|
begin
|
|
flags:=flags or CONTEXT_INTEGER or CONTEXT_CONTROL or CONTEXT_SEGMENTS;
|
|
end;
|
|
if ((src^.uc_flags and _UC_FPU)<>0) then
|
|
begin
|
|
flags:=flags or CONTEXT_FLOATING_POINT;
|
|
end;
|
|
if ((uc_xstate^.Mask and XSTATE_MASK_AVX)<>0) then
|
|
begin
|
|
flags:=flags or CONTEXT_XSTATE;
|
|
end;
|
|
flags:=flags and (not CONTEXT_AMD64);
|
|
end;
|
|
|
|
flags:=flags and dst^.ContextFlags; //filter
|
|
dst^.ContextFlags:=flags or CONTEXT_AMD64; //update
|
|
|
|
if ((flags and CONTEXT_INTEGER)<>0) then
|
|
begin
|
|
dst^.Rax:=src^.uc_mcontext.mc_rax;
|
|
dst^.Rbx:=src^.uc_mcontext.mc_rbx;
|
|
dst^.Rcx:=src^.uc_mcontext.mc_rcx;
|
|
dst^.Rdx:=src^.uc_mcontext.mc_rdx;
|
|
dst^.Rsi:=src^.uc_mcontext.mc_rsi;
|
|
dst^.Rdi:=src^.uc_mcontext.mc_rdi;
|
|
dst^.R8 :=src^.uc_mcontext.mc_r8;
|
|
dst^.R9 :=src^.uc_mcontext.mc_r9;
|
|
dst^.R10:=src^.uc_mcontext.mc_r10;
|
|
dst^.R11:=src^.uc_mcontext.mc_r11;
|
|
dst^.R12:=src^.uc_mcontext.mc_r12;
|
|
dst^.R13:=src^.uc_mcontext.mc_r13;
|
|
dst^.R14:=src^.uc_mcontext.mc_r14;
|
|
dst^.R15:=src^.uc_mcontext.mc_r15;
|
|
end;
|
|
|
|
if ((flags and CONTEXT_CONTROL)<>0) then
|
|
begin
|
|
dst^.Rsp :=src^.uc_mcontext.mc_rsp;
|
|
dst^.Rbp :=src^.uc_mcontext.mc_rbp;
|
|
dst^.Rip :=src^.uc_mcontext.mc_rip;
|
|
dst^.EFlags:=src^.uc_mcontext.mc_rflags;
|
|
dst^.Segcs :=src^.uc_mcontext.mc_cs;
|
|
dst^.Segss :=src^.uc_mcontext.mc_ss;
|
|
end;
|
|
|
|
if ((flags and CONTEXT_SEGMENTS)<>0) then
|
|
begin
|
|
dst^.SegDs:=src^.uc_mcontext.mc_ds;
|
|
dst^.SegEs:=src^.uc_mcontext.mc_es;
|
|
dst^.SegFs:=src^.uc_mcontext.mc_fs;
|
|
dst^.SegGs:=src^.uc_mcontext.mc_gs;
|
|
end;
|
|
|
|
if ((flags and CONTEXT_FLOATING_POINT)<>0) then
|
|
begin
|
|
|
|
if ((uc_xstate^.Mask and XSTATE_MASK_LEGACY_FLOATING_POINT)<>0) then
|
|
begin
|
|
Move(uc_xsave^,dst^.FltSave,ptruint(@PXmmSaveArea(nil)^.MxCsr));
|
|
Move(uc_xsave^.FloatRegisters,dst^.FltSave.FloatRegisters,SizeOf(TXmmSaveArea.FloatRegisters));
|
|
end else
|
|
begin
|
|
FillChar(dst^.FltSave,ptruint(@PXmmSaveArea(nil)^.MxCsr),0);
|
|
FillChar(dst^.FltSave.FloatRegisters,SizeOf(TXmmSaveArea.FloatRegisters),0);
|
|
dst^.FltSave.ControlWord:=$37f;
|
|
end;
|
|
|
|
if ((uc_xstate^.Mask and XSTATE_MASK_LEGACY_SSE)<>0) then
|
|
begin
|
|
Move(uc_xsave^.XmmRegisters,dst^.FltSave.XmmRegisters,SizeOf(TXmmSaveArea.XmmRegisters));
|
|
dst^.FltSave.MxCsr :=uc_xsave^.MxCsr;
|
|
dst^.FltSave.MxCsr_Mask:=uc_xsave^.MxCsr_Mask;
|
|
end else
|
|
begin
|
|
FillChar(dst^.FltSave.XmmRegisters,SizeOf(TXmmSaveArea.XmmRegisters),0);
|
|
dst^.FltSave.MxCsr :=$1f80;
|
|
dst^.FltSave.MxCsr_Mask:=$2ffff;
|
|
end;
|
|
|
|
dst^.MxCsr:=dst^.FltSave.MxCsr;
|
|
end;
|
|
|
|
if ((flags and CONTEXT_XSTATE)<>0) then
|
|
begin
|
|
xs^.Mask:=uc_xstate^.Mask;
|
|
if ((uc_xstate^.Mask and XSTATE_MASK_AVX)<>0) then
|
|
begin
|
|
Move(uc_xstate^.YmmContext,xs^.YmmContext,SizeOf(TYMMCONTEXT));
|
|
end;
|
|
end;
|
|
|
|
if ((flags and CONTEXT_DEBUG_REGISTERS)<>0) then
|
|
begin
|
|
//save it somewhere
|
|
dst^.Dr0:=src^.uc_mcontext.mc_spare[0];
|
|
dst^.Dr1:=src^.uc_mcontext.mc_spare[1];
|
|
dst^.Dr2:=src^.uc_mcontext.mc_spare[2];
|
|
dst^.Dr3:=src^.uc_mcontext.mc_spare[3];
|
|
dst^.Dr6:=src^.uc_mcontext.mc_spare[4];
|
|
dst^.Dr7:=src^.uc_mcontext.mc_spare[5];
|
|
end;
|
|
|
|
end;
|
|
|
|
Procedure Init;
|
|
var
|
|
pc:QWORD;
|
|
begin
|
|
pf_deadlock:=1;
|
|
NtQueryPerformanceCounter(@pc,@pf_deadlock);
|
|
pf_deadlock:=pf_deadlock*2;
|
|
end;
|
|
|
|
initialization
|
|
Init;
|
|
|
|
end.
|
|
|