eh_personality.cc revision 132720
197403Sobrien// -*- C++ -*- The GNU C++ exception personality routine. 2117397Skan// Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. 397403Sobrien// 4132720Skan// This file is part of GCC. 597403Sobrien// 6132720Skan// GCC is free software; you can redistribute it and/or modify 797403Sobrien// it under the terms of the GNU General Public License as published by 897403Sobrien// the Free Software Foundation; either version 2, or (at your option) 997403Sobrien// any later version. 1097403Sobrien// 11132720Skan// GCC is distributed in the hope that it will be useful, 1297403Sobrien// but WITHOUT ANY WARRANTY; without even the implied warranty of 1397403Sobrien// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1497403Sobrien// GNU General Public License for more details. 1597403Sobrien// 1697403Sobrien// You should have received a copy of the GNU General Public License 17132720Skan// along with GCC; see the file COPYING. If not, write to 1897403Sobrien// the Free Software Foundation, 59 Temple Place - Suite 330, 1997403Sobrien// Boston, MA 02111-1307, USA. 2097403Sobrien 2197403Sobrien// As a special exception, you may use this file as part of a free software 2297403Sobrien// library without restriction. Specifically, if other files instantiate 2397403Sobrien// templates or use macros or inline functions from this file, or you compile 2497403Sobrien// this file and link it with other files to produce an executable, this 2597403Sobrien// file does not by itself cause the resulting executable to be covered by 2697403Sobrien// the GNU General Public License. This exception does not however 2797403Sobrien// invalidate any other reasons why the executable file might be covered by 2897403Sobrien// the GNU General Public License. 2997403Sobrien 3097403Sobrien 3197403Sobrien#include <bits/c++config.h> 3297403Sobrien#include <cstdlib> 3397403Sobrien#include <exception_defines.h> 3497403Sobrien#include "unwind-cxx.h" 3597403Sobrien 3697403Sobrienusing namespace __cxxabiv1; 3797403Sobrien 3897403Sobrien#include "unwind-pe.h" 3997403Sobrien 4097403Sobrien 4197403Sobrienstruct lsda_header_info 4297403Sobrien{ 4397403Sobrien _Unwind_Ptr Start; 4497403Sobrien _Unwind_Ptr LPStart; 4597403Sobrien _Unwind_Ptr ttype_base; 4697403Sobrien const unsigned char *TType; 4797403Sobrien const unsigned char *action_table; 4897403Sobrien unsigned char ttype_encoding; 4997403Sobrien unsigned char call_site_encoding; 5097403Sobrien}; 5197403Sobrien 5297403Sobrienstatic const unsigned char * 5397403Sobrienparse_lsda_header (_Unwind_Context *context, const unsigned char *p, 5497403Sobrien lsda_header_info *info) 5597403Sobrien{ 5697403Sobrien _Unwind_Word tmp; 5797403Sobrien unsigned char lpstart_encoding; 5897403Sobrien 5997403Sobrien info->Start = (context ? _Unwind_GetRegionStart (context) : 0); 6097403Sobrien 6197403Sobrien // Find @LPStart, the base to which landing pad offsets are relative. 6297403Sobrien lpstart_encoding = *p++; 6397403Sobrien if (lpstart_encoding != DW_EH_PE_omit) 6497403Sobrien p = read_encoded_value (context, lpstart_encoding, p, &info->LPStart); 6597403Sobrien else 6697403Sobrien info->LPStart = info->Start; 6797403Sobrien 6897403Sobrien // Find @TType, the base of the handler and exception spec type data. 6997403Sobrien info->ttype_encoding = *p++; 7097403Sobrien if (info->ttype_encoding != DW_EH_PE_omit) 7197403Sobrien { 7297403Sobrien p = read_uleb128 (p, &tmp); 7397403Sobrien info->TType = p + tmp; 7497403Sobrien } 7597403Sobrien else 7697403Sobrien info->TType = 0; 7797403Sobrien 7897403Sobrien // The encoding and length of the call-site table; the action table 7997403Sobrien // immediately follows. 8097403Sobrien info->call_site_encoding = *p++; 8197403Sobrien p = read_uleb128 (p, &tmp); 8297403Sobrien info->action_table = p + tmp; 8397403Sobrien 8497403Sobrien return p; 8597403Sobrien} 8697403Sobrien 8797403Sobrienstatic const std::type_info * 8897403Sobrienget_ttype_entry (lsda_header_info *info, _Unwind_Word i) 8997403Sobrien{ 9097403Sobrien _Unwind_Ptr ptr; 9197403Sobrien 9297403Sobrien i *= size_of_encoded_value (info->ttype_encoding); 9397403Sobrien read_encoded_value_with_base (info->ttype_encoding, info->ttype_base, 9497403Sobrien info->TType - i, &ptr); 9597403Sobrien 9697403Sobrien return reinterpret_cast<const std::type_info *>(ptr); 9797403Sobrien} 9897403Sobrien 9997403Sobrien// Given the thrown type THROW_TYPE, pointer to a variable containing a 10097403Sobrien// pointer to the exception object THROWN_PTR_P and a type CATCH_TYPE to 10197403Sobrien// compare against, return whether or not there is a match and if so, 10297403Sobrien// update *THROWN_PTR_P. 10397403Sobrien 10497403Sobrienstatic bool 10597403Sobrienget_adjusted_ptr (const std::type_info *catch_type, 10697403Sobrien const std::type_info *throw_type, 10797403Sobrien void **thrown_ptr_p) 10897403Sobrien{ 10997403Sobrien void *thrown_ptr = *thrown_ptr_p; 11097403Sobrien 11197403Sobrien // Pointer types need to adjust the actual pointer, not 11297403Sobrien // the pointer to pointer that is the exception object. 11397403Sobrien // This also has the effect of passing pointer types 11497403Sobrien // "by value" through the __cxa_begin_catch return value. 11597403Sobrien if (throw_type->__is_pointer_p ()) 11697403Sobrien thrown_ptr = *(void **) thrown_ptr; 11797403Sobrien 11897403Sobrien if (catch_type->__do_catch (throw_type, &thrown_ptr, 1)) 11997403Sobrien { 12097403Sobrien *thrown_ptr_p = thrown_ptr; 12197403Sobrien return true; 12297403Sobrien } 12397403Sobrien 12497403Sobrien return false; 12597403Sobrien} 12697403Sobrien 127117397Skan// Return true if THROW_TYPE matches one if the filter types. 128117397Skan 12997403Sobrienstatic bool 13097403Sobriencheck_exception_spec (lsda_header_info *info, const std::type_info *throw_type, 13197403Sobrien void *thrown_ptr, _Unwind_Sword filter_value) 13297403Sobrien{ 13397403Sobrien const unsigned char *e = info->TType - filter_value - 1; 13497403Sobrien 13597403Sobrien while (1) 13697403Sobrien { 13797403Sobrien const std::type_info *catch_type; 13897403Sobrien _Unwind_Word tmp; 13997403Sobrien 14097403Sobrien e = read_uleb128 (e, &tmp); 14197403Sobrien 14297403Sobrien // Zero signals the end of the list. If we've not found 14397403Sobrien // a match by now, then we've failed the specification. 14497403Sobrien if (tmp == 0) 14597403Sobrien return false; 14697403Sobrien 14797403Sobrien // Match a ttype entry. 14897403Sobrien catch_type = get_ttype_entry (info, tmp); 14997403Sobrien 15097403Sobrien // ??? There is currently no way to ask the RTTI code about the 15197403Sobrien // relationship between two types without reference to a specific 15297403Sobrien // object. There should be; then we wouldn't need to mess with 15397403Sobrien // thrown_ptr here. 15497403Sobrien if (get_adjusted_ptr (catch_type, throw_type, &thrown_ptr)) 15597403Sobrien return true; 15697403Sobrien } 15797403Sobrien} 15897403Sobrien 159117397Skan// Return true if the filter spec is empty, ie throw(). 160117397Skan 161117397Skanstatic bool 162117397Skanempty_exception_spec (lsda_header_info *info, _Unwind_Sword filter_value) 163117397Skan{ 164117397Skan const unsigned char *e = info->TType - filter_value - 1; 165117397Skan _Unwind_Word tmp; 166117397Skan 167117397Skan e = read_uleb128 (e, &tmp); 168117397Skan return tmp == 0; 169117397Skan} 170117397Skan 17197403Sobrien// Using a different personality function name causes link failures 17297403Sobrien// when trying to mix code using different exception handling models. 173132720Skan#ifdef _GLIBCXX_SJLJ_EXCEPTIONS 17497403Sobrien#define PERSONALITY_FUNCTION __gxx_personality_sj0 17597403Sobrien#define __builtin_eh_return_data_regno(x) x 17697403Sobrien#else 17797403Sobrien#define PERSONALITY_FUNCTION __gxx_personality_v0 17897403Sobrien#endif 17997403Sobrien 18097403Sobrienextern "C" _Unwind_Reason_Code 18197403SobrienPERSONALITY_FUNCTION (int version, 18297403Sobrien _Unwind_Action actions, 18397403Sobrien _Unwind_Exception_Class exception_class, 18497403Sobrien struct _Unwind_Exception *ue_header, 18597403Sobrien struct _Unwind_Context *context) 18697403Sobrien{ 18797403Sobrien __cxa_exception *xh = __get_exception_header_from_ue (ue_header); 18897403Sobrien 18997403Sobrien enum found_handler_type 19097403Sobrien { 19197403Sobrien found_nothing, 19297403Sobrien found_terminate, 19397403Sobrien found_cleanup, 19497403Sobrien found_handler 19597403Sobrien } found_type; 19697403Sobrien 19797403Sobrien lsda_header_info info; 19897403Sobrien const unsigned char *language_specific_data; 19997403Sobrien const unsigned char *action_record; 20097403Sobrien const unsigned char *p; 20197403Sobrien _Unwind_Ptr landing_pad, ip; 20297403Sobrien int handler_switch_value; 20397403Sobrien void *thrown_ptr = xh + 1; 20497403Sobrien 20597403Sobrien // Interface version check. 20697403Sobrien if (version != 1) 20797403Sobrien return _URC_FATAL_PHASE1_ERROR; 20897403Sobrien 20997403Sobrien // Shortcut for phase 2 found handler for domestic exception. 21097403Sobrien if (actions == (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME) 21197403Sobrien && exception_class == __gxx_exception_class) 21297403Sobrien { 21397403Sobrien handler_switch_value = xh->handlerSwitchValue; 214117397Skan language_specific_data = xh->languageSpecificData; 21597403Sobrien landing_pad = (_Unwind_Ptr) xh->catchTemp; 21697403Sobrien found_type = (landing_pad == 0 ? found_terminate : found_handler); 21797403Sobrien goto install_context; 21897403Sobrien } 21997403Sobrien 22097403Sobrien language_specific_data = (const unsigned char *) 22197403Sobrien _Unwind_GetLanguageSpecificData (context); 22297403Sobrien 22397403Sobrien // If no LSDA, then there are no handlers or cleanups. 22497403Sobrien if (! language_specific_data) 22597403Sobrien return _URC_CONTINUE_UNWIND; 22697403Sobrien 22797403Sobrien // Parse the LSDA header. 22897403Sobrien p = parse_lsda_header (context, language_specific_data, &info); 22997403Sobrien info.ttype_base = base_of_encoded_value (info.ttype_encoding, context); 23097403Sobrien ip = _Unwind_GetIP (context) - 1; 23197403Sobrien landing_pad = 0; 23297403Sobrien action_record = 0; 23397403Sobrien handler_switch_value = 0; 23497403Sobrien 235132720Skan#ifdef _GLIBCXX_SJLJ_EXCEPTIONS 23697403Sobrien // The given "IP" is an index into the call-site table, with two 23797403Sobrien // exceptions -- -1 means no-action, and 0 means terminate. But 23897403Sobrien // since we're using uleb128 values, we've not got random access 23997403Sobrien // to the array. 24097403Sobrien if ((int) ip < 0) 24197403Sobrien return _URC_CONTINUE_UNWIND; 24297403Sobrien else if (ip == 0) 24397403Sobrien { 24497403Sobrien // Fall through to set found_terminate. 24597403Sobrien } 24697403Sobrien else 24797403Sobrien { 24897403Sobrien _Unwind_Word cs_lp, cs_action; 24997403Sobrien do 25097403Sobrien { 25197403Sobrien p = read_uleb128 (p, &cs_lp); 25297403Sobrien p = read_uleb128 (p, &cs_action); 25397403Sobrien } 25497403Sobrien while (--ip); 25597403Sobrien 25697403Sobrien // Can never have null landing pad for sjlj -- that would have 25797403Sobrien // been indicated by a -1 call site index. 25897403Sobrien landing_pad = cs_lp + 1; 25997403Sobrien if (cs_action) 26097403Sobrien action_record = info.action_table + cs_action - 1; 26197403Sobrien goto found_something; 26297403Sobrien } 26397403Sobrien#else 26497403Sobrien // Search the call-site table for the action associated with this IP. 26597403Sobrien while (p < info.action_table) 26697403Sobrien { 26797403Sobrien _Unwind_Ptr cs_start, cs_len, cs_lp; 26897403Sobrien _Unwind_Word cs_action; 26997403Sobrien 27097403Sobrien // Note that all call-site encodings are "absolute" displacements. 27197403Sobrien p = read_encoded_value (0, info.call_site_encoding, p, &cs_start); 27297403Sobrien p = read_encoded_value (0, info.call_site_encoding, p, &cs_len); 27397403Sobrien p = read_encoded_value (0, info.call_site_encoding, p, &cs_lp); 27497403Sobrien p = read_uleb128 (p, &cs_action); 27597403Sobrien 27697403Sobrien // The table is sorted, so if we've passed the ip, stop. 27797403Sobrien if (ip < info.Start + cs_start) 27897403Sobrien p = info.action_table; 27997403Sobrien else if (ip < info.Start + cs_start + cs_len) 28097403Sobrien { 28197403Sobrien if (cs_lp) 28297403Sobrien landing_pad = info.LPStart + cs_lp; 28397403Sobrien if (cs_action) 28497403Sobrien action_record = info.action_table + cs_action - 1; 28597403Sobrien goto found_something; 28697403Sobrien } 28797403Sobrien } 288132720Skan#endif // _GLIBCXX_SJLJ_EXCEPTIONS 28997403Sobrien 29097403Sobrien // If ip is not present in the table, call terminate. This is for 29197403Sobrien // a destructor inside a cleanup, or a library routine the compiler 29297403Sobrien // was not expecting to throw. 293117397Skan found_type = found_terminate; 29497403Sobrien goto do_something; 29597403Sobrien 29697403Sobrien found_something: 29797403Sobrien if (landing_pad == 0) 29897403Sobrien { 29997403Sobrien // If ip is present, and has a null landing pad, there are 30097403Sobrien // no cleanups or handlers to be run. 30197403Sobrien found_type = found_nothing; 30297403Sobrien } 30397403Sobrien else if (action_record == 0) 30497403Sobrien { 30597403Sobrien // If ip is present, has a non-null landing pad, and a null 30697403Sobrien // action table offset, then there are only cleanups present. 30797403Sobrien // Cleanups use a zero switch value, as set above. 30897403Sobrien found_type = found_cleanup; 30997403Sobrien } 31097403Sobrien else 31197403Sobrien { 31297403Sobrien // Otherwise we have a catch handler or exception specification. 31397403Sobrien 31497403Sobrien _Unwind_Sword ar_filter, ar_disp; 31597403Sobrien const std::type_info *throw_type, *catch_type; 31697403Sobrien bool saw_cleanup = false; 31797403Sobrien bool saw_handler = false; 31897403Sobrien 31997403Sobrien // During forced unwinding, we only run cleanups. With a foreign 32097403Sobrien // exception class, there's no exception type. 32197403Sobrien // ??? What to do about GNU Java and GNU Ada exceptions. 32297403Sobrien 32397403Sobrien if ((actions & _UA_FORCE_UNWIND) 32497403Sobrien || exception_class != __gxx_exception_class) 32597403Sobrien throw_type = 0; 32697403Sobrien else 32797403Sobrien throw_type = xh->exceptionType; 32897403Sobrien 32997403Sobrien while (1) 33097403Sobrien { 33197403Sobrien p = action_record; 33297403Sobrien p = read_sleb128 (p, &ar_filter); 33397403Sobrien read_sleb128 (p, &ar_disp); 33497403Sobrien 33597403Sobrien if (ar_filter == 0) 33697403Sobrien { 33797403Sobrien // Zero filter values are cleanups. 33897403Sobrien saw_cleanup = true; 33997403Sobrien } 34097403Sobrien else if (ar_filter > 0) 34197403Sobrien { 34297403Sobrien // Positive filter values are handlers. 34397403Sobrien catch_type = get_ttype_entry (&info, ar_filter); 34497403Sobrien 345117397Skan // Null catch type is a catch-all handler; we can catch foreign 346117397Skan // exceptions with this. Otherwise we must match types. 347117397Skan if (! catch_type 348117397Skan || (throw_type 349117397Skan && get_adjusted_ptr (catch_type, throw_type, 350117397Skan &thrown_ptr))) 35197403Sobrien { 352117397Skan saw_handler = true; 353117397Skan break; 35497403Sobrien } 35597403Sobrien } 35697403Sobrien else 35797403Sobrien { 35897403Sobrien // Negative filter values are exception specifications. 35997403Sobrien // ??? How do foreign exceptions fit in? As far as I can 36097403Sobrien // see we can't match because there's no __cxa_exception 36197403Sobrien // object to stuff bits in for __cxa_call_unexpected to use. 362117397Skan // Allow them iff the exception spec is non-empty. I.e. 363117397Skan // a throw() specification results in __unexpected. 36497403Sobrien if (throw_type 365117397Skan ? ! check_exception_spec (&info, throw_type, thrown_ptr, 366117397Skan ar_filter) 367117397Skan : empty_exception_spec (&info, ar_filter)) 36897403Sobrien { 36997403Sobrien saw_handler = true; 37097403Sobrien break; 37197403Sobrien } 37297403Sobrien } 37397403Sobrien 37497403Sobrien if (ar_disp == 0) 37597403Sobrien break; 37697403Sobrien action_record = p + ar_disp; 37797403Sobrien } 37897403Sobrien 37997403Sobrien if (saw_handler) 38097403Sobrien { 38197403Sobrien handler_switch_value = ar_filter; 38297403Sobrien found_type = found_handler; 38397403Sobrien } 38497403Sobrien else 38597403Sobrien found_type = (saw_cleanup ? found_cleanup : found_nothing); 38697403Sobrien } 38797403Sobrien 38897403Sobrien do_something: 38997403Sobrien if (found_type == found_nothing) 39097403Sobrien return _URC_CONTINUE_UNWIND; 39197403Sobrien 39297403Sobrien if (actions & _UA_SEARCH_PHASE) 39397403Sobrien { 39497403Sobrien if (found_type == found_cleanup) 39597403Sobrien return _URC_CONTINUE_UNWIND; 39697403Sobrien 39797403Sobrien // For domestic exceptions, we cache data from phase 1 for phase 2. 39897403Sobrien if (exception_class == __gxx_exception_class) 39997403Sobrien { 40097403Sobrien xh->handlerSwitchValue = handler_switch_value; 40197403Sobrien xh->actionRecord = action_record; 40297403Sobrien xh->languageSpecificData = language_specific_data; 40397403Sobrien xh->adjustedPtr = thrown_ptr; 40497403Sobrien 40597403Sobrien // ??? Completely unknown what this field is supposed to be for. 40697403Sobrien // ??? Need to cache TType encoding base for call_unexpected. 407117397Skan xh->catchTemp = landing_pad; 40897403Sobrien } 40997403Sobrien return _URC_HANDLER_FOUND; 41097403Sobrien } 41197403Sobrien 41297403Sobrien install_context: 413117397Skan // We can't use any of the cxa routines with foreign exceptions, 414117397Skan // because they all expect ue_header to be a struct __cxa_exception. 415117397Skan // So in that case, call terminate or unexpected directly. 416117397Skan if ((actions & _UA_FORCE_UNWIND) 417117397Skan || exception_class != __gxx_exception_class) 41897403Sobrien { 419117397Skan if (found_type == found_terminate) 420117397Skan std::terminate (); 421117397Skan else if (handler_switch_value < 0) 422117397Skan { 423117397Skan try 424117397Skan { std::unexpected (); } 425117397Skan catch(...) 426117397Skan { std::terminate (); } 427117397Skan } 42897403Sobrien } 429117397Skan else 430117397Skan { 431117397Skan if (found_type == found_terminate) 432117397Skan { 433117397Skan __cxa_begin_catch (&xh->unwindHeader); 434117397Skan __terminate (xh->terminateHandler); 435117397Skan } 43697403Sobrien 437117397Skan // Cache the TType base value for __cxa_call_unexpected, as we won't 438117397Skan // have an _Unwind_Context then. 439117397Skan if (handler_switch_value < 0) 440117397Skan { 441117397Skan parse_lsda_header (context, language_specific_data, &info); 442117397Skan xh->catchTemp = base_of_encoded_value (info.ttype_encoding, context); 443117397Skan } 44497403Sobrien } 44597403Sobrien 446132720Skan /* For targets with pointers smaller than the word size, we must extend the 447132720Skan pointer, and this extension is target dependent. */ 44897403Sobrien _Unwind_SetGR (context, __builtin_eh_return_data_regno (0), 449132720Skan __builtin_extend_pointer (&xh->unwindHeader)); 45097403Sobrien _Unwind_SetGR (context, __builtin_eh_return_data_regno (1), 45197403Sobrien handler_switch_value); 45297403Sobrien _Unwind_SetIP (context, landing_pad); 45397403Sobrien return _URC_INSTALL_CONTEXT; 45497403Sobrien} 45597403Sobrien 45697403Sobrienextern "C" void 45797403Sobrien__cxa_call_unexpected (void *exc_obj_in) 45897403Sobrien{ 45997403Sobrien _Unwind_Exception *exc_obj 46097403Sobrien = reinterpret_cast <_Unwind_Exception *>(exc_obj_in); 46197403Sobrien 46297403Sobrien __cxa_begin_catch (exc_obj); 46397403Sobrien 46497403Sobrien // This function is a handler for our exception argument. If we exit 46597403Sobrien // by throwing a different exception, we'll need the original cleaned up. 46697403Sobrien struct end_catch_protect 46797403Sobrien { 46897403Sobrien end_catch_protect() { } 46997403Sobrien ~end_catch_protect() { __cxa_end_catch(); } 47097403Sobrien } end_catch_protect_obj; 47197403Sobrien 47297403Sobrien lsda_header_info info; 47397403Sobrien __cxa_exception *xh = __get_exception_header_from_ue (exc_obj); 47497403Sobrien const unsigned char *xh_lsda; 47597403Sobrien _Unwind_Sword xh_switch_value; 47697403Sobrien std::terminate_handler xh_terminate_handler; 47797403Sobrien 47897403Sobrien // If the unexpectedHandler rethrows the exception (e.g. to categorize it), 47997403Sobrien // it will clobber data about the current handler. So copy the data out now. 48097403Sobrien xh_lsda = xh->languageSpecificData; 48197403Sobrien xh_switch_value = xh->handlerSwitchValue; 48297403Sobrien xh_terminate_handler = xh->terminateHandler; 48397403Sobrien info.ttype_base = (_Unwind_Ptr) xh->catchTemp; 48497403Sobrien 48597403Sobrien try 48697403Sobrien { __unexpected (xh->unexpectedHandler); } 48797403Sobrien catch(...) 48897403Sobrien { 48997403Sobrien // Get the exception thrown from unexpected. 490117397Skan 49197403Sobrien __cxa_eh_globals *globals = __cxa_get_globals_fast (); 49297403Sobrien __cxa_exception *new_xh = globals->caughtExceptions; 49397403Sobrien void *new_ptr = new_xh + 1; 494117397Skan 49597403Sobrien // We don't quite have enough stuff cached; re-parse the LSDA. 49697403Sobrien parse_lsda_header (0, xh_lsda, &info); 497117397Skan 49897403Sobrien // If this new exception meets the exception spec, allow it. 49997403Sobrien if (check_exception_spec (&info, new_xh->exceptionType, 50097403Sobrien new_ptr, xh_switch_value)) 50197403Sobrien __throw_exception_again; 502117397Skan 50397403Sobrien // If the exception spec allows std::bad_exception, throw that. 50497403Sobrien // We don't have a thrown object to compare against, but since 50597403Sobrien // bad_exception doesn't have virtual bases, that's OK; just pass 0. 50697403Sobrien#ifdef __EXCEPTIONS 50797403Sobrien const std::type_info &bad_exc = typeid (std::bad_exception); 50897403Sobrien if (check_exception_spec (&info, &bad_exc, 0, xh_switch_value)) 50997403Sobrien throw std::bad_exception(); 51097403Sobrien#endif 511117397Skan 51297403Sobrien // Otherwise, die. 51397403Sobrien __terminate (xh_terminate_handler); 51497403Sobrien } 51597403Sobrien} 516