• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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