You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

816 lines
24 KiB
C++

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

// -*- C++ -*- The GNU C++ exception personality routine.
// Copyright (C) 2001-2022 Free Software Foundation, Inc.
//
// This file is part of GCC.
//
// GCC is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3, or (at your option)
// any later version.
//
// GCC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
#include <bits/c++config.h>
#include <cstdlib>
#include <bits/exception_defines.h>
#include <cxxabi.h>
#include "unwind-cxx.h"
using namespace __cxxabiv1;
#include "unwind-pe.h"
struct lsda_header_info
{
_Unwind_Ptr Start;
_Unwind_Ptr LPStart;
_Unwind_Ptr ttype_base;
const unsigned char *TType;
const unsigned char *action_table;
unsigned char ttype_encoding;
unsigned char call_site_encoding;
};
static const unsigned char *
parse_lsda_header (_Unwind_Context *context, const unsigned char *p,
lsda_header_info *info)
{
_uleb128_t tmp;
unsigned char lpstart_encoding;
info->Start = (context ? _Unwind_GetRegionStart (context) : 0);
// Find @LPStart, the base to which landing pad offsets are relative.
lpstart_encoding = *p++;
if (lpstart_encoding != DW_EH_PE_omit)
p = read_encoded_value (context, lpstart_encoding, p, &info->LPStart);
else
info->LPStart = info->Start;
// Find @TType, the base of the handler and exception spec type data.
info->ttype_encoding = *p++;
if (info->ttype_encoding != DW_EH_PE_omit)
{
#if _GLIBCXX_OVERRIDE_TTYPE_ENCODING
/* Older ARM EABI toolchains set this value incorrectly, so use a
hardcoded OS-specific format. */
info->ttype_encoding = _GLIBCXX_OVERRIDE_TTYPE_ENCODING;
#endif
p = read_uleb128 (p, &tmp);
info->TType = p + tmp;
}
else
info->TType = 0;
// The encoding and length of the call-site table; the action table
// immediately follows.
info->call_site_encoding = *p++;
p = read_uleb128 (p, &tmp);
info->action_table = p + tmp;
return p;
}
// Return an element from a type table.
static const std::type_info *
get_ttype_entry (lsda_header_info *info, _uleb128_t i)
{
_Unwind_Ptr ptr;
i *= size_of_encoded_value (info->ttype_encoding);
read_encoded_value_with_base (
#if __FDPIC__
/* Force these flags to nake sure to
take the GOT into account. */
(DW_EH_PE_pcrel | DW_EH_PE_indirect),
#else
info->ttype_encoding,
#endif
info->ttype_base,
info->TType - i, &ptr);
return reinterpret_cast<const std::type_info *>(ptr);
}
#ifdef __ARM_EABI_UNWINDER__
// The ABI provides a routine for matching exception object types.
typedef _Unwind_Control_Block _throw_typet;
#define get_adjusted_ptr(catch_type, throw_type, thrown_ptr_p) \
(__cxa_type_match (throw_type, catch_type, false, thrown_ptr_p) \
!= ctm_failed)
// Return true if THROW_TYPE matches one if the filter types.
static bool
check_exception_spec(lsda_header_info* info, _throw_typet* throw_type,
void* thrown_ptr, _sleb128_t filter_value)
{
const _uleb128_t* e = ((const _uleb128_t*) info->TType)
- filter_value - 1;
while (1)
{
const std::type_info* catch_type;
_uleb128_t tmp;
tmp = *e;
// Zero signals the end of the list. If we've not found
// a match by now, then we've failed the specification.
if (tmp == 0)
return false;
tmp = _Unwind_decode_typeinfo_ptr(info->ttype_base, (_Unwind_Word) e);
// Match a ttype entry.
catch_type = reinterpret_cast<const std::type_info*>(tmp);
// ??? There is currently no way to ask the RTTI code about the
// relationship between two types without reference to a specific
// object. There should be; then we wouldn't need to mess with
// thrown_ptr here.
if (get_adjusted_ptr(catch_type, throw_type, &thrown_ptr))
return true;
// Advance to the next entry.
e++;
}
}
// Save stage1 handler information in the exception object
static inline void
save_caught_exception(struct _Unwind_Exception* ue_header,
struct _Unwind_Context* context,
void* thrown_ptr,
int handler_switch_value,
const unsigned char* language_specific_data,
_Unwind_Ptr landing_pad,
const unsigned char* action_record
__attribute__((__unused__)))
{
ue_header->barrier_cache.sp = _Unwind_GetGR(context, UNWIND_STACK_REG);
ue_header->barrier_cache.bitpattern[0] = (_uw) thrown_ptr;
ue_header->barrier_cache.bitpattern[1]
= (_uw) handler_switch_value;
ue_header->barrier_cache.bitpattern[2]
= (_uw) language_specific_data;
ue_header->barrier_cache.bitpattern[3] = (_uw) landing_pad;
}
// Restore the catch handler data saved during phase1.
static inline void
restore_caught_exception(struct _Unwind_Exception* ue_header,
int& handler_switch_value,
const unsigned char*& language_specific_data,
_Unwind_Ptr& landing_pad)
{
handler_switch_value = (int) ue_header->barrier_cache.bitpattern[1];
language_specific_data =
(const unsigned char*) ue_header->barrier_cache.bitpattern[2];
landing_pad = (_Unwind_Ptr) ue_header->barrier_cache.bitpattern[3];
}
#define CONTINUE_UNWINDING \
do \
{ \
if (__gnu_unwind_frame(ue_header, context) != _URC_OK) \
return _URC_FAILURE; \
return _URC_CONTINUE_UNWIND; \
} \
while (0)
// Return true if the filter spec is empty, ie throw().
static bool
empty_exception_spec (lsda_header_info *info, _Unwind_Sword filter_value)
{
const _Unwind_Word* e = ((const _Unwind_Word*) info->TType)
- filter_value - 1;
return *e == 0;
}
#else
typedef const std::type_info _throw_typet;
// Given the thrown type THROW_TYPE, pointer to a variable containing a
// pointer to the exception object THROWN_PTR_P and a type CATCH_TYPE to
// compare against, return whether or not there is a match and if so,
// update *THROWN_PTR_P.
static bool
get_adjusted_ptr (const std::type_info *catch_type,
const std::type_info *throw_type,
void **thrown_ptr_p)
{
void *thrown_ptr = *thrown_ptr_p;
// Pointer types need to adjust the actual pointer, not
// the pointer to pointer that is the exception object.
// This also has the effect of passing pointer types
// "by value" through the __cxa_begin_catch return value.
if (throw_type->__is_pointer_p ())
thrown_ptr = *(void **) thrown_ptr;
if (catch_type->__do_catch (throw_type, &thrown_ptr, 1))
{
*thrown_ptr_p = thrown_ptr;
return true;
}
return false;
}
// Return true if THROW_TYPE matches one if the filter types.
static bool
check_exception_spec(lsda_header_info* info, _throw_typet* throw_type,
void* thrown_ptr, _sleb128_t filter_value)
{
const unsigned char *e = info->TType - filter_value - 1;
while (1)
{
const std::type_info *catch_type;
_uleb128_t tmp;
e = read_uleb128 (e, &tmp);
// Zero signals the end of the list. If we've not found
// a match by now, then we've failed the specification.
if (tmp == 0)
return false;
// Match a ttype entry.
catch_type = get_ttype_entry (info, tmp);
// ??? There is currently no way to ask the RTTI code about the
// relationship between two types without reference to a specific
// object. There should be; then we wouldn't need to mess with
// thrown_ptr here.
if (get_adjusted_ptr (catch_type, throw_type, &thrown_ptr))
return true;
}
}
// Save stage1 handler information in the exception object
static inline void
save_caught_exception(struct _Unwind_Exception* ue_header,
struct _Unwind_Context* context
__attribute__((__unused__)),
void* thrown_ptr,
int handler_switch_value,
const unsigned char* language_specific_data,
_Unwind_Ptr landing_pad __attribute__((__unused__)),
const unsigned char* action_record)
{
__cxa_exception* xh = __get_exception_header_from_ue(ue_header);
xh->handlerSwitchValue = handler_switch_value;
xh->actionRecord = action_record;
xh->languageSpecificData = language_specific_data;
xh->adjustedPtr = thrown_ptr;
// ??? Completely unknown what this field is supposed to be for.
// ??? Need to cache TType encoding base for call_unexpected.
xh->catchTemp = landing_pad;
}
// Restore the catch handler information saved during phase1.
static inline void
restore_caught_exception(struct _Unwind_Exception* ue_header,
int& handler_switch_value,
const unsigned char*& language_specific_data,
_Unwind_Ptr& landing_pad)
{
__cxa_exception* xh = __get_exception_header_from_ue(ue_header);
handler_switch_value = xh->handlerSwitchValue;
language_specific_data = xh->languageSpecificData;
landing_pad = (_Unwind_Ptr) xh->catchTemp;
}
#define CONTINUE_UNWINDING return _URC_CONTINUE_UNWIND
// Return true if the filter spec is empty, ie throw().
static bool
empty_exception_spec (lsda_header_info *info, _Unwind_Sword filter_value)
{
const unsigned char *e = info->TType - filter_value - 1;
_uleb128_t tmp;
e = read_uleb128 (e, &tmp);
return tmp == 0;
}
#endif // !__ARM_EABI_UNWINDER__
namespace __cxxabiv1
{
// Using a different personality function name causes link failures
// when trying to mix code using different exception handling models.
#ifdef __USING_SJLJ_EXCEPTIONS__
#define PERSONALITY_FUNCTION __gxx_personality_sj0
#define __builtin_eh_return_data_regno(x) x
#elif defined(__SEH__)
#define PERSONALITY_FUNCTION __gxx_personality_imp
#else
#define PERSONALITY_FUNCTION __gxx_personality_v0
#endif
#if defined (__SEH__) && !defined (__USING_SJLJ_EXCEPTIONS__)
static
#else
extern "C"
#endif
_Unwind_Reason_Code
#ifdef __ARM_EABI_UNWINDER__
__attribute__((target ("general-regs-only")))
PERSONALITY_FUNCTION (_Unwind_State state,
struct _Unwind_Exception* ue_header,
struct _Unwind_Context* context)
#else
PERSONALITY_FUNCTION (int version,
_Unwind_Action actions,
_Unwind_Exception_Class exception_class,
struct _Unwind_Exception *ue_header,
struct _Unwind_Context *context)
#endif
{
enum found_handler_type
{
found_nothing,
found_terminate,
found_cleanup,
found_handler
} found_type;
lsda_header_info info;
const unsigned char *language_specific_data;
const unsigned char *action_record;
const unsigned char *p;
_Unwind_Ptr landing_pad, ip;
int handler_switch_value;
void* thrown_ptr = 0;
bool foreign_exception;
int ip_before_insn = 0;
#ifdef __ARM_EABI_UNWINDER__
_Unwind_Action actions;
switch (state & _US_ACTION_MASK)
{
case _US_VIRTUAL_UNWIND_FRAME:
// If the unwind state pattern is
// _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND
// then we don't need to search for any handler as it is not a real
// exception. Just unwind the stack.
if (state & _US_FORCE_UNWIND)
CONTINUE_UNWINDING;
actions = _UA_SEARCH_PHASE;
break;
case _US_UNWIND_FRAME_STARTING:
actions = _UA_CLEANUP_PHASE;
if (!(state & _US_FORCE_UNWIND)
&& ue_header->barrier_cache.sp == _Unwind_GetGR(context,
UNWIND_STACK_REG))
actions |= _UA_HANDLER_FRAME;
break;
case _US_UNWIND_FRAME_RESUME:
CONTINUE_UNWINDING;
break;
default:
std::abort();
}
actions |= state & _US_FORCE_UNWIND;
// We don't know which runtime we're working with, so can't check this.
// However the ABI routines hide this from us, and we don't actually need
// to know.
foreign_exception = false;
// The dwarf unwinder assumes the context structure holds things like the
// function and LSDA pointers. The ARM implementation caches these in
// the exception header (UCB). To avoid rewriting everything we make a
// virtual scratch register point at the UCB.
ip = (_Unwind_Ptr) ue_header;
_Unwind_SetGR(context, UNWIND_POINTER_REG, ip);
#else
__cxa_exception* xh = __get_exception_header_from_ue(ue_header);
// Interface version check.
if (version != 1)
return _URC_FATAL_PHASE1_ERROR;
foreign_exception = !__is_gxx_exception_class(exception_class);
#endif
// Shortcut for phase 2 found handler for domestic exception.
if (actions == (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME)
&& !foreign_exception)
{
restore_caught_exception(ue_header, handler_switch_value,
language_specific_data, landing_pad);
found_type = (landing_pad == 0 ? found_terminate : found_handler);
goto install_context;
}
language_specific_data = (const unsigned char *)
_Unwind_GetLanguageSpecificData (context);
// If no LSDA, then there are no handlers or cleanups.
if (! language_specific_data)
CONTINUE_UNWINDING;
// Parse the LSDA header.
p = parse_lsda_header (context, language_specific_data, &info);
info.ttype_base = base_of_encoded_value (info.ttype_encoding, context);
#ifdef _GLIBCXX_HAVE_GETIPINFO
ip = _Unwind_GetIPInfo (context, &ip_before_insn);
#else
ip = _Unwind_GetIP (context);
#endif
if (! ip_before_insn)
--ip;
landing_pad = 0;
action_record = 0;
handler_switch_value = 0;
#ifdef __USING_SJLJ_EXCEPTIONS__
// The given "IP" is an index into the call-site table, with two
// exceptions -- -1 means no-action, and 0 means terminate. But
// since we're using uleb128 values, we've not got random access
// to the array.
if ((int) ip < 0)
return _URC_CONTINUE_UNWIND;
else if (ip == 0)
{
// Fall through to set found_terminate.
}
else
{
_uleb128_t cs_lp, cs_action;
do
{
p = read_uleb128 (p, &cs_lp);
p = read_uleb128 (p, &cs_action);
}
while (--ip);
// Can never have null landing pad for sjlj -- that would have
// been indicated by a -1 call site index.
landing_pad = cs_lp + 1;
if (cs_action)
action_record = info.action_table + cs_action - 1;
goto found_something;
}
#else
// Search the call-site table for the action associated with this IP.
while (p < info.action_table)
{
_Unwind_Ptr cs_start, cs_len, cs_lp;
_uleb128_t cs_action;
// Note that all call-site encodings are "absolute" displacements.
p = read_encoded_value (0, info.call_site_encoding, p, &cs_start);
p = read_encoded_value (0, info.call_site_encoding, p, &cs_len);
p = read_encoded_value (0, info.call_site_encoding, p, &cs_lp);
p = read_uleb128 (p, &cs_action);
// The table is sorted, so if we've passed the ip, stop.
if (ip < info.Start + cs_start)
p = info.action_table;
else if (ip < info.Start + cs_start + cs_len)
{
if (cs_lp)
landing_pad = info.LPStart + cs_lp;
if (cs_action)
action_record = info.action_table + cs_action - 1;
goto found_something;
}
}
#endif // __USING_SJLJ_EXCEPTIONS__
// If ip is not present in the table, call terminate. This is for
// a destructor inside a cleanup, or a library routine the compiler
// was not expecting to throw.
found_type = found_terminate;
goto do_something;
found_something:
if (landing_pad == 0)
{
// If ip is present, and has a null landing pad, there are
// no cleanups or handlers to be run.
found_type = found_nothing;
}
else if (action_record == 0)
{
// If ip is present, has a non-null landing pad, and a null
// action table offset, then there are only cleanups present.
// Cleanups use a zero switch value, as set above.
found_type = found_cleanup;
}
else
{
// Otherwise we have a catch handler or exception specification.
_sleb128_t ar_filter, ar_disp;
const std::type_info* catch_type;
_throw_typet* throw_type;
bool saw_cleanup = false;
bool saw_handler = false;
#ifdef __ARM_EABI_UNWINDER__
// ??? How does this work - more importantly, how does it interact with
// dependent exceptions?
throw_type = ue_header;
if (actions & _UA_FORCE_UNWIND)
{
__GXX_INIT_FORCED_UNWIND_CLASS(ue_header->exception_class);
}
else if (!foreign_exception)
thrown_ptr = __get_object_from_ue (ue_header);
#else
#if __cpp_rtti
// During forced unwinding, match a magic exception type.
if (actions & _UA_FORCE_UNWIND)
{
throw_type = &typeid(abi::__forced_unwind);
}
// With a foreign exception class, there's no exception type.
// ??? What to do about GNU Java and GNU Ada exceptions?
else if (foreign_exception)
{
throw_type = &typeid(abi::__foreign_exception);
}
else
#endif
{
thrown_ptr = __get_object_from_ue (ue_header);
throw_type = __get_exception_header_from_obj
(thrown_ptr)->exceptionType;
}
#endif
while (1)
{
p = action_record;
p = read_sleb128 (p, &ar_filter);
read_sleb128 (p, &ar_disp);
if (ar_filter == 0)
{
// Zero filter values are cleanups.
saw_cleanup = true;
}
else if (ar_filter > 0)
{
// Positive filter values are handlers.
catch_type = get_ttype_entry (&info, ar_filter);
// Null catch type is a catch-all handler; we can catch foreign
// exceptions with this. Otherwise we must match types.
if (! catch_type
|| (throw_type
&& get_adjusted_ptr (catch_type, throw_type,
&thrown_ptr)))
{
saw_handler = true;
break;
}
}
else
{
// Negative filter values are exception specifications.
// ??? How do foreign exceptions fit in? As far as I can
// see we can't match because there's no __cxa_exception
// object to stuff bits in for __cxa_call_unexpected to use.
// Allow them iff the exception spec is non-empty. I.e.
// a throw() specification results in __unexpected.
if ((throw_type
&& !(actions & _UA_FORCE_UNWIND)
&& !foreign_exception)
? ! check_exception_spec (&info, throw_type, thrown_ptr,
ar_filter)
: empty_exception_spec (&info, ar_filter))
{
saw_handler = true;
break;
}
}
if (ar_disp == 0)
break;
action_record = p + ar_disp;
}
if (saw_handler)
{
handler_switch_value = ar_filter;
found_type = found_handler;
}
else
found_type = (saw_cleanup ? found_cleanup : found_nothing);
}
do_something:
if (found_type == found_nothing)
CONTINUE_UNWINDING;
if (actions & _UA_SEARCH_PHASE)
{
if (found_type == found_cleanup)
CONTINUE_UNWINDING;
// For domestic exceptions, we cache data from phase 1 for phase 2.
if (!foreign_exception)
{
save_caught_exception(ue_header, context, thrown_ptr,
handler_switch_value, language_specific_data,
landing_pad, action_record);
}
return _URC_HANDLER_FOUND;
}
install_context:
// We can't use any of the cxa routines with foreign exceptions,
// because they all expect ue_header to be a struct __cxa_exception.
// So in that case, call terminate or unexpected directly.
if ((actions & _UA_FORCE_UNWIND)
|| foreign_exception)
{
if (found_type == found_terminate)
std::terminate ();
else if (handler_switch_value < 0)
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
__try
{ std::unexpected (); }
__catch(...)
{ std::terminate (); }
#pragma GCC diagnostic pop
}
}
else
{
if (found_type == found_terminate)
__cxa_call_terminate(ue_header);
// Cache the TType base value for __cxa_call_unexpected, as we won't
// have an _Unwind_Context then.
if (handler_switch_value < 0)
{
parse_lsda_header (context, language_specific_data, &info);
info.ttype_base = base_of_encoded_value (info.ttype_encoding,
context);
#ifdef __ARM_EABI_UNWINDER__
const _Unwind_Word* e;
_Unwind_Word n;
e = ((const _Unwind_Word*) info.TType) - handler_switch_value - 1;
// Count the number of rtti objects.
n = 0;
while (e[n] != 0)
n++;
// Count.
ue_header->barrier_cache.bitpattern[1] = n;
// Base
ue_header->barrier_cache.bitpattern[2] = info.ttype_base;
// Stride.
ue_header->barrier_cache.bitpattern[3] = 4;
// List head.
ue_header->barrier_cache.bitpattern[4] = (_Unwind_Word) e;
#else
xh->catchTemp = base_of_encoded_value (info.ttype_encoding, context);
#endif
}
}
/* For targets with pointers smaller than the word size, we must extend the
pointer, and this extension is target dependent. */
_Unwind_SetGR (context, __builtin_eh_return_data_regno (0),
__builtin_extend_pointer (ue_header));
_Unwind_SetGR (context, __builtin_eh_return_data_regno (1),
handler_switch_value);
_Unwind_SetIP (context, landing_pad);
#ifdef __ARM_EABI_UNWINDER__
if (found_type == found_cleanup)
__cxa_begin_cleanup(ue_header);
#endif
return _URC_INSTALL_CONTEXT;
}
/* The ARM EABI implementation of __cxa_call_unexpected is in a
different file so that the personality routine (PR) can be used
standalone. The generic routine shared datastructures with the PR
so it is most convenient to implement it here. */
#ifndef __ARM_EABI_UNWINDER__
extern "C" void
__cxa_call_unexpected (void *exc_obj_in)
{
_Unwind_Exception *exc_obj
= reinterpret_cast <_Unwind_Exception *>(exc_obj_in);
__cxa_begin_catch (exc_obj);
// This function is a handler for our exception argument. If we exit
// by throwing a different exception, we'll need the original cleaned up.
struct end_catch_protect
{
end_catch_protect() { }
~end_catch_protect() { __cxa_end_catch(); }
} end_catch_protect_obj;
lsda_header_info info;
__cxa_exception *xh = __get_exception_header_from_ue (exc_obj);
const unsigned char *xh_lsda;
_Unwind_Sword xh_switch_value;
std::terminate_handler xh_terminate_handler;
// If the unexpectedHandler rethrows the exception (e.g. to categorize it),
// it will clobber data about the current handler. So copy the data out now.
xh_lsda = xh->languageSpecificData;
xh_switch_value = xh->handlerSwitchValue;
xh_terminate_handler = xh->terminateHandler;
info.ttype_base = (_Unwind_Ptr) xh->catchTemp;
__try
{ __unexpected (xh->unexpectedHandler); }
__catch(...)
{
// Get the exception thrown from unexpected.
__cxa_eh_globals *globals = __cxa_get_globals_fast ();
__cxa_exception *new_xh = globals->caughtExceptions;
void *new_ptr = __get_object_from_ambiguous_exception (new_xh);
// We don't quite have enough stuff cached; re-parse the LSDA.
parse_lsda_header (0, xh_lsda, &info);
// If this new exception meets the exception spec, allow it.
if (check_exception_spec (&info, __get_exception_header_from_obj
(new_ptr)->exceptionType,
new_ptr, xh_switch_value))
{ __throw_exception_again; }
// If the exception spec allows std::bad_exception, throw that.
// We don't have a thrown object to compare against, but since
// bad_exception doesn't have virtual bases, that's OK; just pass 0.
#if __cpp_exceptions && __cpp_rtti
const std::type_info &bad_exc = typeid (std::bad_exception);
if (check_exception_spec (&info, &bad_exc, 0, xh_switch_value))
throw std::bad_exception();
#endif
// Otherwise, die.
__terminate (xh_terminate_handler);
}
}
#endif
#if defined (__SEH__) && !defined (__USING_SJLJ_EXCEPTIONS__)
extern "C"
EXCEPTION_DISPOSITION
__gxx_personality_seh0 (PEXCEPTION_RECORD ms_exc, void *this_frame,
PCONTEXT ms_orig_context, PDISPATCHER_CONTEXT ms_disp)
{
return _GCC_specific_handler (ms_exc, this_frame, ms_orig_context,
ms_disp, __gxx_personality_imp);
}
#endif /* SEH */
} // namespace __cxxabiv1