From: Tulio A M Mendes Date: Mon, 9 Feb 2026 23:25:14 +0000 (-0300) Subject: signal: add SA_SIGINFO and sigaction ABI X-Git-Url: https://projects.tadryanom.me/docs/POSIX_ROADMAP.md?a=commitdiff_plain;h=f65b6d5ad70f6a5e77fa062d14cc88eef47c4f8b;p=AdrOS.git signal: add SA_SIGINFO and sigaction ABI --- diff --git a/Makefile b/Makefile index f635b92..582d1aa 100644 --- a/Makefile +++ b/Makefile @@ -119,10 +119,10 @@ $(MKINITRD): tools/mkinitrd.c @gcc tools/mkinitrd.c -o $(MKINITRD) $(USER_ELF): user/init.c user/linker.ld - @i686-elf-gcc -m32 -ffreestanding -fno-pie -no-pie -nostdlib -Wl,-T,user/linker.ld -o $(USER_ELF) user/init.c user/errno.c + @i686-elf-gcc -m32 -I include -ffreestanding -fno-pie -no-pie -nostdlib -Wl,-T,user/linker.ld -o $(USER_ELF) user/init.c user/errno.c $(ECHO_ELF): user/echo.c user/linker.ld - @i686-elf-gcc -m32 -ffreestanding -fno-pie -no-pie -nostdlib -Wl,-T,user/linker.ld -o $(ECHO_ELF) user/echo.c user/errno.c + @i686-elf-gcc -m32 -I include -ffreestanding -fno-pie -no-pie -nostdlib -Wl,-T,user/linker.ld -o $(ECHO_ELF) user/echo.c user/errno.c $(INITRD_IMG): $(MKINITRD) $(USER_ELF) $(ECHO_ELF) @./$(MKINITRD) $(INITRD_IMG) $(USER_ELF):bin/init.elf $(ECHO_ELF):bin/echo.elf diff --git a/include/process.h b/include/process.h index 7065bd4..c1aee48 100644 --- a/include/process.h +++ b/include/process.h @@ -4,6 +4,7 @@ #include #include "idt.h" // For struct registers #include "fs.h" +#include "signal.h" typedef enum { PROCESS_READY, @@ -39,14 +40,17 @@ struct process { int has_user_regs; struct registers user_regs; - // Minimal signals: handler pointers, blocked mask and pending mask. - // handlers[i] == 0 => default - // handlers[i] == 1 => ignore - // handlers[i] >= 2 => user handler address - uintptr_t sig_handlers[PROCESS_MAX_SIG]; + // Minimal signals: per-signal action, blocked mask and pending mask. + // sa_handler == 0 => default + // sa_handler == 1 => ignore + // sa_handler >= 2 => user handler address + struct sigaction sigactions[PROCESS_MAX_SIG]; uint32_t sig_blocked_mask; uint32_t sig_pending_mask; + // For SIGSEGV: last page fault address (CR2) captured in ring3. + uintptr_t last_fault_addr; + int waiting; int wait_pid; int wait_result_pid; diff --git a/include/signal.h b/include/signal.h new file mode 100644 index 0000000..e6b0973 --- /dev/null +++ b/include/signal.h @@ -0,0 +1,29 @@ +#ifndef SIGNAL_H +#define SIGNAL_H + +#include + +#define SA_SIGINFO 0x00000004U + +struct sigaction { + uintptr_t sa_handler; + uintptr_t sa_sigaction; + uint32_t sa_mask; + uint32_t sa_flags; +}; + +struct siginfo { + int si_signo; + int si_code; + void* si_addr; +}; + +typedef struct siginfo siginfo_t; + +struct ucontext { + uint32_t reserved; +}; + +typedef struct ucontext ucontext_t; + +#endif diff --git a/src/arch/x86/idt.c b/src/arch/x86/idt.c index f8373f2..c0e32ac 100644 --- a/src/arch/x86/idt.c +++ b/src/arch/x86/idt.c @@ -5,6 +5,7 @@ #include "spinlock.h" #include "uaccess.h" #include "syscall.h" +#include "signal.h" #include #define IDT_ENTRIES 256 @@ -70,7 +71,8 @@ static void deliver_signals_to_usermode(struct registers* regs) { current_process->sig_pending_mask &= ~(1U << (uint32_t)sig); - const uintptr_t h = current_process->sig_handlers[sig]; + const struct sigaction act = current_process->sigactions[sig]; + const uintptr_t h = (act.sa_flags & SA_SIGINFO) ? act.sa_sigaction : act.sa_handler; if (h == 1) { return; } @@ -87,19 +89,37 @@ static void deliver_signals_to_usermode(struct registers* regs) { // Build a sigframe + a tiny user trampoline that calls SYSCALL_SIGRETURN. // Stack layout at handler entry (regs->useresp): - // [esp+0] return address -> trampoline - // [esp+4] int sig - // Below that: trampoline code bytes, below that: sigframe. + // non-SA_SIGINFO: + // [esp+0] return address -> trampoline + // [esp+4] int sig + // SA_SIGINFO: + // [esp+0] return address -> trampoline + // [esp+4] int sig + // [esp+8] siginfo_t* + // [esp+12] ucontext_t* + // Below that: trampoline code bytes, below that: siginfo + ucontext + sigframe. struct sigframe f; f.magic = SIGFRAME_MAGIC; f.saved = *regs; const uint32_t tramp_size = 14U; - const uint32_t base = regs->useresp - (8U + tramp_size + (uint32_t)sizeof(f)); + const uint32_t callframe_size = (act.sa_flags & SA_SIGINFO) ? 16U : 8U; + + struct siginfo info; + info.si_signo = sig; + info.si_code = 1; + info.si_addr = (sig == 11) ? (void*)current_process->last_fault_addr : NULL; + + struct ucontext uctx; + uctx.reserved = 0; + + const uint32_t base = regs->useresp - (callframe_size + tramp_size + (uint32_t)sizeof(info) + (uint32_t)sizeof(uctx) + (uint32_t)sizeof(f)); const uint32_t retaddr_slot = base; - const uint32_t tramp_addr = base + 8U; - const uint32_t sigframe_addr = tramp_addr + tramp_size; + const uint32_t tramp_addr = base + callframe_size; + const uint32_t siginfo_addr = tramp_addr + tramp_size; + const uint32_t uctx_addr = siginfo_addr + (uint32_t)sizeof(info); + const uint32_t sigframe_addr = uctx_addr + (uint32_t)sizeof(uctx); // Trampoline bytes: // mov eax, SYSCALL_SIGRETURN @@ -128,6 +148,8 @@ static void deliver_signals_to_usermode(struct registers* regs) { tramp[9] = (uint8_t)((sigframe_addr >> 24) & 0xFFU); if (copy_to_user((void*)(uintptr_t)sigframe_addr, &f, sizeof(f)) < 0 || + copy_to_user((void*)(uintptr_t)siginfo_addr, &info, sizeof(info)) < 0 || + copy_to_user((void*)(uintptr_t)uctx_addr, &uctx, sizeof(uctx)) < 0 || copy_to_user((void*)(uintptr_t)tramp_addr, tramp, sizeof(tramp)) < 0) { const int SIG_SEGV = 11; process_exit_notify(128 + SIG_SEGV); @@ -136,15 +158,30 @@ static void deliver_signals_to_usermode(struct registers* regs) { for (;;) __asm__ volatile("hlt"); } - uint32_t callframe[2]; - callframe[0] = tramp_addr; - callframe[1] = (uint32_t)sig; - if (copy_to_user((void*)(uintptr_t)retaddr_slot, callframe, sizeof(callframe)) < 0) { - const int SIG_SEGV = 11; - process_exit_notify(128 + SIG_SEGV); - __asm__ volatile("sti"); - schedule(); - for (;;) __asm__ volatile("hlt"); + if ((act.sa_flags & SA_SIGINFO) == 0) { + uint32_t callframe[2]; + callframe[0] = tramp_addr; + callframe[1] = (uint32_t)sig; + if (copy_to_user((void*)(uintptr_t)retaddr_slot, callframe, sizeof(callframe)) < 0) { + const int SIG_SEGV = 11; + process_exit_notify(128 + SIG_SEGV); + __asm__ volatile("sti"); + schedule(); + for (;;) __asm__ volatile("hlt"); + } + } else { + uint32_t callframe[4]; + callframe[0] = tramp_addr; + callframe[1] = (uint32_t)sig; + callframe[2] = siginfo_addr; + callframe[3] = uctx_addr; + if (copy_to_user((void*)(uintptr_t)retaddr_slot, callframe, sizeof(callframe)) < 0) { + const int SIG_SEGV = 11; + process_exit_notify(128 + SIG_SEGV); + __asm__ volatile("sti"); + schedule(); + for (;;) __asm__ volatile("hlt"); + } } regs->useresp = retaddr_slot; @@ -290,6 +327,9 @@ void isr_handler(struct registers* regs) { if ((regs->cs & 3U) == 3U) { const int SIG_SEGV = 11; if (current_process) { + uint32_t cr2; + __asm__ volatile("mov %%cr2, %0" : "=r"(cr2)); + current_process->last_fault_addr = (uintptr_t)cr2; current_process->sig_pending_mask |= (1U << (uint32_t)SIG_SEGV); } deliver_signals_to_usermode(regs); diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index 4af02cd..1e42f84 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -897,17 +897,24 @@ static int syscall_getpgrp_impl(void) { return (int)current_process->pgrp_id; } -static int syscall_sigaction_impl(int sig, uintptr_t handler, uintptr_t* old_out) { +static int syscall_sigaction_impl(int sig, const struct sigaction* user_act, struct sigaction* user_oldact) { if (!current_process) return -EINVAL; if (sig <= 0 || sig >= PROCESS_MAX_SIG) return -EINVAL; - if (old_out) { - if (user_range_ok(old_out, sizeof(*old_out)) == 0) return -EFAULT; - uintptr_t oldh = current_process->sig_handlers[sig]; - if (copy_to_user(old_out, &oldh, sizeof(oldh)) < 0) return -EFAULT; + if (user_oldact) { + if (user_range_ok(user_oldact, sizeof(*user_oldact)) == 0) return -EFAULT; + struct sigaction old = current_process->sigactions[sig]; + if (copy_to_user(user_oldact, &old, sizeof(old)) < 0) return -EFAULT; + } + + if (!user_act) { + return 0; } - current_process->sig_handlers[sig] = handler; + if (user_range_ok(user_act, sizeof(*user_act)) == 0) return -EFAULT; + struct sigaction act; + if (copy_from_user(&act, user_act, sizeof(act)) < 0) return -EFAULT; + current_process->sigactions[sig] = act; return 0; } @@ -1155,9 +1162,9 @@ static void syscall_handler(struct registers* regs) { if (syscall_no == SYSCALL_SIGACTION) { int sig = (int)regs->ebx; - uintptr_t handler = (uintptr_t)regs->ecx; - uintptr_t* old_out = (uintptr_t*)regs->edx; - regs->eax = (uint32_t)syscall_sigaction_impl(sig, handler, old_out); + const struct sigaction* act = (const struct sigaction*)regs->ecx; + struct sigaction* oldact = (struct sigaction*)regs->edx; + regs->eax = (uint32_t)syscall_sigaction_impl(sig, act, oldact); return; } diff --git a/user/init.c b/user/init.c index a7a0ef7..4095acb 100644 --- a/user/init.c +++ b/user/init.c @@ -31,6 +31,8 @@ #include "user_errno.h" +#include "signal.h" + enum { SYSCALL_WRITE = 1, SYSCALL_EXIT = 2, @@ -173,17 +175,35 @@ static void write_hex32(uint32_t v) { (void)sys_write(1, b, 8); } -static int sys_sigaction(int sig, void (*handler)(int), uintptr_t* old_out) { +static int sys_sigaction2(int sig, const struct sigaction* act, struct sigaction* oldact) { int ret; __asm__ volatile( "int $0x80" : "=a"(ret) - : "a"(SYSCALL_SIGACTION), "b"(sig), "c"(handler), "d"(old_out) + : "a"(SYSCALL_SIGACTION), "b"(sig), "c"(act), "d"(oldact) : "memory" ); return __syscall_fix(ret); } +static int sys_sigaction(int sig, void (*handler)(int), uintptr_t* old_out) { + struct sigaction act; + act.sa_handler = (uintptr_t)handler; + act.sa_sigaction = 0; + act.sa_mask = 0; + act.sa_flags = 0; + + struct sigaction oldact; + struct sigaction* oldp = old_out ? &oldact : 0; + + int r = sys_sigaction2(sig, &act, oldp); + if (r < 0) return r; + if (old_out) { + *old_out = oldact.sa_handler; + } + return 0; +} + static int sys_select(uint32_t nfds, uint64_t* readfds, uint64_t* writefds, uint64_t* exceptfds, int32_t timeout) { int ret; __asm__ volatile( @@ -462,6 +482,17 @@ static void sigsegv_exit_handler(int sig) { sys_exit(0); } +static void sigsegv_info_handler(int sig, siginfo_t* info, void* uctx) { + (void)uctx; + static const char msg[] = "[init] SIGSEGV siginfo handler invoked\n"; + (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1)); + const uintptr_t expected = 0x12345000U; + if (sig == SIGSEGV && info && (uintptr_t)info->si_addr == expected) { + sys_exit(0); + } + sys_exit(1); +} + void _start(void) { __asm__ volatile( "mov $0x23, %ax\n" @@ -1385,13 +1416,19 @@ void _start(void) { } if (pid == 0) { - if (sys_sigaction(SIGSEGV, sigsegv_exit_handler, 0) < 0) { + struct sigaction act; + act.sa_handler = 0; + act.sa_sigaction = (uintptr_t)sigsegv_info_handler; + act.sa_mask = 0; + act.sa_flags = SA_SIGINFO; + + if (sys_sigaction2(SIGSEGV, &act, 0) < 0) { static const char msg[] = "[init] sigaction(SIGSEGV) failed\n"; (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1)); sys_exit(1); } - *(volatile uint32_t*)0x0 = 123; + *(volatile uint32_t*)0x12345000U = 123; sys_exit(2); }