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