diff --git a/Cargo.lock b/Cargo.lock
index 951145e1..f2ec461a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -421,6 +421,16 @@ version = "0.2.102"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a2a5ac8f984bfcf3a823267e5fde638acc3325f6496633a5da6bb6eb2171e103"
 
+[[package]]
+name = "libloading"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0cf036d15402bea3c5d4de17b3fce76b3e4a56ebc1f577be0e7a72f7c607cf0"
+dependencies = [
+ "cfg-if",
+ "winapi",
+]
+
 [[package]]
 name = "linked-hash-map"
 version = "0.5.4"
@@ -881,6 +891,13 @@ version = "0.6.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
 
+[[package]]
+name = "runkernel"
+version = "0.1.0"
+dependencies = [
+ "libloading",
+]
+
 [[package]]
 name = "rustpython-ast"
 version = "0.1.0"
diff --git a/Cargo.toml b/Cargo.toml
index 4aa4bf22..b301a6a3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,6 +3,7 @@ members = [
   "nac3core",
   "nac3standalone",
   "nac3artiq",
+  "runkernel",
 ]
 
 [profile.release]
diff --git a/runkernel/.cargo/config b/runkernel/.cargo/config
new file mode 100644
index 00000000..a12e0fb8
--- /dev/null
+++ b/runkernel/.cargo/config
@@ -0,0 +1,3 @@
+[build]
+rustflags = ["-C", "link-args=-rdynamic"]
+
diff --git a/runkernel/Cargo.toml b/runkernel/Cargo.toml
new file mode 100644
index 00000000..cbe23524
--- /dev/null
+++ b/runkernel/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "runkernel"
+version = "0.1.0"
+authors = ["M-Labs"]
+edition = "2018"
+
+[dependencies]
+libloading = "0.7"
diff --git a/runkernel/src/main.rs b/runkernel/src/main.rs
new file mode 100644
index 00000000..c869a686
--- /dev/null
+++ b/runkernel/src/main.rs
@@ -0,0 +1,43 @@
+use std::env;
+use libloading;
+
+static mut NOW: i64 = 0;
+
+#[no_mangle]
+pub extern "C" fn now_mu() -> i64 {
+    unsafe { NOW }
+}
+
+#[no_mangle]
+pub extern "C" fn at_mu(t: i64) {
+    unsafe { NOW = t }
+}
+
+#[no_mangle]
+pub extern "C" fn delay_mu(dt: i64) {
+    unsafe { NOW += dt }
+}
+
+#[no_mangle]
+pub extern "C" fn rtio_init() {
+    println!("rtio_init");
+}
+
+#[no_mangle]
+pub extern "C" fn rtio_get_counter() -> i64 {
+    0
+}
+
+#[no_mangle]
+pub extern "C" fn rtio_output(target: i32, data: i32) {
+    println!("rtio_output @{} target={:04x} data={}", unsafe { NOW }, target, data);
+}
+
+fn main() {
+    let filename = env::args().nth(1).unwrap();
+    unsafe {
+        let lib = libloading::Library::new(filename).unwrap();
+        let func: libloading::Symbol<unsafe extern fn()> = lib.get(b"__modinit__").unwrap();
+        func()
+    }
+}