about summary refs log tree commit diff homepage
diff options
context:
space:
mode:
-rw-r--r--runtime/Runtest/Makefile11
-rw-r--r--runtime/Runtest/intrinsics.c69
-rw-r--r--test/CMakeLists.txt18
-rw-r--r--test/Makefile3
-rw-r--r--test/Replay/libkleeruntest/replay_invalid_klee_assume.c44
-rw-r--r--test/Replay/libkleeruntest/replay_invalid_klee_choose.c45
-rw-r--r--test/Replay/libkleeruntest/replay_invalid_klee_range.c45
-rw-r--r--test/Replay/libkleeruntest/replay_invalid_num_objects.c39
-rw-r--r--test/Replay/libkleeruntest/replay_invalid_object_names.c45
-rw-r--r--test/Replay/libkleeruntest/replay_invalid_object_size.c43
-rw-r--r--test/Replay/libkleeruntest/replay_posix_runtime.c35
-rw-r--r--test/Replay/libkleeruntest/replay_simple.c28
-rw-r--r--test/Replay/libkleeruntest/replay_two_objects.c33
-rw-r--r--test/lit.cfg15
-rw-r--r--test/lit.site.cfg.in6
15 files changed, 458 insertions, 21 deletions
diff --git a/runtime/Runtest/Makefile b/runtime/Runtest/Makefile
index 666fe06d..82e21345 100644
--- a/runtime/Runtest/Makefile
+++ b/runtime/Runtest/Makefile
@@ -59,3 +59,14 @@ ifeq ($(HOST_OS), $(filter $(HOST_OS), Linux GNU GNU/kFreeBSD))
     # Don't allow unresolved symbols.
     LLVMLibsOptions += -Wl,--no-undefined
 endif
+
+ifeq ($(HOST_OS), Linux)
+	# HACK: Setup symlinks that `ldconfig` would set up
+	# so that libkleeRuntest can be used from the build directory.
+	# This is needed to run tests.
+	sym_link_name := $(SharedLibDir)/$(SharedPrefix)$(LIBRARYNAME)$(SHLIBEXT).$(SHARED_VERSION)
+
+all:: $(LibName.SO)
+	$(Verb) [ ! -e "$(sym_link_name)" ] && ln -s $(LibName.SO) "$(sym_link_name)" || echo ""
+
+endif
diff --git a/runtime/Runtest/intrinsics.c b/runtime/Runtest/intrinsics.c
index 2302e278..4d785ee2 100644
--- a/runtime/Runtest/intrinsics.c
+++ b/runtime/Runtest/intrinsics.c
@@ -10,8 +10,9 @@
 /* Straight C for linking simplicity */
 
 #include <assert.h>
-#include <stdlib.h>
+#include <stdarg.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <sys/mman.h>
 #include <sys/time.h>
@@ -31,6 +32,23 @@ static unsigned char rand_byte(void) {
   return x & 0xFF;
 }
 
+static void report_internal_error(const char *msg, ...)
+    __attribute__((format(printf, 1, 2)));
+static void report_internal_error(const char *msg, ...) {
+  fprintf(stderr, "KLEE_RUN_TEST_ERROR: ");
+  va_list ap;
+  va_start(ap, msg);
+  vfprintf(stderr, msg, ap);
+  va_end(ap);
+  fprintf(stderr, "\n");
+  char *testErrorsNonFatal = getenv("KLEE_RUN_TEST_ERRORS_NON_FATAL");
+  if (testErrorsNonFatal) {
+    fprintf(stderr, "KLEE_RUN_TEST_ERROR: Forcing execution to continue\n");
+  } else {
+    exit(1);
+  }
+}
+
 void klee_make_symbolic(void *array, size_t nbytes, const char *name) {
   static int rand_init = -1;
 
@@ -80,16 +98,34 @@ void klee_make_symbolic(void *array, size_t nbytes, const char *name) {
     }
   }
 
-  if (testPosition >= testData->numObjects) {
-    fprintf(stderr, "ERROR: out of inputs, using zero\n");
-    memset(array, 0, nbytes);
-  } else {
-    KTestObject *o = &testData->objects[testPosition++];
-    memcpy(array, o->bytes, nbytes<o->numBytes ? nbytes : o->numBytes);
-    if (nbytes != o->numBytes) {
-      fprintf(stderr, "ERROR: object sizes differ\n");
-      if (o->numBytes < nbytes) 
-        memset((char*) array + o->numBytes, 0, nbytes - o->numBytes);
+  for (;; ++testPosition) {
+    if (testPosition >= testData->numObjects) {
+      report_internal_error("out of inputs. Will use zero if continuing.");
+      memset(array, 0, nbytes);
+      break;
+    } else {
+      KTestObject *o = &testData->objects[testPosition];
+      if (strcmp("model_version", o->name) == 0 &&
+          strcmp("model_version", name) != 0) {
+        // Skip over this KTestObject because we've hit
+        // `model_version` which is from the POSIX runtime
+        // and the caller didn't ask for it.
+        continue;
+      }
+      if (strcmp(name, o->name) != 0) {
+        report_internal_error(
+            "object name mismatch. Requesting \"%s\" but returning \"%s\"",
+            name, o->name);
+      }
+      memcpy(array, o->bytes, nbytes < o->numBytes ? nbytes : o->numBytes);
+      if (nbytes != o->numBytes) {
+        report_internal_error("object sizes differ. Expected %zu but got %u",
+                              nbytes, o->numBytes);
+        if (o->numBytes < nbytes)
+          memset((char *)array + o->numBytes, 0, nbytes - o->numBytes);
+      }
+      ++testPosition;
+      break;
     }
   }
 }
@@ -102,14 +138,13 @@ uintptr_t klee_choose(uintptr_t n) {
   uintptr_t x;
   klee_make_symbolic(&x, sizeof x, "klee_choose");
   if(x >= n)
-    fprintf(stderr, "ERROR: max = %ld, got = %ld\n", n, x);
-  assert(x < n);
+    report_internal_error("klee_choose failure. max = %ld, got = %ld\n", n, x);
   return x;
 }
 
 void klee_assume(uintptr_t x) {
   if (!x) {
-    fprintf(stderr, "ERROR: invalid klee_assume\n");
+    report_internal_error("invalid klee_assume");
   }
 }
 
@@ -131,10 +166,8 @@ int klee_range(int begin, int end, const char* name) {
   int x;
   klee_make_symbolic(&x, sizeof x, name);
   if (x<begin || x>=end) {
-    fprintf(stderr, 
-            "KLEE: ERROR: invalid klee_range(%u,%u,%s) value, got: %u\n", 
-            begin, end, name, x);
-    abort();
+    report_internal_error("invalid klee_range(%u,%u,%s) value, got: %u\n",
+                          begin, end, name, x);
   }
   return x;
 }
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 6d3ec926..0ae78faf 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -13,6 +13,8 @@ set(LLVM_TOOLS_DIR "${LLVM_TOOLS_BINARY_DIR}")
 # is shared by both build systems.
 set(LLVMCC "${LLVMCC} -I${CMAKE_SOURCE_DIR}/include")
 set(LLVMCXX "${LLVMCXX} -I${CMAKE_SOURCE_DIR}/include")
+set(NATIVE_CC "${CMAKE_C_COMPILER} -I ${CMAKE_SOURCE_DIR}/include")
+set(NATIVE_CXX "${CMAKE_CXX_COMPILER} -I ${CMAKE_SOURCE_DIR}/include")
 set(TARGET_TRIPLE "${TARGET_TRIPLE}")
 if (ENABLE_KLEE_UCLIBC)
   set(ENABLE_UCLIBC 1)
@@ -105,6 +107,20 @@ add_subdirectory(Concrete)
 ###############################################################################
 # Configure lit test suite
 ###############################################################################
+
+# Find path to libkleeRuntest target for `lit.site.cfg`.
+# FIXME: This is not the right way to get the location of the target we have to
+# set CMP0026 to old.
+# This will likely break if using a multi-configuration generator.
+if (POLICY CMP0026)
+  # HACK: Allow reading `LOCATION` property.
+  cmake_policy(SET CMP0026 OLD)
+endif()
+get_property(LIB_KLEE_RUN_TEST_PATH
+  TARGET kleeRuntest
+  PROPERTY LOCATION
+)
+
 configure_file(lit.site.cfg.in
   ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
   @ONLY
@@ -112,7 +128,7 @@ configure_file(lit.site.cfg.in
 
 add_custom_target(integrationtests
   COMMAND "${LIT_TOOL}" ${LIT_ARGS} "${CMAKE_CURRENT_BINARY_DIR}"
-  DEPENDS klee kleaver
+  DEPENDS klee kleaver kleeRuntest
   COMMENT "Running integration tests"
   ${ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG}
 )
diff --git a/test/Makefile b/test/Makefile
index 0b176769..ed7ba197 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -84,4 +84,7 @@ lit.site.cfg: lit.site.cfg.in
 	     -e "s#@HAVE_SELINUX@#$(HAVE_SELINUX)#g" \
 	     -e "s#@ENABLE_STP@#$(ENABLE_STP)#g" \
 	     -e "s#@ENABLE_Z3@#$(ENABLE_Z3)#g" \
+	     -e "s#@NATIVE_CC@#$(CC) -I$(PROJ_SRC_ROOT)/include#g" \
+	     -e "s#@NATIVE_CXX@#$(CXX) -I$(PROJ_SRC_ROOT)/include#g" \
+	     -e "s#@LIB_KLEE_RUN_TEST_PATH@#$(SharedLibDir)/$(SharedPrefix)kleeRuntest$(SHLIBEXT)#g" \
 	     $(PROJ_SRC_DIR)/lit.site.cfg.in > $@
diff --git a/test/Replay/libkleeruntest/replay_invalid_klee_assume.c b/test/Replay/libkleeruntest/replay_invalid_klee_assume.c
new file mode 100644
index 00000000..12ac006e
--- /dev/null
+++ b/test/Replay/libkleeruntest/replay_invalid_klee_assume.c
@@ -0,0 +1,44 @@
+// RUN: %llvmgcc -DASSUME_VALUE=1 %s -emit-llvm -g -O0 -c -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --search=dfs %t.bc
+// RUN: test -f %t.klee-out/test000001.ktest
+// RUN: test ! -f %t.klee-out/test000002.ktest
+
+// Now try to replay with libkleeRuntest but build the binary to use a different
+// value for the `klee_assume()` call.
+// RUN: %cc -DASSUME_VALUE=32 -DPRINT_VALUE %s %libkleeruntest -Wl,-rpath=%libkleeruntestdir -o %t_runner
+
+// Check that the default is to exit with an error
+// RUN: not env KTEST_FILE=%t.klee-out/test000001.ktest %t_runner 2>&1 | FileCheck -check-prefix=CHECK_FATAL %s
+
+// Check that setting `KLEE_RUN_TEST_ERRORS_NON_FATAL` will not exit with an error
+// and will continue executing.
+// RUN: env KTEST_FILE=%t.klee-out/test000001.ktest KLEE_RUN_TEST_ERRORS_NON_FATAL=1 %t_runner 2>&1 | FileCheck %s
+
+#include "klee/klee.h"
+#include <stdint.h>
+#include <stdio.h>
+
+#ifndef ASSUME_VALUE
+#error ASSUME_VALUE must be defined
+#endif
+
+
+int main(int argc, char** argv) {
+  int x = 54;
+  klee_make_symbolic(&x, sizeof(x), "x");
+  klee_assume(x == ASSUME_VALUE);
+
+#ifdef PRINT_VALUE
+  printf("x=%d\n", x);
+#endif
+
+  return 0;
+}
+// CHECK: KLEE_RUN_TEST_ERROR: invalid klee_assume
+// CHECK: KLEE_RUN_TEST_ERROR: Forcing execution to continue
+// CHECK: x=1
+
+// CHECK_FATAL: KLEE_RUN_TEST_ERROR: invalid klee_assume
+// CHECK_FATAL-NOT: x=1
+
diff --git a/test/Replay/libkleeruntest/replay_invalid_klee_choose.c b/test/Replay/libkleeruntest/replay_invalid_klee_choose.c
new file mode 100644
index 00000000..62f514bf
--- /dev/null
+++ b/test/Replay/libkleeruntest/replay_invalid_klee_choose.c
@@ -0,0 +1,45 @@
+// RUN: %llvmgcc -DBOUND_VALUE=32 -DFORCE_VALUE=20 %s -emit-llvm -g -O0 -c -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=klee --search=dfs %t.bc
+// RUN: test -f %t.klee-out/test000001.ktest
+// RUN: test ! -f %t.klee-out/test000002.ktest
+
+// Now try to replay with libkleeRuntest but build the binary to use a different
+// bound for `klee_choose()`.
+// RUN: %cc -DBOUND_VALUE=2 -DPRINT_VALUE %s %libkleeruntest -Wl,-rpath=%libkleeruntestdir -o %t_runner
+
+// Check that the default is to exit with an error
+// RUN: not env KTEST_FILE=%t.klee-out/test000001.ktest %t_runner 2>&1 | FileCheck -check-prefix=CHECK_FATAL %s
+
+// Check that setting `KLEE_RUN_TEST_ERRORS_NON_FATAL` will not exit with an error
+// and will continue executing.
+// RUN: env KTEST_FILE=%t.klee-out/test000001.ktest KLEE_RUN_TEST_ERRORS_NON_FATAL=1 %t_runner 2>&1 | FileCheck %s
+
+#include "klee/klee.h"
+#include <stdint.h>
+#include <stdio.h>
+
+#ifndef BOUND_VALUE
+#error BOUND_VALUE must be defined
+#endif
+
+
+int main(int argc, char** argv) {
+  int x = klee_choose(BOUND_VALUE);
+#ifdef FORCE_VALUE
+  klee_assume(x == FORCE_VALUE);
+#endif
+
+#ifdef PRINT_VALUE
+  printf("x=%d\n", x);
+#endif
+
+  return 0;
+}
+// CHECK: KLEE_RUN_TEST_ERROR: klee_choose failure. max = 2, got = 20
+// CHECK: KLEE_RUN_TEST_ERROR: Forcing execution to continue
+// CHECK: x=20
+
+// CHECK_FATAL: KLEE_RUN_TEST_ERROR: klee_choose failure. max = 2, got = 20
+// CHECK_FATAL-NOT: x=20
+
diff --git a/test/Replay/libkleeruntest/replay_invalid_klee_range.c b/test/Replay/libkleeruntest/replay_invalid_klee_range.c
new file mode 100644
index 00000000..c7d62027
--- /dev/null
+++ b/test/Replay/libkleeruntest/replay_invalid_klee_range.c
@@ -0,0 +1,45 @@
+// RUN: %llvmgcc -DBOUND_VALUE=32 -DFORCE_VALUE=20 %s -emit-llvm -g -O0 -c -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=klee --search=dfs %t.bc
+// RUN: test -f %t.klee-out/test000001.ktest
+// RUN: test ! -f %t.klee-out/test000002.ktest
+
+// Now try to replay with libkleeRuntest but build the binary to use a different
+// bound for `klee_range()`.
+// RUN: %cc -DBOUND_VALUE=2 -DPRINT_VALUE %s %libkleeruntest -Wl,-rpath=%libkleeruntestdir -o %t_runner
+
+// Check that the default is to exit with an error
+// RUN: not env KTEST_FILE=%t.klee-out/test000001.ktest %t_runner 2>&1 | FileCheck -check-prefix=CHECK_FATAL %s
+
+// Check that setting `KLEE_RUN_TEST_ERRORS_NON_FATAL` will not exit with an error
+// and will continue executing.
+// RUN: env KTEST_FILE=%t.klee-out/test000001.ktest KLEE_RUN_TEST_ERRORS_NON_FATAL=1 %t_runner 2>&1 | FileCheck %s
+
+#include "klee/klee.h"
+#include <stdint.h>
+#include <stdio.h>
+
+#ifndef BOUND_VALUE
+#error BOUND_VALUE must be defined
+#endif
+
+
+int main(int argc, char** argv) {
+  int x = klee_range(0, BOUND_VALUE, "x");
+#ifdef FORCE_VALUE
+  klee_assume(x == FORCE_VALUE);
+#endif
+
+#ifdef PRINT_VALUE
+  printf("x=%d\n", x);
+#endif
+
+  return 0;
+}
+// CHECK: KLEE_RUN_TEST_ERROR: invalid klee_range(0,2,x) value, got: 20
+// CHECK: KLEE_RUN_TEST_ERROR: Forcing execution to continue
+// CHECK: x=20
+
+// CHECK_FATAL: KLEE_RUN_TEST_ERROR: invalid klee_range(0,2,x) value, got: 20
+// CHECK_FATAL-NOT: x=20
+
diff --git a/test/Replay/libkleeruntest/replay_invalid_num_objects.c b/test/Replay/libkleeruntest/replay_invalid_num_objects.c
new file mode 100644
index 00000000..43bc4867
--- /dev/null
+++ b/test/Replay/libkleeruntest/replay_invalid_num_objects.c
@@ -0,0 +1,39 @@
+// Compile program that only makes one klee_make_symbolic() call
+// RUN: %llvmgcc %s -emit-llvm -g -O0 -c -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --search=dfs %t.bc
+// RUN: test -f %t.klee-out/test000001.ktest
+
+// Now try to replay with libkleeRuntest but build the binary so it
+// makes two calls to klee_make_symbolic.
+// RUN: %cc -DEXTRA_MAKE_SYMBOLIC %s %libkleeruntest -Wl,-rpath=%libkleeruntestdir -o %t_runner
+
+// Check that the default is to exit with an error
+// RUN: not env KTEST_FILE=%t.klee-out/test000001.ktest %t_runner 2>&1 | FileCheck -check-prefix=CHECK_FATAL %s
+
+// Check that setting `KLEE_RUN_TEST_ERRORS_NON_FATAL` will not exit with an error
+// and will continue executing.
+// RUN: env KTEST_FILE=%t.klee-out/test000001.ktest KLEE_RUN_TEST_ERRORS_NON_FATAL=1 %t_runner 2>&1 | FileCheck %s
+
+#include "klee/klee.h"
+#include <stdio.h>
+
+int main(int argc, char** argv) {
+  int x = 0;
+  klee_make_symbolic(&x, sizeof(x), "x");
+
+#ifdef EXTRA_MAKE_SYMBOLIC
+  int y = 1;
+  klee_make_symbolic(&y, sizeof(y), "x");
+  klee_assume(y == 0);
+  fprintf(stderr, "y is \"%d\"\n", y);
+#endif
+  return 0;
+}
+// CHECK: KLEE_RUN_TEST_ERROR: out of inputs
+// CHECK: KLEE_RUN_TEST_ERROR: Forcing execution to continue
+// CHECK: y is "0"
+
+// CHECK_FATAL: KLEE_RUN_TEST_ERROR: out of inputs
+// CHECK_FATAL-NOT: y is "0"
+
diff --git a/test/Replay/libkleeruntest/replay_invalid_object_names.c b/test/Replay/libkleeruntest/replay_invalid_object_names.c
new file mode 100644
index 00000000..9c75bebc
--- /dev/null
+++ b/test/Replay/libkleeruntest/replay_invalid_object_names.c
@@ -0,0 +1,45 @@
+// RUN: %llvmgcc -DOBJ_NAME=simple_name %s -emit-llvm -g -O0 -c -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --search=dfs %t.bc
+// RUN: test -f %t.klee-out/test000001.ktest
+
+// Now try to replay with libkleeRuntest but build the binary to use a different
+// object name
+// RUN: %cc -DOBJ_NAME=wrong_name -DPRINT_VALUE %s %libkleeruntest -Wl,-rpath=%libkleeruntestdir -o %t_runner
+
+// Check that the default is to exit with an error
+// RUN: not env KTEST_FILE=%t.klee-out/test000001.ktest %t_runner 2>&1 | FileCheck -check-prefix=CHECK_FATAL %s
+
+// Check that setting `KLEE_RUN_TEST_ERRORS_NON_FATAL` will not exit with an error
+// and will continue executing.
+// RUN: env KTEST_FILE=%t.klee-out/test000001.ktest KLEE_RUN_TEST_ERRORS_NON_FATAL=1 %t_runner 2>&1 | FileCheck %s
+
+#include "klee/klee.h"
+#include <stdio.h>
+
+#ifndef OBJ_NAME
+#error OBJ_NAME must be defined
+#endif
+
+#define STRINGIFY(X) #X
+#define XSTRINGIFY(X) STRINGIFY(X)
+
+
+int main(int argc, char** argv) {
+  int x = 1;
+  klee_make_symbolic(&x, sizeof(x), XSTRINGIFY(OBJ_NAME));
+  klee_assume(x == 0);
+
+#ifdef PRINT_VALUE
+  printf("x=%d\n", x);
+#endif
+
+  return 0;
+}
+// CHECK: KLEE_RUN_TEST_ERROR: object name mismatch. Requesting "wrong_name" but returning "simple_name"
+// CHECK: KLEE_RUN_TEST_ERROR: Forcing execution to continue
+// CHECK: x=0
+
+// CHECK_FATAL: KLEE_RUN_TEST_ERROR: object name mismatch. Requesting "wrong_name" but returning "simple_name"
+// CHECK_FATAL-NOT: x=0
+
diff --git a/test/Replay/libkleeruntest/replay_invalid_object_size.c b/test/Replay/libkleeruntest/replay_invalid_object_size.c
new file mode 100644
index 00000000..a1513ef9
--- /dev/null
+++ b/test/Replay/libkleeruntest/replay_invalid_object_size.c
@@ -0,0 +1,43 @@
+// RUN: %llvmgcc -DINT_TYPE=uint8_t %s -emit-llvm -g -O0 -c -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --search=dfs %t.bc
+// RUN: test -f %t.klee-out/test000001.ktest
+// RUN: test ! -f %t.klee-out/test000002.ktest
+
+// Now try to replay with libkleeRuntest but build the binary to use a different
+// size for variable `x`.
+// RUN: %cc -DINT_TYPE=uint32_t -DPRINT_VALUE %s %libkleeruntest -Wl,-rpath=%libkleeruntestdir -o %t_runner
+
+// Check that the default is to exit with an error
+// RUN: not env KTEST_FILE=%t.klee-out/test000001.ktest %t_runner 2>&1 | FileCheck -check-prefix=CHECK_FATAL %s
+
+// Check that setting `KLEE_RUN_TEST_ERRORS_NON_FATAL` will not exit with an error
+// and will continue executing.
+// RUN: env KTEST_FILE=%t.klee-out/test000001.ktest KLEE_RUN_TEST_ERRORS_NON_FATAL=1 %t_runner 2>&1 | FileCheck %s
+#include "klee/klee.h"
+#include <stdint.h>
+#include <stdio.h>
+
+#ifndef INT_TYPE
+#error INT_TYPE must be defined
+#endif
+
+
+int main(int argc, char** argv) {
+  INT_TYPE x = 1;
+  klee_make_symbolic(&x, sizeof(x), "x");
+  klee_assume(x == 0);
+
+#ifdef PRINT_VALUE
+  printf("x=%d\n", x);
+#endif
+
+  return 0;
+}
+// CHECK: KLEE_RUN_TEST_ERROR: object sizes differ. Expected 4 but got 1
+// CHECK: KLEE_RUN_TEST_ERROR: Forcing execution to continue
+// CHECK: x=0
+
+// CHECK_FATAL: KLEE_RUN_TEST_ERROR: object sizes differ. Expected 4 but got 1
+// CHECK_FATAL-NOT: x=0
+
diff --git a/test/Replay/libkleeruntest/replay_posix_runtime.c b/test/Replay/libkleeruntest/replay_posix_runtime.c
new file mode 100644
index 00000000..70d8a1d9
--- /dev/null
+++ b/test/Replay/libkleeruntest/replay_posix_runtime.c
@@ -0,0 +1,35 @@
+// REQUIRES: posix-runtime
+// FIXME: We need to fix a bug in libkleeRuntest
+// RUN: %llvmgcc %s -emit-llvm -g -O0 -c -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --posix-runtime --search=dfs %t.bc
+// RUN: test -f %t.klee-out/test000001.ktest
+// RUN: test -f %t.klee-out/test000002.ktest
+
+// Now try to replay with libkleeRuntest
+// RUN: %cc %s %libkleeruntest -Wl,-rpath=%libkleeruntestdir -o %t_runner
+// RUN: %ktest-tool %t.klee-out/test000001.ktest | FileCheck -check-prefix=CHECKMODEL %s
+// RUN: env KTEST_FILE=%t.klee-out/test000001.ktest %t_runner | FileCheck -check-prefix=TESTONE %s
+// RUN: env KTEST_FILE=%t.klee-out/test000002.ktest %t_runner | FileCheck -check-prefix=TESTTWO %s
+
+#include "klee/klee.h"
+#include <stdio.h>
+
+int main(int argc, char** argv) {
+  int x = 0;
+  klee_make_symbolic(&x, sizeof(x), "x");
+
+  if (x == 0) {
+    printf("x is 0\n");
+  } else {
+    printf("x is not 0\n");
+  }
+  return 0;
+}
+
+// CHECKMODEL: num objects: 2
+// CHECKMODEL: object 0: name: {{b*}}'model_version'
+// CHECKMODEL: object 1: name: {{b*}}'x'
+
+// TESTONE: x is not 0
+// TESTTWO: x is 0
diff --git a/test/Replay/libkleeruntest/replay_simple.c b/test/Replay/libkleeruntest/replay_simple.c
new file mode 100644
index 00000000..cb9dfb85
--- /dev/null
+++ b/test/Replay/libkleeruntest/replay_simple.c
@@ -0,0 +1,28 @@
+// RUN: %llvmgcc %s -emit-llvm -g -O0 -c -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --search=dfs %t.bc
+// RUN: test -f %t.klee-out/test000001.ktest
+// RUN: test -f %t.klee-out/test000002.ktest
+
+// Now try to replay with libkleeRuntest
+// RUN: %cc %s %libkleeruntest -Wl,-rpath=%libkleeruntestdir -o %t_runner
+// RUN: env KTEST_FILE=%t.klee-out/test000001.ktest %t_runner | FileCheck -check-prefix=TESTONE %s
+// RUN: env KTEST_FILE=%t.klee-out/test000002.ktest %t_runner | FileCheck -check-prefix=TESTTWO %s
+
+#include "klee/klee.h"
+#include <stdio.h>
+
+int main(int argc, char** argv) {
+  int x = 0;
+  klee_make_symbolic(&x, sizeof(x), "x");
+
+  if (x == 0) {
+    printf("x is 0\n");
+  } else {
+    printf("x is not 0\n");
+  }
+  return 0;
+}
+
+// TESTONE: x is not 0
+// TESTTWO: x is 0
diff --git a/test/Replay/libkleeruntest/replay_two_objects.c b/test/Replay/libkleeruntest/replay_two_objects.c
new file mode 100644
index 00000000..f5be657c
--- /dev/null
+++ b/test/Replay/libkleeruntest/replay_two_objects.c
@@ -0,0 +1,33 @@
+// RUN: %llvmgcc %s -emit-llvm -g -O0 -c -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --search=dfs %t.bc 2>&1 | FileCheck %s
+// RUN: test -f %t.klee-out/test000001.ktest
+// RUN: test ! -f %t.klee-out/test000002.ktest
+
+// Now try to replay with libkleeRuntest
+// RUN: %cc -DPRINT_VALUES %s %libkleeruntest -Wl,-rpath=%libkleeruntestdir -o %t_runner
+// RUN: env KTEST_FILE=%t.klee-out/test000001.ktest %t_runner | FileCheck -check-prefix=TESTONE %s
+
+#include "klee/klee.h"
+#include <stdio.h>
+
+int main(int argc, char** argv) {
+  int x = 0;
+  int y = 0;
+  klee_make_symbolic(&x, sizeof(x), "x");
+  klee_make_symbolic(&y, sizeof(x), "y");
+
+  klee_assume(x == 1);
+  klee_assume(y == 128);
+
+#ifdef PRINT_VALUES
+  printf("x=%d\n", x);
+  printf("y=%d\n", y);
+#endif
+
+  return 0;
+}
+// CHECK: KLEE: done: completed paths = 1
+
+// TESTONE: x=1
+// TESTONE: y=128
diff --git a/test/lit.cfg b/test/lit.cfg
index 39d23fc5..31552882 100644
--- a/test/lit.cfg
+++ b/test/lit.cfg
@@ -76,7 +76,7 @@ if config.test_exec_root is None:
 
 
 # Add substitutions from lit.site.cfg
-subs = [ 'llvmgcc', 'llvmgxx']
+subs = [ 'llvmgcc', 'llvmgxx', 'cc', 'cxx']
 for name in subs:
     value = getattr(config, name, None)
     if value == None:
@@ -90,6 +90,10 @@ config.substitutions.append( ('%llvmas', os.path.join(llvm_tools_dir, 'llvm-as')
 # Add a substitution for llvm-ar
 config.substitutions.append( ('%llvmar', os.path.join(llvm_tools_dir, 'llvm-ar')) )
 
+# Add a substition for libkleeruntest
+config.substitutions.append( ('%libkleeruntestdir', os.path.dirname(config.libkleeruntest)) )
+config.substitutions.append( ('%libkleeruntest', config.libkleeruntest) )
+
 # Get KLEE and Kleaver specific parameters passed on llvm-lit cmd line
 # e.g. llvm-lit --param klee_opts=--help
 try:
@@ -107,7 +111,10 @@ if len(kleaver_extra_params) != 0:
     print("Passing extra Kleaver command line args: {0}".format(kleaver_extra_params))
 
 # Set absolute paths and extra cmdline args for KLEE's tools
-subs = [ ('%kleaver', 'kleaver', kleaver_extra_params), ('%klee','klee', klee_extra_params) ]
+subs = [ ('%kleaver', 'kleaver', kleaver_extra_params),
+  ('%klee','klee', klee_extra_params),
+  ('%ktest-tool', 'ktest-tool', '')
+]
 for s,basename,extra_args in subs:
     config.substitutions.append( ( s,
                                    "{0} {1}".format( os.path.join(klee_tools_dir, basename), extra_args ).strip()
@@ -140,3 +147,7 @@ if config.enable_z3:
   config.available_features.add('z3')
 else:
   config.available_features.add('not-z3')
+
+# POSIX runtime feature
+if config.enable_posix_runtime:
+  config.available_features.add('posix-runtime')
diff --git a/test/lit.site.cfg.in b/test/lit.site.cfg.in
index 5aff44ab..e53b1327 100644
--- a/test/lit.site.cfg.in
+++ b/test/lit.site.cfg.in
@@ -14,6 +14,9 @@ config.llvm_version_minor = "@LLVM_VERSION_MINOR@"
 config.llvmgcc = "@LLVMCC@"
 config.llvmgxx = "@LLVMCXX@"
 
+config.cc = "@NATIVE_CC@"
+config.cxx = "@NATIVE_CXX@"
+
 # Features
 config.enable_uclibc = True if @ENABLE_UCLIBC@ == 1 else False
 config.enable_posix_runtime = True if @ENABLE_POSIX_RUNTIME@ == 1 else False
@@ -24,6 +27,9 @@ config.enable_z3 = True if @ENABLE_Z3@ == 1 else False
 # Current target
 config.target_triple = "@TARGET_TRIPLE@"
 
+# Path to libkleeRuntest
+config.libkleeruntest = "@LIB_KLEE_RUN_TEST_PATH@"
+
 # Let the main config do the real work.
 try:
   lit