about summary refs log tree commit diff homepage
path: root/runtime/klee-eh-cxx/klee_eh_cxx.cpp
blob: 9d86bef40efb86e975acd8bad58afa500f8c906b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
//===-- klee_eh_cxx.cpp----------------------------------------------------===//
//
//                     The KLEE Symbolic Virtual Machine
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include <cassert>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <typeinfo>

#include <unwind.h>

#include <klee/klee.h>

// from libcxxabi
#ifdef __has_include
#  if __has_include(<cxa_exception.h>)
#    include <cxa_exception.h> // renamed with LLVM 10
#  elif __has_include(<cxa_exception.hpp>)
#    include <cxa_exception.hpp>
#  else
#    error "missing 'cxa_exception' header from libcxxabi"
#  endif
#else
#  include <cxa_exception.hpp> // assume old name
#endif
#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");
}
}