573 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//8// Implements unw_* functions from <libunwind.h>9//10//===----------------------------------------------------------------------===//11 12#include <libunwind.h>13 14#include "config.h"15#include "libunwind_ext.h"16 17#include <stdlib.h>18 19// Define the __has_feature extension for compilers that do not support it so20// that we can later check for the presence of ASan in a compiler-neutral way.21#if !defined(__has_feature)22#define __has_feature(feature) 023#endif24 25#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)26#include <sanitizer/asan_interface.h>27#endif28 29#if !defined(__USING_SJLJ_EXCEPTIONS__) && !defined(__wasm__)30#include "AddressSpace.hpp"31#include "UnwindCursor.hpp"32 33using namespace libunwind;34 35/// internal object to represent this processes address space36LocalAddressSpace LocalAddressSpace::sThisAddressSpace;37 38_LIBUNWIND_EXPORT unw_addr_space_t unw_local_addr_space =39 (unw_addr_space_t)&LocalAddressSpace::sThisAddressSpace;40 41/// Create a cursor of a thread in this process given 'context' recorded by42/// __unw_getcontext().43_LIBUNWIND_HIDDEN int __unw_init_local(unw_cursor_t *cursor,44 unw_context_t *context) {45 _LIBUNWIND_TRACE_API("__unw_init_local(cursor=%p, context=%p)",46 static_cast<void *>(cursor),47 static_cast<void *>(context));48#if defined(__i386__)49# define REGISTER_KIND Registers_x8650#elif defined(__x86_64__)51# define REGISTER_KIND Registers_x86_6452#elif defined(__powerpc64__)53# define REGISTER_KIND Registers_ppc6454#elif defined(__powerpc__)55# define REGISTER_KIND Registers_ppc56#elif defined(__aarch64__)57# define REGISTER_KIND Registers_arm6458#elif defined(__arm__)59# define REGISTER_KIND Registers_arm60#elif defined(__or1k__)61# define REGISTER_KIND Registers_or1k62#elif defined(__hexagon__)63# define REGISTER_KIND Registers_hexagon64#elif defined(__mips__) && defined(_ABIO32) && _MIPS_SIM == _ABIO3265# define REGISTER_KIND Registers_mips_o3266#elif defined(__mips64)67# define REGISTER_KIND Registers_mips_newabi68#elif defined(__mips__)69# warning The MIPS architecture is not supported with this ABI and environment!70#elif defined(__sparc__) && defined(__arch64__)71#define REGISTER_KIND Registers_sparc6472#elif defined(__sparc__)73# define REGISTER_KIND Registers_sparc74#elif defined(__riscv)75# define REGISTER_KIND Registers_riscv76#elif defined(__ve__)77# define REGISTER_KIND Registers_ve78#elif defined(__s390x__)79# define REGISTER_KIND Registers_s390x80#elif defined(__loongarch__) && __loongarch_grlen == 6481#define REGISTER_KIND Registers_loongarch82#else83# error Architecture not supported84#endif85 // Use "placement new" to allocate UnwindCursor in the cursor buffer.86 new (reinterpret_cast<UnwindCursor<LocalAddressSpace, REGISTER_KIND> *>(cursor))87 UnwindCursor<LocalAddressSpace, REGISTER_KIND>(88 context, LocalAddressSpace::sThisAddressSpace);89#undef REGISTER_KIND90 AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;91 co->setInfoBasedOnIPRegister();92 93 return UNW_ESUCCESS;94}95_LIBUNWIND_WEAK_ALIAS(__unw_init_local, unw_init_local)96 97/// Get value of specified register at cursor position in stack frame.98_LIBUNWIND_HIDDEN int __unw_get_reg(unw_cursor_t *cursor, unw_regnum_t regNum,99 unw_word_t *value) {100 _LIBUNWIND_TRACE_API("__unw_get_reg(cursor=%p, regNum=%d, &value=%p)",101 static_cast<void *>(cursor), regNum,102 static_cast<void *>(value));103 AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;104 if (co->validReg(regNum)) {105 *value = co->getReg(regNum);106 return UNW_ESUCCESS;107 }108 return UNW_EBADREG;109}110_LIBUNWIND_WEAK_ALIAS(__unw_get_reg, unw_get_reg)111 112/// Set value of specified register at cursor position in stack frame.113_LIBUNWIND_HIDDEN int __unw_set_reg(unw_cursor_t *cursor, unw_regnum_t regNum,114 unw_word_t value) {115 _LIBUNWIND_TRACE_API("__unw_set_reg(cursor=%p, regNum=%d, value=0x%" PRIxPTR116 ")",117 static_cast<void *>(cursor), regNum, value);118 typedef LocalAddressSpace::pint_t pint_t;119 AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;120 if (co->validReg(regNum)) {121 // special case altering IP to re-find info (being called by personality122 // function)123 if (regNum == UNW_REG_IP) {124 unw_proc_info_t info;125 // First, get the FDE for the old location and then update it.126 co->getInfo(&info);127 128 pint_t sp = (pint_t)co->getReg(UNW_REG_SP);129 130#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)131 {132 // It is only valid to set the IP within the current function. This is133 // important for ptrauth, otherwise the IP cannot be correctly signed.134 // The current signature of `value` is via the schema:135 // __ptrauth(ptrauth_key_return_address, <<sp>>, 0)136 // For this to be generally usable we manually re-sign it to the137 // directly supported schema:138 // __ptrauth(ptrauth_key_return_address, 1, 0)139 unw_word_t140 __unwind_ptrauth_restricted_intptr(ptrauth_key_return_address, 1,141 0) authenticated_value;142 unw_word_t opaque_value = (uint64_t)ptrauth_auth_and_resign(143 (void *)value, ptrauth_key_return_address, sp,144 ptrauth_key_return_address, &authenticated_value);145 memmove(reinterpret_cast<void *>(&authenticated_value),146 reinterpret_cast<void *>(&opaque_value),147 sizeof(authenticated_value));148 if (authenticated_value < info.start_ip ||149 authenticated_value > info.end_ip)150 _LIBUNWIND_ABORT("PC vs frame info mismatch");151 152 // PC should have been signed with the sp, so we verify that153 // roundtripping does not fail.154 pint_t pc = (pint_t)co->getReg(UNW_REG_IP);155 if (ptrauth_auth_and_resign((void *)pc, ptrauth_key_return_address, sp,156 ptrauth_key_return_address,157 sp) != (void *)pc) {158 _LIBUNWIND_LOG("Bad unwind through arm64e (0x%zX, 0x%zX)->0x%zX\n",159 pc, sp,160 (pint_t)ptrauth_auth_data(161 (void *)pc, ptrauth_key_return_address, sp));162 _LIBUNWIND_ABORT("Bad unwind through arm64e");163 }164 }165#endif166 167 // If the original call expects stack adjustment, perform this now.168 // Normal frame unwinding would have included the offset already in the169 // CFA computation.170 // Note: for PA-RISC and other platforms where the stack grows up,171 // this should actually be - info.gp. LLVM doesn't currently support172 // any such platforms and Clang doesn't export a macro for them.173 if (info.gp)174 co->setReg(UNW_REG_SP, sp + info.gp);175 co->setReg(UNW_REG_IP, value);176 co->setInfoBasedOnIPRegister(false);177 } else {178 co->setReg(regNum, (pint_t)value);179 }180 return UNW_ESUCCESS;181 }182 return UNW_EBADREG;183}184_LIBUNWIND_WEAK_ALIAS(__unw_set_reg, unw_set_reg)185 186/// Get value of specified float register at cursor position in stack frame.187_LIBUNWIND_HIDDEN int __unw_get_fpreg(unw_cursor_t *cursor, unw_regnum_t regNum,188 unw_fpreg_t *value) {189 _LIBUNWIND_TRACE_API("__unw_get_fpreg(cursor=%p, regNum=%d, &value=%p)",190 static_cast<void *>(cursor), regNum,191 static_cast<void *>(value));192 AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;193 if (co->validFloatReg(regNum)) {194 *value = co->getFloatReg(regNum);195 return UNW_ESUCCESS;196 }197 return UNW_EBADREG;198}199_LIBUNWIND_WEAK_ALIAS(__unw_get_fpreg, unw_get_fpreg)200 201/// Set value of specified float register at cursor position in stack frame.202_LIBUNWIND_HIDDEN int __unw_set_fpreg(unw_cursor_t *cursor, unw_regnum_t regNum,203 unw_fpreg_t value) {204#if defined(_LIBUNWIND_ARM_EHABI)205 _LIBUNWIND_TRACE_API("__unw_set_fpreg(cursor=%p, regNum=%d, value=%llX)",206 static_cast<void *>(cursor), regNum, value);207#else208 _LIBUNWIND_TRACE_API("__unw_set_fpreg(cursor=%p, regNum=%d, value=%g)",209 static_cast<void *>(cursor), regNum, value);210#endif211 AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;212 if (co->validFloatReg(regNum)) {213 co->setFloatReg(regNum, value);214 return UNW_ESUCCESS;215 }216 return UNW_EBADREG;217}218_LIBUNWIND_WEAK_ALIAS(__unw_set_fpreg, unw_set_fpreg)219 220/// Move cursor to next frame.221_LIBUNWIND_HIDDEN int __unw_step(unw_cursor_t *cursor) {222 _LIBUNWIND_TRACE_API("__unw_step(cursor=%p)", static_cast<void *>(cursor));223 AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;224 return co->step();225}226_LIBUNWIND_WEAK_ALIAS(__unw_step, unw_step)227 228// Move cursor to next frame and for stage2 of unwinding.229// This resets MTE tags of tagged frames to zero.230extern "C" _LIBUNWIND_HIDDEN int __unw_step_stage2(unw_cursor_t *cursor) {231 _LIBUNWIND_TRACE_API("__unw_step_stage2(cursor=%p)",232 static_cast<void *>(cursor));233 AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;234 return co->step(true);235}236 237/// Get unwind info at cursor position in stack frame.238_LIBUNWIND_HIDDEN int __unw_get_proc_info(unw_cursor_t *cursor,239 unw_proc_info_t *info) {240 _LIBUNWIND_TRACE_API("__unw_get_proc_info(cursor=%p, &info=%p)",241 static_cast<void *>(cursor), static_cast<void *>(info));242 AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;243 co->getInfo(info);244 if (info->end_ip == 0)245 return UNW_ENOINFO;246 return UNW_ESUCCESS;247}248_LIBUNWIND_WEAK_ALIAS(__unw_get_proc_info, unw_get_proc_info)249 250/// Rebalance the execution flow by injecting the right amount of `ret`251/// instruction relatively to the amount of `walkedFrames` then resume execution252/// at cursor position (aka longjump).253_LIBUNWIND_HIDDEN int __unw_resume_with_frames_walked(unw_cursor_t *cursor,254 unsigned walkedFrames) {255 _LIBUNWIND_TRACE_API("__unw_resume(cursor=%p, walkedFrames=%u)",256 static_cast<void *>(cursor), walkedFrames);257#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)258 // Inform the ASan runtime that now might be a good time to clean stuff up.259 __asan_handle_no_return();260#endif261#ifdef _LIBUNWIND_TRACE_RET_INJECT262 AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;263 co->setWalkedFrames(walkedFrames);264#endif265 return __unw_resume(cursor);266}267_LIBUNWIND_WEAK_ALIAS(__unw_resume_with_frames_walked,268 unw_resume_with_frames_walked)269 270/// Legacy function. Resume execution at cursor position (aka longjump).271_LIBUNWIND_HIDDEN int __unw_resume(unw_cursor_t *cursor) {272 _LIBUNWIND_TRACE_API("__unw_resume(cursor=%p)", static_cast<void *>(cursor));273#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)274 // Inform the ASan runtime that now might be a good time to clean stuff up.275 __asan_handle_no_return();276#endif277 AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;278 co->jumpto();279 return UNW_EUNSPEC;280}281_LIBUNWIND_WEAK_ALIAS(__unw_resume, unw_resume)282 283/// Get name of function at cursor position in stack frame.284_LIBUNWIND_HIDDEN int __unw_get_proc_name(unw_cursor_t *cursor, char *buf,285 size_t bufLen, unw_word_t *offset) {286 _LIBUNWIND_TRACE_API("__unw_get_proc_name(cursor=%p, &buf=%p, bufLen=%lu)",287 static_cast<void *>(cursor), static_cast<void *>(buf),288 static_cast<unsigned long>(bufLen));289 AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;290 if (co->getFunctionName(buf, bufLen, offset))291 return UNW_ESUCCESS;292 return UNW_EUNSPEC;293}294_LIBUNWIND_WEAK_ALIAS(__unw_get_proc_name, unw_get_proc_name)295 296/// Checks if a register is a floating-point register.297_LIBUNWIND_HIDDEN int __unw_is_fpreg(unw_cursor_t *cursor,298 unw_regnum_t regNum) {299 _LIBUNWIND_TRACE_API("__unw_is_fpreg(cursor=%p, regNum=%d)",300 static_cast<void *>(cursor), regNum);301 AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;302 return co->validFloatReg(regNum);303}304_LIBUNWIND_WEAK_ALIAS(__unw_is_fpreg, unw_is_fpreg)305 306/// Checks if a register is a floating-point register.307_LIBUNWIND_HIDDEN const char *__unw_regname(unw_cursor_t *cursor,308 unw_regnum_t regNum) {309 _LIBUNWIND_TRACE_API("__unw_regname(cursor=%p, regNum=%d)",310 static_cast<void *>(cursor), regNum);311 AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;312 return co->getRegisterName(regNum);313}314_LIBUNWIND_WEAK_ALIAS(__unw_regname, unw_regname)315 316/// Checks if current frame is signal trampoline.317_LIBUNWIND_HIDDEN int __unw_is_signal_frame(unw_cursor_t *cursor) {318 _LIBUNWIND_TRACE_API("__unw_is_signal_frame(cursor=%p)",319 static_cast<void *>(cursor));320 AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;321 return co->isSignalFrame();322}323_LIBUNWIND_WEAK_ALIAS(__unw_is_signal_frame, unw_is_signal_frame)324 325#ifdef _AIX326_LIBUNWIND_EXPORT uintptr_t __unw_get_data_rel_base(unw_cursor_t *cursor) {327 _LIBUNWIND_TRACE_API("unw_get_data_rel_base(cursor=%p)",328 static_cast<void *>(cursor));329 AbstractUnwindCursor *co = reinterpret_cast<AbstractUnwindCursor *>(cursor);330 return co->getDataRelBase();331}332_LIBUNWIND_WEAK_ALIAS(__unw_get_data_rel_base, unw_get_data_rel_base)333#endif334 335#ifdef __arm__336// Save VFP registers d0-d15 using FSTMIADX instead of FSTMIADD337_LIBUNWIND_HIDDEN void __unw_save_vfp_as_X(unw_cursor_t *cursor) {338 _LIBUNWIND_TRACE_API("__unw_get_fpreg_save_vfp_as_X(cursor=%p)",339 static_cast<void *>(cursor));340 AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;341 return co->saveVFPAsX();342}343_LIBUNWIND_WEAK_ALIAS(__unw_save_vfp_as_X, unw_save_vfp_as_X)344#endif345 346 347#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)348/// SPI: walks cached DWARF entries349_LIBUNWIND_HIDDEN void __unw_iterate_dwarf_unwind_cache(void (*func)(350 unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh)) {351 _LIBUNWIND_TRACE_API("__unw_iterate_dwarf_unwind_cache(func=%p)",352 reinterpret_cast<void *>(func));353 DwarfFDECache<LocalAddressSpace>::iterateCacheEntries(func);354}355_LIBUNWIND_WEAK_ALIAS(__unw_iterate_dwarf_unwind_cache,356 unw_iterate_dwarf_unwind_cache)357 358/// IPI: for __register_frame()359void __unw_add_dynamic_fde(unw_word_t fde) {360 CFI_Parser<LocalAddressSpace>::FDE_Info fdeInfo;361 CFI_Parser<LocalAddressSpace>::CIE_Info cieInfo;362 const char *message = CFI_Parser<LocalAddressSpace>::decodeFDE(363 LocalAddressSpace::sThisAddressSpace,364 (LocalAddressSpace::pint_t) fde, &fdeInfo, &cieInfo);365 if (message == NULL) {366 // dynamically registered FDEs don't have a mach_header group they are in.367 // Use fde as mh_group368 unw_word_t mh_group = fdeInfo.fdeStart;369 DwarfFDECache<LocalAddressSpace>::add((LocalAddressSpace::pint_t)mh_group,370 fdeInfo.pcStart, fdeInfo.pcEnd,371 fdeInfo.fdeStart);372 } else {373 _LIBUNWIND_DEBUG_LOG("__unw_add_dynamic_fde: bad fde: %s", message);374 }375}376 377/// IPI: for __deregister_frame()378void __unw_remove_dynamic_fde(unw_word_t fde) {379 // fde is own mh_group380 DwarfFDECache<LocalAddressSpace>::removeAllIn((LocalAddressSpace::pint_t)fde);381}382 383void __unw_add_dynamic_eh_frame_section(unw_word_t eh_frame_start) {384 // The eh_frame section start serves as the mh_group385 unw_word_t mh_group = eh_frame_start;386 CFI_Parser<LocalAddressSpace>::CIE_Info cieInfo;387 CFI_Parser<LocalAddressSpace>::FDE_Info fdeInfo;388 auto p = (LocalAddressSpace::pint_t)eh_frame_start;389 while (LocalAddressSpace::sThisAddressSpace.get32(p)) {390 if (CFI_Parser<LocalAddressSpace>::decodeFDE(391 LocalAddressSpace::sThisAddressSpace, p, &fdeInfo, &cieInfo,392 true) == NULL) {393 DwarfFDECache<LocalAddressSpace>::add((LocalAddressSpace::pint_t)mh_group,394 fdeInfo.pcStart, fdeInfo.pcEnd,395 fdeInfo.fdeStart);396 p += fdeInfo.fdeLength;397 } else if (CFI_Parser<LocalAddressSpace>::parseCIE(398 LocalAddressSpace::sThisAddressSpace, p, &cieInfo) == NULL) {399 p += cieInfo.cieLength;400 } else401 return;402 }403}404 405void __unw_remove_dynamic_eh_frame_section(unw_word_t eh_frame_start) {406 // The eh_frame section start serves as the mh_group407 DwarfFDECache<LocalAddressSpace>::removeAllIn(408 (LocalAddressSpace::pint_t)eh_frame_start);409}410 411#endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)412 413/// Maps the UNW_* error code to a textual representation414_LIBUNWIND_HIDDEN const char *__unw_strerror(int error_code) {415 switch (error_code) {416 case UNW_ESUCCESS:417 return "no error";418 case UNW_EUNSPEC:419 return "unspecified (general) error";420 case UNW_ENOMEM:421 return "out of memory";422 case UNW_EBADREG:423 return "bad register number";424 case UNW_EREADONLYREG:425 return "attempt to write read-only register";426 case UNW_ESTOPUNWIND:427 return "stop unwinding";428 case UNW_EINVALIDIP:429 return "invalid IP";430 case UNW_EBADFRAME:431 return "bad frame";432 case UNW_EINVAL:433 return "unsupported operation or bad value";434 case UNW_EBADVERSION:435 return "unwind info has unsupported version";436 case UNW_ENOINFO:437 return "no unwind info found";438#if defined(_LIBUNWIND_TARGET_AARCH64) && !defined(_LIBUNWIND_IS_NATIVE_ONLY)439 case UNW_ECROSSRASIGNING:440 return "cross unwind with return address signing";441#endif442 }443 return "invalid error code";444}445_LIBUNWIND_WEAK_ALIAS(__unw_strerror, unw_strerror)446 447#endif // !defined(__USING_SJLJ_EXCEPTIONS__) && !defined(__wasm__)448 449#ifdef __APPLE__450 451namespace libunwind {452 453static constexpr size_t MAX_DYNAMIC_UNWIND_SECTIONS_FINDERS = 8;454 455static RWMutex findDynamicUnwindSectionsLock;456static size_t numDynamicUnwindSectionsFinders = 0;457static unw_find_dynamic_unwind_sections458 dynamicUnwindSectionsFinders[MAX_DYNAMIC_UNWIND_SECTIONS_FINDERS] = {0};459 460bool findDynamicUnwindSections(void *addr, unw_dynamic_unwind_sections *info) {461 bool found = false;462 findDynamicUnwindSectionsLock.lock_shared();463 for (size_t i = 0; i != numDynamicUnwindSectionsFinders; ++i) {464 if (dynamicUnwindSectionsFinders[i]((unw_word_t)addr, info)) {465 found = true;466 break;467 }468 }469 findDynamicUnwindSectionsLock.unlock_shared();470 return found;471}472 473} // namespace libunwind474 475int __unw_add_find_dynamic_unwind_sections(476 unw_find_dynamic_unwind_sections find_dynamic_unwind_sections) {477 findDynamicUnwindSectionsLock.lock();478 479 // Check that we have enough space...480 if (numDynamicUnwindSectionsFinders == MAX_DYNAMIC_UNWIND_SECTIONS_FINDERS) {481 findDynamicUnwindSectionsLock.unlock();482 return UNW_ENOMEM;483 }484 485 // Check for value already present...486 for (size_t i = 0; i != numDynamicUnwindSectionsFinders; ++i) {487 if (dynamicUnwindSectionsFinders[i] == find_dynamic_unwind_sections) {488 findDynamicUnwindSectionsLock.unlock();489 return UNW_EINVAL;490 }491 }492 493 // Success -- add callback entry.494 dynamicUnwindSectionsFinders[numDynamicUnwindSectionsFinders++] =495 find_dynamic_unwind_sections;496 findDynamicUnwindSectionsLock.unlock();497 498 return UNW_ESUCCESS;499}500 501int __unw_remove_find_dynamic_unwind_sections(502 unw_find_dynamic_unwind_sections find_dynamic_unwind_sections) {503 findDynamicUnwindSectionsLock.lock();504 505 // Find index to remove.506 size_t finderIdx = numDynamicUnwindSectionsFinders;507 for (size_t i = 0; i != numDynamicUnwindSectionsFinders; ++i) {508 if (dynamicUnwindSectionsFinders[i] == find_dynamic_unwind_sections) {509 finderIdx = i;510 break;511 }512 }513 514 // If no such registration is present then error out.515 if (finderIdx == numDynamicUnwindSectionsFinders) {516 findDynamicUnwindSectionsLock.unlock();517 return UNW_EINVAL;518 }519 520 // Remove entry.521 for (size_t i = finderIdx; i != numDynamicUnwindSectionsFinders - 1; ++i)522 dynamicUnwindSectionsFinders[i] = dynamicUnwindSectionsFinders[i + 1];523 dynamicUnwindSectionsFinders[--numDynamicUnwindSectionsFinders] = nullptr;524 525 findDynamicUnwindSectionsLock.unlock();526 return UNW_ESUCCESS;527}528 529#endif // __APPLE__530 531// Add logging hooks in Debug builds only532#ifndef NDEBUG533#include <stdlib.h>534 535_LIBUNWIND_HIDDEN536bool logAPIs() {537 // do manual lock to avoid use of _cxa_guard_acquire or initializers538 static bool checked = false;539 static bool log = false;540 if (!checked) {541 log = (getenv("LIBUNWIND_PRINT_APIS") != NULL);542 checked = true;543 }544 return log;545}546 547_LIBUNWIND_HIDDEN548bool logUnwinding() {549 // do manual lock to avoid use of _cxa_guard_acquire or initializers550 static bool checked = false;551 static bool log = false;552 if (!checked) {553 log = (getenv("LIBUNWIND_PRINT_UNWINDING") != NULL);554 checked = true;555 }556 return log;557}558 559_LIBUNWIND_HIDDEN560bool logDWARF() {561 // do manual lock to avoid use of _cxa_guard_acquire or initializers562 static bool checked = false;563 static bool log = false;564 if (!checked) {565 log = (getenv("LIBUNWIND_PRINT_DWARF") != NULL);566 checked = true;567 }568 return log;569}570 571#endif // NDEBUG572 573