diff --git a/Cargo.lock b/Cargo.lock
index b34799febbac44c91f7f4473862a9aaac6dfd9ff..b9f2271d78669c97e00c5927747add9527d2bbaf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -31,6 +31,7 @@ dependencies = [
  "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "goblin 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "linked_list_allocator 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "paste 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
  "raw-cpuid 8.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "redox_syscall 0.1.56",
  "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -63,11 +64,33 @@ dependencies = [
  "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "paste"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "paste-impl 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "paste-impl"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "plain"
 version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "proc-macro-hack"
+version = "0.5.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "raw-cpuid"
 version = "7.0.3"
@@ -175,7 +198,10 @@ dependencies = [
 "checksum linked_list_allocator 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "47de1a43fad0250ee197e9e124e5b5deab3d7b39d4428ae8a6d741ceb340c362"
 "checksum linked_list_allocator 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e70e46c13c0e8374c26cec5752e3347ca1087d9711de8f45aa513a7700efd73d"
 "checksum lock_api 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75"
+"checksum paste 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880"
+"checksum paste-impl 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6"
 "checksum plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
+"checksum proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)" = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4"
 "checksum raw-cpuid 7.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4a349ca83373cfa5d6dbb66fd76e58b2cca08da71a5f6400de0a0a6a9bceeaf"
 "checksum raw-cpuid 8.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9e9c0f2091b865a94bc3c9d34896cc4bbda04453453c391f7eb224491be9ae1d"
 "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
diff --git a/Cargo.toml b/Cargo.toml
index 09bc6e36bcdd976570a90c07d32d0779e24bb9d9..d38edb708f8cc922918f2a334b861b2125174921 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,6 +16,7 @@ raw-cpuid = "8.0.0"
 redox_syscall = { path = "syscall" }
 slab_allocator = { path = "slab_allocator", optional = true }
 spin = "0.5.2"
+paste = "0.1.18"
 
 [dependencies.goblin]
 version = "0.2.1"
diff --git a/build.rs b/build.rs
index 53c7b15d00df6de31278953c3196c5404067540f..a0f1d4d5b9ee1042bfee77d5d42da447b69d007d 100644
--- a/build.rs
+++ b/build.rs
@@ -91,6 +91,26 @@ fn fill_from_location(f: &mut fs::File, loc: &Path) -> Result<(), Error> {
     Ok(())
 }
 
+#[cfg(not(target_arch = "x86_64"))]
+fn asm(_out_dir: &str) {}
+
+#[cfg(target_arch = "x86_64")]
+fn asm(out_dir: &str) {
+    use std::process::Command;
+
+    println!("cargo:rerun-if-changed=src/asm/x86_64/trampoline.asm");
+
+    let status = Command::new("nasm")
+        .arg("-f").arg("bin")
+        .arg("-o").arg(format!("{}/trampoline", out_dir))
+        .arg("src/asm/x86_64/trampoline.asm")
+        .status()
+        .expect("failed to run nasm");
+    if ! status.success() {
+        panic!("nasm failed with exit status {}", status);
+    }
+}
+
 fn main() {
     println!("cargo:rustc-env=TARGET={}", env::var("TARGET").unwrap());
     println!("cargo:rerun-if-env-changed=INITFS_FOLDER");
@@ -100,6 +120,8 @@ fn main() {
     let mut f = fs::File::create(&dest_path).unwrap();
     let src = env::var("INITFS_FOLDER");
 
+    asm(&out_dir);
+
     // Write header
     f.write_all(
         b"
diff --git a/src/acpi/madt.rs b/src/acpi/madt.rs
index 61396b7bc07c29b0fa287c733ae045f1fd47b45a..cae2189e9aedfa406fa921afe80c093c735a5838 100644
--- a/src/acpi/madt.rs
+++ b/src/acpi/madt.rs
@@ -5,7 +5,7 @@ use crate::paging::{ActivePageTable, Page, PhysicalAddress, VirtualAddress};
 use crate::paging::entry::EntryFlags;
 
 use super::sdt::Sdt;
-use super::{AP_STARTUP, TRAMPOLINE, find_sdt, load_table, get_sdt_signature};
+use super::{find_sdt, load_table, get_sdt_signature};
 
 use core::intrinsics::{atomic_load, atomic_store};
 use core::sync::atomic::Ordering;
@@ -22,6 +22,9 @@ pub struct Madt {
     pub flags: u32
 }
 
+const TRAMPOLINE: usize = 0x8000;
+static TRAMPOLINE_DATA: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/trampoline"));
+
 pub static mut MADT: Option<Madt> = None;
 pub const FLAG_PCAT: u32 = 1;
 
@@ -52,13 +55,19 @@ impl Madt {
             }
 
             if cfg!(feature = "multi_core") {
+                // Map trampoline
                 let trampoline_frame = Frame::containing_address(PhysicalAddress::new(TRAMPOLINE));
                 let trampoline_page = Page::containing_address(VirtualAddress::new(TRAMPOLINE));
-
-                // Map trampoline
                 let result = active_table.map_to(trampoline_page, trampoline_frame, EntryFlags::PRESENT | EntryFlags::WRITABLE);
                 result.flush(active_table);
 
+                // Write trampoline, make sure TRAMPOLINE page is free for use
+                for i in 0..TRAMPOLINE_DATA.len() {
+                    unsafe {
+                        atomic_store((TRAMPOLINE as *mut u8).add(i), TRAMPOLINE_DATA[i]);
+                    }
+                }
+
                 for madt_entry in madt.iter() {
                     println!("      {:?}", madt_entry);
                     match madt_entry {
@@ -73,7 +82,7 @@ impl Madt {
                                 let stack_start = allocate_frames(64).expect("no more frames in acpi stack_start").start_address().get() + crate::KERNEL_OFFSET;
                                 let stack_end = stack_start + 64 * 4096;
 
-                                let ap_ready = TRAMPOLINE as *mut u64;
+                                let ap_ready = (TRAMPOLINE + 8) as *mut u64;
                                 let ap_cpu_id = unsafe { ap_ready.offset(1) };
                                 let ap_page_table = unsafe { ap_ready.offset(2) };
                                 let ap_stack_start = unsafe { ap_ready.offset(3) };
@@ -106,7 +115,7 @@ impl Madt {
                                 // Send START IPI
                                 {
                                     //Start at 0x0800:0000 => 0x8000. Hopefully the bootloader code is still there
-                                    let ap_segment = (AP_STARTUP >> 12) & 0xFF;
+                                    let ap_segment = (TRAMPOLINE >> 12) & 0xFF;
                                     let mut icr = 0x4600 | ap_segment as u64;
 
                                     if local_apic.x2 {
diff --git a/src/acpi/mod.rs b/src/acpi/mod.rs
index 4764aa43791474a5b0060b8cc849e50da0bf5f17..25e64eb87002bb64eaf659a990b7043a94e12953 100644
--- a/src/acpi/mod.rs
+++ b/src/acpi/mod.rs
@@ -39,9 +39,6 @@ pub mod aml;
 mod rxsdt;
 mod rsdp;
 
-const TRAMPOLINE: usize = 0x7E00;
-const AP_STARTUP: usize = TRAMPOLINE + 512;
-
 pub fn get_sdt(sdt_address: usize, active_table: &mut ActivePageTable) -> &'static Sdt {
     {
         let page = Page::containing_address(VirtualAddress::new(sdt_address));
diff --git a/src/arch/x86_64/gdt.rs b/src/arch/x86_64/gdt.rs
index f5c950cd9f965a8389e4659bc59a2b15dda0e12d..c1713143ae094f93c16da977872f7f21b11ddfe8 100644
--- a/src/arch/x86_64/gdt.rs
+++ b/src/arch/x86_64/gdt.rs
@@ -99,7 +99,7 @@ pub unsafe fn set_tcb(pid: usize) {
 
 #[cfg(feature = "pti")]
 pub unsafe fn set_tss_stack(stack: usize) {
-    use arch::x86_64::pti::{PTI_CPU_STACK, PTI_CONTEXT_STACK};
+    use super::pti::{PTI_CPU_STACK, PTI_CONTEXT_STACK};
     TSS.rsp[0] = (PTI_CPU_STACK.as_ptr() as usize + PTI_CPU_STACK.len()) as u64;
     PTI_CONTEXT_STACK = stack;
 }
diff --git a/src/arch/x86_64/graphical_debug/display.rs b/src/arch/x86_64/graphical_debug/display.rs
index a88248be6bc7f24d62de6dda659997f87475a531..4f5ecbf8efe2ea707f862d9eb8d4507d2177891f 100644
--- a/src/arch/x86_64/graphical_debug/display.rs
+++ b/src/arch/x86_64/graphical_debug/display.rs
@@ -15,7 +15,7 @@ pub struct Display {
 impl Display {
     pub fn new(width: usize, height: usize, onscreen: usize) -> Display {
         let size = width * height;
-        let offscreen = unsafe { ::ALLOCATOR.alloc(Layout::from_size_align_unchecked(size * 4, 4096)) };
+        let offscreen = unsafe { crate::ALLOCATOR.alloc(Layout::from_size_align_unchecked(size * 4, 4096)) };
         unsafe { fast_set64(offscreen as *mut u64, 0, size/2) };
         Display {
             width: width,
@@ -144,6 +144,6 @@ impl Display {
 
 impl Drop for Display {
     fn drop(&mut self) {
-        unsafe { ::ALLOCATOR.dealloc(self.offscreen.as_mut_ptr() as *mut u8, Layout::from_size_align_unchecked(self.offscreen.len() * 4, 4096)) };
+        unsafe { crate::ALLOCATOR.dealloc(self.offscreen.as_mut_ptr() as *mut u8, Layout::from_size_align_unchecked(self.offscreen.len() * 4, 4096)) };
     }
 }
diff --git a/src/arch/x86_64/graphical_debug/mod.rs b/src/arch/x86_64/graphical_debug/mod.rs
index 081f567865c41849357fe0b9920261c6f00adc91..6ea97ed541ef276690e06eed9b6e31a0983cacf3 100644
--- a/src/arch/x86_64/graphical_debug/mod.rs
+++ b/src/arch/x86_64/graphical_debug/mod.rs
@@ -1,9 +1,9 @@
 use spin::Mutex;
 
-use memory::Frame;
-use paging::{ActivePageTable, Page, PhysicalAddress, VirtualAddress};
-use paging::entry::EntryFlags;
-use paging::mapper::MapperFlushAll;
+use crate::memory::Frame;
+use crate::paging::{ActivePageTable, Page, PhysicalAddress, VirtualAddress};
+use crate::paging::entry::EntryFlags;
+use crate::paging::mapper::MapperFlushAll;
 
 pub use self::debug::DebugDisplay;
 use self::display::Display;
@@ -54,13 +54,13 @@ pub fn init(active_table: &mut ActivePageTable) {
     {
         let size = width * height;
 
-        let onscreen = physbaseptr + ::KERNEL_OFFSET;
+        let onscreen = physbaseptr + crate::KERNEL_OFFSET;
         {
             let mut flush_all = MapperFlushAll::new();
             let start_page = Page::containing_address(VirtualAddress::new(onscreen));
             let end_page = Page::containing_address(VirtualAddress::new(onscreen + size * 4));
             for page in Page::range_inclusive(start_page, end_page) {
-                let frame = Frame::containing_address(PhysicalAddress::new(page.start_address().get() - ::KERNEL_OFFSET));
+                let frame = Frame::containing_address(PhysicalAddress::new(page.start_address().get() - crate::KERNEL_OFFSET));
                 let flags = EntryFlags::PRESENT | EntryFlags::NO_EXECUTE | EntryFlags::WRITABLE | EntryFlags::HUGE_PAGE;
                 let result = active_table.map_to(page, frame, flags);
                 flush_all.consume(result);
diff --git a/src/arch/x86_64/idt.rs b/src/arch/x86_64/idt.rs
index e1c0095cf466b0a39c2f53e44ce1a5e5d1d9e6cc..bb0eb2909daec8bc62959b33d3e85923fd2852e2 100644
--- a/src/arch/x86_64/idt.rs
+++ b/src/arch/x86_64/idt.rs
@@ -172,7 +172,7 @@ pub unsafe fn init_generic(is_bsp: bool, idt: &mut Idt) {
     current_idt[13].set_func(exception::protection);
     current_idt[14].set_func(exception::page);
     // 15 reserved
-    current_idt[16].set_func(exception::fpu);
+    current_idt[16].set_func(exception::fpu_fault);
     current_idt[17].set_func(exception::alignment_check);
     current_idt[18].set_func(exception::machine_check);
     current_idt[19].set_func(exception::simd);
@@ -186,7 +186,7 @@ pub unsafe fn init_generic(is_bsp: bool, idt: &mut Idt) {
 
     if is_bsp {
         // Set up IRQs
-        current_idt[32].set_func(irq::pit);
+        current_idt[32].set_func(irq::pit_stack);
         current_idt[33].set_func(irq::keyboard);
         current_idt[34].set_func(irq::cascade);
         current_idt[35].set_func(irq::com2);
diff --git a/src/arch/x86_64/interrupt/exception.rs b/src/arch/x86_64/interrupt/exception.rs
index b55a13e5d4aabaa233fee488be18c73f6a4e4551..77b78f4fcc1de4cd1f84f6de60a0d97028b9926d 100644
--- a/src/arch/x86_64/interrupt/exception.rs
+++ b/src/arch/x86_64/interrupt/exception.rs
@@ -1,25 +1,26 @@
 use crate::{
     interrupt::stack_trace,
     ptrace,
-    syscall::flag::*
+    syscall::flag::*,
+
+    interrupt_stack,
+    interrupt_error,
 };
 
 extern {
     fn ksignal(signal: usize);
 }
 
-interrupt_stack!(divide_by_zero, stack, {
+interrupt_stack!(divide_by_zero, |stack| {
     println!("Divide by zero");
     stack.dump();
     stack_trace();
     ksignal(SIGFPE);
 });
 
-interrupt_stack!(debug, stack, {
+interrupt_stack!(debug, |stack| {
     let mut handled = false;
 
-    let guard = ptrace::set_process_regs(stack);
-
     // Disable singlestep before there is a breakpoint, since the breakpoint
     // handler might end up setting it again but unless it does we want the
     // default to be false.
@@ -33,8 +34,6 @@ interrupt_stack!(debug, stack, {
         stack.set_singlestep(had_singlestep);
     }
 
-    drop(guard);
-
     if !handled {
         println!("Debug trap");
         stack.dump();
@@ -42,12 +41,12 @@ interrupt_stack!(debug, stack, {
     }
 });
 
-interrupt_stack!(non_maskable, stack, {
+interrupt_stack!(non_maskable, |stack| {
     println!("Non-maskable interrupt");
     stack.dump();
 });
 
-interrupt_stack!(breakpoint, stack, {
+interrupt_stack!(breakpoint, |stack| {
     // The processor lets RIP point to the instruction *after* int3, so
     // unhandled breakpoint interrupt don't go in an infinite loop. But we
     // throw SIGTRAP anyway, so that's not a problem.
@@ -61,81 +60,77 @@ interrupt_stack!(breakpoint, stack, {
     // int3 instruction. After all, it's the sanest thing to do.
     stack.iret.rip -= 1;
 
-    let guard = ptrace::set_process_regs(stack);
-
     if ptrace::breakpoint_callback(PTRACE_STOP_BREAKPOINT, None).is_none() {
-        drop(guard);
-
         println!("Breakpoint trap");
         stack.dump();
         ksignal(SIGTRAP);
     }
 });
 
-interrupt_stack!(overflow, stack, {
+interrupt_stack!(overflow, |stack| {
     println!("Overflow trap");
     stack.dump();
     stack_trace();
     ksignal(SIGFPE);
 });
 
-interrupt_stack!(bound_range, stack, {
+interrupt_stack!(bound_range, |stack| {
     println!("Bound range exceeded fault");
     stack.dump();
     stack_trace();
     ksignal(SIGSEGV);
 });
 
-interrupt_stack!(invalid_opcode, stack, {
+interrupt_stack!(invalid_opcode, |stack| {
     println!("Invalid opcode fault");
     stack.dump();
     stack_trace();
     ksignal(SIGILL);
 });
 
-interrupt_stack!(device_not_available, stack, {
+interrupt_stack!(device_not_available, |stack| {
     println!("Device not available fault");
     stack.dump();
     stack_trace();
     ksignal(SIGILL);
 });
 
-interrupt_error!(double_fault, stack, {
+interrupt_error!(double_fault, |stack| {
     println!("Double fault");
     stack.dump();
     stack_trace();
     ksignal(SIGSEGV);
 });
 
-interrupt_error!(invalid_tss, stack, {
+interrupt_error!(invalid_tss, |stack| {
     println!("Invalid TSS fault");
     stack.dump();
     stack_trace();
     ksignal(SIGSEGV);
 });
 
-interrupt_error!(segment_not_present, stack, {
+interrupt_error!(segment_not_present, |stack| {
     println!("Segment not present fault");
     stack.dump();
     stack_trace();
     ksignal(SIGSEGV);
 });
 
-interrupt_error!(stack_segment, stack, {
+interrupt_error!(stack_segment, |stack| {
     println!("Stack segment fault");
     stack.dump();
     stack_trace();
     ksignal(SIGSEGV);
 });
 
-interrupt_error!(protection, stack, {
+interrupt_error!(protection, |stack| {
     println!("Protection fault");
     stack.dump();
     stack_trace();
     ksignal(SIGSEGV);
 });
 
-interrupt_error!(page, stack, {
+interrupt_error!(page, |stack| {
     let cr2: usize;
     asm!("mov rax, cr2" : "={rax}"(cr2) : : : "intel", "volatile");
     println!("Page fault: {:>016X}", cr2);
@@ -144,42 +139,42 @@ interrupt_error!(page, stack, {
     ksignal(SIGSEGV);
 });
 
-interrupt_stack!(fpu, stack, {
+interrupt_stack!(fpu_fault, |stack| {
     println!("FPU floating point fault");
     stack.dump();
     stack_trace();
     ksignal(SIGFPE);
 });
 
-interrupt_error!(alignment_check, stack, {
+interrupt_error!(alignment_check, |stack| {
     println!("Alignment check fault");
     stack.dump();
     stack_trace();
     ksignal(SIGBUS);
 });
 
-interrupt_stack!(machine_check, stack, {
+interrupt_stack!(machine_check, |stack| {
     println!("Machine check fault");
     stack.dump();
     stack_trace();
     ksignal(SIGBUS);
 });
 
-interrupt_stack!(simd, stack, {
+interrupt_stack!(simd, |stack| {
     println!("SIMD floating point fault");
     stack.dump();
     stack_trace();
     ksignal(SIGFPE);
 });
 
-interrupt_stack!(virtualization, stack, {
+interrupt_stack!(virtualization, |stack| {
     println!("Virtualization fault");
     stack.dump();
     stack_trace();
     ksignal(SIGBUS);
 });
 
-interrupt_error!(security, stack, {
+interrupt_error!(security, |stack| {
     println!("Security exception");
     stack.dump();
     stack_trace();
diff --git a/src/arch/x86_64/interrupt/handler.rs b/src/arch/x86_64/interrupt/handler.rs
new file mode 100644
index 0000000000000000000000000000000000000000..06d060b176ae1a11b3184a6ee32db90c050052e6
--- /dev/null
+++ b/src/arch/x86_64/interrupt/handler.rs
@@ -0,0 +1,435 @@
+use core::mem;
+use syscall::IntRegisters;
+
+const FLAG_SINGLESTEP: usize = 1 << 8;
+
+#[derive(Default)]
+#[repr(packed)]
+pub struct ScratchRegisters {
+    pub r11: usize,
+    pub r10: usize,
+    pub r9: usize,
+    pub r8: usize,
+    pub rsi: usize,
+    pub rdi: usize,
+    pub rdx: usize,
+    pub rcx: usize,
+    pub rax: usize,
+}
+
+impl ScratchRegisters {
+    pub fn dump(&self) {
+        println!("RAX:   {:>016X}", { self.rax });
+        println!("RCX:   {:>016X}", { self.rcx });
+        println!("RDX:   {:>016X}", { self.rdx });
+        println!("RDI:   {:>016X}", { self.rdi });
+        println!("RSI:   {:>016X}", { self.rsi });
+        println!("R8:    {:>016X}", { self.r8 });
+        println!("R9:    {:>016X}", { self.r9 });
+        println!("R10:   {:>016X}", { self.r10 });
+        println!("R11:   {:>016X}", { self.r11 });
+    }
+}
+
+#[derive(Default)]
+#[repr(packed)]
+pub struct PreservedRegisters {
+    pub r15: usize,
+    pub r14: usize,
+    pub r13: usize,
+    pub r12: usize,
+    pub rbp: usize,
+    pub rbx: usize,
+}
+
+impl PreservedRegisters {
+    pub fn dump(&self) {
+        println!("RBX:   {:>016X}", { self.rbx });
+        println!("RBP:   {:>016X}", { self.rbp });
+        println!("R12:   {:>016X}", { self.r12 });
+        println!("R13:   {:>016X}", { self.r13 });
+        println!("R14:   {:>016X}", { self.r14 });
+        println!("R15:   {:>016X}", { self.r15 });
+    }
+}
+
+#[derive(Default)]
+#[repr(packed)]
+pub struct IretRegisters {
+    pub rip: usize,
+    pub cs: usize,
+    pub rflags: usize,
+
+    // ----
+    // The following will only be present if interrupt is raised from another
+    // privilege ring. Otherwise, they are undefined values.
+    // ----
+
+    pub rsp: usize,
+    pub ss: usize
+}
+
+impl IretRegisters {
+    pub fn dump(&self) {
+        println!("RFLAG: {:>016X}", { self.rflags });
+        println!("CS:    {:>016X}", { self.cs });
+        println!("RIP:   {:>016X}", { self.rip });
+    }
+}
+
+#[derive(Default)]
+#[repr(packed)]
+pub struct InterruptStack {
+    pub fs: usize,
+    pub preserved: PreservedRegisters,
+    pub scratch: ScratchRegisters,
+    pub iret: IretRegisters,
+}
+
+impl InterruptStack {
+    pub fn dump(&self) {
+        self.iret.dump();
+        self.scratch.dump();
+        self.preserved.dump();
+        println!("FS:    {:>016X}", { self.fs });
+    }
+    /// Saves all registers to a struct used by the proc:
+    /// scheme to read/write registers.
+    pub fn save(&self, all: &mut IntRegisters) {
+        all.fs = self.fs;
+
+        all.r15 = self.preserved.r15;
+        all.r14 = self.preserved.r14;
+        all.r13 = self.preserved.r13;
+        all.r12 = self.preserved.r12;
+        all.rbp = self.preserved.rbp;
+        all.rbx = self.preserved.rbx;
+        all.r11 = self.scratch.r11;
+        all.r10 = self.scratch.r10;
+        all.r9 = self.scratch.r9;
+        all.r8 = self.scratch.r8;
+        all.rsi = self.scratch.rsi;
+        all.rdi = self.scratch.rdi;
+        all.rdx = self.scratch.rdx;
+        all.rcx = self.scratch.rcx;
+        all.rax = self.scratch.rax;
+        all.rip = self.iret.rip;
+        all.cs = self.iret.cs;
+        all.rflags = self.iret.rflags;
+
+        // Set rsp and ss:
+
+        const CPL_MASK: usize = 0b11;
+
+        let cs: usize;
+        unsafe {
+            asm!("mov $0, cs" : "=r"(cs) ::: "intel");
+        }
+
+        if self.iret.cs & CPL_MASK == cs & CPL_MASK {
+            // Privilege ring didn't change, so neither did the stack
+            all.rsp = self as *const Self as usize // rsp after Self was pushed to the stack
+                + mem::size_of::<Self>() // disregard Self
+                - mem::size_of::<usize>() * 2; // well, almost: rsp and ss need to be excluded as they aren't present
+            unsafe {
+                asm!("mov $0, ss" : "=r"(all.ss) ::: "intel");
+            }
+        } else {
+            all.rsp = self.iret.rsp;
+            all.ss = self.iret.ss;
+        }
+    }
+    /// Loads all registers from a struct used by the proc:
+    /// scheme to read/write registers.
+    pub fn load(&mut self, all: &IntRegisters) {
+        // TODO: Which of these should be allowed to change?
+
+        // self.fs = all.fs;
+        self.preserved.r15 = all.r15;
+        self.preserved.r14 = all.r14;
+        self.preserved.r13 = all.r13;
+        self.preserved.r12 = all.r12;
+        self.preserved.rbp = all.rbp;
+        self.preserved.rbx = all.rbx;
+        self.scratch.r11 = all.r11;
+        self.scratch.r10 = all.r10;
+        self.scratch.r9 = all.r9;
+        self.scratch.r8 = all.r8;
+        self.scratch.rsi = all.rsi;
+        self.scratch.rdi = all.rdi;
+        self.scratch.rdx = all.rdx;
+        self.scratch.rcx = all.rcx;
+        self.scratch.rax = all.rax;
+        self.iret.rip = all.rip;
+
+        // These should probably be restricted
+        // self.iret.cs = all.cs;
+        // self.iret.rflags = all.eflags;
+    }
+    /// Enables the "Trap Flag" in the FLAGS register, causing the CPU
+    /// to send a Debug exception after the next instruction. This is
+    /// used for singlestep in the proc: scheme.
+    pub fn set_singlestep(&mut self, enabled: bool) {
+        if enabled {
+            self.iret.rflags |= FLAG_SINGLESTEP;
+        } else {
+            self.iret.rflags &= !FLAG_SINGLESTEP;
+        }
+    }
+    /// Checks if the trap flag is enabled, see `set_singlestep`
+    pub fn is_singlestep(&self) -> bool {
+        self.iret.rflags & FLAG_SINGLESTEP == FLAG_SINGLESTEP
+    }
+}
+
+#[derive(Default)]
+#[repr(packed)]
+pub struct InterruptErrorStack {
+    pub code: usize,
+    pub inner: InterruptStack,
+}
+
+impl InterruptErrorStack {
+    pub fn dump(&self) {
+        println!("CODE:  {:>016X}", { self.code });
+        self.inner.dump();
+    }
+}
+
+#[macro_export]
+macro_rules! intel_asm {
+    ($($strings:expr,)+) => {
+        global_asm!(concat!(
+            ".intel_syntax noprefix\n",
+            $($strings),+,
+            ".att_syntax prefix\n",
+        ));
+    };
+}
+#[macro_export]
+macro_rules! function {
+    ($name:ident => { $($body:expr,)+ }) => {
+        intel_asm!(
+            ".global ", stringify!($name), "\n",
+            ".type ", stringify!($name), ", @function\n",
+            ".section .text.", stringify!($name), ", \"ax\", @progbits\n",
+            stringify!($name), ":\n",
+            $($body),+,
+            ".size ", stringify!($name), ", . - ", stringify!($name), "\n",
+            ".text\n",
+        );
+        extern "C" {
+            pub fn $name();
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! push_scratch {
+    () => { "
+        // Push scratch registers
+        push rcx
+        push rdx
+        push rdi
+        push rsi
+        push r8
+        push r9
+        push r10
+        push r11
+    " };
+}
+#[macro_export]
+macro_rules! pop_scratch {
+    () => { "
+        // Pop scratch registers
+        pop r11
+        pop r10
+        pop r9
+        pop r8
+        pop rsi
+        pop rdi
+        pop rdx
+        pop rcx
+        pop rax
+    " };
+}
+
+#[macro_export]
+macro_rules! push_preserved {
+    () => { "
+        // Push preserved registers
+        push rbx
+        push rbp
+        push r12
+        push r13
+        push r14
+        push r15
+    " };
+}
+#[macro_export]
+macro_rules! pop_preserved {
+    () => { "
+        // Pop preserved registers
+        pop r15
+        pop r14
+        pop r13
+        pop r12
+        pop rbp
+        pop rbx
+    " };
+}
+
+#[macro_export]
+macro_rules! push_fs {
+    () => { "
+        // Push fs
+        push fs
+
+        // Load kernel tls
+        //
+        // NOTE: We can't load the value directly into `fs`. So we need to use a
+        // scratch register (as preserved registers aren't backed up by the
+        // interrupt! macro) to store it. We also can't use `rax` as the temporary
+        // value, as during errors that's already used for the error code.
+        mov rcx, 0x18
+        mov fs, cx
+    " };
+}
+#[macro_export]
+macro_rules! pop_fs {
+    () => { "
+        // Pop fs
+        pop fs
+    " };
+}
+
+#[macro_export]
+macro_rules! interrupt_stack {
+    ($name:ident, |$stack:ident| $code:block) => {
+        paste::item! {
+            #[no_mangle]
+            unsafe extern "C" fn [<__interrupt_ $name>](stack: *mut $crate::arch::x86_64::interrupt::InterruptStack) {
+                // This inner function is needed because macros are buggy:
+                // https://github.com/dtolnay/paste/issues/7
+                #[inline(always)]
+                unsafe fn inner($stack: &mut $crate::arch::x86_64::interrupt::InterruptStack) {
+                    $code
+                }
+                let _guard = $crate::ptrace::set_process_regs(stack);
+                inner(&mut *stack);
+            }
+
+            function!($name => {
+                // Backup all userspace registers to stack
+                "push rax\n",
+                push_scratch!(),
+                push_preserved!(),
+                push_fs!(),
+
+                // TODO: Map PTI
+                // $crate::arch::x86_64::pti::map();
+
+                // Call inner function with pointer to stack
+                "mov rdi, rsp\n",
+                "call __interrupt_", stringify!($name), "\n",
+
+                // TODO: Unmap PTI
+                // $crate::arch::x86_64::pti::unmap();
+
+                // Restore all userspace registers
+                pop_fs!(),
+                pop_preserved!(),
+                pop_scratch!(),
+
+                "iretq\n",
+            });
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! interrupt {
+    ($name:ident, || $code:block) => {
+        paste::item! {
+            #[no_mangle]
+            unsafe extern "C" fn [<__interrupt_ $name>]() {
+                $code
+            }
+
+            function!($name => {
+                // Backup all userspace registers to stack
+                "push rax\n",
+                push_scratch!(),
+                push_fs!(),
+
+                // TODO: Map PTI
+                // $crate::arch::x86_64::pti::map();
+
+                // Call inner function with pointer to stack
+                "call __interrupt_", stringify!($name), "\n",
+
+                // TODO: Unmap PTI
+                // $crate::arch::x86_64::pti::unmap();
+
+                // Restore all userspace registers
+                pop_fs!(),
+                pop_scratch!(),
+
+                "iretq\n",
+            });
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! interrupt_error {
+    ($name:ident, |$stack:ident| $code:block) => {
+        paste::item! {
+            #[no_mangle]
+            unsafe extern "C" fn [<__interrupt_ $name>](stack: *mut $crate::arch::x86_64::interrupt::handler::InterruptErrorStack) {
+                // This inner function is needed because macros are buggy:
+                // https://github.com/dtolnay/paste/issues/7
+                #[inline(always)]
+                unsafe fn inner($stack: &mut $crate::arch::x86_64::interrupt::handler::InterruptErrorStack) {
+                    $code
+                }
+                let _guard = $crate::ptrace::set_process_regs(&mut (*stack).inner);
+                inner(&mut *stack);
+            }
+
+            function!($name => {
+                // Move rax into code's place, put code in last instead (to be
+                // compatible with InterruptStack)
+                "xchg [rsp], rax\n",
+
+                // Push all userspace registers
+                push_scratch!(),
+                push_preserved!(),
+                push_fs!(),
+
+                // Put code in, it's now in rax
+                "push rax\n",
+
+                // TODO: Map PTI
+                // $crate::arch::x86_64::pti::map();
+
+                // Call inner function with pointer to stack
+                "mov rdi, rsp\n",
+                "call __interrupt_", stringify!($name), "\n",
+
+                // TODO: Unmap PTI
+                // $crate::arch::x86_64::pti::unmap();
+
+                // Pop code
+                "add rsp, 8\n",
+
+                // Restore all userspace registers
+                pop_fs!(),
+                pop_preserved!(),
+                pop_scratch!(),
+
+                "iretq\n",
+            });
+        }
+    };
+}
diff --git a/src/arch/x86_64/interrupt/ipi.rs b/src/arch/x86_64/interrupt/ipi.rs
index eb747e647423ebab7d0d980075a06f5784312bf2..52aad2abe4c1c21a9609cd2aa0978392b73e2684 100644
--- a/src/arch/x86_64/interrupt/ipi.rs
+++ b/src/arch/x86_64/interrupt/ipi.rs
@@ -5,23 +5,23 @@ use crate::context;
 use crate::device::local_apic::LOCAL_APIC;
 use super::irq::PIT_TICKS;
 
-interrupt!(wakeup, {
+interrupt!(wakeup, || {
     LOCAL_APIC.eoi();
 });
 
-interrupt!(tlb, {
+interrupt!(tlb, || {
     LOCAL_APIC.eoi();
 
     tlb::flush_all();
 });
 
-interrupt!(switch, {
+interrupt!(switch, || {
     LOCAL_APIC.eoi();
 
     let _ = context::switch();
 });
 
-interrupt!(pit, {
+interrupt!(pit, || {
     LOCAL_APIC.eoi();
 
     if PIT_TICKS.fetch_add(1, Ordering::SeqCst) >= 10 {
diff --git a/src/arch/x86_64/interrupt/irq.rs b/src/arch/x86_64/interrupt/irq.rs
index a01196f80ffb8e5ac219c04958a48724bd16b0be..a1e2f20ad77c73e2758d2a46af5c93c807d46f0e 100644
--- a/src/arch/x86_64/interrupt/irq.rs
+++ b/src/arch/x86_64/interrupt/irq.rs
@@ -2,12 +2,13 @@ use core::sync::atomic::{AtomicUsize, Ordering};
 
 use alloc::vec::Vec;
 
+use crate::{interrupt, interrupt_stack};
 use crate::context::timeout;
 use crate::device::{local_apic, ioapic, pic};
 use crate::device::serial::{COM1, COM2};
 use crate::ipi::{ipi, IpiKind, IpiTarget};
 use crate::scheme::debug::debug_input;
-use crate::{context, ptrace, time};
+use crate::{context, time};
 
 //resets to 0 in context::switch()
 #[thread_local]
@@ -31,10 +32,10 @@ unsafe fn ps2_interrupt(_index: usize) {
         mov ah, al
         in al, 0x60
         "
-        : "={al}"(data), "={ah}"(status)
-        :
-        : "memory"
-        : "intel", "volatile"
+         : "={al}"(data), "={ah}"(status)
+         :
+         : "memory"
+         : "intel", "volatile"
     );
 
     if status & 1 != 0 {
@@ -168,7 +169,7 @@ unsafe fn ioapic_unmask(irq: usize) {
     ioapic::unmask(irq as u8);
 }
 
-interrupt_stack!(pit, stack, {
+interrupt_stack!(pit_stack, |_stack| {
     // Saves CPU time by not sending IRQ event irq_trigger(0);
 
     const PIT_RATE: u64 = 2_250_286;
@@ -189,46 +190,45 @@ interrupt_stack!(pit, stack, {
     timeout::trigger();
 
     if PIT_TICKS.fetch_add(1, Ordering::SeqCst) >= 10 {
-        let _guard = ptrace::set_process_regs(stack);
         let _ = context::switch();
     }
 });
 
-interrupt!(keyboard, {
+interrupt!(keyboard, || {
     ps2_interrupt(0);
     eoi(1);
 });
 
-interrupt!(cascade, {
+interrupt!(cascade, || {
     // No need to do any operations on cascade
     eoi(2);
 });
 
-interrupt!(com2, {
+interrupt!(com2, || {
     while let Some(c) = COM2.lock().receive() {
         debug_input(c);
     }
     eoi(3);
 });
 
-interrupt!(com1, {
+interrupt!(com1, || {
     while let Some(c) = COM1.lock().receive() {
         debug_input(c);
     }
     eoi(4);
 });
 
-interrupt!(lpt2, {
+interrupt!(lpt2, || {
     trigger(5);
     eoi(5);
 });
 
-interrupt!(floppy, {
+interrupt!(floppy, || {
     trigger(6);
     eoi(6);
 });
 
-interrupt!(lpt1, {
+interrupt!(lpt1, || {
     if irq_method() == IrqMethod::Pic && pic::MASTER.isr() & (1 << 7) == 0 {
         // the IRQ was spurious, ignore it but increment a counter.
         SPURIOUS_COUNT_IRQ7.fetch_add(1, Ordering::Relaxed);
@@ -238,42 +238,42 @@ interrupt!(lpt1, {
     eoi(7);
 });
 
-interrupt!(rtc, {
+interrupt!(rtc, || {
     trigger(8);
     eoi(8);
 });
 
-interrupt!(pci1, {
+interrupt!(pci1, || {
     trigger(9);
     eoi(9);
 });
 
-interrupt!(pci2, {
+interrupt!(pci2, || {
     trigger(10);
     eoi(10);
 });
 
-interrupt!(pci3, {
+interrupt!(pci3, || {
     trigger(11);
     eoi(11);
 });
 
-interrupt!(mouse, {
+interrupt!(mouse, || {
     ps2_interrupt(1);
     eoi(12);
 });
 
-interrupt!(fpu, {
+interrupt!(fpu, || {
     trigger(13);
     eoi(13);
 });
 
-interrupt!(ata1, {
+interrupt!(ata1, || {
     trigger(14);
     eoi(14);
 });
 
-interrupt!(ata2, {
+interrupt!(ata2, || {
     if irq_method() == IrqMethod::Pic && pic::SLAVE.isr() & (1 << 7) == 0 {
         SPURIOUS_COUNT_IRQ15.fetch_add(1, Ordering::Relaxed);
         pic::MASTER.ack();
@@ -283,17 +283,17 @@ interrupt!(ata2, {
     eoi(15);
 });
 
-interrupt!(lapic_timer, {
+interrupt!(lapic_timer, || {
     println!("Local apic timer interrupt");
     lapic_eoi();
 });
 
-interrupt!(lapic_error, {
+interrupt!(lapic_error, || {
     println!("Local apic internal error: ESR={:#0x}", local_apic::LOCAL_APIC.esr());
     lapic_eoi();
 });
 
-interrupt!(calib_pit, {
+interrupt!(calib_pit, || {
     const PIT_RATE: u64 = 2_250_286;
 
     {
@@ -309,7 +309,7 @@ interrupt!(calib_pit, {
 
 macro_rules! allocatable_irq(
     ( $idt:expr, $number:literal, $name:ident ) => {
-        interrupt!($name, {
+        interrupt!($name, || {
             allocatable_irq_generic($number);
         });
     }
diff --git a/src/arch/x86_64/interrupt/mod.rs b/src/arch/x86_64/interrupt/mod.rs
index fcf75d12033ff2227f492206e60a9a1c150c836a..ef2588b1f5ab9d3a7e923d2908ff2141c6819766 100644
--- a/src/arch/x86_64/interrupt/mod.rs
+++ b/src/arch/x86_64/interrupt/mod.rs
@@ -1,11 +1,15 @@
 //! Interrupt instructions
 
+#[macro_use]
+pub mod handler;
+
 pub mod exception;
 pub mod ipi;
 pub mod irq;
 pub mod syscall;
 pub mod trace;
 
+pub use self::handler::InterruptStack;
 pub use self::trace::stack_trace;
 
 pub use super::idt::{available_irqs_iter, is_reserved, set_reserved};
diff --git a/src/arch/x86_64/interrupt/syscall.rs b/src/arch/x86_64/interrupt/syscall.rs
index 9f5ed74b253032f23342867e5e5bb6126220c6f1..d7f64a837c60315de84674c43ff0bfc0417fe7d3 100644
--- a/src/arch/x86_64/interrupt/syscall.rs
+++ b/src/arch/x86_64/interrupt/syscall.rs
@@ -1,7 +1,10 @@
-use crate::arch::macros::InterruptStack;
-use crate::arch::{gdt, pti};
-use crate::syscall::flag::{PTRACE_FLAG_IGNORE, PTRACE_STOP_PRE_SYSCALL, PTRACE_STOP_POST_SYSCALL};
-use crate::{ptrace, syscall};
+use crate::{
+    arch::{gdt, interrupt::InterruptStack},
+    context,
+    ptrace,
+    syscall,
+    syscall::flag::{PTRACE_FLAG_IGNORE, PTRACE_STOP_PRE_SYSCALL, PTRACE_STOP_POST_SYSCALL},
+};
 use x86::msr;
 
 pub unsafe fn init() {
@@ -14,121 +17,113 @@ pub unsafe fn init() {
     msr::wrmsr(msr::IA32_EFER, efer | 1);
 }
 
-// Not a function pointer because it somehow messes up the returning
-// from clone() (via clone_ret()). Not sure what the problem is.
 macro_rules! with_interrupt_stack {
-    (unsafe fn $wrapped:ident($stack:ident) -> usize $code:block) => {
-        #[inline(never)]
-        unsafe fn $wrapped(stack: *mut InterruptStack) {
-            let _guard = ptrace::set_process_regs(stack);
-
-            let allowed = ptrace::breakpoint_callback(PTRACE_STOP_PRE_SYSCALL, None)
-                .and_then(|_| ptrace::next_breakpoint().map(|f| !f.contains(PTRACE_FLAG_IGNORE)));
-
-            if allowed.unwrap_or(true) {
-                // If syscall not ignored
-                let $stack = &mut *stack;
-                $stack.scratch.rax = $code;
-            }
-
-            ptrace::breakpoint_callback(PTRACE_STOP_POST_SYSCALL, None);
+    (|$stack:ident| $code:block) => {{
+        let allowed = ptrace::breakpoint_callback(PTRACE_STOP_PRE_SYSCALL, None)
+            .and_then(|_| ptrace::next_breakpoint().map(|f| !f.contains(PTRACE_FLAG_IGNORE)));
+
+        if allowed.unwrap_or(true) {
+            // If the syscall is `clone`, the clone won't return here. Instead,
+            // it'll return early and leave any undropped values. This is
+            // actually GOOD, because any references are at that point UB
+            // anyway, because they are based on the wrong stack.
+            let $stack = &mut *$stack;
+            (*$stack).scratch.rax = $code;
         }
-    }
-}
 
-#[naked]
-pub unsafe extern fn syscall_instruction() {
-    with_interrupt_stack! {
-        unsafe fn inner(stack) -> usize {
-            let rbp;
-            asm!("" : "={rbp}"(rbp) : : : "intel", "volatile");
-
-            let scratch = &stack.scratch;
-            syscall::syscall(scratch.rax, scratch.rdi, scratch.rsi, scratch.rdx, scratch.r10, scratch.r8, rbp, stack)
-        }
-    }
-
-    // Yes, this is magic. No, you don't need to understand
-    asm!("
-          swapgs                    // Set gs segment to TSS
-          mov gs:[28], rsp          // Save userspace rsp
-          mov rsp, gs:[4]           // Load kernel rsp
-          push 5 * 8 + 3            // Push userspace data segment
-          push qword ptr gs:[28]    // Push userspace rsp
-          mov qword ptr gs:[28], 0  // Clear userspace rsp
-          push r11                  // Push rflags
-          push 4 * 8 + 3            // Push userspace code segment
-          push rcx                  // Push userspace return pointer
-          swapgs                    // Restore gs
-          "
-          :
-          :
-          :
-          : "intel", "volatile");
-
-    // Push scratch registers
-    interrupt_push!();
-
-    // Get reference to stack variables
-    let rsp: usize;
-    asm!("" : "={rsp}"(rsp) : : : "intel", "volatile");
-
-    // Map kernel
-    pti::map();
-
-    inner(rsp as *mut InterruptStack);
-
-    // Unmap kernel
-    pti::unmap();
-
-    // Interrupt return
-    interrupt_pop!();
-    iret!()
+        ptrace::breakpoint_callback(PTRACE_STOP_POST_SYSCALL, None);
+    }}
 }
 
-#[naked]
-pub unsafe extern fn syscall() {
-    with_interrupt_stack! {
-        unsafe fn inner(stack) -> usize {
-            let rbp;
-            asm!("" : "={rbp}"(rbp) : : : "intel", "volatile");
+#[no_mangle]
+pub unsafe extern "C" fn __inner_syscall_instruction(stack: *mut InterruptStack) {
+    let _guard = ptrace::set_process_regs(stack);
+    with_interrupt_stack!(|stack| {
+        // Set a restore point for clone
+        let rbp;
+        asm!("" : "={rbp}"(rbp) : : : "intel", "volatile");
+
+        let scratch = &stack.scratch;
+        syscall::syscall(scratch.rax, scratch.rdi, scratch.rsi, scratch.rdx, scratch.r10, scratch.r8, rbp, stack)
+    });
+}
 
-            let scratch = &stack.scratch;
-            syscall::syscall(scratch.rax, stack.preserved.rbx, scratch.rcx, scratch.rdx, scratch.rsi, scratch.rdi, rbp, stack)
+function!(syscall_instruction => {
+    // Yes, this is magic. No, you don't need to understand
+    "
+        swapgs                    // Set gs segment to TSS
+        mov gs:[28], rsp          // Save userspace rsp
+        mov rsp, gs:[4]           // Load kernel rsp
+        push 5 * 8 + 3            // Push userspace data segment
+        push QWORD PTR gs:[28]    // Push userspace rsp
+        mov QWORD PTR gs:[28], 0  // Clear userspace rsp
+        push r11                  // Push rflags
+        push 4 * 8 + 3            // Push userspace code segment
+        push rcx                  // Push userspace return pointer
+        swapgs                    // Restore gs
+    ",
+
+    // Push context registers
+    "push rax\n",
+    push_scratch!(),
+    push_preserved!(),
+    push_fs!(),
+
+    // TODO: Map PTI
+    // $crate::arch::x86_64::pti::map();
+
+    // Call inner funtion
+    "mov rdi, rsp\n",
+    "call __inner_syscall_instruction\n",
+
+    // TODO: Unmap PTI
+    // $crate::arch::x86_64::pti::unmap();
+
+    // Pop context registers
+    pop_fs!(),
+    pop_preserved!(),
+    pop_scratch!(),
+
+    // Return
+    "iretq\n",
+});
+
+interrupt_stack!(syscall, |stack| {
+    with_interrupt_stack!(|stack| {
+        {
+            let contexts = context::contexts();
+            let context = contexts.current();
+            if let Some(current) = context {
+                let current = current.read();
+                let name = current.name.lock();
+                println!("Warning: Context {} used deprecated `int 0x80` construct", core::str::from_utf8(&name).unwrap_or("(invalid utf8)"));
+            } else {
+                println!("Warning: Unknown context used deprecated `int 0x80` construct");
+            }
         }
-    }
-
-    // Push scratch registers
-    interrupt_push!();
-
-    // Get reference to stack variables
-    let rsp: usize;
-    asm!("" : "={rsp}"(rsp) : : : "intel", "volatile");
-
-    // Map kernel
-    pti::map();
-
-    inner(rsp as *mut InterruptStack);
 
-    // Unmap kernel
-    pti::unmap();
-
-    // Interrupt return
-    interrupt_pop!();
-    iret!();
-}
-
-#[naked]
-pub unsafe extern "C" fn clone_ret() {
-    // The C x86_64 ABI specifies that rbp is pushed to save the old
-    // call frame. Popping rbp means we're using the parent's call
-    // frame and thus will not only return from this function but also
-    // from the function above this one.
-    // When this is called, the stack should have been
-    // interrupt->inner->syscall->clone
-    // then changed to
-    // interrupt->inner->clone_ret->clone
-    // so this will return from "inner".
-
-    asm!("pop rbp" : : : : "intel", "volatile");
-}
+        // Set a restore point for clone
+        let rbp;
+        asm!("" : "={rbp}"(rbp) : : : "intel", "volatile");
+
+        let scratch = &stack.scratch;
+        syscall::syscall(scratch.rax, stack.preserved.rbx, scratch.rcx, scratch.rdx, scratch.rsi, scratch.rdi, rbp, stack)
+    })
+});
+
+function!(clone_ret => {
+    // The address of this instruction is injected by `clone` in process.rs, on
+    // top of the stack syscall->inner in this file, which is done using the rbp
+    // register we save there.
+    //
+    // The top of our stack here is the address pointed to by rbp, which is:
+    //
+    // - the previous rbp
+    // - the return location
+    //
+    // Our goal is to return from the parent function, inner, so we restore
+    // rbp...
+    "pop rbp\n",
+    // ...and we return to the address at the top of the stack
+    "ret\n",
+});
diff --git a/src/arch/x86_64/macros.rs b/src/arch/x86_64/macros.rs
index 6271229b9ff277aa0c4ff1c84f480024e43a4e49..5f888cb082967930fceb5b36606e5336d990c64e 100644
--- a/src/arch/x86_64/macros.rs
+++ b/src/arch/x86_64/macros.rs
@@ -1,6 +1,3 @@
-use core::mem;
-use syscall::data::IntRegisters;
-
 /// Print to console
 #[macro_export]
 macro_rules! print {
@@ -18,400 +15,6 @@ macro_rules! println {
     ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
 }
 
-#[allow(dead_code)]
-#[derive(Default)]
-#[repr(packed)]
-pub struct ScratchRegisters {
-    pub r11: usize,
-    pub r10: usize,
-    pub r9: usize,
-    pub r8: usize,
-    pub rsi: usize,
-    pub rdi: usize,
-    pub rdx: usize,
-    pub rcx: usize,
-    pub rax: usize,
-}
-
-impl ScratchRegisters {
-    pub fn dump(&self) {
-        println!("RAX:   {:>016X}", { self.rax });
-        println!("RCX:   {:>016X}", { self.rcx });
-        println!("RDX:   {:>016X}", { self.rdx });
-        println!("RDI:   {:>016X}", { self.rdi });
-        println!("RSI:   {:>016X}", { self.rsi });
-        println!("R8:    {:>016X}", { self.r8 });
-        println!("R9:    {:>016X}", { self.r9 });
-        println!("R10:   {:>016X}", { self.r10 });
-        println!("R11:   {:>016X}", { self.r11 });
-    }
-}
-
-macro_rules! scratch_push {
-    () => (asm!(
-        "push rax
-        push rcx
-        push rdx
-        push rdi
-        push rsi
-        push r8
-        push r9
-        push r10
-        push r11"
-        : : : : "intel", "volatile"
-    ));
-}
-
-macro_rules! scratch_pop {
-    () => (asm!(
-        "pop r11
-        pop r10
-        pop r9
-        pop r8
-        pop rsi
-        pop rdi
-        pop rdx
-        pop rcx
-        pop rax"
-        : : : : "intel", "volatile"
-    ));
-}
-
-#[allow(dead_code)]
-#[derive(Default)]
-#[repr(packed)]
-pub struct PreservedRegisters {
-    pub r15: usize,
-    pub r14: usize,
-    pub r13: usize,
-    pub r12: usize,
-    pub rbp: usize,
-    pub rbx: usize,
-}
-
-impl PreservedRegisters {
-    pub fn dump(&self) {
-        println!("RBX:   {:>016X}", { self.rbx });
-        println!("RBP:   {:>016X}", { self.rbp });
-        println!("R12:   {:>016X}", { self.r12 });
-        println!("R13:   {:>016X}", { self.r13 });
-        println!("R14:   {:>016X}", { self.r14 });
-        println!("R15:   {:>016X}", { self.r15 });
-    }
-}
-
-macro_rules! preserved_push {
-    () => (asm!(
-        "push rbx
-        push rbp
-        push r12
-        push r13
-        push r14
-        push r15"
-        : : : : "intel", "volatile"
-    ));
-}
-
-macro_rules! preserved_pop {
-    () => (asm!(
-        "pop r15
-        pop r14
-        pop r13
-        pop r12
-        pop rbp
-        pop rbx"
-        : : : : "intel", "volatile"
-    ));
-}
-
-macro_rules! fs_push {
-    () => (asm!(
-        "
-        push fs
-
-        // Load kernel tls
-        mov rax, 0x18
-        mov fs, ax // can't load value directly into `fs`
-        "
-        : : : : "intel", "volatile"
-    ));
-}
-
-macro_rules! fs_pop {
-    () => (asm!(
-        "pop fs"
-        : : : : "intel", "volatile"
-    ));
-}
-
-#[allow(dead_code)]
-#[derive(Default)]
-#[repr(packed)]
-pub struct IretRegisters {
-    pub rip: usize,
-    pub cs: usize,
-    pub rflags: usize,
-    // Will only be present if interrupt is raised from another
-    // privilege ring
-    pub rsp: usize,
-    pub ss: usize
-}
-
-impl IretRegisters {
-    pub fn dump(&self) {
-        println!("RFLAG: {:>016X}", { self.rflags });
-        println!("CS:    {:>016X}", { self.cs });
-        println!("RIP:   {:>016X}", { self.rip });
-    }
-}
-
-macro_rules! iret {
-    () => (asm!(
-        "iretq"
-        : : : : "intel", "volatile"
-    ));
-}
-
-/// Create an interrupt function that can safely run rust code
-#[macro_export]
-macro_rules! interrupt {
-    ($name:ident, $func:block) => {
-        #[naked]
-        pub unsafe extern fn $name () {
-            #[inline(never)]
-            unsafe fn inner() {
-                $func
-            }
-
-            // Push scratch registers
-            scratch_push!();
-            fs_push!();
-
-            // Map kernel
-            $crate::arch::x86_64::pti::map();
-
-            // Call inner rust function
-            inner();
-
-            // Unmap kernel
-            $crate::arch::x86_64::pti::unmap();
-
-            // Pop scratch registers and return
-            fs_pop!();
-            scratch_pop!();
-            iret!();
-        }
-    };
-}
-
-#[allow(dead_code)]
-#[derive(Default)]
-#[repr(packed)]
-pub struct InterruptStack {
-    pub fs: usize,
-    pub preserved: PreservedRegisters,
-    pub scratch: ScratchRegisters,
-    pub iret: IretRegisters,
-}
-
-impl InterruptStack {
-    pub fn dump(&self) {
-        self.iret.dump();
-        self.scratch.dump();
-        self.preserved.dump();
-        println!("FS:    {:>016X}", { self.fs });
-    }
-    /// Saves all registers to a struct used by the proc:
-    /// scheme to read/write registers.
-    pub fn save(&self, all: &mut IntRegisters) {
-        all.fs = self.fs;
-
-        all.r15 = self.preserved.r15;
-        all.r14 = self.preserved.r14;
-        all.r13 = self.preserved.r13;
-        all.r12 = self.preserved.r12;
-        all.rbp = self.preserved.rbp;
-        all.rbx = self.preserved.rbx;
-        all.r11 = self.scratch.r11;
-        all.r10 = self.scratch.r10;
-        all.r9 = self.scratch.r9;
-        all.r8 = self.scratch.r8;
-        all.rsi = self.scratch.rsi;
-        all.rdi = self.scratch.rdi;
-        all.rdx = self.scratch.rdx;
-        all.rcx = self.scratch.rcx;
-        all.rax = self.scratch.rax;
-        all.rip = self.iret.rip;
-        all.cs = self.iret.cs;
-        all.rflags = self.iret.rflags;
-
-        // Set rsp and ss:
-
-        const CPL_MASK: usize = 0b11;
-
-        let cs: usize;
-        unsafe {
-            asm!("mov $0, cs" : "=r"(cs) ::: "intel");
-        }
-
-        if self.iret.cs & CPL_MASK == cs & CPL_MASK {
-            // Privilege ring didn't change, so neither did the stack
-            all.rsp = self as *const Self as usize // rsp after Self was pushed to the stack
-                + mem::size_of::<Self>() // disregard Self
-                - mem::size_of::<usize>() * 2; // well, almost: rsp and ss need to be excluded as they aren't present
-            unsafe {
-                asm!("mov $0, ss" : "=r"(all.ss) ::: "intel");
-            }
-        } else {
-            all.rsp = self.iret.rsp;
-            all.ss = self.iret.ss;
-        }
-    }
-    /// Loads all registers from a struct used by the proc:
-    /// scheme to read/write registers.
-    pub fn load(&mut self, all: &IntRegisters) {
-        // TODO: Which of these should be allowed to change?
-
-        // self.fs = all.fs;
-        self.preserved.r15 = all.r15;
-        self.preserved.r14 = all.r14;
-        self.preserved.r13 = all.r13;
-        self.preserved.r12 = all.r12;
-        self.preserved.rbp = all.rbp;
-        self.preserved.rbx = all.rbx;
-        self.scratch.r11 = all.r11;
-        self.scratch.r10 = all.r10;
-        self.scratch.r9 = all.r9;
-        self.scratch.r8 = all.r8;
-        self.scratch.rsi = all.rsi;
-        self.scratch.rdi = all.rdi;
-        self.scratch.rdx = all.rdx;
-        self.scratch.rcx = all.rcx;
-        self.scratch.rax = all.rax;
-        self.iret.rip = all.rip;
-
-        // These should probably be restricted
-        // self.iret.cs = all.cs;
-        // self.iret.rflags = all.eflags;
-    }
-    /// Enables the "Trap Flag" in the FLAGS register, causing the CPU
-    /// to send a Debug exception after the next instruction. This is
-    /// used for singlestep in the proc: scheme.
-    pub fn set_singlestep(&mut self, enabled: bool) {
-        if enabled {
-            self.iret.rflags |= 1 << 8;
-        } else {
-            self.iret.rflags &= !(1 << 8);
-        }
-    }
-    /// Checks if the trap flag is enabled, see `set_singlestep`
-    pub fn is_singlestep(&self) -> bool {
-        self.iret.rflags & 1 << 8 == 1 << 8
-    }
-}
-
-macro_rules! interrupt_push {
-    () => {
-        scratch_push!();
-        preserved_push!();
-        fs_push!();
-    };
-}
-macro_rules! interrupt_pop {
-    () => {
-        fs_pop!();
-        preserved_pop!();
-        scratch_pop!();
-    };
-}
-
-#[macro_export]
-macro_rules! interrupt_stack {
-    ($name:ident, $stack:ident, $func:block) => {
-        #[naked]
-        pub unsafe extern fn $name () {
-            #[inline(never)]
-            unsafe fn inner($stack: &mut $crate::arch::x86_64::macros::InterruptStack) {
-                $func
-            }
-
-            // Push scratch registers
-            interrupt_push!();
-
-            // Get reference to stack variables
-            let rsp: usize;
-            asm!("" : "={rsp}"(rsp) : : : "intel", "volatile");
-
-            // Map kernel
-            $crate::arch::x86_64::pti::map();
-
-            // Call inner rust function
-            inner(&mut *(rsp as *mut $crate::arch::x86_64::macros::InterruptStack));
-
-            // Unmap kernel
-            $crate::arch::x86_64::pti::unmap();
-
-            // Pop scratch registers and return
-            interrupt_pop!();
-            iret!();
-        }
-    };
-}
-
-#[allow(dead_code)]
-#[derive(Default)]
-#[repr(packed)]
-pub struct InterruptErrorStack {
-    pub fs: usize,
-    pub preserved: PreservedRegisters,
-    pub scratch: ScratchRegisters,
-    pub code: usize,
-    pub iret: IretRegisters,
-}
-
-impl InterruptErrorStack {
-    pub fn dump(&self) {
-        self.iret.dump();
-        println!("CODE:  {:>016X}", { self.code });
-        self.scratch.dump();
-        self.preserved.dump();
-        println!("FS:    {:>016X}", { self.fs });
-    }
-}
-
-#[macro_export]
-macro_rules! interrupt_error {
-    ($name:ident, $stack:ident, $func:block) => {
-        #[naked]
-        pub unsafe extern fn $name () {
-            #[inline(never)]
-            unsafe fn inner($stack: &$crate::arch::x86_64::macros::InterruptErrorStack) {
-                $func
-            }
-
-            // Push scratch registers
-            interrupt_push!();
-
-            // Get reference to stack variables
-            let rsp: usize;
-            asm!("" : "={rsp}"(rsp) : : : "intel", "volatile");
-
-            // Map kernel
-            $crate::arch::x86_64::pti::map();
-
-            // Call inner rust function
-            inner(&*(rsp as *const $crate::arch::x86_64::macros::InterruptErrorStack));
-
-            // Unmap kernel
-            $crate::arch::x86_64::pti::unmap();
-
-            // Pop scratch registers, error code, and return
-            interrupt_pop!();
-            asm!("add rsp, 8" : : : : "intel", "volatile"); // pop error code
-            iret!();
-        }
-    };
-}
 #[macro_export]
 macro_rules! irqs(
     ( [ $( ($idt:expr, $number:literal, $name:ident) ,)* ], $submac:ident ) => {
diff --git a/src/arch/x86_64/paging/entry.rs b/src/arch/x86_64/paging/entry.rs
index 56f9755bc3f2eafe5335e459bde62a219cf58814..ba6f941d3f88dfc6c51d410aa11ef0feb9c2c84a 100644
--- a/src/arch/x86_64/paging/entry.rs
+++ b/src/arch/x86_64/paging/entry.rs
@@ -77,3 +77,12 @@ impl Entry {
         self.0 = (self.0 & !COUNTER_MASK) | (count << 52);
     }
 }
+
+#[cfg(test)]
+mod tests {
+    #[test]
+    fn entry_has_required_arch_alignment() {
+        use super::Entry;
+        assert!(core::mem::align_of::<Entry>() >= core::mem::align_of::<u64>(), "alignment of Entry is less than the required alignment of u64 ({} < {})", core::mem::align_of::<Entry>(), core::mem::align_of::<u64>());
+    }
+}
diff --git a/src/arch/x86_64/paging/table.rs b/src/arch/x86_64/paging/table.rs
index 9ebfcef6efcb25319652c4b7c3834da00bff6695..a6f45c52c32115fa8d4eca1fa27504d699d92620 100644
--- a/src/arch/x86_64/paging/table.rs
+++ b/src/arch/x86_64/paging/table.rs
@@ -55,7 +55,7 @@ impl<L> Table<L> where L: TableLevel {
     }
 
     pub fn zero(&mut self) {
-        for entry in self.entries.iter_mut() {
+        for entry in unsafe { &mut self.entries }.iter_mut() {
             entry.set_zero();
         }
     }
@@ -63,12 +63,12 @@ impl<L> Table<L> where L: TableLevel {
     /// Set number of entries in first table entry
     fn set_entry_count(&mut self, count: u64) {
         debug_assert!(count <= ENTRY_COUNT as u64, "count can't be greater than ENTRY_COUNT");
-        self.entries[0].set_counter_bits(count);
+        unsafe { &mut self.entries[0] }.set_counter_bits(count)
     }
 
     /// Get number of entries in first table entry
     fn entry_count(&self) -> u64 {
-        self.entries[0].counter_bits()
+        unsafe { &self.entries[0] }.counter_bits()
     }
 
     pub fn increment_entry_count(&mut self) {
@@ -118,12 +118,12 @@ impl<L> Index<usize> for Table<L> where L: TableLevel {
     type Output = Entry;
 
     fn index(&self, index: usize) -> &Entry {
-        &self.entries[index]
+        unsafe { &self.entries[index] }
     }
 }
 
 impl<L> IndexMut<usize> for Table<L> where L: TableLevel {
     fn index_mut(&mut self, index: usize) -> &mut Entry {
-        &mut self.entries[index]
+        unsafe { &mut self.entries[index] }
     }
 }
diff --git a/src/arch/x86_64/pti.rs b/src/arch/x86_64/pti.rs
index 336371557377f46aec3c93203abd0818817d214e..8312521b706af63afa2f3bd8f3049c5ac343e351 100644
--- a/src/arch/x86_64/pti.rs
+++ b/src/arch/x86_64/pti.rs
@@ -2,11 +2,11 @@
 use core::ptr;
 
 #[cfg(feature = "pti")]
-use memory::Frame;
+use crate::memory::Frame;
 #[cfg(feature = "pti")]
-use paging::ActivePageTable;
+use crate::paging::ActivePageTable;
 #[cfg(feature = "pti")]
-use paging::entry::EntryFlags;
+use crate::paging::entry::EntryFlags;
 
 #[cfg(feature = "pti")]
 #[thread_local]
diff --git a/src/arch/x86_64/start.rs b/src/arch/x86_64/start.rs
index 30425f0e2033b2adce9fe9b7ceefa3cc6c94841f..7cdec3d5de362568954768bba55feec4b14bebb9 100644
--- a/src/arch/x86_64/start.rs
+++ b/src/arch/x86_64/start.rs
@@ -10,7 +10,7 @@ use crate::allocator;
 #[cfg(feature = "acpi")]
 use crate::acpi;
 #[cfg(feature = "graphical_debug")]
-use arch::x86_64::graphical_debug;
+use crate::arch::x86_64::graphical_debug;
 use crate::arch::x86_64::pti;
 use crate::device;
 use crate::gdt;
diff --git a/src/asm/x86_64/trampoline.asm b/src/asm/x86_64/trampoline.asm
new file mode 100644
index 0000000000000000000000000000000000000000..39d89bcc083b0a594709cd1b6dff388981d1274b
--- /dev/null
+++ b/src/asm/x86_64/trampoline.asm
@@ -0,0 +1,174 @@
+; trampoline for bringing up APs
+; compiled with nasm by build.rs, and included in src/acpi/madt.rs
+
+ORG 0x8000
+SECTION .text
+USE16
+
+trampoline:
+    jmp short startup_ap
+    times 8 - ($ - trampoline) nop
+    .ready: dq 0
+    .cpu_id: dq 0
+    .page_table: dq 0
+    .stack_start: dq 0
+    .stack_end: dq 0
+    .code: dq 0
+
+startup_ap:
+    cli
+
+    xor ax, ax
+    mov ds, ax
+    mov es, ax
+    mov ss, ax
+
+    ; initialize stack to invalid value
+    mov sp, 0
+
+    ;cr3 holds pointer to PML4
+    mov edi, 0x70000
+    mov cr3, edi
+
+    ; Enable FPU
+    mov eax, cr0
+    and al, 11110011b ; Clear task switched (3) and emulation (2)
+    or al, 00100010b ; Set numeric error (5) monitor co-processor (1)
+    mov cr0, eax
+
+    ; 18: Enable OSXSAVE
+    ; 10: Unmasked SSE exceptions
+    ; 9: FXSAVE/FXRSTOR
+    ; 7: Page Global
+    ; 5: Page Address Extension
+    ; 4: Page Size Extension
+    mov eax, cr4
+    or eax, 1 << 18 | 1 << 10 | 1 << 9 | 1 << 7 | 1 << 5 | 1 << 4
+    mov cr4, eax
+
+    ; initialize floating point registers
+    fninit
+
+    ; load protected mode GDT
+    lgdt [gdtr]
+
+    mov ecx, 0xC0000080               ; Read from the EFER MSR.
+    rdmsr
+    or eax, 1 << 11 | 1 << 8          ; Set the Long-Mode-Enable and NXE bit.
+    wrmsr
+
+    ;enabling paging and protection simultaneously
+    mov ebx, cr0
+    ; 31: Paging
+    ; 16: write protect kernel
+    ; 0: Protected Mode
+    or ebx, 1 << 31 | 1 << 16 | 1
+    mov cr0, ebx
+
+    ; far jump to enable Long Mode and load CS with 64 bit segment
+    jmp gdt.kernel_code:long_mode_ap
+
+USE64
+long_mode_ap:
+    mov rax, gdt.kernel_data
+    mov ds, rax
+    mov es, rax
+    mov fs, rax
+    mov gs, rax
+    mov ss, rax
+
+    mov rcx, [trampoline.stack_end]
+    lea rsp, [rcx - 256]
+
+    mov rdi, trampoline.cpu_id
+
+    mov rax, [trampoline.code]
+    mov qword [trampoline.ready], 1
+    jmp rax
+
+struc GDTEntry
+    .limitl resw 1
+    .basel resw 1
+    .basem resb 1
+    .attribute resb 1
+    .flags__limith resb 1
+    .baseh resb 1
+endstruc
+
+attrib:
+    .present              equ 1 << 7
+    .ring1                equ 1 << 5
+    .ring2                equ 1 << 6
+    .ring3                equ 1 << 5 | 1 << 6
+    .user                 equ 1 << 4
+;user
+    .code                 equ 1 << 3
+;   code
+    .conforming           equ 1 << 2
+    .readable             equ 1 << 1
+;   data
+    .expand_down          equ 1 << 2
+    .writable             equ 1 << 1
+    .accessed             equ 1 << 0
+;system
+;   legacy
+    .tssAvailabe16        equ 0x1
+    .ldt                  equ 0x2
+    .tssBusy16            equ 0x3
+    .call16               equ 0x4
+    .task                 equ 0x5
+    .interrupt16          equ 0x6
+    .trap16               equ 0x7
+    .tssAvailabe32        equ 0x9
+    .tssBusy32            equ 0xB
+    .call32               equ 0xC
+    .interrupt32          equ 0xE
+    .trap32               equ 0xF
+;   long mode
+    .ldt32                equ 0x2
+    .tssAvailabe64        equ 0x9
+    .tssBusy64            equ 0xB
+    .call64               equ 0xC
+    .interrupt64          equ 0xE
+    .trap64               equ 0xF
+
+flags:
+    .granularity equ 1 << 7
+    .available equ 1 << 4
+;user
+    .default_operand_size equ 1 << 6
+;   code
+    .long_mode equ 1 << 5
+;   data
+    .reserved equ 1 << 5
+
+gdtr:
+    dw gdt.end + 1  ; size
+    dq gdt          ; offset
+
+gdt:
+.null equ $ - gdt
+    dq 0
+
+.kernel_code equ $ - gdt
+istruc GDTEntry
+    at GDTEntry.limitl, dw 0
+    at GDTEntry.basel, dw 0
+    at GDTEntry.basem, db 0
+    at GDTEntry.attribute, db attrib.present | attrib.user | attrib.code
+    at GDTEntry.flags__limith, db flags.long_mode
+    at GDTEntry.baseh, db 0
+iend
+
+.kernel_data equ $ - gdt
+istruc GDTEntry
+    at GDTEntry.limitl, dw 0
+    at GDTEntry.basel, dw 0
+    at GDTEntry.basem, db 0
+; AMD System Programming Manual states that the writeable bit is ignored in long mode, but ss can not be set to this descriptor without it
+    at GDTEntry.attribute, db attrib.present | attrib.user | attrib.writable
+    at GDTEntry.flags__limith, db 0
+    at GDTEntry.baseh, db 0
+iend
+
+.end equ $ - gdt
diff --git a/src/context/context.rs b/src/context/context.rs
index 1627b6efdb311c1a003c551eaed3f336c37171b2..0814ee67d7d806bf91f358a41c81f1d3e8893c0d 100644
--- a/src/context/context.rs
+++ b/src/context/context.rs
@@ -7,7 +7,7 @@ use core::cmp::Ordering;
 use core::mem;
 use spin::Mutex;
 
-use crate::arch::{macros::InterruptStack, paging::PAGE_SIZE};
+use crate::arch::{interrupt::InterruptStack, paging::PAGE_SIZE};
 use crate::common::unique::Unique;
 use crate::context::arch;
 use crate::context::file::{FileDescriptor, FileDescription};
diff --git a/src/lib.rs b/src/lib.rs
index 2966c1b2259b51a802636c40adb2755eaffa4c80..c86197f78ee9c6bbfb6008820bdeeb45f31a82fe 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -45,10 +45,11 @@
 #![feature(concat_idents)]
 #![feature(const_fn)]
 #![feature(core_intrinsics)]
+#![feature(global_asm)]
 #![feature(integer_atomics)]
 #![feature(lang_items)]
-#![feature(naked_functions)]
 #![feature(matches_macro)] // stable in current Rust
+#![feature(naked_functions)]
 #![feature(ptr_internals)]
 #![feature(thread_local)]
 #![no_std]
@@ -274,5 +275,11 @@ pub extern fn ksignal(signal: usize) {
             println!("NAME {}", unsafe { ::core::str::from_utf8_unchecked(&context.name.lock()) });
         }
     }
-    syscall::exit(signal & 0x7F);
+
+    // Try running kill(getpid(), signal), but fallback to exiting
+    syscall::getpid()
+        .and_then(|pid| syscall::kill(pid, signal).map(|_| ()))
+        .unwrap_or_else(|_| {
+            syscall::exit(signal & 0x7F);
+        });
 }
diff --git a/src/ptrace.rs b/src/ptrace.rs
index d098d9e2b22fc54a346d3c9bdd3cf1228118eb19..6158df1cffad556ef87aba5f3ad005b42086b70b 100644
--- a/src/ptrace.rs
+++ b/src/ptrace.rs
@@ -4,7 +4,7 @@
 
 use crate::{
     arch::{
-        macros::InterruptStack,
+        interrupt::InterruptStack,
         paging::{
             entry::EntryFlags,
             mapper::MapperFlushAll,
diff --git a/src/syscall/driver.rs b/src/syscall/driver.rs
index 7cf168db4f5a896566e36799527946912b68539a..63184269e47c2ea6286bd3a5d34c100c72c8817e 100644
--- a/src/syscall/driver.rs
+++ b/src/syscall/driver.rs
@@ -1,4 +1,4 @@
-use crate::macros::InterruptStack;
+use crate::interrupt::InterruptStack;
 use crate::memory::{allocate_frames_complex, deallocate_frames, Frame};
 use crate::paging::{ActivePageTable, PhysicalAddress, VirtualAddress};
 use crate::paging::entry::EntryFlags;
diff --git a/src/syscall/mod.rs b/src/syscall/mod.rs
index 886865fabff43db8ba144d63bda2fb391b79aab5..bee91ab038ec04b8c0c8e7d653d59a4d624e9c21 100644
--- a/src/syscall/mod.rs
+++ b/src/syscall/mod.rs
@@ -20,7 +20,7 @@ use self::flag::{CloneFlags, MapFlags, PhysmapFlags, WaitFlags};
 use self::number::*;
 
 use crate::context::ContextId;
-use crate::macros::InterruptStack;
+use crate::interrupt::InterruptStack;
 use crate::scheme::{FileHandle, SchemeNamespace};
 
 /// Debug
diff --git a/src/syscall/process.rs b/src/syscall/process.rs
index c9b041a06e56fe9f937e8924fc395657a4dff209..1dd447c74cfd01f11ea2ddd40bf37bb0668eacbc 100644
--- a/src/syscall/process.rs
+++ b/src/syscall/process.rs
@@ -131,25 +131,23 @@ pub fn clone(flags: CloneFlags, stack_base: usize) -> Result<ContextId> {
             }
 
             if let Some(ref stack) = context.kstack {
-                // Get the relative offset to the return address of this function
+                // Get the relative offset to the return address of the function
+                // obtaining `stack_base`.
+                //
                 // (base pointer - start of stack) - one
                 offset = stack_base - stack.as_ptr() as usize - mem::size_of::<usize>(); // Add clone ret
                 let mut new_stack = stack.clone();
 
                 unsafe {
+                    // Set clone's return value to zero. This is done because
+                    // the clone won't return like normal, which means the value
+                    // would otherwise never get set.
                     if let Some(regs) = ptrace::rebase_regs_ptr_mut(context.regs, Some(&mut new_stack)) {
-                        // We'll need to tell the clone that it should
-                        // return 0, but that's it. We don't actually
-                        // clone the registers, because it will then
-                        // become None and be exempt from all kinds of
-                        // ptracing until the current syscall has
-                        // completed.
                         (*regs).scratch.rax = 0;
                     }
 
-                    // Change the return address of the child
-                    // (previously syscall) to the arch-specific
-                    // clone_ret callback
+                    // Change the return address of the child (previously
+                    // syscall) to the arch-specific clone_ret callback
                     let func_ptr = new_stack.as_mut_ptr().add(offset);
                     *(func_ptr as *mut usize) = interrupt::syscall::clone_ret as usize;
                 }