Skip to content
Snippets Groups Projects
Select Git revision
  • c81e7e956a9a21173eafb911c09a756e54c7644e
  • master default
2 results

get_sign.rs

  • Forked from Per Lindgren / klee_tutorial
    Source project has a limited visibility.
    get_sign.rs 5.75 KiB
    // get_sign.rs
    // Showcase how we manually can interface Rust to KLEE
    //
    
    #![no_std]
    #![no_main]
    
    fn get_sign(x: i32) -> i32 {
        if x == 0 {
            return 0;
        }
        if x < 0 {
            return -1;
        } else {
            return 1;
        }
    }
    
    #[no_mangle]
    fn main() {
        let mut a: i32 = 0;
        klee_make_symbolic!(&mut a, "a");
        get_sign(a);
    }
    
    // KLEE bindings
    
    #[panic_handler]
    fn panic(_info: &core::panic::PanicInfo) -> ! {
        // abort symbol caught by LLVM-KLEE
        unsafe { ll::abort() }
    }
    
    #[inline(always)]
    pub fn klee_make_symbolic<T>(t: &mut T, name: &'static cstr_core::CStr) {
        unsafe {
            crate::ll::klee_make_symbolic(
                t as *mut T as *mut core::ffi::c_void,
                core::mem::size_of::<T>(),
                name.as_ptr() as *const cstr_core::c_char,
            )
        }
    }
    
    #[macro_export]
    macro_rules! klee_make_symbolic {
        (&mut $id:expr, $name:expr) => {
            klee_make_symbolic(&mut $id, unsafe {
                cstr_core::CStr::from_bytes_with_nul_unchecked(concat!($name, "\0").as_bytes())
            })
        };
    }
    
    mod ll {
        //! Low level bindings
        extern "C" {
            pub fn abort() -> !;
            pub fn klee_make_symbolic(
                ptr: *mut core::ffi::c_void,    // pointer to the data
                size: usize,                    // size (in bytes)
                name: *const cstr_core::c_char, // pointer to zero-terminated C string
            );
        }
    }
    
    // A) Compile a Rust program for KLEE analysis.
    //
    // In order for KLEE analysis we need to generate LLVM-IR.
    //
    // > cargo rustc -v --example get_sign -- -C linker=true -C lto --emit=llvm-ir
    //
    // Here `rustc` tells cargo that we will call the compiler with arguments.
    //
    // `-v` that we want verbose output.
    //
    // Options after `--` is passed directly to to the compiler (`rustc`).
    //
    // `-C linker=true`, binds the linker to the `true` binary (a program that just returns).
    // This way we can prevent the compiler to link to a binary (ugly right!).
    //
    // `-C --emit=llvm-ir`, to emit LLVM-IR in `.ll` form.
    //
    // `cargo` places the generated artifacts under the `target` directory.
    //
    // > ls target/debug/examples/
    // get_sign-85c57be6132dac1d.d  get_sign-85c57be6132dac1d.ll  get_sign.d
    //
    // If you look at the compilation, above you find that `cargo` adds
    // `extra-filename=-85c57be6132dac1d` (the hash might be different but)
    // matches the hash for the `.ll` file.
    //
    // The hash is based on the setting for this particular build.
    // This way, `cargo` keeps track of the builds, so you can several builds
    // of the same files with different settings without name collisions.
    //
    // Now we can run KLEE on the generate LLVM-IR.
    // (KLEE can read both `.bc` and `.ll` files, `.ll` files are human readable.)
    //
    // > klee target/debug/examples/get_sign-85c57be6132dac1d.ll
    // KLEE: output directory is "/home/pln/courses/d7020e/klee_tutorial/target/debug/examples/klee-out-0"
    // KLEE: Using Z3 solver backend
    //
    // KLEE: done: total instructions = 92
    // KLEE: done: completed paths = 3
    // KLEE: done: generated tests = 3
    //
    // (You need to give the right hash for the `.ll` file.)
    //
    // What was the generated hash.
    //
    // [your answer here]
    //
    // B) Inspecting the test cases.
    //
    // Figure out to run `ktest-tool` on the generated test cases.
    // (Hint, it is just a matter of paths.)
    //
    // [your answer here]
    //
    // C) Replaying your test cases.
    //
    // Now we need to re-compile the `get_sign.rs` with bindings to
    // the `libkleeRuntest`.
    //
    // The first thing we need to do is to generate an object file `.o`.
    //
    // > cd target/debug/examples
    // > llc -filetype=obj -relocation-model=pic get_sign-85c57be6132dac1d.ll
    //
    // `llc` is the LLVM compiler, `-filetype=obj` to generate an object file and
    // `-relocation-model=pic` is to obtain Position Independent Code (https://en.wikipedia.org/wiki/Position-independent_code)
    // (This is needed since we don't know where it is going to be placed/linked later.)
    //
    // Verify that you now have an object file:
    //
    // > ls *.o
    //
    // [your answer here]
    //
    // Now we need to link it with the `libkleeRuntest`.
    //
    // > clang get_sign-85c57be6132dac1d.o -l kleeRuntest -o a.out
    //
    // (See `get_sign.c` for linking options if you don't have KLEE libs in default location.)
    //
    // Now we can replay the test case. However, we have compiled the example for `no_std`
    // (as we target embedded applications).
    //
    // > KTEST_FILE=klee-last/test000001.ktest gdb a.out
    //
    // (gdb) break get_sign
    // Breakpoint 1 at 0x1165: file examples/get_sign.rs, line 9.
    //
    // Now run the code in the debugger. What path was triggered.
    //
    // [your answer here]
    //
    // Change to test000002, what path does it trigger.
    //
    // [your answer here]
    //
    // And finally change to test000003, what path was triggered.
    //
    // [your answer here]
    //
    // D) Remarks and conclusions.
    //
    // You have now successfully:
    //
    // - Generated LLVM-IR from Rust code
    // - Run KLEE on Rust code to generate tests.
    // - Linked with the 'kleeRuntest`.
    // - Replayed code in `gdb'.
    //
    // It was a bit of a hassle right!
    //
    // In the next part of the lab we will:
    //
    // Introduce a Rust library for the LLVM bindings.
    // Introduce a cargo sub-command for:
    // - building and running KLEE
    // - replaying test cases
    //
    // With this at hand, ergonomics using KLEE is improved.
    // (Arguably better than the C/C++ integration.)
    //
    // So why did we not start directly with the `cargo klee`.
    //
    // Well this is course at advanced level, where you
    // learn not only to use tools, but how the work.
    //
    // The library `klee-sys` basically extends on the
    // primitive KLEE bindings in this file.
    //
    // The `cargo klee` command basically extends on the
    // steps you have taken to compile and run the tools
    // in this lab.
    //
    // If you grasp this lab, the rest is just coding.
    // You may find and fix bugs in the `klee-sys`,
    // improve and `cargo klee` etc.
    //
    // At that point you are `computer science master`!