@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
#include <stdint.h>
#include "idt.h" // For struct registers
#include "fs.h"
+#include "signal.h"
typedef enum {
PROCESS_READY,
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;
--- /dev/null
+#ifndef SIGNAL_H
+#define SIGNAL_H
+
+#include <stdint.h>
+
+#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
#include "spinlock.h"
#include "uaccess.h"
#include "syscall.h"
+#include "signal.h"
#include <stddef.h>
#define IDT_ENTRIES 256
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;
}
// 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
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);
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;
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);
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;
}
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;
}
#include "user_errno.h"
+#include "signal.h"
+
enum {
SYSCALL_WRITE = 1,
SYSCALL_EXIT = 2,
(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(
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"
}
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);
}