diff --git a/.cargo/config b/.cargo/config
index 1ab52827c2bfa858bc120a5e4f7a33137de3649c..db63f627d5ec01272dad07f3fe36091b94eb52c9 100644
--- a/.cargo/config
+++ b/.cargo/config
@@ -30,5 +30,13 @@ rustflags = [
   "-Z", "linker-flavor=ld",
 ]
 
+# used when building for klee
+# must build in --release as we are not doing any (post) linking
+[target.x86_64-unknown-linux-gnu]
+rustflags = [
+  "--emit=llvm-bc,llvm-ir",
+  "-C", "linker=true"
+]
+
 [build]
-target = "thumbv7em-none-eabihf"
\ No newline at end of file
+target = "thumbv7em-none-eabihf"
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index c81d405dcde19a226273819fc97ae1fc8e6b1f17..33db0f2305cb7c538c0defe598ec624fd9d51db8 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -15,6 +15,18 @@
                 "$rustc"
             ]
         },
+        {
+            "label": "xargo build --example empty --features klee_mode --release --target x86_64-unknown-linux-gnu",
+            "type": "shell",
+            "command": "xargo build --example empty --features klee_mode --release --target x86_64-unknown-linux-gnu",
+            "group": {
+                "kind": "build",
+                "isDefault": true
+            },
+            "problemMatcher": [
+                "$rustc"
+            ]
+        },
         {
             "label": "xargo build --example nested",
             "type": "shell",
diff --git a/Cargo.toml b/Cargo.toml
index 3f64f5b9aa6c8e154fd0e0895aa217801741eee2..48fdb0e11e89daba8f2c5d582c17347b73551636 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,8 +18,8 @@ untagged-option = "0.1.1"
 rtfm-core = "0.1.0"
 cortex-m-rtfm-macros = { path = "macros" }
 
-[target.'cfg(target_arch = "x86_64")'.dev-dependencies]
-compiletest_rs = "0.2.8"
+#[target.'cfg(target_arch = "x86_64")'.dev-dependencies]
+#compiletest_rs = "0.2.8"
 
 [dev-dependencies.cortex-m-rt]
 features = ["abort-on-panic"]
@@ -30,15 +30,20 @@ git = "https://gitlab.henriktjader.com/pln/STM32F40x"
 features = ["rt"]
 version = "0.2.0"
 
+[dev-dependencies.klee]
+path = "./klee"
+
 [features]
 wcet_bkpt = [] 
 wcet_nop = []
-klee = [] 
+klee_mode = ["cortex-m-rtfm-macros/klee_mode"] 
 
 [profile.dev]
 codegen-units = 1
 incremental = false
+panic = "abort"
 
 [profile.release]
 lto = true
 debug = true
+panic = "abort"
diff --git a/README.md b/README.md
index 240e2f8fa726ac52363401899afb4dacd8928493..e80322c5870a1dc4504a34145f29dd3055ab77ae 100644
--- a/README.md
+++ b/README.md
@@ -18,6 +18,70 @@ Licensed under either of
 
 at your option.
 
+# Klee based analysis.
+
+## Complilation
+
+Use a Rust toolchain with a llwm4 backend.
+
+
+> xargo build --example empty --features klee_mode --release --target x86_64-unknown-linux-gnu
+
+The `--features klee_mode` implies the following:
+
+- the set of tasks is generated in ./klee/tasks.txt
+- the example is built without HW dependencies 
+  claim does NOT affect basepri register
+
+The `--target x86_64-unknown-linux-gnu` implies the following:
+
+``` text
+rustflags = [
+  "--emit=llvm-bc,llvm-ir",
+  "-C", "linker=true"
+]
+```
+
+So both `.bc` and `.ll` are generated for the application at hande (`empty.rs`). (Only `.bc` is indeed needed/used for KLEE analysis, but the `.ll` is neat as being human readable for code inspection).
+
+The files will contain all symbols besides the *weakly* defined default handlers (which should not be analysed by KLEE in any case).
+
+TODO: there is still a warning on module level assembly.
+
+The `--relesase` flag implies:
+
+``` text
+[profile.release]
+lto = true
+debug = true
+panic = "abort"
+```
+
+`lto = true` ensures pre-llvm linknig of dependencies for the generated `.bc` and `.ll` files.
+
+`panic = "abort"` is required to override the default panic (unwrap) behavior of the x86 (host) target.
+
+
+
+## KLEE Analysis
+
+A docker daemon should be started/enabled.
+
+> systemctl start docker.service # if not already started/enabled
+
+In the current folder:
+
+> docker run --rm --user $(id -u):$(id -g) -v ($PDW)/klee-examples:/mnt -w /mnt -it afoht/llvm-klee-4 /bin/bash
+
+This starts a shell in the docker `llvm-klee-4`, with a shared mount to the examples directory, where your `empty-xxxx.bc` is located.
+
+From there you can run various commands like:
+
+> klee empty-xxxxx.bc
+
+See KLEE for detailed information.
+
+
 ## Contribution
 
 Unless you explicitly state otherwise, any contribution intentionally submitted
diff --git a/examples/empty.rs b/examples/empty.rs
new file mode 100644
index 0000000000000000000000000000000000000000..216137ef82e310998e45178cf21c9eaf15260188
--- /dev/null
+++ b/examples/empty.rs
@@ -0,0 +1,39 @@
+//! Minimal example with zero tasks
+//#![deny(unsafe_code)]
+// IMPORTANT always include this feature gate
+#![feature(proc_macro)]
+#![no_std]
+
+extern crate cortex_m_rtfm as rtfm;
+// IMPORTANT always do this rename
+extern crate stm32f40x; // the device crate
+
+#[macro_use]
+extern crate klee;
+use klee::{k_abort, k_assert};
+
+// import the procedural macro
+use rtfm::app;
+
+app! {
+    // this is the path to the device crate
+    device: stm32f40x,
+}
+
+static mut X: u32 = 32;
+
+#[inline(never)]
+fn init(_p: init::Peripherals) {
+    k_symbol!(X, "X");
+    k_assert(unsafe { X } == 33);
+}
+
+// The idle loop.
+//
+// This runs after `init` and has a priority of 0. All tasks can preempt this
+// function. This function can never return so it must contain some sort of
+// endless loop.
+#[inline(never)]
+fn idle() -> ! {
+    k_abort();
+}
diff --git a/klee/.gitignore b/klee/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..ffa3bbd20ea4d6c2a64af9a17260e5ad4460ffa2
--- /dev/null
+++ b/klee/.gitignore
@@ -0,0 +1 @@
+Cargo.lock
\ No newline at end of file
diff --git a/klee/Cargo.toml b/klee/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..de26d8c8a877e86d91a548922da3b7e253e9eed8
--- /dev/null
+++ b/klee/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+authors = ["Jorge Aparicio <jorge@japaric.io>"]
+name = "klee"
+version = "0.1.0"
+
+[dependencies]
+cstr_core = "0.1.0"
+cty = "0.1.5"
diff --git a/klee/src/lang_items.rs b/klee/src/lang_items.rs
new file mode 100644
index 0000000000000000000000000000000000000000..f81e9e09ebd7a311765546bb55bba86b0a67a2dd
--- /dev/null
+++ b/klee/src/lang_items.rs
@@ -0,0 +1,25 @@
+#[lang = "panic_fmt"]
+unsafe extern "C" fn panic_fmt(_: ::core::fmt::Arguments, _: &'static str, _: u32, _: u32) -> ! {
+    ::abort()
+}
+
+#[lang = "start"]
+extern "C" fn start<T>(main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize
+    where
+    T: Termination,
+{
+    main();
+
+    0
+}
+
+#[lang = "termination"]
+pub trait Termination {
+    fn report(self) -> i32;
+}
+
+impl Termination for () {
+    fn report(self) -> i32 {
+        0
+    }
+}
diff --git a/klee/src/lib.rs b/klee/src/lib.rs
new file mode 100644
index 0000000000000000000000000000000000000000..1d1949e3e35cbad0e17d68f10430fec5a1566b0b
--- /dev/null
+++ b/klee/src/lib.rs
@@ -0,0 +1,76 @@
+#![no_std]
+#![feature(compiler_builtins_lib)]
+#![feature(lang_items)]
+
+extern crate compiler_builtins;
+extern crate cstr_core;
+extern crate cty;
+
+// mod lang_items;
+mod ll;
+
+use core::mem;
+
+use cty::c_void;
+
+#[doc(hidden)]
+pub use cstr_core::CStr;
+
+#[doc(hidden)]
+#[inline]
+pub fn k_symbol<T>(name: &CStr) -> T {
+    let mut t: T = unsafe { mem::uninitialized() };
+    unsafe {
+        ll::klee_make_symbolic(
+            &mut t as *mut T as *mut c_void,
+            mem::size_of::<T>(),
+            name.as_ptr(),
+        )
+    }
+    t
+}
+
+#[doc(hidden)]
+#[inline]
+pub fn k_mk_symbol<T>(t: &mut T, name: &CStr) {
+    unsafe {
+        ll::klee_make_symbolic(
+            t as *mut T as *mut c_void,
+            mem::size_of::<T>(),
+            name.as_ptr(),
+        )
+    }
+}
+
+#[inline(never)]
+pub fn k_abort() -> ! {
+    unsafe { ll::abort() }
+}
+
+/// assume a condition involving symbolic variables
+#[inline(never)]
+pub fn k_assume(cond: bool) {
+    unsafe {
+        ll::klee_assume(cond);
+    }
+}
+
+/// assert a condition involving symbolic variables
+#[inline(never)]
+pub fn k_assert(e: bool) {
+    if !e {
+        k_abort();
+    }
+}
+
+/// make a variable symbolic
+#[macro_export]
+macro_rules! k_symbol {
+    ($id:ident, $name:expr) => {
+        #[allow(unsafe_code)]
+        $crate::k_mk_symbol(
+            unsafe { &mut $id },
+            unsafe { $crate::CStr::from_bytes_with_nul_unchecked(concat!($name, "\0").as_bytes()) }
+        )
+    }
+}
diff --git a/klee/src/ll.rs b/klee/src/ll.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a7f44ce52334ed33f0e5ac97991e40dc6ebaf373
--- /dev/null
+++ b/klee/src/ll.rs
@@ -0,0 +1,7 @@
+use cty::{c_char, c_void};
+
+extern "C" {
+    pub fn abort() -> !;
+    pub fn klee_assume(cond: bool);
+    pub fn klee_make_symbolic(ptr: *mut c_void, size: usize, name: *const c_char);
+}
diff --git a/klee/tasks.txt b/klee/tasks.txt
new file mode 100644
index 0000000000000000000000000000000000000000..fb62ad331f6b05c445c05a4cdfee52e0cc01d985
--- /dev/null
+++ b/klee/tasks.txt
@@ -0,0 +1,2 @@
+// autogenertated file 
+ []
\ No newline at end of file
diff --git a/macros/Cargo.toml b/macros/Cargo.toml
index 748637c16971d5a4d1fde13f85b3c22acbdaf23a..afa5bd660d64e8a7c719fc13c9891928ed389e92 100644
--- a/macros/Cargo.toml
+++ b/macros/Cargo.toml
@@ -17,3 +17,6 @@ syn = "0.11.11"
 
 [lib]
 proc-macro = true
+
+[features]
+klee_mode = [] 
diff --git a/macros/src/lib.rs b/macros/src/lib.rs
index f488eb32c4c776939088b76da27cd4d55545b958..9183e248619ece7af09b8d7d686079f4f9c93c2e 100644
--- a/macros/src/lib.rs
+++ b/macros/src/lib.rs
@@ -184,17 +184,19 @@ fn run(ts: TokenStream) -> Result<TokenStream> {
     let ownerships = analyze::app(&app);
     let tokens = trans::app(&app, &ownerships);
 
-    println!("tasks");
-    let mut tasks = Vec::new();
-    for (id, _task) in app.tasks {
-        println!("{}", id);
-        tasks.push(format!("{}", id));
-    }
+    if cfg!(feature = "klee_mode") {
+        println!("tasks");
+        let mut tasks = Vec::new();
+        for (id, _task) in app.tasks {
+            println!("{}", id);
+            tasks.push(format!("{}", id));
+        }
 
-    let path = std::path::Path::new("klee/tasks.txt");
+        let path = std::path::Path::new("klee/tasks.txt");
 
-    let mut file = File::create(path).unwrap();
-    write!(file, "// autogenertated file \n {:?}", tasks).unwrap();
+        let mut file = File::create(path).unwrap();
+        write!(file, "// autogenertated file \n {:?}", tasks).unwrap();
+    }
 
     Ok(format!("{}", tokens)
         .parse()
diff --git a/src/lib.rs b/src/lib.rs
index 0bb4ee95c498aab556be9e163206120c9f17bcad..ee73521ccce6349cca6ef606b1428d0a9f6de328 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -142,14 +142,14 @@ where
                     let mut old = 0;
                     // klee mode code generation
                     // the generated code should not access the hardware
-                    if !cfg!(feature = "klee") {
+                    if !cfg!(feature = "klee_mode") {
                         old = basepri::read();
                     }
                     let hw = (max_priority - ceiling) << (8 - _nvic_prio_bits);
 
                     // klee mode code generation
                     // the generated code should not access the hardware
-                    if !cfg!(feature = "klee") {
+                    if !cfg!(feature = "klee_mode") {
                         basepri::write(hw);
                     }
 
@@ -181,7 +181,7 @@ where
 
                     // klee mode code generation
                     // the generated code should not access the hardware
-                    if !cfg!(feature = "klee") {
+                    if !cfg!(feature = "klee_mode") {
                         basepri::write(old);
                     }
                     ret