about summary refs log tree commit diff homepage
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt6
-rw-r--r--lib/Core/ExecutionState.cpp3
-rw-r--r--lib/Core/ExecutionState.h81
-rw-r--r--lib/Core/Executor.cpp364
-rw-r--r--lib/Core/Executor.h23
-rw-r--r--lib/Core/SpecialFunctionHandler.cpp50
-rw-r--r--lib/Core/SpecialFunctionHandler.h2
-rw-r--r--lib/Module/IntrinsicCleaner.cpp6
-rw-r--r--lib/Module/ModuleUtil.cpp64
-rw-r--r--runtime/CMakeLists.txt14
-rw-r--r--runtime/Makefile.cmake.bitcode4
-rw-r--r--runtime/Makefile.cmake.bitcode.config.in5
-rw-r--r--runtime/klee-eh-cxx/Makefile.cmake.bitcode13
-rw-r--r--runtime/klee-eh-cxx/klee_eh_cxx.cpp218
-rw-r--r--test/CXX/symex/basic_c++/diamond.cpp45
-rw-r--r--test/CXX/symex/basic_c++/inheritance.cpp35
-rw-r--r--test/CXX/symex/basic_c++/lambda.cpp20
-rw-r--r--test/CXX/symex/basic_c++/namespace.cpp18
-rw-r--r--test/CXX/symex/basic_c++/reinterpret_cast.cpp26
-rw-r--r--test/CXX/symex/basic_c++/simple.cpp33
-rw-r--r--test/CXX/symex/basic_c++/templates.cpp28
-rw-r--r--test/CXX/symex/basic_c++/virtual.cpp35
-rw-r--r--test/CXX/symex/libc++/atexit.cpp19
-rw-r--r--test/CXX/symex/libc++/can_catch_test.cpp88
-rw-r--r--test/CXX/symex/libc++/catch_recover.cpp26
-rw-r--r--test/CXX/symex/libc++/catch_with_adjusted_exception_pointer.cpp37
-rw-r--r--test/CXX/symex/libc++/cout.cpp4
-rw-r--r--test/CXX/symex/libc++/cout_sym.cpp16
-rw-r--r--test/CXX/symex/libc++/dynamic_cast.cpp56
-rw-r--r--test/CXX/symex/libc++/exception.cpp33
-rw-r--r--test/CXX/symex/libc++/exception_inheritance.cpp78
-rw-r--r--test/CXX/symex/libc++/general_catch.cpp60
-rw-r--r--test/CXX/symex/libc++/landingpad.cpp53
-rw-r--r--test/CXX/symex/libc++/multi_throw.cpp47
-rw-r--r--test/CXX/symex/libc++/multi_unwind.cpp70
-rw-r--r--test/CXX/symex/libc++/nested.cpp26
-rw-r--r--test/CXX/symex/libc++/nested_fail.cpp26
-rw-r--r--test/CXX/symex/libc++/rethrow.cpp35
-rw-r--r--test/CXX/symex/libc++/simple_exception.cpp19
-rw-r--r--test/CXX/symex/libc++/simple_exception_fail.cpp13
-rw-r--r--test/CXX/symex/libc++/symbolic_exception.cpp29
-rw-r--r--test/CXX/symex/libc++/throw_specifiers.cpp35
-rw-r--r--test/CXX/symex/libc++/throwing_exception_destructor.cpp45
-rw-r--r--test/CXX/symex/libc++/uncaught_exception.cpp13
-rw-r--r--test/CXX/symex/libc++/vector.cpp5
-rw-r--r--tools/klee/main.cpp14
46 files changed, 1866 insertions, 74 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1388d3f3..8aaae43b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -653,6 +653,12 @@ if (ENABLE_KLEE_LIBCXX)
   endif()
   message(STATUS "Use libc++ include path: \"${KLEE_LIBCXX_INCLUDE_DIR}\"")
 
+  if (NOT IS_DIRECTORY "${KLEE_LIBCXXABI_DIR}")
+    message(FATAL_ERROR
+      "${KLEE_LIBCXXABI_DIR} does not exist. Try passing -DKLEE_LIBCXXABI_DIR=<path>")
+  endif()
+  message(STATUS "Use libc++abi path: \"${KLEE_LIBCXXABI_DIR}\"")
+
   # Find the library bitcode archive
 
   # Check for static first
diff --git a/lib/Core/ExecutionState.cpp b/lib/Core/ExecutionState.cpp
index 5dfabe9e..9004d706 100644
--- a/lib/Core/ExecutionState.cpp
+++ b/lib/Core/ExecutionState.cpp
@@ -106,6 +106,9 @@ ExecutionState::ExecutionState(const ExecutionState& state):
     openMergeStack(state.openMergeStack),
     steppedInstructions(state.steppedInstructions),
     instsSinceCovNew(state.instsSinceCovNew),
+    unwindingInformation(state.unwindingInformation
+                             ? state.unwindingInformation->cloned()
+                             : nullptr),
     coveredNew(state.coveredNew),
     forkDisabled(state.forkDisabled) {
   for (const auto &cur_mergehandler: openMergeStack)
diff --git a/lib/Core/ExecutionState.h b/lib/Core/ExecutionState.h
index 7be69ea7..b3560caf 100644
--- a/lib/Core/ExecutionState.h
+++ b/lib/Core/ExecutionState.h
@@ -21,6 +21,7 @@
 #include "klee/System/Time.h"
 
 #include <map>
+#include <memory>
 #include <set>
 #include <vector>
 
@@ -63,6 +64,83 @@ struct StackFrame {
   ~StackFrame();
 };
 
+/// Contains information related to unwinding (Itanium ABI/2-Phase unwinding)
+struct UnwindingInformation {
+  enum class Kind {
+    SearchPhase, // first phase
+    CleanupPhase // second phase
+  };
+
+private:
+  const Kind kind;
+
+public:
+  // _Unwind_Exception* of the thrown exception, used in both phases
+  ref<ConstantExpr> exceptionObject;
+
+  Kind getKind() const { return kind; }
+
+  explicit UnwindingInformation(ref<ConstantExpr> exceptionObject, Kind k)
+      : kind(k), exceptionObject(exceptionObject) {}
+  virtual ~UnwindingInformation() = default;
+
+  virtual std::unique_ptr<UnwindingInformation> cloned() const = 0;
+};
+
+struct SearchPhaseUnwindingInformation : public UnwindingInformation {
+  // Keeps track of the stack index we have so far unwound to.
+  std::size_t unwindingProgress;
+
+  // MemoryObject that contains a serialized version of the last executed
+  // landingpad, so we can clean it up after the personality fn returns.
+  MemoryObject *serializedLandingpad = nullptr;
+
+  SearchPhaseUnwindingInformation(ref<ConstantExpr> exceptionObject,
+                                  std::size_t const unwindingProgress)
+      : UnwindingInformation(exceptionObject,
+                             UnwindingInformation::Kind::SearchPhase),
+        unwindingProgress(unwindingProgress) {}
+
+  std::unique_ptr<UnwindingInformation> cloned() const {
+    return std::make_unique<SearchPhaseUnwindingInformation>(*this);
+  }
+
+  static bool classof(const UnwindingInformation *u) {
+    return u->getKind() == UnwindingInformation::Kind::SearchPhase;
+  }
+};
+
+struct CleanupPhaseUnwindingInformation : public UnwindingInformation {
+  // Phase 1 will try to find a catching landingpad.
+  // Phase 2 will unwind up to this landingpad or return from
+  // _Unwind_RaiseException if none was found.
+
+  // The selector value of the catching landingpad that was found
+  // during the search phase.
+  ref<ConstantExpr> selectorValue;
+
+  // Used to know when we have to stop unwinding and to
+  // ensure that unwinding stops at the frame for which
+  // we first found a handler in the search phase.
+  const std::size_t catchingStackIndex;
+
+  CleanupPhaseUnwindingInformation(ref<ConstantExpr> exceptionObject,
+                                   ref<ConstantExpr> selectorValue,
+                                   const std::size_t catchingStackIndex)
+      : UnwindingInformation(exceptionObject,
+                             UnwindingInformation::Kind::CleanupPhase),
+        selectorValue(selectorValue),
+        catchingStackIndex(catchingStackIndex) {}
+
+  std::unique_ptr<UnwindingInformation> cloned() const {
+    return std::make_unique<CleanupPhaseUnwindingInformation>(*this);
+  }
+
+  static bool classof(const UnwindingInformation *u) {
+    return u->getKind() == UnwindingInformation::Kind::CleanupPhase;
+  }
+};
+
 /// @brief ExecutionState representing a path under exploration
 class ExecutionState {
 #ifdef KLEE_UNITTEST
@@ -141,6 +219,9 @@ public:
   /// instruction was covered.
   std::uint32_t instsSinceCovNew;
 
+  /// @brief Keep track of unwinding state while unwinding, otherwise empty
+  std::unique_ptr<UnwindingInformation> unwindingInformation;
+
   /// @brief the global state counter
   static std::uint32_t nextID;
 
diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp
index de35710f..64dcee43 100644
--- a/lib/Core/Executor.cpp
+++ b/lib/Core/Executor.cpp
@@ -65,6 +65,7 @@
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Module.h"
+#include "llvm/IR/Operator.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/FileSystem.h"
@@ -80,10 +81,12 @@ typedef unsigned TypeSize;
 #include <algorithm>
 #include <cassert>
 #include <cerrno>
+#include <cstring>
 #include <cxxabi.h>
 #include <fstream>
 #include <iomanip>
 #include <iosfwd>
+#include <limits>
 #include <sstream>
 #include <string>
 #include <sys/mman.h>
@@ -284,6 +287,10 @@ cl::list<Executor::TerminateReason> ExitOnErrorType(
         clEnumValN(Executor::ReportError, "ReportError",
                    "klee_report_error called"),
         clEnumValN(Executor::User, "User", "Wrong klee_* functions invocation"),
+        clEnumValN(Executor::UncaughtException, "UncaughtException",
+                   "Exception was not caught"),
+        clEnumValN(Executor::UnexpectedException, "UnexpectedException",
+                   "An unexpected exception was thrown"),
         clEnumValN(Executor::Unhandled, "Unhandled",
                    "Unhandled instruction hit") KLEE_LLVM_CL_VAL_END),
     cl::ZeroOrMore,
@@ -433,6 +440,8 @@ const char *Executor::TerminateReasonNames[] = {
   [ ReadOnly ] = "readonly",
   [ ReportError ] = "reporterror",
   [ User ] = "user",
+  [ UncaughtException ] = "uncaught_exception",
+  [ UnexpectedException ] = "unexpected_exception",
   [ Unhandled ] = "xxx",
 };
 
@@ -1407,6 +1416,235 @@ static inline const llvm::fltSemantics *fpWidthToSemantics(unsigned width) {
   }
 }
 
+MemoryObject *Executor::serializeLandingpad(ExecutionState &state,
+                                            const llvm::LandingPadInst &lpi,
+                                            bool &stateTerminated) {
+  stateTerminated = false;
+
+  std::vector<unsigned char> serialized;
+
+  for (unsigned current_clause_id = 0; current_clause_id < lpi.getNumClauses();
+       ++current_clause_id) {
+    llvm::Constant *current_clause = lpi.getClause(current_clause_id);
+    if (lpi.isCatch(current_clause_id)) {
+      // catch-clause
+      serialized.push_back(0);
+
+      std::uint64_t ti_addr = 0;
+
+      llvm::BitCastOperator *clause_bitcast =
+          dyn_cast<llvm::BitCastOperator>(current_clause);
+      if (clause_bitcast) {
+        llvm::GlobalValue *clause_type =
+            dyn_cast<GlobalValue>(clause_bitcast->getOperand(0));
+
+        ti_addr = globalAddresses[clause_type]->getZExtValue();
+      } else if (current_clause->isNullValue()) {
+        ti_addr = 0;
+      } else {
+        terminateStateOnError(
+            state, "Internal: Clause is not a bitcast or null (catch-all)",
+            Exec);
+        stateTerminated = true;
+        return nullptr;
+      }
+      const std::size_t old_size = serialized.size();
+      serialized.resize(old_size + 8);
+      memcpy(serialized.data() + old_size, &ti_addr, sizeof(ti_addr));
+    } else if (lpi.isFilter(current_clause_id)) {
+      if (current_clause->isNullValue()) {
+        // special handling for a catch-all filter clause, i.e., "[0 x i8*]"
+        // for this case we serialize 1 element..
+        serialized.push_back(1);
+        // which is a 64bit-wide 0.
+        serialized.resize(serialized.size() + 8, 0);
+      } else {
+        llvm::ConstantArray const *ca =
+            cast<llvm::ConstantArray>(current_clause);
+
+        // serialize `num_elements+1` as unsigned char
+        unsigned const num_elements = ca->getNumOperands();
+        unsigned char serialized_num_elements = 0;
+
+        if (num_elements >=
+            std::numeric_limits<decltype(serialized_num_elements)>::max()) {
+          terminateStateOnError(
+              state, "Internal: too many elements in landingpad filter", Exec);
+          stateTerminated = true;
+          return nullptr;
+        }
+
+        serialized_num_elements = num_elements;
+        serialized.push_back(serialized_num_elements + 1);
+
+        // serialize the exception-types occurring in this filter-clause
+        for (llvm::Value const *v : ca->operands()) {
+          llvm::BitCastOperator const *bitcast =
+              dyn_cast<llvm::BitCastOperator>(v);
+          if (!bitcast) {
+            terminateStateOnError(state,
+                                  "Internal: expected value inside a "
+                                  "filter-clause to be a bitcast",
+                                  Exec);
+            stateTerminated = true;
+            return nullptr;
+          }
+
+          llvm::GlobalValue const *clause_value =
+              dyn_cast<GlobalValue>(bitcast->getOperand(0));
+          if (!clause_value) {
+            terminateStateOnError(state,
+                                  "Internal: expected value inside a "
+                                  "filter-clause bitcast to be a GlobalValue",
+                                  Exec);
+            stateTerminated = true;
+            return nullptr;
+          }
+
+          std::uint64_t const ti_addr =
+              globalAddresses[clause_value]->getZExtValue();
+
+          const std::size_t old_size = serialized.size();
+          serialized.resize(old_size + 8);
+          memcpy(serialized.data() + old_size, &ti_addr, sizeof(ti_addr));
+        }
+      }
+    }
+  }
+
+  MemoryObject *mo =
+      memory->allocate(serialized.size(), true, false, nullptr, 1);
+  ObjectState *os = bindObjectInState(state, mo, false);
+  for (unsigned i = 0; i < serialized.size(); i++) {
+    os->write8(i, serialized[i]);
+  }
+
+  return mo;
+}
+
+void Executor::unwindToNextLandingpad(ExecutionState &state) {
+  UnwindingInformation *ui = state.unwindingInformation.get();
+  assert(ui && "unwinding without unwinding information");
+
+  std::size_t startIndex;
+  std::size_t lowestStackIndex;
+  bool popFrames;
+
+  if (auto *sui = dyn_cast<SearchPhaseUnwindingInformation>(ui)) {
+    startIndex = sui->unwindingProgress;
+    lowestStackIndex = 0;
+    popFrames = false;
+  } else if (auto *cui = dyn_cast<CleanupPhaseUnwindingInformation>(ui)) {
+    startIndex = state.stack.size() - 1;
+    lowestStackIndex = cui->catchingStackIndex;
+    popFrames = true;
+  } else {
+    assert(false && "invalid UnwindingInformation subclass");
+  }
+
+  for (std::size_t i = startIndex; i > lowestStackIndex; i--) {
+    auto const &sf = state.stack.at(i);
+
+    Instruction *inst = sf.caller ? sf.caller->inst : nullptr;
+
+    if (popFrames) {
+      state.popFrame();
+      if (statsTracker != nullptr) {
+        statsTracker->framePopped(state);
+      }
+    }
+
+    if (InvokeInst *invoke = dyn_cast<InvokeInst>(inst)) {
+      // we found the next invoke instruction in the call stack, handle it
+      // depending on the current phase.
+      if (auto *sui = dyn_cast<SearchPhaseUnwindingInformation>(ui)) {
+        // in the search phase, run personality function to check if this
+        // landingpad catches the exception
+
+        LandingPadInst *lpi = invoke->getUnwindDest()->getLandingPadInst();
+        assert(lpi && "unwind target of an invoke instruction did not lead to "
+                      "a landingpad");
+
+        // check if this is a pure cleanup landingpad first
+        if (lpi->isCleanup() && lpi->getNumClauses() == 0) {
+          // pure cleanup lpi, this can't be a handler, so skip it
+          continue;
+        }
+
+        bool stateTerminated = false;
+        MemoryObject *clauses_mo =
+            serializeLandingpad(state, *lpi, stateTerminated);
+        assert((stateTerminated != bool(clauses_mo)) &&
+               "illegal serializeLandingpad result");
+
+        if (stateTerminated) {
+          return;
+        }
+
+        assert(sui->serializedLandingpad == nullptr &&
+               "serializedLandingpad should be reset");
+        sui->serializedLandingpad = clauses_mo;
+
+        llvm::Function *personality_fn =
+            kmodule->module->getFunction("_klee_eh_cxx_personality");
+        KFunction *kf = kmodule->functionMap[personality_fn];
+
+        state.pushFrame(state.prevPC, kf);
+        state.pc = kf->instructions;
+        bindArgument(kf, 0, state, sui->exceptionObject);
+        bindArgument(kf, 1, state, clauses_mo->getSizeExpr());
+        bindArgument(kf, 2, state, clauses_mo->getBaseExpr());
+
+        if (statsTracker) {
+          statsTracker->framePushed(state,
+                                    &state.stack[state.stack.size() - 2]);
+        }
+
+        // make sure we remember our search progress afterwards
+        sui->unwindingProgress = i - 1;
+      } else {
+        // in the cleanup phase, redirect control flow
+        transferToBasicBlock(invoke->getUnwindDest(), invoke->getParent(),
+                             state);
+      }
+
+      // we are done, stop search/unwinding here
+      return;
+    }
+  }
+
+  // no further invoke instruction/landingpad found
+  if (isa<SearchPhaseUnwindingInformation>(ui)) {
+    // in phase 1, simply stop unwinding. this will return
+    // control flow back to _Unwind_RaiseException, which will
+    // return the correct error.
+
+    // clean up unwinding state
+    state.unwindingInformation.reset();
+  } else {
+    // in phase 2, this represent a situation that should
+    // not happen, as we only progressed to phase 2 because
+    // we found a handler in phase 1.
+    // therefore terminate the state.
+    terminateStateOnExecError(state,
+                              "Missing landingpad in phase 2 of unwinding");
+  }
+}
+
+ref<klee::ConstantExpr> Executor::getEhTypeidFor(ref<Expr> type_info) {
+  auto eh_type_iterator =
+      std::find(std::begin(eh_typeids), std::end(eh_typeids), type_info);
+  if (eh_type_iterator == std::end(eh_typeids)) {
+    eh_typeids.push_back(type_info);
+    eh_type_iterator = std::prev(std::end(eh_typeids));
+  }
+  // +1 because typeids must always be positive, so they can be distinguished
+  // from 'no landingpad clause matched' which has value 0
+  auto res = ConstantExpr::create(eh_type_iterator - std::begin(eh_typeids) + 1,
+                                  Expr::Int32);
+  return res;
+}
+
 void Executor::executeCall(ExecutionState &state, KInstruction *ki, Function *f,
                            std::vector<ref<Expr>> &arguments) {
   Instruction *i = ki->inst;
@@ -1500,6 +1738,12 @@ void Executor::executeCall(ExecutionState &state, KInstruction *ki, Function *f,
       }
       break;
     }
+
+    case Intrinsic::eh_typeid_for: {
+      bindLocal(ki, state, getEhTypeidFor(arguments.at(0)));
+      break;
+    }
+
     case Intrinsic::vaend:
       // va_end is a noop for the interpreter.
       //
@@ -1518,8 +1762,15 @@ void Executor::executeCall(ExecutionState &state, KInstruction *ki, Function *f,
       return;
     }
 
-    if (InvokeInst *ii = dyn_cast<InvokeInst>(i))
-      transferToBasicBlock(ii->getNormalDest(), i->getParent(), state);
+    // __cxa_throw & _rethrow are already handled in their
+    // SpecialFunctionHandlers and have already been redirected to their unwind
+    // destinations, so we must not transfer them to their regular targets.
+    if (InvokeInst *ii = dyn_cast<InvokeInst>(i)) {
+      if (f->getName() != std::string("__cxa_throw") &&
+          f->getName() != std::string("__cxa_rethrow")) {
+        transferToBasicBlock(ii->getNormalDest(), i->getParent(), state);
+      }
+    }
   } else {
     // Check if maximum stack size was reached.
     // We currently only count the number of stack frames
@@ -1747,6 +1998,8 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) {
     Instruction *caller = kcaller ? kcaller->inst : 0;
     bool isVoidReturn = (ri->getNumOperands() == 0);
     ref<Expr> result = ConstantExpr::alloc(0, Expr::Bool);
+    bool isReturnFromPersonalityFn =
+        ri->getFunction()->getName() == "_klee_eh_cxx_personality";
     
     if (!isVoidReturn) {
       result = eval(ki, 0, state).value;
@@ -1768,6 +2021,40 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) {
         ++state.pc;
       }
 
+      if (isReturnFromPersonalityFn) {
+        assert(dyn_cast<ConstantExpr>(result) &&
+               "result from personality fn must be a concrete value");
+
+        auto *sui = dyn_cast_or_null<SearchPhaseUnwindingInformation>(
+            state.unwindingInformation.get());
+        assert(sui && "return from personality function outside of "
+                      "search phase unwinding");
+
+        // unbind the MO we used to pass the serialized landingpad
+        state.addressSpace.unbindObject(sui->serializedLandingpad);
+        sui->serializedLandingpad = nullptr;
+
+        if (result->isZero()) {
+          // this lpi doesn't handle the exception, continue the search
+          unwindToNextLandingpad(state);
+        } else {
+          // a clause (or a catch-all clause or filter clause) matches:
+          // remember the stack index and switch to cleanup phase
+          state.unwindingInformation =
+              std::make_unique<CleanupPhaseUnwindingInformation>(
+                  sui->exceptionObject, cast<ConstantExpr>(result),
+                  sui->unwindingProgress);
+          // this pointer is now invalidated
+          sui = nullptr;
+          // continue the unwinding process (which will now start with the
+          // cleanup phase)
+          unwindToNextLandingpad(state);
+        }
+
+        // never return normally from the personality fn
+        break;
+      }
+
       if (!isVoidReturn) {
         Type *t = caller->getType();
         if (t != Type::getVoidTy(i->getContext())) {
@@ -2858,6 +3145,79 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) {
     // instructions.
     terminateStateOnExecError(state, "Unexpected ShuffleVector instruction");
     break;
+
+  case Instruction::Resume: {
+    auto *cui = dyn_cast_or_null<CleanupPhaseUnwindingInformation>(
+        state.unwindingInformation.get());
+
+    if (!cui) {
+      terminateStateOnExecError(
+          state,
+          "resume-instruction executed outside of cleanup phase unwinding");
+      break;
+    }
+
+    ref<Expr> arg = eval(ki, 0, state).value;
+    ref<Expr> expPtr = ExtractExpr::create(arg, 0, Expr::Int64);
+    ref<Expr> selector = ExtractExpr::create(arg, Expr::Int64, Expr::Int32);
+
+    if (!Expr::createIsZero(selector)->isTrue()) {
+      llvm::errs() << "resume-instruction called with non-0 selector value '"
+                   << selector << "'\n";
+    }
+
+    if (!EqExpr::create(expPtr, cui->exceptionObject)->isTrue()) {
+      terminateStateOnExecError(
+          state, "resume-instruction called with unexpected exception pointer");
+      break;
+    }
+
+    unwindToNextLandingpad(state);
+    break;
+  }
+
+  case Instruction::LandingPad: {
+    auto *cui = dyn_cast_or_null<CleanupPhaseUnwindingInformation>(
+        state.unwindingInformation.get());
+
+    if (!cui) {
+      terminateStateOnExecError(
+          state, "Executing landing pad but not in unwinding phase 2");
+      break;
+    }
+
+    ref<ConstantExpr> exceptionPointer = cui->exceptionObject;
+    ref<ConstantExpr> selectorValue;
+
+    // check on which frame we are currently
+    if (state.stack.size() - 1 == cui->catchingStackIndex) {
+      // we are in the target stack frame, return the selector value
+      // that was returned by the personality fn in phase 1 and stop unwinding.
+      selectorValue = cui->selectorValue;
+
+      // stop unwinding by cleaning up our unwinding information.
+      state.unwindingInformation.reset();
+
+      // this would otherwise now be a dangling pointer
+      cui = nullptr;
+    } else {
+      // we are not yet at the target stack frame. the landingpad might have
+      // a cleanup clause or not, anyway, we give it the selector value "0",
+      // which represents a cleanup, and expect it to handle it.
+      // This is explicitly allowed by LLVM, see
+      // https://llvm.org/docs/ExceptionHandling.html#id18
+      selectorValue = ConstantExpr::create(0, Expr::Int32);
+    }
+
+    // we have to return a {i8*, i32}
+    ref<Expr> result = ConcatExpr::create(
+        ZExtExpr::create(selectorValue, Expr::Int32), exceptionPointer);
+
+    bindLocal(ki, state, result);
+
+    break;
+  }
+
   case Instruction::AtomicRMW:
     terminateStateOnExecError(state, "Unexpected Atomic instruction, should be "
                                      "lowered by LowerAtomicInstructionPass");
diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h
index 25a874cd..30d1dd92 100644
--- a/lib/Core/Executor.h
+++ b/lib/Core/Executor.h
@@ -42,6 +42,7 @@ namespace llvm {
   class BasicBlock;
   class BranchInst;
   class CallInst;
+  class LandingPadInst;
   class Constant;
   class ConstantExpr;
   class Function;
@@ -109,7 +110,9 @@ public:
     ReadOnly,
     ReportError,
     User,
-    Unhandled
+    UncaughtException,
+    UnexpectedException,
+    Unhandled,
   };
 
   /// The random number generator.
@@ -219,6 +222,12 @@ private:
   /// `nullptr` if merging is disabled
   MergingSearcher *mergingSearcher = nullptr;
 
+  /// Typeids used during exception handling
+  std::vector<ref<Expr>> eh_typeids;
+
+  /// Return the typeid corresponding to a certain `type_info`
+  ref<ConstantExpr> getEhTypeidFor(ref<Expr> type_info);
+
   llvm::Function* getTargetFunction(llvm::Value *calledVal,
                                     ExecutionState &state);
   
@@ -304,7 +313,17 @@ private:
   void executeFree(ExecutionState &state,
                    ref<Expr> address,
                    KInstruction *target = 0);
-  
+
+  /// Serialize a landingpad instruction so it can be handled by the
+  /// libcxxabi-runtime
+  MemoryObject *serializeLandingpad(ExecutionState &state,
+                                    const llvm::LandingPadInst &lpi,
+                                    bool &stateTerminated);
+
+  /// Unwind the given state until it hits a landingpad. This is used
+  /// for exception handling.
+  void unwindToNextLandingpad(ExecutionState &state);
+
   void executeCall(ExecutionState &state, 
                    KInstruction *ki,
                    llvm::Function *f,
diff --git a/lib/Core/SpecialFunctionHandler.cpp b/lib/Core/SpecialFunctionHandler.cpp
index 57f059a4..a36d4324 100644
--- a/lib/Core/SpecialFunctionHandler.cpp
+++ b/lib/Core/SpecialFunctionHandler.cpp
@@ -15,6 +15,7 @@
 #include "MemoryManager.h"
 #include "MergeHandler.h"
 #include "Searcher.h"
+#include "StatsTracker.h"
 #include "TimingSolver.h"
 
 #include "klee/Module/KInstruction.h"
@@ -26,6 +27,7 @@
 
 #include "llvm/ADT/Twine.h"
 #include "llvm/IR/DataLayout.h"
+#include "llvm/IR/Instructions.h"
 #include "llvm/IR/Module.h"
 
 #include <errno.h>
@@ -112,6 +114,7 @@ static SpecialFunctionHandler::HandlerInfo handlerInfo[] = {
   add("malloc", handleMalloc, true),
   add("memalign", handleMemalign, true),
   add("realloc", handleRealloc, true),
+  add("_klee_eh_Unwind_RaiseException_impl", handleEhUnwindRaiseExceptionImpl, false),
 
   // operator delete[](void*)
   add("_ZdaPv", handleDeleteArray, false),
@@ -136,6 +139,7 @@ static SpecialFunctionHandler::HandlerInfo handlerInfo[] = {
   add("__ubsan_handle_sub_overflow", handleSubOverflow, false),
   add("__ubsan_handle_mul_overflow", handleMulOverflow, false),
   add("__ubsan_handle_divrem_overflow", handleDivRemOverflow, false),
+  add("klee_eh_typeid_for", handleEhTypeid, true),
 
 #undef addDNR
 #undef add
@@ -453,6 +457,52 @@ void SpecialFunctionHandler::handleMemalign(ExecutionState &state,
                         alignment);
 }
 
+void SpecialFunctionHandler::handleEhUnwindRaiseExceptionImpl(
+    ExecutionState &state, KInstruction *target,
+    std::vector<ref<Expr>> &arguments) {
+  if (arguments.size() != 1) {
+    executor.terminateStateOnError(
+        state,
+        "Internal Error: Incorrect number of arguments to "
+        "_klee_eh_Unwind_RaiseException_impl, "
+        "should not happen",
+        Executor::Unhandled);
+    return;
+  }
+
+  ref<ConstantExpr> exceptionObject = dyn_cast<ConstantExpr>(arguments[0]);
+  if (!exceptionObject.get()) {
+    executor.terminateStateOnError(state,
+                                   "Internal error: Symbolic exception pointer",
+                                   Executor::Unhandled);
+    return;
+  }
+
+  if (isa_and_nonnull<SearchPhaseUnwindingInformation>(
+          state.unwindingInformation)) {
+    executor.terminateStateOnError(
+        state,
+        "Internal error: Unwinding restarted during an ongoing search phase",
+        Executor::Unhandled);
+    return;
+  }
+
+  state.unwindingInformation =
+      std::make_unique<SearchPhaseUnwindingInformation>(exceptionObject,
+                                                        state.stack.size() - 1);
+
+  executor.unwindToNextLandingpad(state);
+}
+
+void SpecialFunctionHandler::handleEhTypeid(ExecutionState &state,
+                                            KInstruction *target,
+                                            std::vector<ref<Expr>> &arguments) {
+  assert(arguments.size() == 1 &&
+         "invalid number of arguments to klee_eh_typeid_for");
+
+  executor.bindLocal(target, state, executor.getEhTypeidFor(arguments[0]));
+}
+
 void SpecialFunctionHandler::handleAssume(ExecutionState &state,
                             KInstruction *target,
                             std::vector<ref<Expr> > &arguments) {
diff --git a/lib/Core/SpecialFunctionHandler.h b/lib/Core/SpecialFunctionHandler.h
index 0ad04049..3db650d7 100644
--- a/lib/Core/SpecialFunctionHandler.h
+++ b/lib/Core/SpecialFunctionHandler.h
@@ -119,6 +119,8 @@ namespace klee {
     HANDLER(handleMakeSymbolic);
     HANDLER(handleMalloc);
     HANDLER(handleMemalign);
+    HANDLER(handleEhUnwindRaiseExceptionImpl);
+    HANDLER(handleEhTypeid);
     HANDLER(handleMarkGlobal);
     HANDLER(handleOpenMerge);
     HANDLER(handleCloseMerge);
diff --git a/lib/Module/IntrinsicCleaner.cpp b/lib/Module/IntrinsicCleaner.cpp
index 030a75ca..cd93b3ec 100644
--- a/lib/Module/IntrinsicCleaner.cpp
+++ b/lib/Module/IntrinsicCleaner.cpp
@@ -364,7 +364,6 @@ bool IntrinsicCleanerPass::runOnBasicBlock(BasicBlock &b, Module &M) {
 #if LLVM_VERSION_CODE >= LLVM_VERSION(7, 0)
       case Intrinsic::dbg_label:
 #endif
-      case Intrinsic::eh_typeid_for:
       case Intrinsic::exp2:
       case Intrinsic::exp:
       case Intrinsic::expect:
@@ -400,6 +399,11 @@ bool IntrinsicCleanerPass::runOnBasicBlock(BasicBlock &b, Module &M) {
         dirty = true;
         break;
 
+      case Intrinsic::eh_typeid_for: {
+        // Don't lower this, we need it for exception handling
+        break;
+      }
+
         // Warn about any unrecognized intrinsics.
       default: {
         const Function *Callee = ii->getCalledFunction();
diff --git a/lib/Module/ModuleUtil.cpp b/lib/Module/ModuleUtil.cpp
index f4cc3048..2316ef45 100644
--- a/lib/Module/ModuleUtil.cpp
+++ b/lib/Module/ModuleUtil.cpp
@@ -203,41 +203,69 @@ klee::linkModules(std::vector<std::unique_ptr<llvm::Module>> &modules,
     return nullptr;
   }
 
-  while (true) {
+  auto containsUsedSymbols = [](const llvm::Module *module) {
+    GlobalValue *GV =
+        dyn_cast_or_null<GlobalValue>(module->getNamedValue("llvm.used"));
+    if (!GV)
+      return false;
+    KLEE_DEBUG_WITH_TYPE("klee_linker", dbgs() << "Used attribute in "
+                                               << module->getModuleIdentifier()
+                                               << '\n');
+    return true;
+  };
+
+  for (auto &module : modules) {
+    if (!module || !containsUsedSymbols(module.get()))
+      continue;
+    if (!linkTwoModules(composite.get(), std::move(module), errorMsg)) {
+      // Linking failed
+      errorMsg = "Linking module containing '__attribute__((used))'"
+                 " symbols with composite failed:" +
+                 errorMsg;
+      return nullptr;
+    }
+    module = nullptr;
+  }
+
+  bool symbolsLinked = true;
+  while (symbolsLinked) {
+    symbolsLinked = false;
     std::set<std::string> undefinedSymbols;
     GetAllUndefinedSymbols(composite.get(), undefinedSymbols);
+    auto hasRequiredDefinition = [&undefinedSymbols](
+                                     const llvm::Module *module) {
+      for (auto symbol : undefinedSymbols) {
+        GlobalValue *GV =
+            dyn_cast_or_null<GlobalValue>(module->getNamedValue(symbol));
+        if (GV && !GV->isDeclaration()) {
+          KLEE_DEBUG_WITH_TYPE("klee_linker",
+                               dbgs() << "Found " << GV->getName() << " in "
+                                      << module->getModuleIdentifier() << "\n");
+          return true;
+        }
+      }
+      return false;
+    };
 
     // Stop in nothing is undefined
     if (undefinedSymbols.empty())
       break;
 
-    bool merged = false;
     for (auto &module : modules) {
       if (!module)
         continue;
 
-      for (auto symbol : undefinedSymbols) {
-        GlobalValue *GV =
-            dyn_cast_or_null<GlobalValue>(module->getNamedValue(symbol));
-        if (!GV || GV->isDeclaration())
-          continue;
+      if (!hasRequiredDefinition(module.get()))
+        continue;
 
-        // Found symbol, therefore merge in module
-        KLEE_DEBUG_WITH_TYPE("klee_linker",
-                             dbgs() << "Found " << GV->getName() << " in "
-                                    << module->getModuleIdentifier() << "\n");
-        if (linkTwoModules(composite.get(), std::move(module), errorMsg)) {
-          module = nullptr;
-          merged = true;
-          break;
-        }
+      if (!linkTwoModules(composite.get(), std::move(module), errorMsg)) {
         // Linking failed
         errorMsg = "Linking archive module with composite failed:" + errorMsg;
         return nullptr;
       }
+      module = nullptr;
+      symbolsLinked = true;
     }
-    if (!merged)
-      break;
   }
 
   // Condense the module array
diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt
index e3619f3d..8e7e7226 100644
--- a/runtime/CMakeLists.txt
+++ b/runtime/CMakeLists.txt
@@ -32,6 +32,12 @@ else()
   set(BUILD_POSIX_RUNTIME 0)
 endif()
 
+if (ENABLE_KLEE_LIBCXX)
+  set(BUILD_KLEE_EH_CXX 1)
+else()
+  set(BUILD_KLEE_EH_CXX 0)
+endif()
+
 # Configure the bitcode build system
 configure_file("Makefile.cmake.bitcode.config.in"
   "Makefile.cmake.bitcode.config"
@@ -48,6 +54,9 @@ set(BITCODE_LIBRARIES "Intrinsic" "klee-libc" "FreeStanding")
 if (ENABLE_POSIX_RUNTIME)
   list(APPEND BITCODE_LIBRARIES "POSIX")
 endif()
+if (ENABLE_KLEE_LIBCXX)
+  list(APPEND BITCODE_LIBRARIES "klee-eh-cxx")
+endif()
 foreach (bl ${BITCODE_LIBRARIES})
   configure_file("${bl}/Makefile.cmake.bitcode"
     "${CMAKE_CURRENT_BINARY_DIR}/${bl}/Makefile.cmake.bitcode"
@@ -136,6 +145,11 @@ if (ENABLE_POSIX_RUNTIME)
     "${KLEE_RUNTIME_DIRECTORY}/libkleeRuntimePOSIX.bca")
 endif()
 
+if (ENABLE_KLEE_LIBCXX)
+  list(APPEND RUNTIME_FILES_TO_INSTALL
+    "${KLEE_RUNTIME_DIRECTORY}/libklee-eh-cxx.bca")
+endif()
+
 install(FILES
   ${RUNTIME_FILES_TO_INSTALL}
   DESTINATION "${KLEE_INSTALL_RUNTIME_DIR}")
diff --git a/runtime/Makefile.cmake.bitcode b/runtime/Makefile.cmake.bitcode
index 03da435e..1dfdfde6 100644
--- a/runtime/Makefile.cmake.bitcode
+++ b/runtime/Makefile.cmake.bitcode
@@ -17,4 +17,8 @@ ifneq ($(ENABLE_POSIX_RUNTIME),0)
 	DIRS += POSIX
 endif
 
+ifneq ($(BUILD_KLEE_EH_CXX),0)
+	DIRS += klee-eh-cxx
+endif
+
 include $(LEVEL)/Makefile.cmake.bitcode.rules
diff --git a/runtime/Makefile.cmake.bitcode.config.in b/runtime/Makefile.cmake.bitcode.config.in
index 5916d79a..4e2354b8 100644
--- a/runtime/Makefile.cmake.bitcode.config.in
+++ b/runtime/Makefile.cmake.bitcode.config.in
@@ -11,6 +11,7 @@
 #
 #===------------------------------------------------------------------------===#
 LLVMCC := @LLVMCC@
+LLVMCXX := @LLVMCXX@
 LLVM_LINK := @LLVM_LINK@
 LLVM_AR := @LLVM_AR@
 LLVM_VERSION_MAJOR := @LLVM_VERSION_MAJOR@
@@ -31,6 +32,10 @@ RUNTIME_CONFIG_STRING := @KLEE_RUNTIME_BUILD_TYPE@
 
 # Optional features
 ENABLE_POSIX_RUNTIME := @BUILD_POSIX_RUNTIME@
+BUILD_KLEE_CXXABI := @BUILD_KLEE_CXXABI@
+KLEE_LIBCXX_INCLUDE_DIR := @KLEE_LIBCXX_INCLUDE_DIR@
+KLEE_LIBCXXABI_DIR := @KLEE_LIBCXXABI_DIR@
+KLEE_INCLUDE_DIR := @CMAKE_SOURCE_DIR@/include
 
 # Commands
 MKDIR := mkdir
diff --git a/runtime/klee-eh-cxx/Makefile.cmake.bitcode b/runtime/klee-eh-cxx/Makefile.cmake.bitcode
new file mode 100644
index 00000000..f29b947d
--- /dev/null
+++ b/runtime/klee-eh-cxx/Makefile.cmake.bitcode
@@ -0,0 +1,13 @@
+include ../Makefile.cmake.bitcode.config
+
+SRC_DIR=$(ROOT_SRC)/klee-eh-cxx
+
+ARCHIVE_FILE=$(ARCHIVE_DEST)/libklee-eh-cxx.bca
+
+all: $(ARCHIVE_FILE)
+
+klee_eh_cxx.bc: $(SRC_DIR)/klee_eh_cxx.cpp
+	$(LLVMCXX) -nostdinc++ -emit-llvm -c -I$(KLEE_INCLUDE_DIR) -I $(KLEE_LIBCXXABI_DIR)/include -I $(KLEE_LIBCXXABI_DIR)/src -I $(KLEE_LIBCXX_INCLUDE_DIR) $(SRC_DIR)/klee_eh_cxx.cpp -o $@
+
+$(ARCHIVE_FILE): klee_eh_cxx.bc
+	$(LLVM_AR) rcs $@ $<
diff --git a/runtime/klee-eh-cxx/klee_eh_cxx.cpp b/runtime/klee-eh-cxx/klee_eh_cxx.cpp
new file mode 100644
index 00000000..8eafac2c
--- /dev/null
+++ b/runtime/klee-eh-cxx/klee_eh_cxx.cpp
@@ -0,0 +1,218 @@
+#include <cassert>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <typeinfo>
+
+#include <unwind.h>
+
+#include <klee/klee.h>
+
+// from libcxxabi
+#include <cxa_exception.h>
+#include <private_typeinfo.h>
+
+// implemented in the SpecialFunctionHandler
+extern "C" std::uint32_t klee_eh_typeid_for(const void *);
+
+// we use some internals of libcxxabi, so we make our life easier here
+using __shim_type_info = __cxxabiv1::__shim_type_info;
+using __cxa_exception = __cxxabiv1::__cxa_exception;
+
+namespace {
+// Used to determine whether a catch-clause can catch an exception
+bool _klee_eh_can_catch(const void *catcherType,
+                        const __shim_type_info *exceptionType,
+                        void *&adjustedPtr) {
+  return static_cast<const __shim_type_info *>(catcherType)
+      ->can_catch(exceptionType, adjustedPtr);
+}
+
+// Some utility functions to convert between the different exception
+// representations. These are mostly copied from libcxxabi, except
+// for small adjustments so they work in our code as well.
+
+// Copied from libcxxabi/cxa_exception.cpp (including comments)
+inline
+__cxa_exception*
+cxa_exception_from_thrown_object(void* thrown_object)
+{
+    return static_cast<__cxa_exception*>(thrown_object) - 1;
+}
+
+// Copied from libcxxabi/cxa_exception.cpp (including comments)
+//
+//  Get the exception object from the unwind pointer.
+//  Relies on the structure layout, where the unwind pointer is right in
+//  front of the user's exception object
+inline
+__cxa_exception*
+cxa_exception_from_exception_unwind_exception(_Unwind_Exception* unwind_exception)
+{
+  return cxa_exception_from_thrown_object(unwind_exception + 1);
+}
+
+// Copied from libcxxabi/cxa_personality.cpp (including comments)
+void*
+get_thrown_object_ptr(_Unwind_Exception* unwind_exception)
+{
+  // added for usage inside KLEE (i.e., outside of libcxxabi)
+  using namespace __cxxabiv1;
+
+  // Even for foreign exceptions, the exception object is *probably* at unwind_exception + 1
+  //    Regardless, this library is prohibited from touching a foreign exception
+  void* adjustedPtr = unwind_exception + 1;
+  if (__getExceptionClass(unwind_exception) == kOurDependentExceptionClass)
+      adjustedPtr = ((__cxa_dependent_exception*)adjustedPtr - 1)->primaryException;
+  return adjustedPtr;
+}
+} // namespace
+
+// Our implementation of a personality function for handling
+// libcxxabi-exceptions. Follows how libcxxabi's __gxx_personality_v0 handles
+// exceptions.
+[[gnu::used]] extern "C" std::int32_t
+_klee_eh_cxx_personality(_Unwind_Exception *exceptionPointer,
+                         const std::size_t num_bytes,
+                         const unsigned char *lp_clauses) {
+  assert(__cxxabiv1::__isOurExceptionClass(exceptionPointer) &&
+         "unexpected exception class found");
+
+  __cxa_exception *exception =
+      cxa_exception_from_exception_unwind_exception(exceptionPointer);
+  const __shim_type_info *exceptionType =
+      static_cast<const __shim_type_info *>(exception->exceptionType);
+  void *const exceptionObjectPtr = get_thrown_object_ptr(exceptionPointer);
+
+  const unsigned char *cur_pos = lp_clauses;
+  const unsigned char *lp_clauses_end = lp_clauses + num_bytes;
+
+  while (cur_pos < lp_clauses_end) {
+    const unsigned char type = *cur_pos;
+    cur_pos += 1;
+    if (type == 0) { // catch-clause
+      const void *catcherType = *reinterpret_cast<const void *const *>(cur_pos);
+      cur_pos += sizeof(const void *);
+      if (catcherType == NULL) {
+        // catch-all clause
+        exception->adjustedPtr = exceptionObjectPtr;
+        return klee_eh_typeid_for(catcherType);
+      }
+      void *adjustedPtr = exceptionObjectPtr;
+      if (_klee_eh_can_catch(catcherType, exceptionType, adjustedPtr)) {
+        exception->adjustedPtr = adjustedPtr;
+        return klee_eh_typeid_for(catcherType);
+      }
+    } else { // filter-clause
+      unsigned char num_filters = type - 1;
+      while (num_filters > 0) {
+        const void *catcher = *reinterpret_cast<const void *const *>(cur_pos);
+        cur_pos += sizeof(const void *);
+        if (catcher == NULL) {
+          // catch-all filter clause, should always be entered
+          exception->adjustedPtr = exceptionObjectPtr;
+          return -1;
+        }
+        void *adjustedPtr = exceptionObjectPtr;
+        if (_klee_eh_can_catch(catcher, exceptionType, adjustedPtr)) {
+          exception->adjustedPtr = adjustedPtr;
+          break;
+        }
+        num_filters -= 1;
+      }
+      if (num_filters == 0) {
+        // exception was not one of the given ones -> filter it out
+        return -1;
+      }
+    }
+  }
+
+  // no handling clause found
+  return 0;
+}
+
+// Implemented in the SpecialFunctionHandler
+extern "C" void _klee_eh_Unwind_RaiseException_impl(void *);
+
+// Provide an implementation of the Itanium unwinding base-API
+// (https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#base-abi)
+// These functions will be called by language-specific exception handling
+// implementations, such as libcxxabi.
+extern "C" {
+// The main entry point for stack unwinding. This function performs the 2-phase
+// unwinding process, calling the personality function as required. We implement
+// this mostly inside KLEE as it requires direct manipulation of the state's
+// stack.
+_Unwind_Reason_Code
+_Unwind_RaiseException(struct _Unwind_Exception *exception_object) {
+  // this will either unwind the stack if a handler is found, or return if not
+  _klee_eh_Unwind_RaiseException_impl(exception_object);
+
+  // if we couldn't find a handler, return an error
+  return _URC_END_OF_STACK;
+}
+
+// According to the Itanium ABI:
+// "Deletes the given exception object. If a given runtime resumes normal
+// execution after catching a foreign exception, it will not know how to delete
+// that exception. Such an exception will be deleted by calling
+// _Unwind_DeleteException. This is a convenience function that calls the
+// function pointed to by the exception_cleanup field of the exception header."
+void _Unwind_DeleteException(struct _Unwind_Exception *exception_object) {
+  // Implemented in the same way as LLVM's libunwind version
+  if (exception_object->exception_cleanup != NULL)
+    exception_object->exception_cleanup(_URC_FOREIGN_EXCEPTION_CAUGHT,
+                                        exception_object);
+}
+
+// At the LLVM IR level we will encounter the `resume`-Instruction instead of
+// calls to this function. LLVM lowers `resume` to a call to `_Unwind_Resume`
+// during later compilation stages.
+void _Unwind_Resume(struct _Unwind_Exception *exception_object) {
+  klee_report_error(__FILE__, __LINE__, "_Unwind_Resume should not appear",
+                    "unexpected.err");
+}
+
+// The rest of these assume a binary execution environment and thus aren't
+// supported. While ForcedUnwind could be called by users, it is never actually
+// called inside of libcxxabi. The others are usually called by the personality
+// function, but we implement that ourselves.
+_Unwind_Reason_Code
+_Unwind_ForcedUnwind(struct _Unwind_Exception *exception_object,
+                     _Unwind_Stop_Fn stop, void *stop_parameter) {
+  klee_report_error(__FILE__, __LINE__, "_Unwind_ForcedUnwind not supported",
+                    "unsupported.err");
+}
+
+_Unwind_Word _Unwind_GetGR(struct _Unwind_Context *context, int index) {
+  klee_report_error(__FILE__, __LINE__, "_Unwind_GetGR not supported",
+                    "unsupported.err");
+}
+
+void _Unwind_SetGR(struct _Unwind_Context *context, int index,
+                   _Unwind_Word new_value) {
+  klee_report_error(__FILE__, __LINE__, "_Unwind_SetGR not supported",
+                    "unsupported.err");
+}
+
+_Unwind_Word _Unwind_GetIP(struct _Unwind_Context *context) {
+  klee_report_error(__FILE__, __LINE__, "_Unwind_GetIP not supported",
+                    "unsupported.err");
+}
+
+void _Unwind_SetIP(struct _Unwind_Context *context, _Unwind_Word new_value) {
+  klee_report_error(__FILE__, __LINE__, "_Unwind_SetIP not unsupported",
+                    "unsupported.err");
+}
+
+void *_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) {
+  klee_report_error(__FILE__, __LINE__,
+                    "_Unwind_GetLanguageSpecificData not supported",
+                    "unsupported.err");
+}
+
+_Unwind_Ptr _Unwind_GetRegionStart(struct _Unwind_Context *context) {
+  klee_report_error(__FILE__, __LINE__, "_Unwind_GetRegionStart not supported",
+                    "unsupported.err");
+}
+}
\ No newline at end of file
diff --git a/test/CXX/symex/basic_c++/diamond.cpp b/test/CXX/symex/basic_c++/diamond.cpp
new file mode 100644
index 00000000..479a9090
--- /dev/null
+++ b/test/CXX/symex/basic_c++/diamond.cpp
@@ -0,0 +1,45 @@
+// REQUIRES: uclibc
+// RUN: %clangxx %s -emit-llvm %O0opt -c -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc %t.bc 2>&1 | FileCheck %s
+
+#include <cstdio>
+
+struct A {
+  int a;
+  A() {
+    printf("A constructor\n");
+  }
+};
+
+struct B : virtual A {
+  int b;
+  B() {
+    printf("B constructor\n");
+  }
+};
+
+struct C : virtual A {
+  int c;
+  C() {
+    printf("C constructor\n");
+  }
+};
+
+struct D : B, C {
+  int d;
+};
+
+int main(int argc, char **args) {
+  D x;
+  // CHECK: A constructor
+  // CHECK: B constructor
+  // CHECK: C constructor
+
+  x.a = 7;
+  B *y = &x;
+  printf("B::a = %d\n", y->a);
+  // CHECK: B::a = 7
+
+  return 0;
+}
diff --git a/test/CXX/symex/basic_c++/inheritance.cpp b/test/CXX/symex/basic_c++/inheritance.cpp
new file mode 100644
index 00000000..88a0d150
--- /dev/null
+++ b/test/CXX/symex/basic_c++/inheritance.cpp
@@ -0,0 +1,35 @@
+// REQUIRES: uclibc
+// RUN: %clangxx %s -emit-llvm %O0opt -c -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc %t.bc 2>&1 | FileCheck %s
+
+#include <cstdio>
+class Testclass {
+public:
+  Testclass() {
+    printf("Testclass constructor\n");
+  }
+  int a() {
+    return 1;
+  }
+};
+class Derivedclass : public Testclass {
+public:
+  Derivedclass() {
+    printf("Derivedclass constructor\n");
+  }
+  int b() {
+    return 2;
+  }
+};
+
+int main(int argc, char **args) {
+  Derivedclass x;
+  // CHECK: Testclass constructor
+  // CHECK: Derivedclass constructor
+
+  printf("%d / %d", x.a(), x.b());
+  // CHECK: 1 / 2
+
+  return x.a() + x.b();
+}
diff --git a/test/CXX/symex/basic_c++/lambda.cpp b/test/CXX/symex/basic_c++/lambda.cpp
new file mode 100644
index 00000000..e7f8f971
--- /dev/null
+++ b/test/CXX/symex/basic_c++/lambda.cpp
@@ -0,0 +1,20 @@
+// REQUIRES: uclibc
+// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc %t.bc 2>&1 | FileCheck %s
+
+#include <cstdio>
+
+int main(int argc, char **args) {
+  int x = 0;
+  auto y = [&](int h) { return x + h; };
+  auto z = [=](int h) { return x + h; };
+  x = 1;
+
+  printf("y(0) == %d\n", y(0));
+  // CHECK: y(0) == 1
+  printf("z(0) == %d\n", z(0));
+  // CHECK: z(0) == 0
+
+  return 0;
+}
diff --git a/test/CXX/symex/basic_c++/namespace.cpp b/test/CXX/symex/basic_c++/namespace.cpp
new file mode 100644
index 00000000..939e9bcd
--- /dev/null
+++ b/test/CXX/symex/basic_c++/namespace.cpp
@@ -0,0 +1,18 @@
+// REQUIRES: uclibc
+// RUN: %clangxx %s -emit-llvm %O0opt -c -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc %t.bc 2>&1 | FileCheck %s
+
+#include <cstdio>
+
+namespace test {
+int a() {
+  return 2;
+}
+} // namespace test
+
+int main(int argc, char **args) {
+  printf("test::a() == %d\n", test::a());
+  //CHECK: test::a() == 2
+  return 0;
+}
diff --git a/test/CXX/symex/basic_c++/reinterpret_cast.cpp b/test/CXX/symex/basic_c++/reinterpret_cast.cpp
new file mode 100644
index 00000000..6883226b
--- /dev/null
+++ b/test/CXX/symex/basic_c++/reinterpret_cast.cpp
@@ -0,0 +1,26 @@
+// REQUIRES: uclibc
+// RUN: %clangxx %s -emit-llvm %O0opt -c -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc %t.bc 2>&1 | FileCheck %s
+
+#include <cstdio>
+
+int main(int argc, char **args) {
+  unsigned int *x = new unsigned int(0);
+  char *y = reinterpret_cast<char *>(x);
+
+  printf("address(x) == %p\n", x);
+  printf("address(y) == %p\n", y);
+  // CHECK: address(x) == [[POINTER_X:0x[a-fA-F0-9]+]]
+  // CHECK: address(y) == [[POINTER_X]]
+
+  printf("first x == 0x%08X\n", *x);
+  // CHECK: first x == 0x00000000
+  y[3] = 0xAB;
+  y[1] = 0x78;
+  printf("second x == 0x%08X\n", *x);
+  // CHECK: second x == 0xAB007800
+
+  delete x;
+  return 0;
+}
diff --git a/test/CXX/symex/basic_c++/simple.cpp b/test/CXX/symex/basic_c++/simple.cpp
new file mode 100644
index 00000000..f66dbd1e
--- /dev/null
+++ b/test/CXX/symex/basic_c++/simple.cpp
@@ -0,0 +1,33 @@
+// REQUIRES: uclibc
+// RUN: %clangxx %s -emit-llvm %O0opt -c -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc %t.bc 2>&1 | FileCheck %s
+
+// CHECK: KLEE: done: completed paths = 5
+
+#include "klee/klee.h"
+
+class Test {
+  int x;
+
+public:
+  Test(int _x) : x(_x) {}
+
+  int test() {
+    if (x % 2 == 0)
+      return 0;
+    if (x % 3 == 0)
+      return 1;
+    if (x % 5 == 0)
+      return 2;
+    if (x % 7 == 0)
+      return 3;
+    return 4;
+  }
+};
+
+int main(int argc, char **args) {
+  Test a(klee_int("Test"));
+
+  return a.test();
+}
diff --git a/test/CXX/symex/basic_c++/templates.cpp b/test/CXX/symex/basic_c++/templates.cpp
new file mode 100644
index 00000000..c5abb983
--- /dev/null
+++ b/test/CXX/symex/basic_c++/templates.cpp
@@ -0,0 +1,28 @@
+// REQUIRES: uclibc
+// RUN: %clangxx %s -emit-llvm %O0opt -c -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc %t.bc 2>&1
+
+#include <cassert>
+
+template <typename X, typename Y>
+X multiply(X a, Y b) {
+  return a * b;
+}
+
+template <int A>
+int add(int summand) {
+  return A + summand;
+}
+
+int main(int argc, char **args) {
+  assert(add<7>(3) == 10);
+
+  assert(multiply(7, 3) == 21);
+
+  // Check if the float arguments are handled correctly
+  assert(multiply(7.1f, 3) > 21);
+  assert(multiply(3, 7.1f) == 21);
+
+  return 0;
+}
diff --git a/test/CXX/symex/basic_c++/virtual.cpp b/test/CXX/symex/basic_c++/virtual.cpp
new file mode 100644
index 00000000..b4850992
--- /dev/null
+++ b/test/CXX/symex/basic_c++/virtual.cpp
@@ -0,0 +1,35 @@
+// REQUIRES: uclibc
+// RUN: %clangxx %s -emit-llvm %O0opt -c -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --exit-on-error --libc=uclibc %t.bc 2>&1
+
+#include <cassert>
+
+class Testclass {
+public:
+  virtual int vtl() {
+    return 1;
+  }
+};
+
+class Derivedclass : public Testclass {
+};
+
+class Overridingclass : public Derivedclass {
+public:
+  int vtl() override {
+    return 2;
+  }
+};
+
+int main(int argc, char **args) {
+  Testclass x;
+  Derivedclass y;
+  Overridingclass z;
+
+  assert(x.vtl() == 1);
+  assert(y.vtl() == 1);
+  assert(z.vtl() == 2);
+
+  return 0;
+}
diff --git a/test/CXX/symex/libc++/atexit.cpp b/test/CXX/symex/libc++/atexit.cpp
index 03b559ae..fa8df475 100644
--- a/test/CXX/symex/libc++/atexit.cpp
+++ b/test/CXX/symex/libc++/atexit.cpp
@@ -1,19 +1,20 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
 // REQUIRES: libcxx
 // REQUIRES: uclibc
 // RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
 // RUN: rm -rf %t.klee-out
-// RUN: %klee --output-dir=%t.klee-out --exit-on-error --libc=uclibc --libcxx %t1.bc | FileCheck %s
-//
-// CHECK: Returning from main
-// CHECK: atexit_1
-// CHECK: atexit_2
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
 
-#include <iostream>
 #include <cstdlib>
+#include <iostream>
 
-int main(int argc, char** args){
-  auto atexithandler2 = std::atexit([](){std::cout << "atexit_2" << std::endl;});
-  auto atexithandler1 = std::atexit([](){std::cout << "atexit_1" << std::endl;});
+int main(int argc, char **args) {
+  auto atexithandler2 = std::atexit([]() { std::cout << "atexit_2" << std::endl; });
+  auto atexithandler1 = std::atexit([]() { std::cout << "atexit_1" << std::endl; });
   std::cout << "Returning from main" << std::endl;
+  // CHECK: Returning from main
   return 0;
+  // CHECK: atexit_1
+  // CHECK: atexit_2
 }
diff --git a/test/CXX/symex/libc++/can_catch_test.cpp b/test/CXX/symex/libc++/can_catch_test.cpp
new file mode 100644
index 00000000..9d3a2d23
--- /dev/null
+++ b/test/CXX/symex/libc++/can_catch_test.cpp
@@ -0,0 +1,88 @@
+// REQUIRES: uclibc
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -std=c++11 -c -I "%libcxx_include" -g -nostdinc++ -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --exit-on-error --libcxx --libc=uclibc %t.bc 2>&1 | FileCheck %s
+
+#include <cstdio>
+#include <typeinfo>
+
+extern bool _klee_eh_can_catch(const void *catcher, const void *exception);
+
+class other {};
+class yet_another {};
+class ya_derived : private yet_another {};
+
+class TestClass {
+public:
+  const char *classname = "TestClass";
+
+  TestClass() { printf("%s: Normal constructor called\n", classname); }
+  TestClass(const TestClass &other) {
+    printf("%s: Copy constructor called\n", classname);
+  }
+  TestClass(TestClass &&other) {
+    printf("%s: Move constructor called\n", classname);
+  }
+  TestClass &operator=(const TestClass &&other) {
+    printf("%s: Assignment constructor called\n", classname);
+    return *this;
+  }
+  virtual ~TestClass() { printf("%s: Destructor called\n", classname); }
+};
+
+class DerivedClass : public TestClass {
+public:
+  const char *classname = "DerivedClass";
+
+  DerivedClass() { printf("%s: Normal constructor called\n", classname); }
+  DerivedClass(const DerivedClass &other) {
+    printf("%s: Copy constructor called\n", classname);
+  }
+  DerivedClass(DerivedClass &&other) {
+    printf("%s: Move constructor called\n", classname);
+  }
+  DerivedClass &operator=(const DerivedClass &&other) {
+    printf("%s: Assignment constructor called\n", classname);
+    return *this;
+  }
+  ~DerivedClass() { printf("%s: Destructor called\n", classname); }
+};
+
+class SecondDerivedClass : public DerivedClass, other, yet_another {
+public:
+  const char *classname = "SecondDerivedClass";
+
+  SecondDerivedClass() { printf("%s: Normal constructor called\n", classname); }
+  SecondDerivedClass(const SecondDerivedClass &other) {
+    printf("%s: Copy constructor called\n", classname);
+  }
+  SecondDerivedClass(SecondDerivedClass &&other) {
+    printf("%s: Move constructor called\n", classname);
+  }
+  SecondDerivedClass &operator=(const SecondDerivedClass &&other) {
+    printf("%s: Assignment constructor called\n", classname);
+    return *this;
+  }
+  ~SecondDerivedClass() { printf("%s: Destructor called\n", classname); }
+};
+
+int main(int argc, char **args) {
+  try {
+    try {
+      throw SecondDerivedClass();
+    } catch (SecondDerivedClass &p) {
+      puts("DerivedClass caught\n");
+      // CHECK: DerivedClass caught
+      throw;
+    }
+  } catch (TestClass &tc) {
+    puts("TestClass caught\n");
+    // CHECK: TestClass caught
+  } catch (int) {
+  } catch (int *) {
+  } catch (ya_derived &) {
+  }
+
+  return 0;
+}
diff --git a/test/CXX/symex/libc++/catch_recover.cpp b/test/CXX/symex/libc++/catch_recover.cpp
new file mode 100644
index 00000000..44362b25
--- /dev/null
+++ b/test/CXX/symex/libc++/catch_recover.cpp
@@ -0,0 +1,26 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
+
+#include <cstdio>
+#include <stdexcept>
+
+int main(int argc, char **args) {
+  try {
+    throw std::runtime_error("foo");
+  } catch (const std::runtime_error &ex) {
+    printf("In first catch\n");
+    // CHECK-DAG: In first catch
+  }
+  try {
+    throw std::runtime_error("bar");
+  } catch (const std::runtime_error &ex) {
+    printf("In second catch\n");
+    // CHECK-DAG: In second catch
+  }
+}
+
+// CHECK-DAG: KLEE: done: completed paths = 1
diff --git a/test/CXX/symex/libc++/catch_with_adjusted_exception_pointer.cpp b/test/CXX/symex/libc++/catch_with_adjusted_exception_pointer.cpp
new file mode 100644
index 00000000..bfb69be4
--- /dev/null
+++ b/test/CXX/symex/libc++/catch_with_adjusted_exception_pointer.cpp
@@ -0,0 +1,37 @@
+// REQUIRES: uclibc
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -std=c++11 -c -I "%libcxx_include" -g -nostdinc++ -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --exit-on-error --libcxx --libc=uclibc %t.bc 2>&1 | FileCheck %s
+
+#include <iostream>
+
+class Base {
+public:
+  int base_int = 42;
+  virtual void print() {
+    std::cout << "Base\n";
+  }
+};
+
+class Derived : public Base {
+public:
+  virtual void print() {
+    std::cout << "Derived\n";
+  }
+};
+
+int main() {
+  Base *bd = new Derived();
+  // CHECK: Derived
+  bd->print();
+
+  try {
+    throw bd;
+  } catch (Base *b) {
+    // CHECK: Derived
+    b->print();
+    // CHECK: 42
+    std::cout << b->base_int << "\n";
+  }
+}
diff --git a/test/CXX/symex/libc++/cout.cpp b/test/CXX/symex/libc++/cout.cpp
index 0f3c338a..62cd0406 100644
--- a/test/CXX/symex/libc++/cout.cpp
+++ b/test/CXX/symex/libc++/cout.cpp
@@ -1,8 +1,10 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
 // REQUIRES: libcxx
 // REQUIRES: uclibc
 // RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
 // RUN: rm -rf %t.klee-out
-// RUN: %klee --output-dir=%t.klee-out --exit-on-error --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
 
 // CHECK-DAG: cout
 // CHECK-DAG: cerr
diff --git a/test/CXX/symex/libc++/cout_sym.cpp b/test/CXX/symex/libc++/cout_sym.cpp
index 8bf81122..177c3ed7 100644
--- a/test/CXX/symex/libc++/cout_sym.cpp
+++ b/test/CXX/symex/libc++/cout_sym.cpp
@@ -1,22 +1,22 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
 // REQUIRES: libcxx
 // REQUIRES: uclibc
 // RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
 // RUN: rm -rf %t.klee-out
-// RUN: %klee --output-dir=%t.klee-out --exit-on-error --libc=uclibc --libcxx %t1.bc | FileCheck %s
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
 
-// CHECK-DAG: greater
-// CHECK-DAG: lower
-
-
-#include <iostream>
 #include "klee/klee.h"
+#include <iostream>
 
-int main(int argc, char** args){
+int main(int argc, char **args) {
   int x = klee_int("x");
-  if (x > 0){
+  if (x > 0) {
     std::cout << "greater: " << x << std::endl;
+    // CHECK-DAG: greater
   } else {
     std::cout << "lower: " << x << std::endl;
+    // CHECK-DAG: lower
   }
   return 0;
 }
diff --git a/test/CXX/symex/libc++/dynamic_cast.cpp b/test/CXX/symex/libc++/dynamic_cast.cpp
index c2d8b528..a2fc8b82 100644
--- a/test/CXX/symex/libc++/dynamic_cast.cpp
+++ b/test/CXX/symex/libc++/dynamic_cast.cpp
@@ -1,54 +1,52 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
 // REQUIRES: libcxx
 // REQUIRES: uclibc
 // RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
 // RUN: rm -rf %t.klee-out
-// RUN: %klee --output-dir=%t.klee-out --exit-on-error --libc=uclibc --libcxx %t1.bc
-//
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc
+
 // Copied from 'http://en.cppreference.com/w/cpp/language/dynamic_cast'
 
 struct V {
-    virtual void f() {};  // must be polymorphic to use runtime-checked dynamic_cast
+  virtual void f(){}; // must be polymorphic to use runtime-checked dynamic_cast
 };
 struct A : virtual V {};
 struct B : virtual V {
-  B(V* v, A* a) {
+  B(V *v, A *a) {
     // casts during construction (see the call in the constructor of D below)
-    dynamic_cast<B*>(v); // well-defined: v of type V*, V base of B, results in B*
-    dynamic_cast<B*>(a); // undefined behavior: a has type A*, A not a base of B
+    dynamic_cast<B *>(v); // well-defined: v of type V*, V base of B, results in B*
+    dynamic_cast<B *>(a); // undefined behavior: a has type A*, A not a base of B
   }
 };
 struct D : A, B {
-    D() : B((A*)this, this) { }
+  D() : B((A *)this, this) {}
 };
 
 struct Base {
-    virtual ~Base() {}
+  virtual ~Base() {}
 };
 
-struct Derived: Base {
-    virtual void name() {}
+struct Derived : Base {
+  virtual void name() {}
 };
 
-int main()
-{
-    D d; // the most derived object
-    A& a = d; // upcast, dynamic_cast may be used, but unnecessary
-    D& new_d = dynamic_cast<D&>(a); // downcast
-    B& new_b = dynamic_cast<B&>(a); // sidecast
-
+int main() {
+  D d;                             // the most derived object
+  A &a = d;                        // upcast, dynamic_cast may be used, but unnecessary
+  D &new_d = dynamic_cast<D &>(a); // downcast
+  B &new_b = dynamic_cast<B &>(a); // sidecast
 
-    Base* b1 = new Base;
-    if(Derived* d = dynamic_cast<Derived*>(b1))
-    {
-        d->name(); // safe to call
-    }
+  Base *b1 = new Base;
+  if (Derived *d = dynamic_cast<Derived *>(b1)) {
+    d->name(); // safe to call
+  }
 
-    Base* b2 = new Derived;
-    if(Derived* d = dynamic_cast<Derived*>(b2))
-    {
-        d->name(); // safe to call
-    }
+  Base *b2 = new Derived;
+  if (Derived *d = dynamic_cast<Derived *>(b2)) {
+    d->name(); // safe to call
+  }
 
-    delete b1;
-    delete b2;
+  delete b1;
+  delete b2;
 }
diff --git a/test/CXX/symex/libc++/exception.cpp b/test/CXX/symex/libc++/exception.cpp
new file mode 100644
index 00000000..b46d474e
--- /dev/null
+++ b/test/CXX/symex/libc++/exception.cpp
@@ -0,0 +1,33 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
+
+#include "klee/klee.h"
+#include <cstdio>
+
+int main(int argc, char **args) {
+  int x = klee_int("Test");
+
+  try {
+    if (x % 2 == 0) {
+      printf("Normal return\n");
+      // CHECK-DAG: Normal return
+      return 0;
+    }
+    throw x;
+  } catch (int b) {
+    if (b % 3 == 0) {
+      printf("First exceptional return\n");
+      // CHECK-DAG: First exceptional return
+      return 2;
+    }
+    printf("Second exceptional return\n");
+    // CHECK-DAG: Second exceptional return
+    return 3;
+  }
+}
+
+// CHECK-DAG: KLEE: done: completed paths = 3
diff --git a/test/CXX/symex/libc++/exception_inheritance.cpp b/test/CXX/symex/libc++/exception_inheritance.cpp
new file mode 100644
index 00000000..4e42fbb7
--- /dev/null
+++ b/test/CXX/symex/libc++/exception_inheritance.cpp
@@ -0,0 +1,78 @@
+// REQUIRES: uclibc
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -std=c++11 -c -I "%libcxx_include" -g -nostdinc++ -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --exit-on-error --libcxx --libc=uclibc %t.bc 2>&1 | FileCheck %s
+
+#include <cstdio>
+
+class TestClass {
+public:
+  const char *classname = "TestClass";
+
+  TestClass() {
+    printf("%s: Normal constructor called\n", classname);
+  }
+  TestClass(const TestClass &other) {
+    printf("%s: Copy constructor called\n", classname);
+  }
+  TestClass(TestClass &&other) { printf("%s: Move constructor called\n", classname); }
+  TestClass &operator=(const TestClass &&other) {
+    printf("%s: Assignment constructor called\n", classname);
+    return *this;
+  }
+  virtual ~TestClass() { printf("%s: Destructor called\n", classname); }
+};
+
+class DerivedClass : public TestClass {
+public:
+  const char *classname = "DerivedClass";
+
+  DerivedClass() {
+    printf("%s: Normal constructor called\n", classname);
+  }
+  DerivedClass(const DerivedClass &other) {
+    printf("%s: Copy constructor called\n", classname);
+  }
+  DerivedClass(DerivedClass &&other) { printf("%s: Move constructor called\n", classname); }
+  DerivedClass &operator=(const DerivedClass &&other) {
+    printf("%s: Assignment constructor called\n", classname);
+    return *this;
+  }
+  ~DerivedClass() { printf("%s: Destructor called\n", classname); }
+};
+
+class SecondDerivedClass : public DerivedClass {
+public:
+  const char *classname = "SecondDerivedClass";
+
+  SecondDerivedClass() {
+    printf("%s: Normal constructor called\n", classname);
+  }
+  SecondDerivedClass(const SecondDerivedClass &other) {
+    printf("%s: Copy constructor called\n", classname);
+  }
+  SecondDerivedClass(SecondDerivedClass &&other) { printf("%s: Move constructor called\n", classname); }
+  SecondDerivedClass &operator=(const SecondDerivedClass &&other) {
+    printf("%s: Assignment constructor called\n", classname);
+    return *this;
+  }
+  ~SecondDerivedClass() { printf("%s: Destructor called\n", classname); }
+};
+
+int main(int argc, char **args) {
+  try {
+    try {
+      throw SecondDerivedClass();
+    } catch (DerivedClass &p) {
+      puts("DerivedClass caught\n");
+      // CHECK: DerivedClass caught
+      throw;
+    }
+  } catch (TestClass &tc) {
+    puts("TestClass caught\n");
+    // CHECK: TestClass caught
+  }
+
+  return 0;
+}
diff --git a/test/CXX/symex/libc++/general_catch.cpp b/test/CXX/symex/libc++/general_catch.cpp
new file mode 100644
index 00000000..ee7f9983
--- /dev/null
+++ b/test/CXX/symex/libc++/general_catch.cpp
@@ -0,0 +1,60 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
+
+#include "klee/klee.h"
+#include <cassert>
+#include <cstdio>
+
+struct CustomStruct {
+  int a;
+};
+
+int thrower(int x) {
+  if (x == 0) {
+    CustomStruct p;
+    throw p;
+  } else if (x == 1) {
+    throw 7;
+  } else if (x == 2) {
+    throw new CustomStruct();
+  } else {
+    throw &thrower;
+  }
+}
+
+int catcher(int x) {
+  try {
+    thrower(x);
+  } catch (int ex) {
+    printf("Caught int\n");
+    // CHECK-DAG: Caught int
+    return 1;
+  } catch (CustomStruct ex) {
+    printf("Caught normal CustomStruct\n");
+    // CHECK-DAG: Caught normal CustomStruct
+    return 2;
+  } catch (CustomStruct *ex) {
+    printf("Caught pointer CustomStruct\n");
+    // CHECK-DAG: Caught pointer CustomStruct
+    return 3;
+  } catch (...) {
+    printf("Caught general exception\n");
+    // CHECK-DAG: Caught general exception
+    return 4;
+  }
+  // Unreachable instruction
+  assert(0);
+  return 0;
+}
+
+int main(int argc, char **args) {
+  int x = klee_int("x");
+  int res = catcher(x);
+  return res;
+}
+
+// CHECK-DAG: KLEE: done: completed paths = 4
diff --git a/test/CXX/symex/libc++/landingpad.cpp b/test/CXX/symex/libc++/landingpad.cpp
new file mode 100644
index 00000000..21e55536
--- /dev/null
+++ b/test/CXX/symex/libc++/landingpad.cpp
@@ -0,0 +1,53 @@
+// Testcase for proper handling of
+// c++ type, constructors and destructors.
+// Based on: https://gcc.gnu.org/wiki/Dwarf2EHNewbiesHowto
+// REQUIRES: uclibc
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -std=c++11 -c -I "%libcxx_include" -g -nostdinc++ -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --exit-on-error --libcxx --libc=uclibc  %t.bc | FileCheck %s
+// Expect the following output:
+// CHECK: Throwing 1...
+// CHECK: A() 1
+// CHECK: A(const A&) 2
+// CHECK: Caught.
+// CHECK: ~A() 2
+// CHECK: ~A() 1
+// CHECK: c == 2, d == 2
+
+#include <stdio.h>
+
+int c, d;
+
+struct A {
+  int i;
+  A() {
+    i = ++c;
+    printf("A() %d\n", i);
+  }
+  A(const A &) {
+    i = ++c;
+    printf("A(const A&) %d\n", i);
+  }
+  ~A() {
+    printf("~A() %d\n", i);
+    ++d;
+  }
+};
+
+void f() {
+  printf("Throwing 1...\n");
+  throw A();
+}
+
+int main() {
+  c = 0;
+  d = 0;
+  try {
+    f();
+  } catch (A) {
+    printf("Caught.\n");
+  }
+  printf("c == %d, d == %d\n", c, d);
+  return c != d;
+}
diff --git a/test/CXX/symex/libc++/multi_throw.cpp b/test/CXX/symex/libc++/multi_throw.cpp
new file mode 100644
index 00000000..be07bc17
--- /dev/null
+++ b/test/CXX/symex/libc++/multi_throw.cpp
@@ -0,0 +1,47 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
+
+#include "klee/klee.h"
+#include <cstdio>
+
+static void x(int a) {
+  if (a == 7) {
+    throw static_cast<float>(a);
+  }
+}
+
+struct ThrowingDestructor {
+  int p = 0;
+  ~ThrowingDestructor() {
+    try {
+      x(p);
+    } catch (float ex) {
+      printf("Caught float in ThrowingDestructor()\n");
+      // CHECK-DAG: Caught float in ThrowingDestructor()
+    }
+  }
+};
+
+static void y(int a) {
+  ThrowingDestructor p;
+  p.p = klee_int("struct_int");
+  try {
+    if (a == 7) {
+      throw a;
+    }
+  } catch (int ex) {
+    printf("Caught int in y()\n");
+    // CHECK-DAG: Caught int in y()
+  }
+}
+
+int main(int argc, char **args) {
+  y(klee_int("fun_int"));
+  return 0;
+}
+
+// CHECK-DAG: KLEE: done: completed paths = 4
diff --git a/test/CXX/symex/libc++/multi_unwind.cpp b/test/CXX/symex/libc++/multi_unwind.cpp
new file mode 100644
index 00000000..98bae1dc
--- /dev/null
+++ b/test/CXX/symex/libc++/multi_unwind.cpp
@@ -0,0 +1,70 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
+
+#include "klee/klee.h"
+#include <cassert>
+#include <cstdio>
+
+struct CustomStruct {
+  int a;
+};
+
+int thrower(int x) {
+  if (x == 0) {
+    CustomStruct p;
+    throw p;
+  } else if (x == 1) {
+    throw 7;
+  } else {
+    throw new CustomStruct();
+  }
+}
+int rethrower(int x) {
+  thrower(x);
+  return 7;
+}
+
+int failed_catch(int x) {
+  try {
+    rethrower(x);
+  } catch (char *ex) {
+    // Don't catch wrong type
+    assert(0);
+  }
+  // rethrower always throws, cannot reach this instruction
+  assert(0);
+  return 0;
+}
+
+int catcher(int x) {
+  try {
+    failed_catch(x);
+  } catch (int ex) {
+    printf("Caught int\n");
+    // CHECK-DAG: Caught int
+    return 1;
+  } catch (CustomStruct ex) {
+    printf("Caught normal CustomStruct\n");
+    // CHECK-DAG: Caught normal CustomStruct
+    return 2;
+  } catch (CustomStruct *ex) {
+    printf("Caught pointer CustomStruct\n");
+    // CHECK-DAG: Caught pointer CustomStruct
+    return 3;
+  }
+  // Unreachable instruction
+  assert(0);
+  return 0;
+}
+
+int main(int argc, char **args) {
+  int x = klee_int("x");
+  int res = catcher(x);
+  return res;
+}
+
+// CHECK-DAG: KLEE: done: completed paths = 3
diff --git a/test/CXX/symex/libc++/nested.cpp b/test/CXX/symex/libc++/nested.cpp
new file mode 100644
index 00000000..b85d6759
--- /dev/null
+++ b/test/CXX/symex/libc++/nested.cpp
@@ -0,0 +1,26 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --exit-on-error --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
+
+#include <cstdio>
+
+int main(int argc, char **args) {
+  try {
+    try {
+      char *p = new char[8];
+      throw p;
+    } catch (char *ex) {
+      int i = 7;
+      throw i;
+    }
+  } catch (int ex) {
+    printf("Reached outer catch with i = %d\n", ex);
+    // CHECK-DAG: Reached outer catch with i = 7
+  }
+  return 0;
+}
+
+// CHECK-DAG: KLEE: done: completed paths = 1
diff --git a/test/CXX/symex/libc++/nested_fail.cpp b/test/CXX/symex/libc++/nested_fail.cpp
new file mode 100644
index 00000000..919434f1
--- /dev/null
+++ b/test/CXX/symex/libc++/nested_fail.cpp
@@ -0,0 +1,26 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
+
+#include "klee/klee.h"
+#include <cstdio>
+
+int main(int argc, char **args) {
+  try {
+    try {
+      char *p = new char[8];
+      throw p;
+    } catch (int ex) {
+      printf("Landed in wrong inner catch\n");
+      // CHECK-NOT: Landed in wrong inner catch
+    }
+  } catch (int ex) {
+    printf("Landed in wrong outer catch\n");
+    // CHECK-NOT: Landed in wrong outer catch
+  }
+  return 0;
+}
+// CHECK: terminating with uncaught exception of type char*
diff --git a/test/CXX/symex/libc++/rethrow.cpp b/test/CXX/symex/libc++/rethrow.cpp
new file mode 100644
index 00000000..d6a0954d
--- /dev/null
+++ b/test/CXX/symex/libc++/rethrow.cpp
@@ -0,0 +1,35 @@
+// REQUIRES: uclibc
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -std=c++11 -c -I "%libcxx_include" -g -nostdinc++ -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --exit-on-error --libcxx --libc=uclibc %t.bc 2>&1 | FileCheck %s
+
+#include <cstdio>
+
+class TestClass {
+public:
+  virtual ~TestClass() {}
+};
+
+class DerivedClass : public TestClass {
+};
+
+class SecondDerivedClass : public DerivedClass {
+};
+
+int main(int argc, char **args) {
+  try {
+    try {
+      throw SecondDerivedClass();
+    } catch (DerivedClass &p) {
+      puts("DerivedClass caught\n");
+      // CHECK: DerivedClass caught
+      throw;
+    }
+  } catch (TestClass &tc) {
+    puts("TestClass caught\n");
+    // CHECK: TestClass caught
+  }
+
+  return 0;
+}
diff --git a/test/CXX/symex/libc++/simple_exception.cpp b/test/CXX/symex/libc++/simple_exception.cpp
new file mode 100644
index 00000000..af002253
--- /dev/null
+++ b/test/CXX/symex/libc++/simple_exception.cpp
@@ -0,0 +1,19 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
+
+// CHECK: KLEE: done: completed paths = 1
+
+#include "klee/klee.h"
+
+#include <stdexcept>
+
+int main(int argc, char **args) {
+  try {
+    throw std::runtime_error("foo");
+  } catch (const std::runtime_error &ex) {
+  }
+}
diff --git a/test/CXX/symex/libc++/simple_exception_fail.cpp b/test/CXX/symex/libc++/simple_exception_fail.cpp
new file mode 100644
index 00000000..d4a30b25
--- /dev/null
+++ b/test/CXX/symex/libc++/simple_exception_fail.cpp
@@ -0,0 +1,13 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
+
+#include <stdexcept>
+
+int main(int argc, char **args) {
+  throw std::runtime_error("foo");
+}
+// CHECK: terminating with uncaught exception of type std::runtime_error: foo
\ No newline at end of file
diff --git a/test/CXX/symex/libc++/symbolic_exception.cpp b/test/CXX/symex/libc++/symbolic_exception.cpp
new file mode 100644
index 00000000..49d432a8
--- /dev/null
+++ b/test/CXX/symex/libc++/symbolic_exception.cpp
@@ -0,0 +1,29 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
+
+#include "klee/klee.h"
+#include <cstdio>
+
+int main(int argc, char **args) {
+  try {
+    throw klee_int("x");
+  } catch (int ex) {
+    if (ex > 7) {
+      puts("ex > 7");
+      // CHECK-DAG: ex > 7
+    } else if (ex < 2) {
+      puts("ex < 2");
+      // CHECK-DAG: ex < 2
+    } else {
+      puts("2 <= ex <= 7");
+      // CHECK-DAG: 2 <= ex <= 7
+    }
+  }
+  return 0;
+}
+
+// CHECK-DAG: KLEE: done: completed paths = 3
diff --git a/test/CXX/symex/libc++/throw_specifiers.cpp b/test/CXX/symex/libc++/throw_specifiers.cpp
new file mode 100644
index 00000000..5a7955fb
--- /dev/null
+++ b/test/CXX/symex/libc++/throw_specifiers.cpp
@@ -0,0 +1,35 @@
+// Testcase for proper handling of
+// throw specifications on functions
+// REQUIRES: uclibc
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -std=c++11 -c -I "%libcxx_include" -g -nostdinc++ -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libcxx --libc=uclibc  %t.bc | FileCheck %s
+
+#include <stdio.h>
+
+namespace {
+void throw_expected() throw(int) {
+  throw 5;
+}
+
+void throw_unexpected() throw(int, double) {
+  throw "unexpected string";
+}
+} // namespace
+
+int main() {
+  try {
+    throw_expected();
+  } catch (int i) {
+    puts("caught expected int");
+    // CHECK: caught expected int
+  }
+
+  try {
+    throw_unexpected();
+  } catch (char const *s) {
+    puts("caught unexpected string");
+    // CHECK-NOT: caught unexpected string
+  }
+}
diff --git a/test/CXX/symex/libc++/throwing_exception_destructor.cpp b/test/CXX/symex/libc++/throwing_exception_destructor.cpp
new file mode 100644
index 00000000..d24a15b0
--- /dev/null
+++ b/test/CXX/symex/libc++/throwing_exception_destructor.cpp
@@ -0,0 +1,45 @@
+// Testcase for proper handling of exception destructors that throw.
+// REQUIRES: uclibc
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm -O0 -std=c++11 -c -I "%libcxx_include" -g -nostdinc++ -o %t.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libcxx --libc=uclibc --exit-on-error  %t.bc
+
+#include <cassert>
+#include <cstdlib>
+#include <exception>
+
+bool destructor_throw = false;
+bool test_catch = false;
+bool int_catch = false;
+
+struct Test {
+  ~Test() noexcept(false) {
+    destructor_throw = true;
+    throw 5;
+  }
+};
+
+int main() {
+  // For some reason, printing to stdout in this test did not work when running
+  // with FileCheck. I tried puts, printf and std::cout, using fflush(stdout)
+  // and std::endl as well, but the output would only be there when not piping
+  // to FileCheck. Therefore we use a terminate handler here, which will just
+  // do the checks instead of printing strings and using FileCheck.
+  std::set_terminate([]() {
+    assert(destructor_throw && test_catch && !int_catch);
+    std::exit(0);
+  });
+
+  try {
+    try {
+      throw Test();
+    } catch (Test &t) {
+      test_catch = true;
+    }
+  } catch (int i) {
+    int_catch = true;
+  }
+
+  assert(false && "should call terminate due to an uncaught exception");
+}
diff --git a/test/CXX/symex/libc++/uncaught_exception.cpp b/test/CXX/symex/libc++/uncaught_exception.cpp
new file mode 100644
index 00000000..4e8f90f5
--- /dev/null
+++ b/test/CXX/symex/libc++/uncaught_exception.cpp
@@ -0,0 +1,13 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
+// REQUIRES: libcxx
+// RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
+// RUN: rm -rf %t.klee-out
+// RUN: %klee --output-dir=%t.klee-out --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
+
+int main() {
+  throw 7;
+}
+
+// CHECK: KLEE: ERROR:
+// CHECK: KLEE: done: completed paths = 1
diff --git a/test/CXX/symex/libc++/vector.cpp b/test/CXX/symex/libc++/vector.cpp
index 32afecd2..6f69ad65 100644
--- a/test/CXX/symex/libc++/vector.cpp
+++ b/test/CXX/symex/libc++/vector.cpp
@@ -1,10 +1,10 @@
+// REQUIRES: not-msan
+// Disabling msan because it times out on CI
 // REQUIRES: libcxx
 // REQUIRES: uclibc
 // RUN: %clangxx %s -emit-llvm %O0opt -c -std=c++11 -I "%libcxx_include" -g -nostdinc++ -o %t1.bc
 // RUN: rm -rf %t.klee-out
 // RUN: %klee --output-dir=%t.klee-out --exit-on-error --libc=uclibc --libcxx %t1.bc 2>&1 | FileCheck %s
-//
-// CHECK: KLEE: done: completed paths = 1
 
 #include "klee/klee.h"
 #include <vector>
@@ -14,3 +14,4 @@ int main(int argc, char **args) {
   a.push_back(klee_int("Test"));
   return a.at(0);
 }
+// CHECK: KLEE: done: completed paths = 1
diff --git a/tools/klee/main.cpp b/tools/klee/main.cpp
index 87d7ccf9..61a9f164 100644
--- a/tools/klee/main.cpp
+++ b/tools/klee/main.cpp
@@ -782,6 +782,8 @@ static const char *modelledExternals[] = {
   "klee_warning",
   "klee_warning_once",
   "klee_stack_trace",
+  "_klee_eh_Unwind_RaiseException_impl",
+  "klee_eh_typeid_for",
   "llvm.dbg.declare",
   "llvm.dbg.value",
   "llvm.va_start",
@@ -1267,9 +1269,17 @@ int main(int argc, char **argv, char **envp) {
     llvm::sys::path::append(LibcxxBC, KLEE_LIBCXX_BC_NAME);
     if (!klee::loadFile(LibcxxBC.c_str(), mainModule->getContext(), loadedModules,
                         errorMsg))
-      klee_error("error loading free standing support '%s': %s",
-                 LibcxxBC.c_str(), errorMsg.c_str());
+      klee_error("error loading libcxx '%s': %s", LibcxxBC.c_str(),
+                 errorMsg.c_str());
     klee_message("NOTE: Using libcxx : %s", LibcxxBC.c_str());
+
+    SmallString<128> EhCxxPath(Opts.LibraryDir);
+    llvm::sys::path::append(EhCxxPath, "libklee-eh-cxx.bca");
+    if (!klee::loadFile(EhCxxPath.c_str(), mainModule->getContext(),
+                        loadedModules, errorMsg))
+      klee_error("error loading libklee-eh-cxx '%s': %s", EhCxxPath.c_str(),
+                 errorMsg.c_str());
+
 #endif
   }