236
|
1 //===----------------------------------------------------------------------===//
|
|
2 //
|
|
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
4 // See https://llvm.org/LICENSE.txt for license information.
|
|
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
6 //
|
|
7 //
|
|
8 // This file implements the personality and helper functions for the state
|
|
9 // table based EH used by IBM legacy compilers xlC and xlclang++ on AIX.
|
|
10 //
|
|
11 //===----------------------------------------------------------------------===//
|
|
12
|
|
13 #include <new>
|
|
14 #include <stdio.h>
|
|
15 #include <sys/debug.h>
|
|
16
|
|
17 #if !__has_cpp_attribute(clang::optnone)
|
|
18 #error This file requires clang::optnone attribute support
|
|
19 #endif
|
|
20
|
|
21 /*
|
|
22 The legacy IBM xlC and xlclang++ compilers use the state table for EH
|
|
23 instead of the range table. Destructors, or addresses of the possible catch
|
|
24 sites or cleanup code are specified in the state table which is a finite
|
|
25 state machine (FSM). Each function that has a state table also has an
|
|
26 autolocal state variable. The state variable represents the current state
|
|
27 of the function for EH and is found through the traceback table of the
|
|
28 function during unwinding, which is located at the end of each function.
|
|
29 The FSM is an array of state entries. Each state entry has the following
|
|
30 fields:
|
|
31
|
|
32 * offset/address/pointer - the offset used to locate the object, or the
|
|
33 address of a global object, or the address of the next state if it is an
|
|
34 old conditional state change entry;
|
|
35 * dtor/landing pad - address of the destructor function to invoke,
|
|
36 or address of the catch block or cleanup code in the user code to branch to;
|
|
37 * element count/action flag - the number of elements or the flag for actions;
|
|
38 * element size - if the object is an array this is the size of one element
|
|
39 of the array;
|
|
40 * flags - flags used to control how fields in the entry are interpreted;
|
|
41 * next state - the state to execute next after the action for this state is
|
|
42 performed. The value of zero indicates the end of the state for this
|
|
43 function.
|
|
44
|
|
45 The following is the description of 'element count/action flag' field.
|
|
46 +-----------------------------------------------------------------------------+
|
|
47 | value | description | action |
|
|
48 +-------+------------------------+--------------------------------------------+
|
|
49 | > 1 | object is an array | calls __cxa_vec_cleanup to run dtor for |
|
|
50 | | | each member of the array |
|
|
51 +-------+------------------------+--------------------------------------------+
|
|
52 | 1, 0 | object is a scalar | calls dtor for the object |
|
|
53 +-------+------------------------+--------------------------------------------+
|
|
54 | -1 | begin catch | branches to the handler which performes |
|
|
55 | | | catch-match. If there is no catch that |
|
|
56 | | | matches the exception it will be rethrown |
|
|
57 +-------+------------------------+--------------------------------------------+
|
|
58 | -2 | end catch | ends current catch block and continues |
|
|
59 | | | attempting to catch the exception |
|
|
60 +-------+------------------------+--------------------------------------------+
|
|
61 | -3 | delete the object | calls the delete function of the object |
|
|
62 +-------+------------------------+--------------------------------------------+
|
|
63 | -4 | cleanup label | branches to the user code for cleaning up |
|
|
64 +-------+------------------------+--------------------------------------------+
|
|
65 */
|
|
66
|
|
67 namespace __cxxabiv1 {
|
|
68
|
|
69 extern "C" {
|
|
70
|
|
71 // Macros for debugging the state table parsing.
|
|
72 #ifdef NDEBUG
|
|
73 # define _LIBCXXABI_TRACE_STATETAB(msg, ...)
|
|
74 # define _LIBCXXABI_TRACE_STATETAB0(msg)
|
|
75 # define _LIBCXXABI_TRACE_STATETAB1(msg)
|
|
76 # define _LIBCXXABI_TRACING_STATETAB 0
|
|
77 #else
|
|
78 static bool state_tab_dbg() {
|
|
79 static bool checked = false;
|
|
80 static bool log = false;
|
|
81 if (!checked) {
|
|
82 log = (getenv("LIBCXXABI_PRINT_STATTAB") != NULL);
|
|
83 checked = true;
|
|
84 }
|
|
85 return log;
|
|
86 }
|
|
87
|
|
88 # define _LIBCXXABI_TRACE_STATETAB(msg, ...) \
|
|
89 do { \
|
|
90 if (state_tab_dbg()) \
|
|
91 fprintf(stderr, "libcxxabi: " msg, __VA_ARGS__); \
|
|
92 } while (0)
|
|
93 # define _LIBCXXABI_TRACE_STATETAB0(msg) \
|
|
94 do { \
|
|
95 if (state_tab_dbg()) \
|
|
96 fprintf(stderr, "libcxxabi: " msg); \
|
|
97 } while (0)
|
|
98 # define _LIBCXXABI_TRACE_STATETAB1(msg) \
|
|
99 do { \
|
|
100 if (state_tab_dbg()) \
|
|
101 fprintf(stderr, msg); \
|
|
102 } while (0)
|
|
103
|
|
104 # define _LIBCXXABI_TRACING_STATETAB state_tab_dbg()
|
|
105 #endif // NDEBUG
|
|
106
|
|
107 namespace __state_table_eh {
|
|
108
|
|
109 using destruct_f = void (*)(void*);
|
|
110
|
|
111 // Definition of flags for the state table entry field 'action flag'.
|
|
112 enum FSMEntryCount : intptr_t { beginCatch = -1, endCatch = -2, deleteObject = -3, cleanupLabel = -4, terminate = -5 };
|
|
113
|
|
114 // Definition of flags for the state table entry field 'flags'.
|
|
115 enum FSMEntryFlag : int16_t {
|
|
116 indirect = 0x100, // Object was thrown from a function where
|
|
117 // the return value optimization was used.
|
|
118 oldConditionalStateChange = 0x400, // State table entry is an indirect state
|
|
119 // change, dereference the address in
|
|
120 // offset as int for the target state.
|
|
121 // This is deprecated. This indicates
|
|
122 // the address is direct. (static local).
|
|
123 conditionalStateChange = 0x800, // State table entry is an indirect state
|
|
124 // change, dereference the address in
|
|
125 // offset as int for the target state.
|
|
126 // The temporary is an automatic. State
|
|
127 // change is used in cases such as
|
|
128 // (b?(T1(),foo()):(T2(),foo())),throw 42;
|
|
129 // which causes a conditional state change
|
|
130 // so that we know if T1 or T2 need to be
|
|
131 // destroyed.
|
|
132 thisFlag = 0x01, // The address of the object for the
|
|
133 // cleanup action is based on the
|
|
134 // StateVariable::thisValue.
|
|
135 vBaseFlag = 0x02, // The object is of a virtual base class.
|
|
136 globalObj = 0x04 // FSMEntry::address is the address of
|
|
137 // a global object.
|
|
138 };
|
|
139
|
|
140 namespace {
|
|
141 // The finite state machine to be walked.
|
|
142 struct FSMEntry {
|
|
143 union {
|
|
144 // Offset of the object within its stack frame or containing object.
|
|
145 intptr_t offset;
|
|
146 // Address of a global object.
|
|
147 intptr_t address;
|
|
148 // Address of the next state if it is an old conditional state change entry.
|
|
149 intptr_t nextStatePtr;
|
|
150 };
|
|
151 union {
|
|
152 // Address of the destructor function.
|
|
153 void (*destructor)(void*, size_t);
|
|
154 // The address of the catch block or cleanup code.
|
|
155 void* landingPad;
|
|
156 };
|
|
157 union {
|
|
158 // The flag for actions (when the value is negative).
|
|
159 FSMEntryCount actionFlag;
|
|
160 // The element count (when the value is positive or zero).
|
|
161 size_t elementCount;
|
|
162 };
|
|
163 size_t elemSize;
|
|
164 FSMEntryFlag flags;
|
|
165 uint16_t nextState;
|
|
166 };
|
|
167
|
|
168 struct FSM {
|
|
169 uint32_t magic; // Magic number of the state table.
|
|
170 int32_t numberOfStates;
|
|
171 FSMEntry table[1]; // Actually table[numberOfStates].
|
|
172 };
|
|
173
|
|
174 // The state variable on the stack.
|
|
175 struct StateVariable {
|
|
176 int32_t state;
|
|
177 struct FSM* table;
|
|
178 intptr_t thisValue;
|
|
179 int32_t ignoreVBasePtrs;
|
|
180 };
|
|
181 } // namespace
|
|
182
|
|
183 // State table magic number
|
|
184 enum FSMMagic : uint32_t {
|
|
185 number = 0xbeefdead, // State table generated by xlC compiler.
|
|
186 number2 = 0xbeeedead, // State table generated by early version xlC compiler.
|
|
187 number3 = 0x1cedbeef // State table generated by xlclang++ compiler.
|
|
188 };
|
|
189
|
|
190 constexpr size_t dtorArgument = 0x02; // Flag to destructor indicating to free
|
|
191 // virtual bases, don't delete object.
|
|
192
|
|
193 static void invoke_destructor(FSMEntry* fsmEntry, void* addr) {
|
|
194 _LIBCXXABI_TRACE_STATETAB("Destruct object=%p, fsmEntry=%p\n", addr, reinterpret_cast<void*>(fsmEntry));
|
|
195 try {
|
|
196 if (fsmEntry->elementCount == 1) {
|
|
197 _LIBCXXABI_TRACE_STATETAB0("calling scalar destructor\n");
|
|
198 (*fsmEntry->destructor)(addr, dtorArgument);
|
|
199 _LIBCXXABI_TRACE_STATETAB0("returned from scalar destructor\n");
|
|
200 } else {
|
|
201 _LIBCXXABI_TRACE_STATETAB0("calling vector destructor\n");
|
|
202 __cxa_vec_cleanup(addr, reinterpret_cast<size_t>(fsmEntry->elementCount), fsmEntry->elemSize,
|
|
203 reinterpret_cast<destruct_f>(fsmEntry->destructor));
|
|
204 _LIBCXXABI_TRACE_STATETAB0("returned from vector destructor\n");
|
|
205 }
|
|
206 } catch (...) {
|
|
207 _LIBCXXABI_TRACE_STATETAB0("Uncaught exception in destructor, terminating\n");
|
|
208 std::terminate();
|
|
209 }
|
|
210 }
|
|
211
|
|
212 static void invoke_delete(FSMEntry* fsmEntry, void* addr) {
|
|
213 char* objectAddress = *reinterpret_cast<char**>(addr);
|
|
214
|
|
215 _LIBCXXABI_TRACE_STATETAB("Delete object=%p, fsmEntry=%p\n", reinterpret_cast<void*>(objectAddress),
|
|
216 reinterpret_cast<void*>(fsmEntry));
|
|
217 try {
|
|
218 _LIBCXXABI_TRACE_STATETAB0("..calling delete()\n");
|
|
219 // 'destructor' holds a function pointer to delete().
|
|
220 (*fsmEntry->destructor)(objectAddress, fsmEntry->elemSize);
|
|
221 _LIBCXXABI_TRACE_STATETAB0("..returned from delete()\n");
|
|
222 } catch (...) {
|
|
223 _LIBCXXABI_TRACE_STATETAB0("Uncaught exception in delete(), terminating\n");
|
|
224 std::terminate();
|
|
225 }
|
|
226 }
|
|
227
|
|
228 // Get the frame address of the current function from its traceback table
|
|
229 // which is at the end of each function.
|
|
230 static uintptr_t get_frame_addr(_Unwind_Context* context) {
|
|
231 int framePointerReg = 1; // default frame pointer == SP.
|
|
232 uint32_t* p = reinterpret_cast<uint32_t*>(_Unwind_GetIP(context));
|
|
233
|
|
234 // Keep looking forward until a word of 0 is found. The traceback
|
|
235 // table starts at the following word.
|
|
236 while (*p)
|
|
237 ++p;
|
|
238 tbtable* TBTable = reinterpret_cast<tbtable*>(p + 1);
|
|
239
|
|
240 p = reinterpret_cast<uint32_t*>(&TBTable->tb_ext);
|
|
241
|
|
242 // Skip field parminfo if it exists.
|
|
243 if (TBTable->tb.fixedparms || TBTable->tb.floatparms)
|
|
244 ++p;
|
|
245
|
|
246 // Skip field tb_offset if it exists.
|
|
247 if (TBTable->tb.has_tboff)
|
|
248 ++p;
|
|
249
|
|
250 // Skip field hand_mask if it exists.
|
|
251 if (TBTable->tb.int_hndl)
|
|
252 ++p;
|
|
253
|
|
254 // Skip fields ctl_info and ctl_info_disp if they exist.
|
|
255 if (TBTable->tb.has_ctl)
|
|
256 p += 1 + *p;
|
|
257
|
|
258 // Skip fields name_len and name if exist.
|
|
259 if (TBTable->tb.name_present) {
|
|
260 const uint16_t name_len = *reinterpret_cast<uint16_t*>(p);
|
|
261 p = reinterpret_cast<uint32_t*>(reinterpret_cast<char*>(p) + name_len + sizeof(uint16_t));
|
|
262 }
|
|
263
|
|
264 if (TBTable->tb.uses_alloca)
|
|
265 framePointerReg = *reinterpret_cast<char*>(p);
|
|
266
|
|
267 return _Unwind_GetGR(context, framePointerReg);
|
|
268 }
|
|
269
|
|
270 // Calculate the object address from the FSM entry.
|
|
271 static void* compute_addr_from_table(FSMEntry* fsmEntry, StateVariable* const state, _Unwind_Context* context) {
|
|
272 void* addr;
|
|
273 if (fsmEntry->flags & FSMEntryFlag::globalObj) {
|
|
274 addr = reinterpret_cast<void*>(fsmEntry->address);
|
|
275 _LIBCXXABI_TRACE_STATETAB("Address calculation (global obj) addr=fsmEntry->address=%p\n", addr);
|
|
276 } else if (fsmEntry->flags & FSMEntryFlag::thisFlag) {
|
|
277 addr = reinterpret_cast<void*>(state->thisValue + fsmEntry->offset);
|
|
278 _LIBCXXABI_TRACE_STATETAB("Address calculation (this obj) fsmEntry->offset=%ld : "
|
|
279 "state->thisValue=%ld addr=(fsmEntry->offset+state->thisValue)=%p\n",
|
|
280 fsmEntry->offset, state->thisValue, addr);
|
|
281 } else if (fsmEntry->flags & FSMEntryFlag::indirect) {
|
|
282 addr = reinterpret_cast<void*>(
|
|
283 *reinterpret_cast<char**>(get_frame_addr(context) + static_cast<uintptr_t>(fsmEntry->offset)));
|
|
284 _LIBCXXABI_TRACE_STATETAB("Address calculation (indirect obj) addr=%p, fsmEntry->offset=%ld \n",
|
|
285 addr, fsmEntry->offset);
|
|
286 } else {
|
|
287 addr = reinterpret_cast<void*>(get_frame_addr(context) + static_cast<uintptr_t>(fsmEntry->offset));
|
|
288 _LIBCXXABI_TRACE_STATETAB("Address calculation. (local obj) addr=fsmEntry->offset=%p\n",
|
|
289 addr);
|
|
290 }
|
|
291 return addr;
|
|
292 }
|
|
293
|
|
294 static void scan_state_tab(scan_results& results, _Unwind_Action actions, bool native_exception,
|
|
295 _Unwind_Exception* unwind_exception, _Unwind_Context* context) {
|
|
296 // Initialize results to found nothing but an error.
|
|
297 results.ttypeIndex = 0;
|
|
298 results.actionRecord = 0;
|
|
299 results.languageSpecificData = 0;
|
|
300 results.landingPad = 0;
|
|
301 results.adjustedPtr = 0;
|
|
302 results.reason = _URC_FATAL_PHASE1_ERROR;
|
|
303
|
|
304 // Check for consistent actions.
|
|
305 if (actions & _UA_SEARCH_PHASE) {
|
|
306 // Do Phase 1
|
|
307 if (actions & (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME | _UA_FORCE_UNWIND)) {
|
|
308 // None of these flags should be set during Phase 1.
|
|
309 // Client error
|
|
310 results.reason = _URC_FATAL_PHASE1_ERROR;
|
|
311 return;
|
|
312 }
|
|
313 } else if (actions & _UA_CLEANUP_PHASE) {
|
|
314 if ((actions & _UA_HANDLER_FRAME) && (actions & _UA_FORCE_UNWIND)) {
|
|
315 // _UA_HANDLER_FRAME should only be set if phase 1 found a handler.
|
|
316 // If _UA_FORCE_UNWIND is set, phase 1 shouldn't have happened.
|
|
317 // Client error
|
|
318 results.reason = _URC_FATAL_PHASE2_ERROR;
|
|
319 return;
|
|
320 }
|
|
321 } else {
|
|
322 // Neither _UA_SEARCH_PHASE nor _UA_CLEANUP_PHASE is set.
|
|
323 // Client error
|
|
324 results.reason = _URC_FATAL_PHASE1_ERROR;
|
|
325 return;
|
|
326 }
|
|
327
|
|
328 if (_LIBCXXABI_TRACING_STATETAB) {
|
|
329 _LIBCXXABI_TRACE_STATETAB1("\n");
|
|
330 _LIBCXXABI_TRACE_STATETAB("%s: actions=%d (", __func__, actions);
|
|
331
|
|
332 if (_UA_SEARCH_PHASE & actions)
|
|
333 _LIBCXXABI_TRACE_STATETAB1("_UA_SEARCH_PHASE ");
|
|
334 if (_UA_CLEANUP_PHASE & actions)
|
|
335 _LIBCXXABI_TRACE_STATETAB1("_UA_CLEANUP_PHASE ");
|
|
336 if (_UA_HANDLER_FRAME & actions)
|
|
337 _LIBCXXABI_TRACE_STATETAB1("_UA_HANDLER_FRAME ");
|
|
338 if (_UA_FORCE_UNWIND & actions)
|
|
339 _LIBCXXABI_TRACE_STATETAB1("_UA_FORCE_UNWIND ");
|
|
340 _LIBCXXABI_TRACE_STATETAB1(")\n");
|
|
341 _LIBCXXABI_TRACE_STATETAB(" unwind_exception=%p context=%p\n", reinterpret_cast<void*>(unwind_exception),
|
|
342 reinterpret_cast<void*>(context));
|
|
343 }
|
|
344
|
|
345 // Start scan by getting state table address.
|
|
346 StateVariable* const state = reinterpret_cast<StateVariable* const>(_Unwind_GetLanguageSpecificData(context));
|
|
347 if (state->state <= 0) {
|
|
348 // The state is not correct - give up on this routine.
|
|
349 _LIBCXXABI_TRACE_STATETAB("state=%d and is <= 0), continue unwinding\n", state->state);
|
|
350 results.reason = _URC_CONTINUE_UNWIND;
|
|
351 return;
|
|
352 }
|
|
353 // Parse the state table.
|
|
354 FSM* const fsm = state->table;
|
|
355 FSMEntry* currFSMEntry;
|
|
356
|
|
357 if (fsm->magic != FSMMagic::number && fsm->magic != FSMMagic::number2 && fsm->magic != FSMMagic::number3) {
|
|
358 // Something is wrong with the state table we found.
|
|
359 if (_UA_SEARCH_PHASE & actions) {
|
|
360 _LIBCXXABI_TRACE_STATETAB0("Invalid FSM table, return _URC_FATAL_PHASE1_ERROR\n");
|
|
361 results.reason = _URC_FATAL_PHASE1_ERROR;
|
|
362 } else if (_UA_CLEANUP_PHASE & actions) {
|
|
363 _LIBCXXABI_TRACE_STATETAB0("Invalid FSM table, return _URC_FATAL_PHASE2_ERROR\n");
|
|
364 results.reason = _URC_FATAL_PHASE2_ERROR;
|
|
365 } else {
|
|
366 // We should never get here.
|
|
367 _LIBCXXABI_TRACE_STATETAB0("Invalid FSM table + RT Internal error, return _URC_FATAL_PHASE2_ERROR\n");
|
|
368 results.reason = _URC_FATAL_PHASE2_ERROR;
|
|
369 }
|
|
370 return;
|
|
371 }
|
|
372
|
|
373 if (_LIBCXXABI_TRACING_STATETAB) {
|
|
374 // Print the state table for debugging purposes.
|
|
375 _LIBCXXABI_TRACE_STATETAB("state->state=%d, state->ignoreVBasePtrs=%d\n", state->state, state->ignoreVBasePtrs);
|
|
376 _LIBCXXABI_TRACE_STATETAB("fsm->magic=%#x, fsm->numberOfStates=%d\n", fsm->magic, fsm->numberOfStates);
|
|
377 // Print out the FSM table.
|
|
378 _LIBCXXABI_TRACE_STATETAB0("FSM table:\n");
|
|
379 _LIBCXXABI_TRACE_STATETAB("%12s %10s %8s %10s %7s %7s %7s %7s\n", "Entry Addr", "state", "Offset", "DTR/lpad",
|
|
380 "count", "el_size", "flags", "next");
|
|
381 for (int i = 0; i < fsm->numberOfStates; i++) {
|
|
382 currFSMEntry = &fsm->table[i];
|
|
383 _LIBCXXABI_TRACE_STATETAB("%12p (%8d) %8ld %10p %7ld "
|
|
384 "%7ld %#7x %7d\n",
|
|
385 reinterpret_cast<void*>(&currFSMEntry), i + 1, currFSMEntry->offset,
|
|
386 reinterpret_cast<void*>(currFSMEntry->destructor),
|
|
387 currFSMEntry->elementCount, currFSMEntry->elemSize, currFSMEntry->flags,
|
|
388 currFSMEntry->nextState);
|
|
389 }
|
|
390 }
|
|
391
|
|
392 if (_UA_SEARCH_PHASE & actions) {
|
|
393 // Start walking the state table. Use a local copy of state->state so when
|
|
394 // we return from search phase we don't change the state number.
|
|
395 int currState = state->state;
|
|
396
|
|
397 while (currState > 0) {
|
|
398 currFSMEntry = &fsm->table[currState - 1];
|
|
399 _LIBCXXABI_TRACE_STATETAB("Processing state=%d, flags=0x%hx\n", currState, currFSMEntry->flags);
|
|
400
|
|
401 if (currFSMEntry->actionFlag == FSMEntryCount::beginCatch) {
|
|
402 // Found a catch handler.
|
|
403 if (fsm->magic == FSMMagic::number) {
|
|
404 _LIBCXXABI_TRACE_STATETAB0("Found a xlC catch handler, return _URC_FATAL_PHASE1_ERROR\n");
|
|
405 // xlC catch handlers cannot be entered because they use a
|
|
406 // proprietary EH runtime that is not interoperable.
|
|
407 results.reason = _URC_FATAL_PHASE1_ERROR;
|
|
408 return;
|
|
409 }
|
|
410 // xlclang++ compiled frames use CXA-abi EH calls and any catch
|
|
411 // block will include a catch(...) block so it is safe to assume that
|
|
412 // the handler is found without checking the catch match. The
|
|
413 // catch(...) block will rethrow the exception if there isn't a
|
|
414 // match.
|
|
415 _LIBCXXABI_TRACE_STATETAB0("Found a catch handler, return _URC_HANDLER_FOUND\n");
|
|
416 results.reason = _URC_HANDLER_FOUND;
|
|
417 return;
|
|
418 }
|
|
419 if (currFSMEntry->actionFlag == FSMEntryCount::terminate) {
|
|
420 _LIBCXXABI_TRACE_STATETAB0("Found the terminate state, return _URC_HANDLER_FOUND\n");
|
|
421 results.reason = _URC_HANDLER_FOUND;
|
|
422 return;
|
|
423 }
|
|
424 if (currFSMEntry->flags & FSMEntryFlag::oldConditionalStateChange) {
|
|
425 // Deprecated conditional expression.
|
|
426 currState = *reinterpret_cast<int*>(currFSMEntry->nextStatePtr);
|
|
427 _LIBCXXABI_TRACE_STATETAB("Flag: FSMEntryFlag::oldConditionalStateChange, dereference "
|
|
428 "currFSMEntry->nextStatePtr(%ld), set state=%d\n",
|
|
429 currFSMEntry->nextStatePtr, currState);
|
|
430 continue; // We are done this iteration of the loop, since
|
|
431 // we changed a state.
|
|
432 }
|
|
433 if (currFSMEntry->flags & FSMEntryFlag::conditionalStateChange) {
|
|
434 void* addr = compute_addr_from_table(currFSMEntry, state, context);
|
|
435 currState = *reinterpret_cast<int*>(addr);
|
|
436 _LIBCXXABI_TRACE_STATETAB("Flag: FSMEntryFlag::conditionalStateChange, dereference "
|
|
437 "addr(%p), set state=%d\n", addr, currState);
|
|
438 continue; // We are done this iteration of the loop, since we
|
|
439 // changed the state.
|
|
440 }
|
|
441 // Go to the next state.
|
|
442 currState = currFSMEntry->nextState;
|
|
443 }
|
|
444 _LIBCXXABI_TRACE_STATETAB0("No catch handler found, return _URC_CONTINUE_UNWIND\n");
|
|
445 results.reason = _URC_CONTINUE_UNWIND;
|
|
446 return;
|
|
447 }
|
|
448 if (_UA_CLEANUP_PHASE & actions) {
|
|
449 // Start walking the state table.
|
|
450 while (state->state > 0) {
|
|
451 currFSMEntry = &fsm->table[state->state - 1];
|
|
452
|
|
453 if (currFSMEntry->actionFlag == FSMEntryCount::terminate) {
|
|
454 _LIBCXXABI_TRACE_STATETAB0("Reached terminate state. Call terminate.\n");
|
|
455 std::terminate();
|
|
456 }
|
|
457 // Perform action according to the currFSMEntry->actionFlag,
|
|
458 // except when flag is FSMEntryFlag::conditionalStateChange or
|
|
459 // FSMEntryFlag::oldConditionalStateChange.
|
|
460 _LIBCXXABI_TRACE_STATETAB("Processing state=%d, flags=0x%hx\n", state->state, currFSMEntry->flags);
|
|
461 if (currFSMEntry->flags & FSMEntryFlag::oldConditionalStateChange) {
|
|
462 state->state = *reinterpret_cast<int*>(currFSMEntry->nextStatePtr);
|
|
463 _LIBCXXABI_TRACE_STATETAB("Flag: FSMEntryFlag::oldConditionalStateChange, dereference "
|
|
464 "currFSMEntry->nextStatePtr(%ld), set state=%d\n",
|
|
465 currFSMEntry->nextStatePtr, state->state);
|
|
466 continue; // We are done with this iteration of the loop, since we changed a state.
|
|
467 }
|
|
468 if (currFSMEntry->flags & FSMEntryFlag::conditionalStateChange) {
|
|
469 // A conditional state table entry holds the address of a local
|
|
470 // that holds the next state.
|
|
471 void* addr = compute_addr_from_table(currFSMEntry, state, context);
|
|
472 state->state = *reinterpret_cast<int*>(addr);
|
|
473 _LIBCXXABI_TRACE_STATETAB("Flag: FSMEntryFlag::conditionalStateChange, dereference "
|
|
474 "addr(%p), set state=%d\n", addr, state->state);
|
|
475 continue; // We are done with this iteration of the loop, since we changed a state.
|
|
476 }
|
|
477 if (currFSMEntry->actionFlag == FSMEntryCount::beginCatch || currFSMEntry->actionFlag == FSMEntryCount::endCatch ||
|
|
478 currFSMEntry->actionFlag == FSMEntryCount::cleanupLabel) {
|
|
479
|
|
480 _LIBCXXABI_TRACE_STATETAB(
|
|
481 "FSMEntryCount::%s: handler %p/%p, return _URC_HANDLER_FOUND\n",
|
|
482 (currFSMEntry->actionFlag == FSMEntryCount::beginCatch
|
|
483 ? "beginCatch"
|
|
484 : (currFSMEntry->actionFlag == FSMEntryCount::endCatch ? "endCatch" : "cleanupLabel")),
|
|
485 currFSMEntry->landingPad, *reinterpret_cast<void**>(currFSMEntry->landingPad));
|
|
486
|
|
487 state->state = currFSMEntry->nextState;
|
|
488 results.landingPad = reinterpret_cast<uintptr_t>(*reinterpret_cast<void**>(currFSMEntry->landingPad));
|
|
489 results.reason = _URC_HANDLER_FOUND;
|
|
490 return;
|
|
491 }
|
|
492 if (currFSMEntry->elementCount > 0) {
|
|
493 if (currFSMEntry->flags & FSMEntryFlag::vBaseFlag && state->ignoreVBasePtrs) {
|
|
494 _LIBCXXABI_TRACE_STATETAB0("Ignoring virtual base dtor.\n");
|
|
495 } else {
|
|
496 // We need to invoke the virtual base destructor. This must be
|
|
497 // a frame from the legacy xlC compiler as the xlclang++ compiler
|
|
498 // generates inline cleanup code rather than specifying
|
|
499 // the destructor via the state table.
|
|
500 void* addr = compute_addr_from_table(currFSMEntry, state, context);
|
|
501
|
|
502 // An extra indirect to get to the object according to the object
|
|
503 // model used by the xlC compiler.
|
|
504 addr = reinterpret_cast<void*>(*reinterpret_cast<char**>(addr));
|
|
505 _LIBCXXABI_TRACE_STATETAB("Invoke dtor for object=%p\n", addr);
|
|
506 invoke_destructor(currFSMEntry, addr);
|
|
507 }
|
|
508 } else if (currFSMEntry->actionFlag == FSMEntryCount::deleteObject) {
|
|
509 void* addr = compute_addr_from_table(currFSMEntry, state, context);
|
|
510 if (currFSMEntry->flags & FSMEntryFlag::vBaseFlag) {
|
|
511 // We need to invoke the virtual base delete function. This must be
|
|
512 // a frame from the legacy xlC compiler as the xlclang++ compiler
|
|
513 // generates inline cleanup code rather than specifying
|
|
514 // the delete function via the state table.
|
|
515
|
|
516 // An extra indirect to get to the object according to the object
|
|
517 // model used by the xlC compiler.
|
|
518 addr = reinterpret_cast<void*>(*reinterpret_cast<char**>(addr));
|
|
519 }
|
|
520 _LIBCXXABI_TRACE_STATETAB("Delete object at %p\n", addr);
|
|
521 invoke_delete(currFSMEntry, addr);
|
|
522 } else {
|
|
523 _LIBCXXABI_TRACE_STATETAB("Unknown entry in FSM (count=%ld), ignored\n",
|
|
524 currFSMEntry->elementCount);
|
|
525 } // End of action switching.
|
|
526
|
|
527 // Go to next state.
|
|
528 state->state = currFSMEntry->nextState;
|
|
529 }
|
|
530 _LIBCXXABI_TRACE_STATETAB0("No catch handler, return _URC_CONTINUE_UNWIND\n");
|
|
531 results.reason = _URC_CONTINUE_UNWIND;
|
|
532 return;
|
|
533 }
|
|
534 _LIBCXXABI_TRACE_STATETAB0("No state table entry for this exception, call_terminate()\n");
|
|
535 // It is possible that no state table entry specify how to handle
|
|
536 // this exception. By spec, terminate it immediately.
|
|
537 call_terminate(native_exception, unwind_exception);
|
|
538 }
|
|
539
|
|
540 // Personality routine for EH using the state table.
|
|
541 _LIBCXXABI_FUNC_VIS _Unwind_Reason_Code
|
|
542 __xlcxx_personality_v0(int version, _Unwind_Action actions, uint64_t exceptionClass,
|
|
543 _Unwind_Exception* unwind_exception, _Unwind_Context* context) {
|
|
544 if (version != 1 || unwind_exception == 0 || context == 0)
|
|
545 return _URC_FATAL_PHASE1_ERROR;
|
|
546
|
|
547 bool native_exception = (exceptionClass & get_vendor_and_language) == (kOurExceptionClass & get_vendor_and_language);
|
|
548 scan_results results;
|
|
549 scan_state_tab(results, actions, native_exception, unwind_exception, context);
|
|
550 if (actions & _UA_SEARCH_PHASE) {
|
|
551 // Phase 1 search: All we're looking for in phase 1 is a handler that
|
|
552 // halts unwinding
|
|
553 return results.reason;
|
|
554 }
|
|
555 if (actions & _UA_CLEANUP_PHASE) {
|
|
556 // Phase 2 cleanup:
|
|
557 if (results.reason == _URC_HANDLER_FOUND) {
|
|
558 // Store the address of unwind_exception in the stack field
|
|
559 // reserved for compilers (SP + 3 * sizeof(uintptr_t)) in the stack of
|
|
560 // the caller of the function containing the landing pad (within the link
|
|
561 // area for the call to the latter) for __xlc_exception_handle()
|
|
562 // to retrieve when it is called by the landing pad.
|
|
563 uintptr_t *currentSP = reinterpret_cast<uintptr_t*>(_Unwind_GetGR(context, 1));
|
|
564 uintptr_t *callersSP = reinterpret_cast<uintptr_t*>(currentSP[0]);
|
|
565 callersSP[3] = reinterpret_cast<uintptr_t>(unwind_exception);
|
|
566 _LIBCXXABI_TRACE_STATETAB("Handshake: set unwind_exception=%p in stack=%p\n", reinterpret_cast<void*>(unwind_exception), reinterpret_cast<void*>(callersSP));
|
|
567 // Jump to the handler.
|
|
568 _Unwind_SetIP(context, results.landingPad);
|
|
569 return _URC_INSTALL_CONTEXT;
|
|
570 }
|
|
571 // Did not find a handler. Return the results of the scan. Normally
|
|
572 // _URC_CONTINUE_UNWIND, but could have been _URC_FATAL_PHASE2_ERROR.
|
|
573 return results.reason;
|
|
574 }
|
|
575 // We were called improperly: neither a phase 1 or phase 2 search.
|
|
576 return _URC_FATAL_PHASE1_ERROR;
|
|
577 }
|
|
578 } // namespace __state_table_eh
|
|
579
|
|
580 // The following are EH helper functions for xlclang++ compiled code.
|
|
581
|
|
582 // __xlc_catch_matchv2
|
|
583 // Check whether the thrown object matches the catch handler's exception
|
|
584 // declaration. If there is a match, the function returns true with adjusted
|
|
585 // address of the thrown object. Otherwise, returns false.
|
|
586 _LIBCXXABI_FUNC_VIS bool
|
|
587 __xlc_catch_matchv2(_Unwind_Exception* exceptionObject, std::type_info* catchTypeInfo, void*& obj) {
|
|
588 _LIBCXXABI_TRACE_STATETAB("Entering %s, exceptionObject=%p\n", __func__, reinterpret_cast<void*>(exceptionObject));
|
|
589
|
|
590 if (!__isOurExceptionClass(exceptionObject)) {
|
|
591 _LIBCXXABI_TRACE_STATETAB0("No match, not a C++ exception\n");
|
|
592 return false;
|
|
593 }
|
|
594
|
|
595 __cxa_exception* exceptionHeader = 0;
|
|
596
|
|
597 if (__getExceptionClass(exceptionObject) == kOurDependentExceptionClass) {
|
|
598 // Walk to the __cxa_dependent_exception primary exception for the
|
|
599 // exception object and its type_info.
|
|
600 __cxa_dependent_exception* dependentExceptionHeader =
|
|
601 reinterpret_cast<__cxa_dependent_exception*>(exceptionObject + 1) - 1;
|
|
602 exceptionHeader = reinterpret_cast<__cxa_exception*>(dependentExceptionHeader->primaryException) - 1;
|
|
603 _LIBCXXABI_TRACE_STATETAB("exceptionObject 0x%p is a dependent, primary 0x%p\n",
|
|
604 reinterpret_cast<void*>(exceptionObject),
|
|
605 reinterpret_cast<void*>(&exceptionHeader->unwindHeader));
|
|
606 exceptionObject = &exceptionHeader->unwindHeader;
|
|
607 } else {
|
|
608 _LIBCXXABI_TRACE_STATETAB("exceptionObject %p is NOT a dependent\n", reinterpret_cast<void*>(exceptionObject));
|
|
609 exceptionHeader = reinterpret_cast<__cxa_exception*>(exceptionObject + 1) - 1;
|
|
610 }
|
|
611
|
|
612 void* thrownObject = reinterpret_cast<void*>(exceptionObject + 1);
|
|
613 std::type_info* throwTypeInfo = exceptionHeader->exceptionType;
|
|
614
|
|
615 // Get the type info for the thrown type and this catch clause and
|
|
616 // see if the catch caluse can catch that type.
|
|
617
|
|
618 __cxxabiv1::__shim_type_info* catchType = reinterpret_cast<__cxxabiv1::__shim_type_info*>(catchTypeInfo);
|
|
619 __cxxabiv1::__shim_type_info* throwType = reinterpret_cast<__cxxabiv1::__shim_type_info*>(throwTypeInfo);
|
|
620 _LIBCXXABI_TRACE_STATETAB("UnwindException=%p, thrownObject=%p, throwTypeInfo=%p(%s), catchTypeInfo=%p(%s)\n",
|
|
621 reinterpret_cast<void*>(exceptionObject), thrownObject, reinterpret_cast<void*>(throwType),
|
|
622 throwType->name(), reinterpret_cast<void*>(catchType), catchType->name());
|
|
623 if (catchType->can_catch(throwType, thrownObject)) {
|
|
624 exceptionHeader->adjustedPtr = thrownObject;
|
|
625 obj = thrownObject;
|
|
626 _LIBCXXABI_TRACE_STATETAB("Match found for thrownObject=%p\n", thrownObject);
|
|
627 return true;
|
|
628 }
|
|
629 _LIBCXXABI_TRACE_STATETAB0("No match\n");
|
|
630 return false;
|
|
631 }
|
|
632
|
|
633 // __xlc_throw_badexception
|
|
634 // This function is for xlclang++. It allocates and throws a bad_exception.
|
|
635 // During unwinding for this bad_exception, the previous exception which is
|
|
636 // not matching the throw spec will be cleaned up. Thus having the same
|
|
637 // effect as replace the top most exception (which is bad) with a bad_exception.
|
|
638 _LIBCXXABI_FUNC_VIS void __xlc_throw_badexception() {
|
|
639 _LIBCXXABI_TRACE_STATETAB("Entering function: %s\n\n", __func__);
|
|
640 void* newexception = new (__cxa_allocate_exception(sizeof(std::bad_exception))) std::bad_exception;
|
|
641 __cxa_throw(newexception, const_cast<std::type_info*>(&typeid(std::bad_exception)), 0);
|
|
642 }
|
|
643
|
|
644 // force_a_stackframe
|
|
645 // This function is called by __xlc_exception_handle() to ensure a stack frame
|
|
646 // is created for __xlc_exception_handle().
|
|
647 __attribute__((noinline, optnone))
|
|
648 static void force_a_stackframe() {}
|
|
649
|
|
650 // __xlc_exception_handle
|
|
651 // This function is for xlclang++. It returns the address of the exception
|
|
652 // object stored in the reserved field in the stack of the caller of the
|
|
653 // function that calls __xlc_exception_handle() (within the link area for the
|
|
654 // call to the latter). The address is stored by the personality routine for
|
|
655 // xlclang++ compiled code. The implementation of __xlc_exception_handle()
|
|
656 // assumes a stack frame is created for it. The following ensures this
|
|
657 // assumption holds true: 1) a call to force_a_stackframe() is made inside
|
|
658 // __xlc_exception_handle() to make it non-leaf; and 2) optimizations are
|
|
659 // disabled for this function with attribute 'optnone'. Note: this function
|
|
660 // may not work as expected if these are changed.
|
|
661 __attribute__((optnone))
|
|
662 _LIBCXXABI_FUNC_VIS uintptr_t __xlc_exception_handle() {
|
|
663 // Make a call to force_a_stackframe() so that the compiler creates a stack
|
|
664 // frame for this function.
|
|
665 force_a_stackframe();
|
|
666
|
|
667 // Get the SP of this function, i.e., __xlc_exception_handle().
|
|
668 uintptr_t *lastStack;
|
|
669 asm("mr %0, 1" : "=r"(lastStack));
|
|
670 // Get the SP of the caller of __xlc_exception_handle().
|
|
671 uintptr_t *callerStack = reinterpret_cast<uintptr_t*>(lastStack[0]);
|
|
672 // Get the SP of the caller of the caller.
|
|
673 uintptr_t *callerStack2 = reinterpret_cast<uintptr_t*>(callerStack[0]);
|
|
674 uintptr_t exceptionObject = callerStack2[3];
|
|
675 _LIBCXXABI_TRACE_STATETAB("Handshake: exceptionObject=%p from stack=%p\n", reinterpret_cast<void*>(exceptionObject), reinterpret_cast<void*>(callerStack2));
|
|
676 return exceptionObject;
|
|
677 }
|
|
678
|
|
679 // xlclang++ may generate calls to __Deleted_Virtual.
|
|
680 _LIBCXXABI_FUNC_VIS void __Deleted_Virtual() { abort(); }
|
|
681
|
|
682 // __catchThrownException is called during AIX library initialization and
|
|
683 // termination to handle exceptions. An implementation is also provided in
|
|
684 // libC.a(shrcore.o). This implementation is provided for applications that
|
|
685 // link with -lc++ (the xlclang++ or ibm-clang++ link default.)
|
|
686 _LIBCXXABI_FUNC_VIS int
|
|
687 __catchThrownException(void (*cdfunc)(void), // function which may fail
|
|
688 void (*cleanup)(void*), // cleanup function
|
|
689 void* cleanuparg, // parameter to cleanup function
|
|
690 int action) { // control exception throwing and termination
|
|
691 enum Action : int { None = 0, Rethrow = 1, Terminate = 2 };
|
|
692 if (!cdfunc)
|
|
693 return 0;
|
|
694 if (action == Action::Rethrow && !cleanup) {
|
|
695 // No cleanup and rethrow is effectively no-op.
|
|
696 // Avoid the catch handler when possible to allow exceptions generated
|
|
697 // from xlC binaries to flow through.
|
|
698 (*cdfunc)();
|
|
699 return 0;
|
|
700 }
|
|
701 try {
|
|
702 (*cdfunc)();
|
|
703 } catch (...) {
|
|
704 if (action == Action::Terminate)
|
|
705 std::terminate();
|
|
706 if (cleanup)
|
|
707 (*cleanup)(cleanuparg);
|
|
708 if (action == Action::Rethrow)
|
|
709 throw;
|
|
710 assert(action == Action::None);
|
|
711 return -1; // FAILED
|
|
712 }
|
|
713 return 0;
|
|
714 }
|
|
715
|
|
716 } // extern "C"
|
|
717
|
|
718 } // __cxxabiv1
|