feat: add sigOnSigReturn and sigInstallTrampoline to signal

This commit is contained in:
dualfroz committed 2026-09-08 14:46:45 +02:00
1 parent 8f2554114f
commit 730467ce1f
1 file changed
+85
+85
View File
@@ -315,3 +315,88 @@ SigOutcome sigConsumePending(Thread* thread, Registers* regs, int* outSignum)
newBlocked &= ~(1ull << sigStop); newBlocked &= ~(1ull << sigStop);
thread->sigBlocked = newBlocked; thread->sigBlocked = newBlocked;
/* Rewrite `regs` in place to enter the shared trampoline: rip is
the trampoline's entry, rax is the handler address its
`call *%rax` invokes, rdi is signum (the handler's sole SysV
argument). regs->rsp (the interrupted user stack) is left
untouched -- the trampoline runs on the target's own stack,
right where it already was. */
regs->rax = handler;
regs->rdi = (uint64)signum;
regs->rip = sigTrampolineVirtualAddr;
/* sigFlagSigInfo (signalnum.h): copy the SigInfo sigRaiseInfo
attached to this signum into THIS (thread, depth) pair's own
slot of the shared sigInfoVirtualAddr page -- a different slot
than any still-active outer handler's own siginfo pointer, by
the exact same depth-indexed reasoning sigSavedRegs/
sigSavedBlocked already use, so a nested sigFlagSigInfo delivery
can never clobber an outer one's still-unread copy. rdx is
always 0 (no ucontext_t this kernel can expose). Left completely
untouched otherwise -- see this function's own doc comment. */
if (thread->sigHandlerFlags[signum] & sigFlagSigInfo && sigInfoPageCount > 0) {
uint32 slot = thread->id * sigMaxNestDepth + depth;
uint32 page = slot / sigInfoSlotsPerPage;
uint32 offset = (slot % sigInfoSlotsPerPage) * (uint32)sizeof(SigInfo);
SigInfo* dst = (SigInfo*)(sigInfoFrames[page] + offset);
*dst = thread->sigPendingInfo[signum];
regs->rsi = sigInfoVirtualAddr + (uint64)page * frameSize + offset;
regs->rdx = 0;
}
return sigOutcomeHandler;
}
return sigOutcomeNone;
}
bool sigOnSigReturn(Thread* thread, Registers* regs)
{
if (thread->sigHandlerDepth == 0)
return false;
uint32 depth = thread->sigHandlerDepth - 1;
*regs = thread->sigSavedRegs[depth];
thread->sigBlocked = thread->sigSavedBlocked[depth];
thread->sigHandlerDepth = depth;
return true;
}
void sigInstallTrampoline(void)
{
uint64 frame = pmmAlloc();
if (frame == 0)
return;
uint64 length = (uint64)(sigTrampolineEnd - sigTrampolineStart);
memset((void*)frame, 0, frameSize);
memcpy((void*)frame, sigTrampolineStart, length);
pagingMap(sigTrampolineVirtualAddr, frame, pagePresent | pageUser);
}
void sigInstallInfoPage(void)
{
uint64 totalSlots = (uint64)threadCapacity() * sigMaxNestDepth;
uint64 pagesNeeded = (totalSlots + sigInfoSlotsPerPage - 1) / sigInfoSlotsPerPage;
if (pagesNeeded > sigInfoMaxPages)
pagesNeeded = sigInfoMaxPages;
for (uint32 i = 0; i < pagesNeeded; i++) {
uint64 frame = pmmAlloc();
if (frame == 0)
return;
memset((void*)frame, 0, frameSize);
/* pageUser, no pageWrite: a handler may only ever READ the SigInfo
it was handed a pointer to, exactly like the trampoline page
above is read+execute, never write -- the kernel is the only
writer, via `frame`'s own physical address (see the frame
comment above sigInfoFrames), never through this user-facing
mapping. */
pagingMap(sigInfoVirtualAddr + (uint64)i * frameSize, frame, pagePresent | pageUser);
sigInfoFrames[i] = frame;
}
sigInfoPageCount = (uint32)pagesNeeded;
}