1 // Copyright (C) 2012 The Android Open Source Project 2 // All rights reserved. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions 6 // are met: 7 // 1. Redistributions of source code must retain the above copyright 8 // notice, this list of conditions and the following disclaimer. 9 // 2. Redistributions in binary form must reproduce the above copyright 10 // notice, this list of conditions and the following disclaimer in the 11 // documentation and/or other materials provided with the distribution. 12 // 3. Neither the name of the project nor the names of its contributors 13 // may be used to endorse or promote products derived from this software 14 // without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 17 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 // ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 20 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 // SUCH DAMAGE. 27 //===----------------------------------------------------------------------===// 28 // The LLVM Compiler Infrastructure 29 // 30 // This file is dual licensed under the MIT and the University of Illinois Open 31 // Source Licenses. See LICENSE.TXT for details. 32 // 33 // 34 // This file implements the "Exception Handling APIs" 35 // http://www.codesourcery.com/public/cxx-abi/abi-eh.html 36 // http://www.intel.com/design/itanium/downloads/245358.htm 37 // 38 //===----------------------------------------------------------------------===// 39 40 #include <cxxabi.h> 41 #include <exception> 42 #include <unwind.h> 43 #include "helper_func_internal.h" 44 45 #include <android/log.h> 46 #include <dlfcn.h> 47 #include <stdio.h> 48 49 namespace __cxxabiv1 { 50 51 const __shim_type_info* getTypePtr(uint64_t ttypeIndex, 52 const uint8_t* classInfo, 53 uint8_t ttypeEncoding, 54 _Unwind_Exception* unwind_exception); 55 call_terminate(_Unwind_Exception * unwind_exception)56 void call_terminate(_Unwind_Exception* unwind_exception) { 57 __cxa_begin_catch(unwind_exception); // terminate is also a handler 58 std::terminate(); 59 } 60 61 // Boring stuff which has lots of encode/decode details scanEHTable(ScanResultInternal & results,_Unwind_Action actions,bool native_exception,_Unwind_Exception * unwind_exception,_Unwind_Context * context)62 void scanEHTable(ScanResultInternal& results, 63 _Unwind_Action actions, 64 bool native_exception, 65 _Unwind_Exception* unwind_exception, 66 _Unwind_Context* context) { 67 // Initialize results to found nothing but an error 68 results.ttypeIndex = 0; 69 results.actionRecord = 0; 70 results.languageSpecificData = 0; 71 results.landingPad = 0; 72 results.adjustedPtr = 0; 73 results.reason = _URC_FATAL_PHASE1_ERROR; 74 75 // Check for consistent actions 76 if (actions & _UA_SEARCH_PHASE) { 77 if (actions & (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME | _UA_FORCE_UNWIND)) { 78 results.reason = _URC_FATAL_PHASE1_ERROR; 79 return; 80 } 81 } else if (actions & _UA_CLEANUP_PHASE) { 82 if ((actions & _UA_HANDLER_FRAME) && (actions & _UA_FORCE_UNWIND)) { 83 results.reason = _URC_FATAL_PHASE2_ERROR; 84 return; 85 } 86 } else { 87 results.reason = _URC_FATAL_PHASE1_ERROR; 88 return; 89 } 90 91 92 // Start scan by getting exception table address 93 const uint8_t* lsda = (const uint8_t*)_Unwind_GetLanguageSpecificData(context); 94 if (lsda == 0) { 95 // No exception table 96 results.reason = _URC_CONTINUE_UNWIND; 97 return; 98 } 99 results.languageSpecificData = lsda; 100 uintptr_t ip = _Unwind_GetIP(context) - 1; 101 uintptr_t funcStart = _Unwind_GetRegionStart(context); 102 uintptr_t ipOffset = ip - funcStart; 103 const uint8_t* classInfo = NULL; 104 uint8_t lpStartEncoding = *lsda++; 105 const uint8_t* lpStart = (const uint8_t*)readEncodedPointer(&lsda, lpStartEncoding); 106 if (lpStart == 0) { 107 lpStart = (const uint8_t*)funcStart; 108 } 109 uint8_t ttypeEncoding = *lsda++; 110 if (ttypeEncoding != DW_EH_PE_omit) { 111 uintptr_t classInfoOffset = readULEB128(&lsda); 112 classInfo = lsda + classInfoOffset; 113 } 114 uint8_t callSiteEncoding = *lsda++; 115 uint32_t callSiteTableLength = static_cast<uint32_t>(readULEB128(&lsda)); 116 const uint8_t* callSiteTableStart = lsda; 117 const uint8_t* callSiteTableEnd = callSiteTableStart + callSiteTableLength; 118 const uint8_t* actionTableStart = callSiteTableEnd; 119 const uint8_t* callSitePtr = callSiteTableStart; 120 121 122 while (callSitePtr < callSiteTableEnd) { 123 uintptr_t start = readEncodedPointer(&callSitePtr, callSiteEncoding); 124 uintptr_t length = readEncodedPointer(&callSitePtr, callSiteEncoding); 125 uintptr_t landingPad = readEncodedPointer(&callSitePtr, callSiteEncoding); 126 uintptr_t actionEntry = readULEB128(&callSitePtr); 127 if ((start <= ipOffset) && (ipOffset < (start + length))) { 128 if (landingPad == 0) { 129 // No handler here 130 results.reason = _URC_CONTINUE_UNWIND; 131 return; 132 } 133 134 landingPad = (uintptr_t)lpStart + landingPad; 135 if (actionEntry == 0) { 136 if ((actions & _UA_CLEANUP_PHASE) && !(actions & _UA_HANDLER_FRAME)) 137 { 138 results.ttypeIndex = 0; 139 results.landingPad = landingPad; 140 results.reason = _URC_HANDLER_FOUND; 141 return; 142 } 143 // No handler here 144 results.reason = _URC_CONTINUE_UNWIND; 145 return; 146 } 147 148 const uint8_t* action = actionTableStart + (actionEntry - 1); 149 while (true) { 150 const uint8_t* actionRecord = action; 151 int64_t ttypeIndex = readSLEB128(&action); 152 if (ttypeIndex > 0) { 153 // Found a catch, does it actually catch? 154 // First check for catch (...) 155 const __shim_type_info* catchType = 156 getTypePtr(static_cast<uint64_t>(ttypeIndex), 157 classInfo, ttypeEncoding, unwind_exception); 158 if (catchType == 0) { 159 // Found catch (...) catches everything, including foreign exceptions 160 if ((actions & _UA_SEARCH_PHASE) || (actions & _UA_HANDLER_FRAME)) 161 { 162 // Save state and return _URC_HANDLER_FOUND 163 results.ttypeIndex = ttypeIndex; 164 results.actionRecord = actionRecord; 165 results.landingPad = landingPad; 166 results.adjustedPtr = unwind_exception+1; 167 results.reason = _URC_HANDLER_FOUND; 168 return; 169 } 170 else if (!(actions & _UA_FORCE_UNWIND)) 171 { 172 // It looks like the exception table has changed 173 // on us. Likely stack corruption! 174 call_terminate(unwind_exception); 175 } 176 } else if (native_exception) { 177 __cxa_exception* exception_header = (__cxa_exception*)(unwind_exception+1) - 1; 178 void* adjustedPtr = unwind_exception+1; 179 const __shim_type_info* excpType = 180 static_cast<const __shim_type_info*>(exception_header->exceptionType); 181 if (adjustedPtr == 0 || excpType == 0) { 182 // Such a disaster! What's wrong? 183 call_terminate(unwind_exception); 184 } 185 186 // Only derefence once, so put ouside the recursive search below 187 if (dynamic_cast<const __pointer_type_info*>(excpType)) { 188 adjustedPtr = *static_cast<void**>(adjustedPtr); 189 } 190 191 // Let's play! 192 if (catchType->can_catch(excpType, adjustedPtr)) { 193 if (actions & _UA_SEARCH_PHASE) { 194 // Cache it. 195 results.ttypeIndex = ttypeIndex; 196 results.actionRecord = actionRecord; 197 results.landingPad = landingPad; 198 results.adjustedPtr = adjustedPtr; 199 results.reason = _URC_HANDLER_FOUND; 200 return; 201 } else if (!(actions & _UA_FORCE_UNWIND)) { 202 // It looks like the exception table has changed 203 // on us. Likely stack corruption! 204 call_terminate(unwind_exception); 205 } 206 } // catchType->can_catch 207 } // if (catchType == 0) 208 } else if (ttypeIndex < 0) { 209 // Found an exception spec. 210 if (native_exception) { 211 __cxa_exception* header = reinterpret_cast<__cxa_exception*>(unwind_exception+1)-1; 212 void* adjustedPtr = unwind_exception+1; 213 const std::type_info* excpType = header->exceptionType; 214 if (adjustedPtr == 0 || excpType == 0) { 215 // Such a disaster! What's wrong? 216 call_terminate(unwind_exception); 217 } 218 219 // Let's play! 220 if (canExceptionSpecCatch(ttypeIndex, classInfo, 221 ttypeEncoding, excpType, 222 adjustedPtr, unwind_exception)) { 223 if (actions & _UA_SEARCH_PHASE) { 224 // Cache it. 225 results.ttypeIndex = ttypeIndex; 226 results.actionRecord = actionRecord; 227 results.landingPad = landingPad; 228 results.adjustedPtr = adjustedPtr; 229 results.reason = _URC_HANDLER_FOUND; 230 return; 231 } else if (!(actions & _UA_FORCE_UNWIND)) { 232 // It looks like the exception table has changed 233 // on us. Likely stack corruption! 234 call_terminate(unwind_exception); 235 } 236 } 237 } else { // ! native_exception 238 // foreign exception must be caught by exception spec 239 if ((actions & _UA_SEARCH_PHASE) || (actions & _UA_HANDLER_FRAME)) { 240 results.ttypeIndex = ttypeIndex; 241 results.actionRecord = actionRecord; 242 results.landingPad = landingPad; 243 results.adjustedPtr = unwind_exception+1; 244 results.reason = _URC_HANDLER_FOUND; 245 return; 246 } 247 else if (!(actions & _UA_FORCE_UNWIND)) { 248 // It looks like the exception table has changed 249 // on us. Likely stack corruption! 250 call_terminate(unwind_exception); 251 } 252 } 253 } else { // ttypeIndex == 0 254 // Found a cleanup, or nothing 255 if ((actions & _UA_CLEANUP_PHASE) && !(actions & _UA_HANDLER_FRAME)) { 256 results.ttypeIndex = ttypeIndex; 257 results.actionRecord = actionRecord; 258 results.landingPad = landingPad; 259 results.adjustedPtr = unwind_exception+1; 260 results.reason = _URC_HANDLER_FOUND; 261 return; 262 } 263 } 264 265 266 const uint8_t* temp = action; 267 int64_t actionOffset = readSLEB128(&temp); 268 if (actionOffset == 0) { 269 // End of action list, no matching handler or cleanup found 270 results.reason = _URC_CONTINUE_UNWIND; 271 return; 272 } 273 274 // Go to next action 275 action += actionOffset; 276 } 277 } else if (ipOffset < start) { 278 // There is no call site for this ip 279 call_terminate(unwind_exception); 280 } 281 } // while (callSitePtr < callSiteTableEnd) 282 283 call_terminate(unwind_exception); 284 } 285 286 /* 287 * Below is target-dependent part 288 */ 289 290 #ifdef __arm__ 291 292 /* Decode an R_ARM_TARGET2 relocation. */ decodeRelocTarget2(uint32_t ptr)293 uint32_t decodeRelocTarget2 (uint32_t ptr) { 294 uint32_t tmp; 295 296 tmp = *reinterpret_cast<uint32_t*>(ptr); 297 if (!tmp) { 298 return 0; 299 } 300 301 tmp += ptr; 302 tmp = *reinterpret_cast<uint32_t*>(tmp); 303 return tmp; 304 } 305 getTypePtr(uint64_t ttypeIndex,const uint8_t * classInfo,uint8_t ttypeEncoding,_Unwind_Exception * unwind_exception)306 const __shim_type_info* getTypePtr(uint64_t ttypeIndex, 307 const uint8_t* classInfo, 308 uint8_t ttypeEncoding, 309 _Unwind_Exception* unwind_exception) { 310 if (classInfo == 0) { // eh table corrupted! 311 call_terminate(unwind_exception); 312 } 313 const uint8_t* ptr = classInfo - ttypeIndex * 4; 314 return (const __shim_type_info*)decodeRelocTarget2((uint32_t)ptr); 315 } 316 canExceptionSpecCatch(int64_t specIndex,const uint8_t * classInfo,uint8_t ttypeEncoding,const std::type_info * excpType,void * adjustedPtr,_Unwind_Exception * unwind_exception)317 bool canExceptionSpecCatch(int64_t specIndex, 318 const uint8_t* classInfo, 319 uint8_t ttypeEncoding, 320 const std::type_info* excpType, 321 void* adjustedPtr, 322 _Unwind_Exception* unwind_exception) { 323 if (classInfo == 0) { // eh table corrupted! 324 call_terminate(unwind_exception); 325 } 326 327 specIndex = -specIndex; 328 specIndex -= 1; 329 const uint32_t* temp = reinterpret_cast<const uint32_t*>(classInfo) + specIndex; 330 331 while (true) { 332 uint32_t ttypeIndex = *temp; 333 if (ttypeIndex == 0) { 334 break; 335 } 336 ttypeIndex = decodeRelocTarget2((uint32_t)temp); 337 temp += 1; 338 const __shim_type_info* catchType = (const __shim_type_info*) ttypeIndex; 339 void* tempPtr = adjustedPtr; 340 if (catchType->can_catch( 341 static_cast<const __shim_type_info*>(excpType), tempPtr)) { 342 return false; 343 } 344 } // while 345 return true; 346 } 347 348 // lower-level runtime library API function that unwinds the frame 349 extern "C" bool __gnu_unwind_frame(_Unwind_Exception*, _Unwind_Context*); 350 setRegisters(_Unwind_Exception * unwind_exception,_Unwind_Context * context,const ScanResultInternal & results)351 void setRegisters(_Unwind_Exception* unwind_exception, 352 _Unwind_Context* context, 353 const ScanResultInternal& results) { 354 _Unwind_SetGR(context, 0, reinterpret_cast<uintptr_t>(unwind_exception)); 355 _Unwind_SetGR(context, 1, static_cast<uintptr_t>(results.ttypeIndex)); 356 _Unwind_SetIP(context, results.landingPad); 357 } 358 continueUnwinding(_Unwind_Exception * ex,_Unwind_Context * context)359 _Unwind_Reason_Code continueUnwinding(_Unwind_Exception *ex, 360 _Unwind_Context *context) { 361 if (__gnu_unwind_frame(ex, context) != _URC_OK) { 362 return _URC_FAILURE; 363 } 364 return _URC_CONTINUE_UNWIND; 365 } 366 saveDataToBarrierCache(_Unwind_Exception * exc,_Unwind_Context * ctx,const ScanResultInternal & results)367 void saveDataToBarrierCache(_Unwind_Exception* exc, 368 _Unwind_Context* ctx, 369 const ScanResultInternal& results) { 370 exc->barrier_cache.sp = _Unwind_GetGR(ctx, UNWIND_STACK_REG); 371 exc->barrier_cache.bitpattern[0] = (uint32_t)results.adjustedPtr; 372 exc->barrier_cache.bitpattern[1] = (uint32_t)results.ttypeIndex; 373 exc->barrier_cache.bitpattern[3] = (uint32_t)results.landingPad; 374 } 375 loadDataFromBarrierCache(_Unwind_Exception * exc,ScanResultInternal & results)376 void loadDataFromBarrierCache(_Unwind_Exception* exc, 377 ScanResultInternal& results) { 378 results.adjustedPtr = (void*) exc->barrier_cache.bitpattern[0]; 379 results.ttypeIndex = (int64_t) exc->barrier_cache.bitpattern[1]; 380 results.landingPad = (uintptr_t) exc->barrier_cache.bitpattern[3]; 381 } 382 prepareBeginCleanup(_Unwind_Exception * exc)383 void prepareBeginCleanup(_Unwind_Exception* exc) { 384 __cxa_begin_cleanup(exc); 385 } 386 saveUnexpectedDataToBarrierCache(_Unwind_Exception * exc,_Unwind_Context * ctx,const ScanResultInternal & results)387 void saveUnexpectedDataToBarrierCache(_Unwind_Exception* exc, 388 _Unwind_Context* ctx, 389 const ScanResultInternal& results) { 390 prepareBeginCleanup(exc); 391 392 const uint8_t* lsda = (const uint8_t*)_Unwind_GetLanguageSpecificData(ctx); 393 const uint8_t* classInfo = NULL; 394 uint8_t lpStartEncoding = *lsda++; 395 const uint8_t* lpStart = (const uint8_t*)readEncodedPointer(&lsda, lpStartEncoding); 396 uintptr_t funcStart = _Unwind_GetRegionStart(ctx); 397 if (lpStart == 0) { 398 lpStart = (const uint8_t*)funcStart; 399 } 400 uint8_t ttypeEncoding = *lsda++; 401 if (ttypeEncoding != DW_EH_PE_omit) { 402 uintptr_t classInfoOffset = readULEB128(&lsda); 403 classInfo = lsda + classInfoOffset; 404 } 405 406 const uint32_t* e = (const uint32_t*) classInfo - results.ttypeIndex - 1; 407 uint32_t n = 0; 408 while (e[n] != 0) { 409 ++n; 410 } 411 412 exc->barrier_cache.bitpattern[1] = n; 413 exc->barrier_cache.bitpattern[3] = 4; 414 exc->barrier_cache.bitpattern[4] = (uint32_t)e; 415 } 416 417 #else // ! __arm__ 418 getTypePtr(uint64_t ttypeIndex,const uint8_t * classInfo,uint8_t ttypeEncoding,_Unwind_Exception * unwind_exception)419 const __shim_type_info* getTypePtr(uint64_t ttypeIndex, 420 const uint8_t* classInfo, 421 uint8_t ttypeEncoding, 422 _Unwind_Exception* unwind_exception) { 423 if (classInfo == 0) { // eh table corrupted! 424 call_terminate(unwind_exception); 425 } 426 427 switch (ttypeEncoding & 0x0F) { 428 case DW_EH_PE_absptr: 429 ttypeIndex *= sizeof(void*); 430 break; 431 case DW_EH_PE_udata2: 432 case DW_EH_PE_sdata2: 433 ttypeIndex *= 2; 434 break; 435 case DW_EH_PE_udata4: 436 case DW_EH_PE_sdata4: 437 ttypeIndex *= 4; 438 break; 439 case DW_EH_PE_udata8: 440 case DW_EH_PE_sdata8: 441 ttypeIndex *= 8; 442 break; 443 default: 444 // this should not happen. 445 call_terminate(unwind_exception); 446 } 447 classInfo -= ttypeIndex; 448 return (const __shim_type_info*)readEncodedPointer(&classInfo, ttypeEncoding); 449 } 450 canExceptionSpecCatch(int64_t specIndex,const uint8_t * classInfo,uint8_t ttypeEncoding,const std::type_info * excpType,void * adjustedPtr,_Unwind_Exception * unwind_exception)451 bool canExceptionSpecCatch(int64_t specIndex, 452 const uint8_t* classInfo, 453 uint8_t ttypeEncoding, 454 const std::type_info* excpType, 455 void* adjustedPtr, 456 _Unwind_Exception* unwind_exception) { 457 if (classInfo == 0) { // eh table corrupted! 458 call_terminate(unwind_exception); 459 } 460 461 specIndex = -specIndex; 462 specIndex -= 1; 463 const uint8_t* temp = classInfo + specIndex; 464 465 while (true) { 466 uint64_t ttypeIndex = readULEB128(&temp); 467 if (ttypeIndex == 0) { 468 break; 469 } 470 const __shim_type_info* catchType = getTypePtr(ttypeIndex, 471 classInfo, 472 ttypeEncoding, 473 unwind_exception); 474 void* tempPtr = adjustedPtr; 475 if (catchType->can_catch( 476 static_cast<const __shim_type_info*>(excpType), tempPtr)) { 477 return false; 478 } 479 } // while 480 return true; 481 } 482 setRegisters(_Unwind_Exception * unwind_exception,_Unwind_Context * context,const ScanResultInternal & results)483 void setRegisters(_Unwind_Exception* unwind_exception, 484 _Unwind_Context* context, 485 const ScanResultInternal& results) { 486 _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), 487 reinterpret_cast<uintptr_t>(unwind_exception)); 488 _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 489 static_cast<uintptr_t>(results.ttypeIndex)); 490 _Unwind_SetIP(context, results.landingPad); 491 } 492 continueUnwinding(_Unwind_Exception * ex,_Unwind_Context * context)493 _Unwind_Reason_Code continueUnwinding(_Unwind_Exception *ex, 494 _Unwind_Context *context) { 495 return _URC_CONTINUE_UNWIND; 496 } 497 498 // Do nothing, only for API compatibility 499 // We don't use C++ polymorphism since we hope no virtual table cost. saveDataToBarrierCache(_Unwind_Exception * exc,_Unwind_Context * ctx,const ScanResultInternal & results)500 void saveDataToBarrierCache(_Unwind_Exception* exc, 501 _Unwind_Context* ctx, 502 const ScanResultInternal& results) {} 503 loadDataFromBarrierCache(_Unwind_Exception * exc,ScanResultInternal & results)504 void loadDataFromBarrierCache(_Unwind_Exception* exc, 505 ScanResultInternal& results) {} 506 prepareBeginCleanup(_Unwind_Exception * exc)507 void prepareBeginCleanup(_Unwind_Exception* exc) {} 508 saveUnexpectedDataToBarrierCache(_Unwind_Exception * exc,_Unwind_Context * ctx,const ScanResultInternal & results)509 void saveUnexpectedDataToBarrierCache(_Unwind_Exception* exc, 510 _Unwind_Context* ctx, 511 const ScanResultInternal& results) {} 512 513 #endif // __arm__ 514 fatalError(const char * message)515 void fatalError(const char* message) { 516 517 // Note: Printing to stderr is only useful when running an executable 518 // from a shell, e.g. when using 'adb shell'. For regular 519 // applications, stderr is redirected to /dev/null by default. 520 fprintf(stderr, "PANIC:GAbi++:%s\n", message); 521 522 // Always print the message to the log, when possible. Use 523 // dlopen()/dlsym() to avoid adding an explicit dependency 524 // to -llog in GAbi++ for this sole feature. 525 // 526 // An explicit dependency to -ldl can be avoided because these 527 // functions are implemented directly by the dynamic linker. 528 // That is, except when this code is linked into a static 529 // executable. In this case, adding -ldl to the final link command 530 // will be necessary, but the dlopen() will always return NULL. 531 // 532 // There is unfortunately no way to detect where this code is going 533 // to be used at compile time, but static executables are strongly 534 // discouraged on the platform because they can't implement ASLR. 535 // 536 typedef void (*logfunc_t)(int, const char*, const char*); 537 logfunc_t logger = NULL; 538 539 // Note that this should always succeed in a regular application, 540 // because the library is already loaded into the process' address 541 // space by Zygote before forking the application process. 542 // This will fail in static executables, because the static 543 // version of -ldl only contains empty stubs. 544 void* liblog = dlopen("liblog.so", RTLD_NOW); 545 546 if (liblog != NULL) { 547 logger = reinterpret_cast<logfunc_t>(dlsym(liblog, "__android_log_print")); 548 if (logger != NULL) { 549 (*logger)(ANDROID_LOG_FATAL, "GAbi++", message); 550 } 551 dlclose(liblog); 552 } 553 554 std::terminate(); 555 } 556 557 } // namespace __cxxabiv1 558