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