diff --git a/Cargo.lock b/Cargo.lock
index be2b611..bf63848 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -190,6 +190,17 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "elfloader"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a7b18d35bf8ec3bac59c3ec29cf1f1b46e764e00b42a9c0c754d06e38e78f3b"
+dependencies = [
+ "bitflags",
+ "log",
+ "xmas-elf",
+]
+
 [[package]]
 name = "env_logger"
 version = "0.10.0"
@@ -235,6 +246,15 @@ dependencies = [
  "rustc_version",
 ]
 
+[[package]]
+name = "error-stack"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f00447f331c7f726db5b8532ebc9163519eed03c6d7c8b73c90b3ff5646ac85"
+dependencies = [
+ "rustc_version",
+]
+
 [[package]]
 name = "fatfs"
 version = "0.3.6"
@@ -338,6 +358,8 @@ version = "0.2.0"
 dependencies = [
  "crossbeam-queue",
  "derive_more",
+ "elfloader",
+ "error-stack 0.3.1",
  "limine",
  "log",
  "sbi",
@@ -489,7 +511,7 @@ name = "repbuild"
 version = "0.2.0"
 dependencies = [
  "env_logger",
- "error-stack",
+ "error-stack 0.2.4",
  "fatfs",
  "log",
 ]
@@ -835,3 +857,18 @@ dependencies = [
  "rustversion",
  "volatile",
 ]
+
+[[package]]
+name = "xmas-elf"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d29b4d8e7beaceb4e77447ba941a7600d23d0319ab52da0461abea214832d5a"
+dependencies = [
+ "zero",
+]
+
+[[package]]
+name = "zero"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fe21bcc34ca7fe6dd56cc2cb1261ea59d6b93620215aefb5ea6032265527784"
diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml
index 52f0593..ce03c06 100644
--- a/kernel/Cargo.toml
+++ b/kernel/Cargo.toml
@@ -4,11 +4,13 @@ name = "kernel"
 version = "0.2.0"
 
 [dependencies]
-slab = { version = "0.4", default-features = false }
-spin = "0.9"
-versioning = { git = "https://git.ablecorp.us/able/aos_userland" }
-log = "0.4"
-uart_16550 = "0.2"
+elfloader   = "0.16"
+error-stack = { version = "0.3", default-features = false}
+log         = "0.4"
+spin        = "0.9"
+uart_16550  = "0.2"
+slab        = { version = "0.4", default-features = false }
+versioning  = { git = "https://git.ablecorp.us/able/aos_userland" }
 
 [dependencies.crossbeam-queue]
 version = "0.3"
diff --git a/kernel/src/arch/x86_64/gdt.rs b/kernel/src/arch/x86_64/gdt.rs
index f5b6d1f..c6aa677 100644
--- a/kernel/src/arch/x86_64/gdt.rs
+++ b/kernel/src/arch/x86_64/gdt.rs
@@ -15,13 +15,12 @@ pub unsafe fn init() {
 
     log::info!("Initialising GDT");
     GDT.0.load();
-    unsafe {
-        CS::set_reg(GDT.1.kcode);
-        DS::set_reg(GDT.1.kdata);
-        ES::set_reg(GDT.1.kdata);
-        SS::set_reg(GDT.1.kdata);
-        load_tss(GDT.1.tss);
-    }
+    CS::set_reg(GDT.1.kcode);
+    DS::set_reg(GDT.1.kdata);
+    ES::set_reg(GDT.1.kdata);
+    SS::set_reg(GDT.1.kdata);
+    load_tss(GDT.1.tss);
+    init_syscalls(&GDT.1);
 }
 
 struct Selectors {
@@ -57,3 +56,21 @@ static GDT: Lazy<(GlobalDescriptorTable, Selectors)> = Lazy::new(|| {
     };
     (gdt, sels)
 });
+
+/// Initialize syscalls
+unsafe fn init_syscalls(sls: &Selectors) {
+    use x86_64::registers::{
+        model_specific::{Efer, EferFlags, LStar, SFMask, Star},
+        rflags::RFlags,
+    };
+
+    Star::write(sls.ucode, sls.udata, sls.kcode, sls.kdata);
+    SFMask::write(RFlags::from_bits(0x200).expect("Invalid RFlags for SFMask"));
+    LStar::write(VirtAddr::from_ptr(handle_syscall as *const ()));
+    Efer::update(|x| *x |= EferFlags::SYSTEM_CALL_EXTENSIONS);
+}
+
+extern "C" fn handle_syscall(a: u64) -> ! {
+    log::info!("called syscall {a}");
+    todo!("Return from syscall");
+}
diff --git a/kernel/src/kmain.rs b/kernel/src/kmain.rs
index 9d562f9..79f1392 100644
--- a/kernel/src/kmain.rs
+++ b/kernel/src/kmain.rs
@@ -1,6 +1,6 @@
 //! AbleOS Kernel Entrypoint
 
-pub fn kmain(cmdline: &str, initrd: Option<&'static [u8]>) -> ! {
+pub fn kmain(cmdline: &str, bootstrap: Option<&'static [u8]>) -> ! {
     log::debug!("Entered kmain");
 
     // TODO: Actual cmdline parsing (Serde?)
@@ -9,7 +9,7 @@ pub fn kmain(cmdline: &str, initrd: Option<&'static [u8]>) -> ! {
     }
 
     log::info!("Cmdline: \"{cmdline}\"");
-    let initrd = initrd.expect("no initrd found");
+    let bootstrap = bootstrap.expect("no bootstrap found");
 
     crate::arch::sloop()
 }
diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs
index 9fa076b..798d096 100644
--- a/kernel/src/lib.rs
+++ b/kernel/src/lib.rs
@@ -19,6 +19,7 @@ mod kmain;
 mod logger;
 mod memory;
 mod task;
+mod ubloader;
 
 use versioning::Version;
 
@@ -31,11 +32,6 @@ pub const VERSION: Version = Version {
 
 #[panic_handler]
 fn panic(info: &core::panic::PanicInfo) -> ! {
-    // TODO: Better panic handler
-    // let _ = crate::arch::log(format_args!(
-    //     "\r\n\x1b[1m\x1b[4m\x1b[38;5;125mKernel Panic\x1b[0m\r\n",
-    // ));
-
     if let Some(loc) = info.location() {
         let _ = crate::arch::log(format_args!(
             "Location: {}: {}, {}\r\n",
diff --git a/kernel/src/ubloader.rs b/kernel/src/ubloader.rs
new file mode 100644
index 0000000..e69de29