tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

StackWalk.cpp (46882B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /* API for getting a stack trace of the C/C++ stack on the current thread */
      8 
      9 #include "mozilla/Array.h"
     10 #include "mozilla/Atomics.h"
     11 #include "mozilla/Attributes.h"
     12 #include "mozilla/StackWalk.h"
     13 #ifdef XP_WIN
     14 #  include "mozilla/StackWalkThread.h"
     15 #  include <io.h>
     16 #else
     17 #  include <unistd.h>
     18 #endif
     19 #include "mozilla/Sprintf.h"
     20 
     21 #include <string.h>
     22 
     23 #if defined(ANDROID) && defined(MOZ_LINKER)
     24 #  include "Linker.h"
     25 #  include <android/log.h>
     26 #endif
     27 
     28 using namespace mozilla;
     29 
     30 // for _Unwind_Backtrace from libcxxrt or libunwind
     31 // cxxabi.h from libcxxrt implicitly includes unwind.h first
     32 #if defined(HAVE__UNWIND_BACKTRACE) && !defined(_GNU_SOURCE)
     33 #  define _GNU_SOURCE
     34 #endif
     35 
     36 #if defined(HAVE_DLFCN_H) || defined(XP_DARWIN)
     37 #  include <dlfcn.h>
     38 #endif
     39 
     40 #if (defined(XP_DARWIN) && \
     41     (defined(__i386) || defined(__ppc__) || defined(HAVE__UNWIND_BACKTRACE)))
     42 #  define MOZ_STACKWALK_SUPPORTS_MACOSX 1
     43 #else
     44 #  define MOZ_STACKWALK_SUPPORTS_MACOSX 0
     45 #endif
     46 
     47 #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1)
     48 #  define HAVE___LIBC_STACK_END 1
     49 #else
     50 #  define HAVE___LIBC_STACK_END 0
     51 #endif
     52 
     53 #if (defined(linux) &&                                            \
     54     ((defined(__GNUC__) && (defined(__i386) || defined(PPC))) || \
     55      defined(HAVE__UNWIND_BACKTRACE)) &&                         \
     56     (HAVE___LIBC_STACK_END || ANDROID))
     57 #  define MOZ_STACKWALK_SUPPORTS_LINUX 1
     58 #else
     59 #  define MOZ_STACKWALK_SUPPORTS_LINUX 0
     60 #endif
     61 
     62 #if HAVE___LIBC_STACK_END
     63 extern MOZ_EXPORT void* __libc_stack_end;  // from ld-linux.so
     64 #  ifdef __aarch64__
     65 static Atomic<uintptr_t> ldso_base;
     66 #  endif
     67 #endif
     68 
     69 #ifdef ANDROID
     70 #  include <algorithm>
     71 #  include <unistd.h>
     72 #  include <pthread.h>
     73 #endif
     74 
     75 class FrameSkipper {
     76 public:
     77  constexpr FrameSkipper() : mSkipUntilAddr(0) {}
     78  static uintptr_t AddressFromPC(const void* aPC) {
     79 #ifdef __arm__
     80    // On 32-bit ARM, mask off the thumb bit to get the instruction address.
     81    return uintptr_t(aPC) & ~1;
     82 #else
     83    return uintptr_t(aPC);
     84 #endif
     85  }
     86  bool ShouldSkipPC(void* aPC) {
     87    // Skip frames until we encounter the one we were initialized with,
     88    // and then never skip again.
     89    uintptr_t instructionAddress = AddressFromPC(aPC);
     90    if (mSkipUntilAddr != 0) {
     91      if (mSkipUntilAddr != instructionAddress) {
     92        return true;
     93      }
     94      mSkipUntilAddr = 0;
     95    }
     96    return false;
     97  }
     98  explicit FrameSkipper(const void* aPC) : mSkipUntilAddr(AddressFromPC(aPC)) {}
     99 
    100 private:
    101  uintptr_t mSkipUntilAddr;
    102 };
    103 
    104 #ifdef XP_WIN
    105 
    106 #  include <windows.h>
    107 #  include <process.h>
    108 #  include <stdio.h>
    109 #  include <malloc.h>
    110 #  include "mozilla/Atomics.h"
    111 #  include "mozilla/StackWalk_windows.h"
    112 
    113 #  include <imagehlp.h>
    114 // We need a way to know if we are building for WXP (or later), as if we are, we
    115 // need to use the newer 64-bit APIs. API_VERSION_NUMBER seems to fit the bill.
    116 // A value of 9 indicates we want to use the new APIs.
    117 #  if API_VERSION_NUMBER < 9
    118 #    error Too old imagehlp.h
    119 #  endif
    120 
    121 #  if defined(_M_AMD64) || defined(_M_ARM64)
    122 // We must use RtlLookupFunctionEntry to do stack walking on x86-64 and arm64,
    123 // but internally this function does a blocking shared acquire of SRW locks
    124 // that live in ntdll and are not exported. This is problematic when we want to
    125 // suspend a thread and walk its stack, like we do in the profiler and the
    126 // background hang reporter. If the suspended thread happens to hold one of the
    127 // locks exclusively while suspended, then the stack walking thread will
    128 // deadlock if it calls RtlLookupFunctionEntry.
    129 //
    130 // Note that we only care about deadlocks between the stack walking thread and
    131 // the suspended thread. Any other deadlock scenario is considered out of
    132 // scope, because they are unlikely to be our fault -- these other scenarios
    133 // imply that some thread that we did not suspend is stuck holding one of the
    134 // locks exclusively, and exclusive acquisition of these locks only happens for
    135 // a brief time during Microsoft API calls (e.g. LdrLoadDll, LdrUnloadDll).
    136 //
    137 // We use one of two alternative strategies to gracefully fail to capture a
    138 // stack instead of running into a deadlock:
    139 //    (1) collect pointers to the ntdll internal locks at stack walk
    140 //        initialization, then try to acquire them non-blockingly before
    141 //        initiating any stack walk;
    142 // or (2) mark all code paths that can potentially end up doing an exclusive
    143 //        acquisition of the locks as stack walk suppression paths, then check
    144 //        if any thread is currently on a stack walk suppression path before
    145 //        initiating any stack walk;
    146 //
    147 // Strategy (2) can only avoid all deadlocks under the easily wronged
    148 // assumption that we have correctly identified all existing paths that should
    149 // be stack suppression paths. With strategy (2) we cannot collect stacks e.g.
    150 // during the whole duration of a DLL load happening on any thread so the
    151 // profiling results are worse.
    152 //
    153 // Strategy (1) guarantees no deadlock. It also gives better profiling results
    154 // because it is more fine-grained. Therefore we always prefer strategy (1),
    155 // and we only use strategy (2) as a fallback.
    156 
    157 // Strategy (1): Ntdll Internal Locks
    158 //
    159 // The external stack walk initialization code will feed us pointers to the
    160 // ntdll internal locks. Once we have them, we no longer need to rely on
    161 // strategy (2).
    162 static Atomic<bool> sStackWalkLocksInitialized;
    163 static Array<SRWLOCK*, 2> sStackWalkLocks;
    164 
    165 MFBT_API
    166 void InitializeStackWalkLocks(const Array<void*, 2>& aStackWalkLocks) {
    167  sStackWalkLocks[0] = reinterpret_cast<SRWLOCK*>(aStackWalkLocks[0]);
    168  sStackWalkLocks[1] = reinterpret_cast<SRWLOCK*>(aStackWalkLocks[1]);
    169  sStackWalkLocksInitialized = true;
    170 }
    171 
    172 // Strategy (2): Stack Walk Suppressions
    173 //
    174 // We're using an atomic counter rather than a critical section because we
    175 // don't require mutual exclusion with the stack walker. If the stack walker
    176 // determines that it's safe to start unwinding the suspended thread (i.e.
    177 // there are no suppressions when the unwind begins), then it's safe to
    178 // continue unwinding that thread even if other threads request suppressions
    179 // in the meantime, because we can't deadlock with those other threads.
    180 //
    181 // XXX: This global variable is a larger-than-necessary hammer. A more scoped
    182 // solution would be to maintain a counter per thread, but then it would be
    183 // more difficult for WalkStackMain64 to read the suspended thread's counter.
    184 static Atomic<size_t> sStackWalkSuppressions;
    185 
    186 void SuppressStackWalking() { ++sStackWalkSuppressions; }
    187 
    188 void DesuppressStackWalking() {
    189  auto previousValue = sStackWalkSuppressions--;
    190  // We should never desuppress from 0. See bug 1687510 comment 10 for an
    191  // example in which this occured.
    192  MOZ_RELEASE_ASSERT(previousValue);
    193 }
    194 
    195 MFBT_API
    196 AutoSuppressStackWalking::AutoSuppressStackWalking() { SuppressStackWalking(); }
    197 
    198 MFBT_API
    199 AutoSuppressStackWalking::~AutoSuppressStackWalking() {
    200  DesuppressStackWalking();
    201 }
    202 
    203 bool IsStackWalkingSafe() {
    204  // Use strategy (1), if initialized.
    205  if (sStackWalkLocksInitialized) {
    206    bool isSafe = false;
    207    if (::TryAcquireSRWLockShared(sStackWalkLocks[0])) {
    208      if (::TryAcquireSRWLockShared(sStackWalkLocks[1])) {
    209        isSafe = true;
    210        ::ReleaseSRWLockShared(sStackWalkLocks[1]);
    211      }
    212      ::ReleaseSRWLockShared(sStackWalkLocks[0]);
    213    }
    214    return isSafe;
    215  }
    216 
    217  // Otherwise, fall back to strategy (2).
    218  return sStackWalkSuppressions == 0;
    219 }
    220 
    221 static uint8_t* sJitCodeRegionStart;
    222 static size_t sJitCodeRegionSize;
    223 uint8_t* sMsMpegJitCodeRegionStart;
    224 size_t sMsMpegJitCodeRegionSize;
    225 
    226 MFBT_API void RegisterJitCodeRegion(uint8_t* aStart, size_t aSize) {
    227  // Currently we can only handle one JIT code region at a time
    228  MOZ_RELEASE_ASSERT(!sJitCodeRegionStart);
    229 
    230  sJitCodeRegionStart = aStart;
    231  sJitCodeRegionSize = aSize;
    232 }
    233 
    234 MFBT_API void UnregisterJitCodeRegion(uint8_t* aStart, size_t aSize) {
    235  // Currently we can only handle one JIT code region at a time
    236  MOZ_RELEASE_ASSERT(sJitCodeRegionStart && sJitCodeRegionStart == aStart &&
    237                     sJitCodeRegionSize == aSize);
    238 
    239  sJitCodeRegionStart = nullptr;
    240  sJitCodeRegionSize = 0;
    241 }
    242 
    243 #  endif  // _M_AMD64 || _M_ARM64
    244 
    245 // Routine to print an error message to standard error.
    246 static void PrintError(const char* aPrefix) {
    247  LPSTR lpMsgBuf;
    248  DWORD lastErr = GetLastError();
    249  FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
    250                     FORMAT_MESSAGE_IGNORE_INSERTS,
    251                 nullptr, lastErr,
    252                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),  // Default language
    253                 (LPSTR)&lpMsgBuf, 0, nullptr);
    254  fprintf(stderr, "### ERROR: %s: %s", aPrefix,
    255          lpMsgBuf ? lpMsgBuf : "(null)\n");
    256  fflush(stderr);
    257  LocalFree(lpMsgBuf);
    258 }
    259 
    260 class MOZ_RAII AutoCriticalSection {
    261 public:
    262  explicit inline AutoCriticalSection(LPCRITICAL_SECTION aCriticalSection)
    263      : mCriticalSection{aCriticalSection} {
    264    ::EnterCriticalSection(mCriticalSection);
    265  }
    266  inline ~AutoCriticalSection() { ::LeaveCriticalSection(mCriticalSection); }
    267 
    268  AutoCriticalSection(AutoCriticalSection&& other) = delete;
    269  AutoCriticalSection operator=(AutoCriticalSection&& other) = delete;
    270  AutoCriticalSection(const AutoCriticalSection&) = delete;
    271  AutoCriticalSection operator=(const AutoCriticalSection&) = delete;
    272 
    273 private:
    274  LPCRITICAL_SECTION mCriticalSection;
    275 };
    276 
    277 // A thread-safe safe object interface for Microsoft's DbgHelp.dll. DbgHelp
    278 // APIs are not thread-safe and they require the use of a unique HANDLE value
    279 // as an identifier for the current session. All this is handled internally by
    280 // DbgHelpWrapper.
    281 class DbgHelpWrapper {
    282 public:
    283  explicit inline DbgHelpWrapper() : DbgHelpWrapper(InitFlag::BasicInit) {}
    284  DbgHelpWrapper(DbgHelpWrapper&& other) = delete;
    285  DbgHelpWrapper operator=(DbgHelpWrapper&& other) = delete;
    286  DbgHelpWrapper(const DbgHelpWrapper&) = delete;
    287  DbgHelpWrapper operator=(const DbgHelpWrapper&) = delete;
    288 
    289  inline bool ReadyToUse() { return mInitSuccess; }
    290 
    291  inline BOOL StackWalk64(
    292      DWORD aMachineType, HANDLE aThread, LPSTACKFRAME64 aStackFrame,
    293      PVOID aContextRecord, PREAD_PROCESS_MEMORY_ROUTINE64 aReadMemoryRoutine,
    294      PFUNCTION_TABLE_ACCESS_ROUTINE64 aFunctionTableAccessRoutine,
    295      PGET_MODULE_BASE_ROUTINE64 aGetModuleBaseRoutine,
    296      PTRANSLATE_ADDRESS_ROUTINE64 aTranslateAddress) {
    297    if (!ReadyToUse()) {
    298      return FALSE;
    299    }
    300 
    301    AutoCriticalSection guard(&sCriticalSection);
    302    return ::StackWalk64(aMachineType, sSessionId, aThread, aStackFrame,
    303                         aContextRecord, aReadMemoryRoutine,
    304                         aFunctionTableAccessRoutine, aGetModuleBaseRoutine,
    305                         aTranslateAddress);
    306  }
    307 
    308 protected:
    309  enum class InitFlag : bool {
    310    BasicInit,
    311    WithSymbolSupport,
    312  };
    313 
    314  explicit inline DbgHelpWrapper(InitFlag initFlag) {
    315    mInitSuccess = Initialize(initFlag);
    316  }
    317 
    318  // DbgHelp functions are not thread-safe and should therefore be protected
    319  // by using this critical section through a AutoCriticalSection.
    320  static CRITICAL_SECTION sCriticalSection;
    321 
    322  // DbgHelp functions require a unique HANDLE hProcess that should be the same
    323  // throughout the current session. We refer to this handle as a session id.
    324  // Ideally the session id should be a valid HANDLE to the target process,
    325  // which in our case is the current process.
    326  //
    327  // However, in order to avoid conflicts with other sessions, the session id
    328  // should be unique and therefore not just GetCurrentProcess(), which other
    329  // pieces of code tend to already use (see bug 1699328).
    330  //
    331  // We therefore define sSessionId as a duplicate of the current process
    332  // handle, a solution that meets all the requirements listed above.
    333  static HANDLE sSessionId;
    334 
    335 private:
    336  bool mInitSuccess;
    337 
    338  // This function initializes sCriticalSection, sSessionId and loads DbgHelp.
    339  // It also calls SymInitialize if called with aInitFlag::WithSymbolSupport.
    340  // It is thread-safe and reentrancy-safe.
    341  [[nodiscard]] static bool Initialize(InitFlag aInitFlag);
    342 
    343  // In debug and fuzzing builds, MOZ_ASSERT and MOZ_CRASH walk the stack to
    344  // print it before actually crashing. This code path uses a DbgHelpWrapper
    345  // object, hence *any* MOZ_ASSERT or MOZ_CRASH failure reached from
    346  // Initialize() leads to rentrancy (see bug 1869997 for an example). Such
    347  // failures can occur indirectly when we load dbghelp.dll, because we
    348  // override various Microsoft-internal functions that are called upon DLL
    349  // loading. We protect against reentrancy by keeping track of the ID of the
    350  // thread that runs the initialization code.
    351  static Atomic<DWORD> sInitializationThreadId;
    352 };
    353 
    354 CRITICAL_SECTION DbgHelpWrapper::sCriticalSection{};
    355 Atomic<DWORD> DbgHelpWrapper::sInitializationThreadId{0};
    356 HANDLE DbgHelpWrapper::sSessionId{nullptr};
    357 
    358 // Thread-safety here is ensured by the C++ standard: scoped static
    359 // initialization is thread-safe. sInitializationThreadId is used to protect
    360 // against reentrancy -- and only for that purpose.
    361 [[nodiscard]] /* static */ bool DbgHelpWrapper::Initialize(
    362    DbgHelpWrapper::InitFlag aInitFlag) {
    363  // In the code below, it is only safe to reach MOZ_ASSERT or MOZ_CRASH while
    364  // sInitializationThreadId is set to the current thread id.
    365  static Atomic<DWORD> sInitializationThreadId{0};
    366  DWORD currentThreadId = ::GetCurrentThreadId();
    367 
    368  // This code relies on Windows never giving us a current thread ID of zero.
    369  // We make this assumption explicit, by failing if that should ever occur.
    370  if (!currentThreadId) {
    371    return false;
    372  }
    373 
    374  if (sInitializationThreadId == currentThreadId) {
    375    // This is a reentrant call and we must abort here.
    376    return false;
    377  }
    378 
    379  static const bool sHasInitializedDbgHelp = [currentThreadId]() {
    380    // Per the C++ standard, only one thread evers reaches this path.
    381    sInitializationThreadId = currentThreadId;
    382 
    383    ::InitializeCriticalSection(&sCriticalSection);
    384 
    385    if (!::DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(),
    386                           GetCurrentProcess(), &sSessionId, 0, FALSE,
    387                           DUPLICATE_SAME_ACCESS)) {
    388      return false;
    389    }
    390 
    391    bool dbgHelpLoaded = static_cast<bool>(::LoadLibraryW(L"dbghelp.dll"));
    392 
    393    MOZ_ASSERT(dbgHelpLoaded);
    394    sInitializationThreadId = 0;
    395    return dbgHelpLoaded;
    396  }();
    397 
    398  // If we don't need symbol initialization, we are done. If we need it, we
    399  // can only proceed if DbgHelp initialization was successful.
    400  if (aInitFlag == InitFlag::BasicInit || !sHasInitializedDbgHelp) {
    401    return sHasInitializedDbgHelp;
    402  }
    403 
    404  static const bool sHasInitializedSymbols = [currentThreadId]() {
    405    // Per the C++ standard, only one thread evers reaches this path.
    406    sInitializationThreadId = currentThreadId;
    407 
    408    bool symbolsInitialized = false;
    409 
    410    {
    411      AutoCriticalSection guard(&sCriticalSection);
    412      ::SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
    413      symbolsInitialized =
    414          static_cast<bool>(::SymInitializeW(sSessionId, nullptr, TRUE));
    415      /* XXX At some point we need to arrange to call SymCleanup */
    416    }
    417 
    418    if (!symbolsInitialized) {
    419      PrintError("SymInitialize");
    420    }
    421 
    422    MOZ_ASSERT(symbolsInitialized);
    423    sInitializationThreadId = 0;
    424    return symbolsInitialized;
    425  }();
    426 
    427  return sHasInitializedSymbols;
    428 }
    429 
    430 // Some APIs such as SymFromAddr also require that the session id has gone
    431 // through SymInitialize. This is handled by child class DbgHelpWrapperSym.
    432 class DbgHelpWrapperSym : public DbgHelpWrapper {
    433 public:
    434  explicit DbgHelpWrapperSym() : DbgHelpWrapper(InitFlag::WithSymbolSupport) {}
    435 
    436  inline BOOL SymFromAddr(DWORD64 aAddress, PDWORD64 aDisplacement,
    437                          PSYMBOL_INFO aSymbol) {
    438    if (!ReadyToUse()) {
    439      return FALSE;
    440    }
    441 
    442    AutoCriticalSection guard(&sCriticalSection);
    443    return ::SymFromAddr(sSessionId, aAddress, aDisplacement, aSymbol);
    444  }
    445 
    446  BOOL SymGetModuleInfoEspecial64(DWORD64 aAddr, PIMAGEHLP_MODULE64 aModuleInfo,
    447                                  PIMAGEHLP_LINE64 aLineInfo);
    448 
    449 private:
    450  // Helpers for SymGetModuleInfoEspecial64
    451  struct CallbackEspecial64UserContext {
    452    HANDLE mSessionId;
    453    DWORD64 mAddr;
    454  };
    455 
    456  static BOOL CALLBACK CallbackEspecial64(PCSTR aModuleName,
    457                                          DWORD64 aModuleBase,
    458                                          ULONG aModuleSize,
    459                                          PVOID aUserContext);
    460 };
    461 
    462 // Wrapper around a reference to a CONTEXT, to simplify access to main
    463 // platform-specific execution registers.
    464 // It also avoids using CONTEXT* nullable pointers.
    465 class CONTEXTGenericAccessors {
    466 public:
    467  explicit CONTEXTGenericAccessors(CONTEXT& aCONTEXT) : mCONTEXT(aCONTEXT) {}
    468 
    469  CONTEXT* CONTEXTPtr() { return &mCONTEXT; }
    470 
    471  inline auto& PC() {
    472 #  if defined(_M_AMD64)
    473    return mCONTEXT.Rip;
    474 #  elif defined(_M_ARM64)
    475    return mCONTEXT.Pc;
    476 #  elif defined(_M_IX86)
    477    return mCONTEXT.Eip;
    478 #  else
    479 #    error "unknown platform"
    480 #  endif
    481  }
    482 
    483  inline auto& SP() {
    484 #  if defined(_M_AMD64)
    485    return mCONTEXT.Rsp;
    486 #  elif defined(_M_ARM64)
    487    return mCONTEXT.Sp;
    488 #  elif defined(_M_IX86)
    489    return mCONTEXT.Esp;
    490 #  else
    491 #    error "unknown platform"
    492 #  endif
    493  }
    494 
    495  inline auto& BP() {
    496 #  if defined(_M_AMD64)
    497    return mCONTEXT.Rbp;
    498 #  elif defined(_M_ARM64)
    499    return mCONTEXT.Fp;
    500 #  elif defined(_M_IX86)
    501    return mCONTEXT.Ebp;
    502 #  else
    503 #    error "unknown platform"
    504 #  endif
    505  }
    506 
    507 private:
    508  CONTEXT& mCONTEXT;
    509 };
    510 
    511 /**
    512 * Walk the stack, translating PC's found into strings and recording the
    513 * chain in aBuffer. For this to work properly, the DLLs must be rebased
    514 * so that the address in the file agrees with the address in memory.
    515 * Otherwise StackWalk will return FALSE when it hits a frame in a DLL
    516 * whose in memory address doesn't match its in-file address.
    517 */
    518 
    519 static void DoMozStackWalkThread(MozWalkStackCallback aCallback,
    520                                 const void* aFirstFramePC, uint32_t aMaxFrames,
    521                                 void* aClosure, HANDLE aThread,
    522                                 CONTEXT* aContext) {
    523 #  if defined(_M_IX86)
    524  DbgHelpWrapper dbgHelp;
    525  if (!dbgHelp.ReadyToUse()) {
    526    return;
    527  }
    528 #  endif
    529 
    530  HANDLE targetThread = aThread;
    531  bool walkCallingThread;
    532  if (!targetThread) {
    533    targetThread = ::GetCurrentThread();
    534    walkCallingThread = true;
    535  } else {
    536    DWORD targetThreadId = ::GetThreadId(targetThread);
    537    DWORD currentThreadId = ::GetCurrentThreadId();
    538    walkCallingThread = (targetThreadId == currentThreadId);
    539  }
    540 
    541  // If not already provided, get a context for the specified thread.
    542  CONTEXT context_buf;
    543  if (!aContext) {
    544    memset(&context_buf, 0, sizeof(CONTEXT));
    545    context_buf.ContextFlags = CONTEXT_FULL;
    546    if (walkCallingThread) {
    547      ::RtlCaptureContext(&context_buf);
    548    } else if (!GetThreadContext(targetThread, &context_buf)) {
    549      return;
    550    }
    551  }
    552  CONTEXTGenericAccessors context{aContext ? *aContext : context_buf};
    553 
    554 #  if defined(_M_IX86)
    555  // Setup initial stack frame to walk from.
    556  STACKFRAME64 frame64;
    557  memset(&frame64, 0, sizeof(frame64));
    558  frame64.AddrPC.Offset = context.PC();
    559  frame64.AddrStack.Offset = context.SP();
    560  frame64.AddrFrame.Offset = context.BP();
    561  frame64.AddrPC.Mode = AddrModeFlat;
    562  frame64.AddrStack.Mode = AddrModeFlat;
    563  frame64.AddrFrame.Mode = AddrModeFlat;
    564  frame64.AddrReturn.Mode = AddrModeFlat;
    565 #  endif
    566 
    567 #  if defined(_M_AMD64) || defined(_M_ARM64)
    568  // If at least one thread (we don't know which) may be holding a lock that
    569  // can deadlock RtlLookupFunctionEntry, we can't proceed because that thread
    570  // may be the one that we're trying to walk the stack of.
    571  //
    572  // But if there is no such thread by this point, then our target thread can't
    573  // be holding a lock, so it's safe to proceed. By virtue of being suspended,
    574  // the target thread can't acquire any new locks during our stack walking, so
    575  // we only need to do this check once. Other threads may temporarily acquire
    576  // the locks while we're walking the stack, but that's mostly fine -- calling
    577  // RtlLookupFunctionEntry will make us wait for them to release the locks,
    578  // but at least we won't deadlock.
    579  if (!IsStackWalkingSafe()) {
    580    return;
    581  }
    582 
    583  bool firstFrame = true;
    584 #  endif
    585 
    586  FrameSkipper skipper(aFirstFramePC);
    587 
    588  uint32_t frames = 0;
    589 
    590  // Now walk the stack.
    591  while (true) {
    592    DWORD64 addr;
    593    DWORD64 spaddr;
    594 
    595 #  if defined(_M_IX86)
    596    // 32-bit frame unwinding.
    597    BOOL ok = dbgHelp.StackWalk64(
    598        IMAGE_FILE_MACHINE_I386, targetThread, &frame64, context.CONTEXTPtr(),
    599        nullptr,
    600        ::SymFunctionTableAccess64,  // function table access routine
    601        ::SymGetModuleBase64,        // module base routine
    602        0);
    603 
    604    if (ok) {
    605      addr = frame64.AddrPC.Offset;
    606      spaddr = frame64.AddrStack.Offset;
    607    } else {
    608      addr = 0;
    609      spaddr = 0;
    610      if (walkCallingThread) {
    611        PrintError("WalkStack64");
    612      }
    613    }
    614 
    615    if (!ok) {
    616      break;
    617    }
    618 
    619 #  elif defined(_M_AMD64) || defined(_M_ARM64)
    620 
    621    auto currentInstr = context.PC();
    622 
    623    // If we reach a frame in JIT code, we don't have enough information to
    624    // unwind, so we have to give up.
    625    if (sJitCodeRegionStart && (uint8_t*)currentInstr >= sJitCodeRegionStart &&
    626        (uint8_t*)currentInstr < sJitCodeRegionStart + sJitCodeRegionSize) {
    627      break;
    628    }
    629 
    630    // We must also avoid msmpeg2vdec.dll's JIT region: they don't generate
    631    // unwind data, so their JIT unwind callback just throws up its hands and
    632    // terminates the process.
    633    if (sMsMpegJitCodeRegionStart &&
    634        (uint8_t*)currentInstr >= sMsMpegJitCodeRegionStart &&
    635        (uint8_t*)currentInstr <
    636            sMsMpegJitCodeRegionStart + sMsMpegJitCodeRegionSize) {
    637      break;
    638    }
    639 
    640    // 64-bit frame unwinding.
    641    // Try to look up unwind metadata for the current function.
    642    ULONG64 imageBase;
    643    PRUNTIME_FUNCTION runtimeFunction =
    644        RtlLookupFunctionEntry(currentInstr, &imageBase, NULL);
    645 
    646    if (runtimeFunction) {
    647      PVOID dummyHandlerData;
    648      ULONG64 dummyEstablisherFrame;
    649      RtlVirtualUnwind(UNW_FLAG_NHANDLER, imageBase, currentInstr,
    650                       runtimeFunction, context.CONTEXTPtr(), &dummyHandlerData,
    651                       &dummyEstablisherFrame, nullptr);
    652    } else if (firstFrame) {
    653      // Leaf functions can be unwound by hand.
    654      context.PC() = *reinterpret_cast<DWORD64*>(context.SP());
    655      context.SP() += sizeof(void*);
    656    } else {
    657      // Something went wrong.
    658      break;
    659    }
    660 
    661    addr = context.PC();
    662    spaddr = context.SP();
    663    firstFrame = false;
    664 #  else
    665 #    error "unknown platform"
    666 #  endif
    667 
    668    if (addr == 0) {
    669      break;
    670    }
    671 
    672    if (skipper.ShouldSkipPC((void*)addr)) {
    673      continue;
    674    }
    675 
    676    aCallback(++frames, (void*)addr, (void*)spaddr, aClosure);
    677 
    678    if (aMaxFrames != 0 && frames == aMaxFrames) {
    679      break;
    680    }
    681 
    682 #  if defined(_M_IX86)
    683    if (frame64.AddrReturn.Offset == 0) {
    684      break;
    685    }
    686 #  endif
    687  }
    688 }
    689 
    690 MFBT_API void MozStackWalkThread(MozWalkStackCallback aCallback,
    691                                 uint32_t aMaxFrames, void* aClosure,
    692                                 HANDLE aThread, CONTEXT* aContext) {
    693  // We don't pass a aFirstFramePC because we walk the stack for another
    694  // thread.
    695  DoMozStackWalkThread(aCallback, nullptr, aMaxFrames, aClosure, aThread,
    696                       aContext);
    697 }
    698 
    699 MFBT_API void MozStackWalk(MozWalkStackCallback aCallback,
    700                           const void* aFirstFramePC, uint32_t aMaxFrames,
    701                           void* aClosure) {
    702  DoMozStackWalkThread(aCallback, aFirstFramePC ? aFirstFramePC : CallerPC(),
    703                       aMaxFrames, aClosure, nullptr, nullptr);
    704 }
    705 
    706 /* static */ BOOL CALLBACK
    707 DbgHelpWrapperSym::CallbackEspecial64(PCSTR aModuleName, DWORD64 aModuleBase,
    708                                      ULONG aModuleSize, PVOID aUserContext) {
    709  BOOL retval = TRUE;
    710  auto context = reinterpret_cast<CallbackEspecial64UserContext*>(aUserContext);
    711  DWORD64 addr = context->mAddr;
    712 
    713  /*
    714   * You'll want to control this if we are running on an
    715   *  architecture where the addresses go the other direction.
    716   * Not sure this is even a realistic consideration.
    717   */
    718  const BOOL addressIncreases = TRUE;
    719 
    720  /*
    721   * If it falls in side the known range, load the symbols.
    722   */
    723  if (addressIncreases
    724          ? (addr >= aModuleBase && addr <= (aModuleBase + aModuleSize))
    725          : (addr <= aModuleBase && addr >= (aModuleBase - aModuleSize))) {
    726    retval = !!::SymLoadModule64(context->mSessionId, nullptr, aModuleName,
    727                                 nullptr, aModuleBase, aModuleSize);
    728    if (!retval) {
    729      PrintError("SymLoadModule64");
    730    }
    731  }
    732 
    733  return retval;
    734 }
    735 
    736 /*
    737 * SymGetModuleInfoEspecial
    738 *
    739 * Attempt to determine the module information.
    740 * Bug 112196 says this DLL may not have been loaded at the time
    741 *  SymInitialize was called, and thus the module information
    742 *  and symbol information is not available.
    743 * This code rectifies that problem.
    744 */
    745 
    746 // New members were added to IMAGEHLP_MODULE64 (that show up in the
    747 // Platform SDK that ships with VC8, but not the Platform SDK that ships
    748 // with VC7.1, i.e., between DbgHelp 6.0 and 6.1), but we don't need to
    749 // use them, and it's useful to be able to function correctly with the
    750 // older library.  (Stock Windows XP SP2 seems to ship with dbghelp.dll
    751 // version 5.1.)  Since Platform SDK version need not correspond to
    752 // compiler version, and the version number in debughlp.h was NOT bumped
    753 // when these changes were made, ifdef based on a constant that was
    754 // added between these versions.
    755 #  ifdef SSRVOPT_SETCONTEXT
    756 #    define NS_IMAGEHLP_MODULE64_SIZE                                        \
    757      (((offsetof(IMAGEHLP_MODULE64, LoadedPdbName) + sizeof(DWORD64) - 1) / \
    758        sizeof(DWORD64)) *                                                   \
    759       sizeof(DWORD64))
    760 #  else
    761 #    define NS_IMAGEHLP_MODULE64_SIZE sizeof(IMAGEHLP_MODULE64)
    762 #  endif
    763 
    764 BOOL DbgHelpWrapperSym::SymGetModuleInfoEspecial64(
    765    DWORD64 aAddr, PIMAGEHLP_MODULE64 aModuleInfo, PIMAGEHLP_LINE64 aLineInfo) {
    766  if (!ReadyToUse()) {
    767    return FALSE;
    768  }
    769 
    770  AutoCriticalSection guard(&sCriticalSection);
    771  BOOL retval = FALSE;
    772 
    773  /*
    774   * Init the vars if we have em.
    775   */
    776  aModuleInfo->SizeOfStruct = NS_IMAGEHLP_MODULE64_SIZE;
    777  if (aLineInfo) {
    778    aLineInfo->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
    779  }
    780 
    781  /*
    782   * Give it a go.
    783   * It may already be loaded.
    784   */
    785  retval = ::SymGetModuleInfo64(sSessionId, aAddr, aModuleInfo);
    786  if (retval == FALSE) {
    787    /*
    788     * Not loaded, here's the magic.
    789     * Go through all the modules.
    790     */
    791    CallbackEspecial64UserContext context{
    792        .mSessionId = sSessionId,
    793        .mAddr = aAddr,
    794    };
    795    BOOL enumRes = ::EnumerateLoadedModules64(
    796        sSessionId, CallbackEspecial64, reinterpret_cast<PVOID>(&context));
    797    if (enumRes != FALSE) {
    798      /*
    799       * One final go.
    800       * If it fails, then well, we have other problems.
    801       */
    802      retval = ::SymGetModuleInfo64(sSessionId, aAddr, aModuleInfo);
    803    }
    804  }
    805 
    806  /*
    807   * If we got module info, we may attempt line info as well.
    808   * We will not report failure if this does not work.
    809   */
    810  if (retval != FALSE && aLineInfo) {
    811    DWORD displacement = 0;
    812    BOOL lineRes = FALSE;
    813    lineRes =
    814        ::SymGetLineFromAddr64(sSessionId, aAddr, &displacement, aLineInfo);
    815    if (!lineRes) {
    816      // Clear out aLineInfo to indicate that it's not valid
    817      memset(aLineInfo, 0, sizeof(*aLineInfo));
    818    }
    819  }
    820 
    821  return retval;
    822 }
    823 
    824 MFBT_API bool MozDescribeCodeAddress(void* aPC,
    825                                     MozCodeAddressDetails* aDetails) {
    826  aDetails->library[0] = '\0';
    827  aDetails->loffset = 0;
    828  aDetails->filename[0] = '\0';
    829  aDetails->lineno = 0;
    830  aDetails->function[0] = '\0';
    831  aDetails->foffset = 0;
    832 
    833  DbgHelpWrapperSym dbgHelp;
    834  if (!dbgHelp.ReadyToUse()) {
    835    return false;
    836  }
    837 
    838  // Attempt to load module info before we attempt to resolve the symbol.
    839  // This just makes sure we get good info if available.
    840  DWORD64 addr = (DWORD64)aPC;
    841  IMAGEHLP_MODULE64 modInfo;
    842  IMAGEHLP_LINE64 lineInfo;
    843  BOOL modInfoRes;
    844  modInfoRes = dbgHelp.SymGetModuleInfoEspecial64(addr, &modInfo, &lineInfo);
    845 
    846  if (modInfoRes) {
    847    strncpy(aDetails->library, modInfo.LoadedImageName,
    848            sizeof(aDetails->library));
    849    aDetails->library[std::size(aDetails->library) - 1] = '\0';
    850    aDetails->loffset = (char*)aPC - (char*)modInfo.BaseOfImage;
    851 
    852    if (lineInfo.FileName) {
    853      strncpy(aDetails->filename, lineInfo.FileName,
    854              sizeof(aDetails->filename));
    855      aDetails->filename[std::size(aDetails->filename) - 1] = '\0';
    856      aDetails->lineno = lineInfo.LineNumber;
    857    }
    858  }
    859 
    860  ULONG64 buffer[(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR) +
    861                  sizeof(ULONG64) - 1) /
    862                 sizeof(ULONG64)];
    863  PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
    864  pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
    865  pSymbol->MaxNameLen = MAX_SYM_NAME;
    866 
    867  DWORD64 displacement;
    868  BOOL ok = dbgHelp.SymFromAddr(addr, &displacement, pSymbol);
    869 
    870  if (ok) {
    871    strncpy(aDetails->function, pSymbol->Name, sizeof(aDetails->function));
    872    aDetails->function[std::size(aDetails->function) - 1] = '\0';
    873    aDetails->foffset = static_cast<ptrdiff_t>(displacement);
    874  }
    875 
    876  return true;
    877 }
    878 
    879 // i386 or PPC Linux stackwalking code
    880 //
    881 // Changes to to OS/Architecture support here should be reflected in
    882 // build/moz.configure/memory.configure
    883 #elif HAVE_DLADDR &&                                           \
    884    (HAVE__UNWIND_BACKTRACE || MOZ_STACKWALK_SUPPORTS_LINUX || \
    885     MOZ_STACKWALK_SUPPORTS_MACOSX)
    886 
    887 #  include <stdlib.h>
    888 #  include <stdio.h>
    889 
    890 // On glibc 2.1, the Dl_info api defined in <dlfcn.h> is only exposed
    891 // if __USE_GNU is defined.  I suppose its some kind of standards
    892 // adherence thing.
    893 //
    894 #  if (__GLIBC_MINOR__ >= 1) && !defined(__USE_GNU)
    895 #    define __USE_GNU
    896 #  endif
    897 
    898 // This thing is exported by libstdc++
    899 // Yes, this is a gcc only hack
    900 #  if defined(MOZ_DEMANGLE_SYMBOLS)
    901 #    include <cxxabi.h>
    902 #  endif  // MOZ_DEMANGLE_SYMBOLS
    903 
    904 namespace mozilla {
    905 
    906 void DemangleSymbol(const char* aSymbol, char* aBuffer, int aBufLen) {
    907  aBuffer[0] = '\0';
    908 
    909 #  if defined(MOZ_DEMANGLE_SYMBOLS)
    910  /* See demangle.h in the gcc source for the voodoo */
    911  char* demangled = abi::__cxa_demangle(aSymbol, 0, 0, 0);
    912 
    913  if (demangled) {
    914    strncpy(aBuffer, demangled, aBufLen);
    915    aBuffer[aBufLen - 1] = '\0';
    916    free(demangled);
    917  }
    918 #  endif  // MOZ_DEMANGLE_SYMBOLS
    919 }
    920 
    921 }  // namespace mozilla
    922 
    923 // {x86, ppc} x {Linux, Mac} stackwalking code.
    924 //
    925 // Changes to to OS/Architecture support here should be reflected in
    926 // build/moz.configure/memory.configure
    927 #  if ((defined(__i386) || defined(PPC) || defined(__ppc__)) && \
    928       (MOZ_STACKWALK_SUPPORTS_MACOSX || MOZ_STACKWALK_SUPPORTS_LINUX))
    929 
    930 static void DoFramePointerStackWalk(MozWalkStackCallback aCallback,
    931                                    const void* aFirstFramePC,
    932                                    uint32_t aMaxFrames, void* aClosure,
    933                                    void** aBp, void* aStackEnd);
    934 
    935 MFBT_API void MozStackWalk(MozWalkStackCallback aCallback,
    936                           const void* aFirstFramePC, uint32_t aMaxFrames,
    937                           void* aClosure) {
    938  // Get the frame pointer
    939  void** bp = (void**)__builtin_frame_address(0);
    940 
    941  void* stackEnd;
    942 #    if HAVE___LIBC_STACK_END
    943  stackEnd = __libc_stack_end;
    944 #    elif defined(XP_DARWIN)
    945  stackEnd = pthread_get_stackaddr_np(pthread_self());
    946 #    elif defined(ANDROID)
    947  pthread_attr_t sattr;
    948  pthread_attr_init(&sattr);
    949  int rc = pthread_getattr_np(pthread_self(), &sattr);
    950  MOZ_RELEASE_ASSERT(rc == 0, "pthread_getattr_np failed");
    951  void* stackBase = stackEnd = nullptr;
    952  size_t stackSize = 0;
    953  if (gettid() != getpid()) {
    954    // bionic's pthread_attr_getstack doesn't tell the truth for the main
    955    // thread (see bug 846670). So don't use it for the main thread.
    956    if (!pthread_attr_getstack(&sattr, &stackBase, &stackSize)) {
    957      stackEnd = static_cast<char*>(stackBase) + stackSize;
    958    } else {
    959      stackEnd = nullptr;
    960    }
    961  }
    962  if (!stackEnd) {
    963    // So consider the current frame pointer + an arbitrary size of 8MB
    964    // (modulo overflow ; not really arbitrary as it's the default stack
    965    // size for the main thread) if pthread_attr_getstack failed for
    966    // some reason (or was skipped).
    967    static const uintptr_t kMaxStackSize = 8 * 1024 * 1024;
    968    uintptr_t maxStackStart = uintptr_t(-1) - kMaxStackSize;
    969    uintptr_t stackStart = std::max(maxStackStart, uintptr_t(bp));
    970    stackEnd = reinterpret_cast<void*>(stackStart + kMaxStackSize);
    971  }
    972 #    else
    973 #      error Unsupported configuration
    974 #    endif
    975  DoFramePointerStackWalk(aCallback, aFirstFramePC, aMaxFrames, aClosure, bp,
    976                          stackEnd);
    977 }
    978 
    979 #  elif defined(HAVE__UNWIND_BACKTRACE)
    980 
    981 // libgcc_s.so symbols _Unwind_Backtrace@@GCC_3.3 and _Unwind_GetIP@@GCC_3.0
    982 #    include <unwind.h>
    983 
    984 struct unwind_info {
    985  MozWalkStackCallback callback;
    986  FrameSkipper skipper;
    987  int maxFrames;
    988  int numFrames;
    989  void* closure;
    990 };
    991 
    992 static _Unwind_Reason_Code unwind_callback(struct _Unwind_Context* context,
    993                                           void* closure) {
    994  _Unwind_Reason_Code ret = _URC_NO_REASON;
    995  unwind_info* info = static_cast<unwind_info*>(closure);
    996  void* pc = reinterpret_cast<void*>(_Unwind_GetIP(context));
    997 #    if HAVE___LIBC_STACK_END && defined(__aarch64__)
    998  // Work around https://sourceware.org/bugzilla/show_bug.cgi?id=32612
    999  // The _dl_tlsdesc_dynamic function can't be unwound through with
   1000  // _Unwind_Backtrace when glibc is built with aarch64 PAC (that leads
   1001  // to a crash).
   1002  // Unfortunately, we can't get the address of that specific function, so
   1003  // we just disallow all of ld-linux-aarch64.so.1: when we hit an address
   1004  // in there, we make _Unwind_Backtrace stop.
   1005  // In the case of _dl_tlsdesc_dynamic, this would stop the stackwalk at
   1006  // tls_get_addr_tail, which is enough information to know the stack comes
   1007  // from ld.so, and we even get inlining info giving us malloc,
   1008  // allocate_dtv_entry and allocate_and_init, which is plenty enough and
   1009  // better than nothing^Hcrashing.
   1010  // To figure out whether the frame falls into ld-linux-aarch64.so.1, we
   1011  // use __libc_stack_end (which lives there and is .data) as upper bound
   1012  // (assuming .data comes after .text), and get the base address of the
   1013  // library via dladdr.
   1014  if (!ldso_base) {
   1015    Dl_info info;
   1016    dladdr(&__libc_stack_end, &info);
   1017    ldso_base = (uintptr_t)info.dli_fbase;
   1018  }
   1019  if (ldso_base && ((uintptr_t)pc > ldso_base) &&
   1020      (uintptr_t)pc < (uintptr_t)&__libc_stack_end) {
   1021    // Any error code will do, we just want to stop the walk even when
   1022    // we haven't reached the limit.
   1023    ret = _URC_FOREIGN_EXCEPTION_CAUGHT;
   1024  }
   1025 #    endif
   1026  // TODO Use something like '_Unwind_GetGR()' to get the stack pointer.
   1027  if (!info->skipper.ShouldSkipPC(pc)) {
   1028    info->numFrames++;
   1029    (*info->callback)(info->numFrames, pc, nullptr, info->closure);
   1030    if (info->maxFrames != 0 && info->numFrames == info->maxFrames) {
   1031      // Again, any error code that stops the walk will do.
   1032      return _URC_FOREIGN_EXCEPTION_CAUGHT;
   1033    }
   1034  }
   1035  return ret;
   1036 }
   1037 
   1038 MFBT_API void MozStackWalk(MozWalkStackCallback aCallback,
   1039                           const void* aFirstFramePC, uint32_t aMaxFrames,
   1040                           void* aClosure) {
   1041  unwind_info info;
   1042  info.callback = aCallback;
   1043  info.skipper = FrameSkipper(aFirstFramePC ? aFirstFramePC : CallerPC());
   1044  info.maxFrames = aMaxFrames;
   1045  info.numFrames = 0;
   1046  info.closure = aClosure;
   1047 
   1048  // We ignore the return value from _Unwind_Backtrace. There are three main
   1049  // reasons for this.
   1050  // - On ARM/Android bionic's _Unwind_Backtrace usually (always?) returns
   1051  //   _URC_FAILURE.  See
   1052  //   https://bugzilla.mozilla.org/show_bug.cgi?id=717853#c110.
   1053  // - If aMaxFrames != 0, we want to stop early, and the only way to do that
   1054  //   is to make unwind_callback return something other than _URC_NO_REASON,
   1055  //   which causes _Unwind_Backtrace to return a non-success code.
   1056  // - MozStackWalk doesn't have a return value anyway.
   1057  (void)_Unwind_Backtrace(unwind_callback, &info);
   1058 }
   1059 
   1060 #  endif
   1061 
   1062 bool MFBT_API MozDescribeCodeAddress(void* aPC,
   1063                                     MozCodeAddressDetails* aDetails) {
   1064  aDetails->library[0] = '\0';
   1065  aDetails->loffset = 0;
   1066  aDetails->filename[0] = '\0';
   1067  aDetails->lineno = 0;
   1068  aDetails->function[0] = '\0';
   1069  aDetails->foffset = 0;
   1070 
   1071  Dl_info info;
   1072 
   1073 #  if defined(ANDROID) && defined(MOZ_LINKER)
   1074  int ok = __wrap_dladdr(aPC, &info);
   1075 #  else
   1076  int ok = dladdr(aPC, &info);
   1077 #  endif
   1078 
   1079  if (!ok) {
   1080    return true;
   1081  }
   1082 
   1083  strncpy(aDetails->library, info.dli_fname, sizeof(aDetails->library));
   1084  aDetails->library[std::size(aDetails->library) - 1] = '\0';
   1085  aDetails->loffset = (char*)aPC - (char*)info.dli_fbase;
   1086 
   1087 #  if !defined(XP_FREEBSD)
   1088  // On FreeBSD, dli_sname is unusably bad, it often returns things like
   1089  // 'gtk_xtbin_new' or 'XRE_GetBootstrap' instead of long C++ symbols. Just let
   1090  // GetFunction do the lookup directly in the ELF image.
   1091 
   1092  const char* symbol = info.dli_sname;
   1093  if (!symbol || symbol[0] == '\0') {
   1094    return true;
   1095  }
   1096 
   1097  DemangleSymbol(symbol, aDetails->function, sizeof(aDetails->function));
   1098 
   1099  if (aDetails->function[0] == '\0') {
   1100    // Just use the mangled symbol if demangling failed.
   1101    strncpy(aDetails->function, symbol, sizeof(aDetails->function));
   1102    aDetails->function[std::size(aDetails->function) - 1] = '\0';
   1103  }
   1104 
   1105  aDetails->foffset = (char*)aPC - (char*)info.dli_saddr;
   1106 #  endif
   1107 
   1108  return true;
   1109 }
   1110 
   1111 #else  // unsupported platform.
   1112 
   1113 MFBT_API void MozStackWalk(MozWalkStackCallback aCallback,
   1114                           const void* aFirstFramePC, uint32_t aMaxFrames,
   1115                           void* aClosure) {}
   1116 
   1117 MFBT_API bool MozDescribeCodeAddress(void* aPC,
   1118                                     MozCodeAddressDetails* aDetails) {
   1119  aDetails->library[0] = '\0';
   1120  aDetails->loffset = 0;
   1121  aDetails->filename[0] = '\0';
   1122  aDetails->lineno = 0;
   1123  aDetails->function[0] = '\0';
   1124  aDetails->foffset = 0;
   1125  return false;
   1126 }
   1127 
   1128 #endif
   1129 
   1130 #if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX)
   1131 
   1132 #  if defined(XP_MACOSX) && defined(__aarch64__)
   1133 // On macOS arm64, system libraries are arm64e binaries, and arm64e can do
   1134 // pointer authentication: The low bits of the pointer are the actual pointer
   1135 // value, and the high bits are an encrypted hash. During stackwalking, we need
   1136 // to strip off this hash. In theory, ptrauth_strip would be the right function
   1137 // to call for this. However, that function is a no-op unless it's called from
   1138 // code which also builds as arm64e - which we do not. So we cannot use it. So
   1139 // for now, we hardcode a mask that seems to work today: 40 bits for the pointer
   1140 // and 24 bits for the hash seems to do the trick. We can worry about
   1141 // dynamically computing the correct mask if this ever stops working.
   1142 const uintptr_t kPointerMask =
   1143    (uintptr_t(1) << 40) - 1;  // 40 bits pointer, 24 bit PAC
   1144 #  else
   1145 const uintptr_t kPointerMask = ~uintptr_t(0);
   1146 #  endif
   1147 
   1148 MOZ_ASAN_IGNORE
   1149 static void DoFramePointerStackWalk(MozWalkStackCallback aCallback,
   1150                                    const void* aFirstFramePC,
   1151                                    uint32_t aMaxFrames, void* aClosure,
   1152                                    void** aBp, void* aStackEnd) {
   1153  // Stack walking code courtesy Kipp's "leaky".
   1154 
   1155  FrameSkipper skipper(aFirstFramePC);
   1156  uint32_t numFrames = 0;
   1157 
   1158  // Sanitize the given aBp. Assume that something reasonably close to
   1159  // but before the stack end is going be a valid frame pointer. Also
   1160  // check that it is an aligned address. This increases the chances
   1161  // that if the pointer is not valid (which might happen if the caller
   1162  // called __builtin_frame_address(1) and its frame is busted for some
   1163  // reason), we won't read it, leading to a crash. Because the calling
   1164  // code is not using frame pointers when returning, it might actually
   1165  // recover just fine.
   1166  static const uintptr_t kMaxStackSize = 8 * 1024 * 1024;
   1167  if (uintptr_t(aBp) < uintptr_t(aStackEnd) -
   1168                           std::min(kMaxStackSize, uintptr_t(aStackEnd)) ||
   1169      aBp >= aStackEnd || (uintptr_t(aBp) & 3)) {
   1170    return;
   1171  }
   1172 
   1173  while (aBp) {
   1174    void** next = (void**)*aBp;
   1175    // aBp may not be a frame pointer on i386 if code was compiled with
   1176    // -fomit-frame-pointer, so do some sanity checks.
   1177    // (aBp should be a frame pointer on ppc(64) but checking anyway may help
   1178    // a little if the stack has been corrupted.)
   1179    // We don't need to check against the begining of the stack because
   1180    // we can assume that aBp > sp
   1181    if (next <= aBp || next >= aStackEnd || (uintptr_t(next) & 3)) {
   1182      break;
   1183    }
   1184 #  if (defined(__ppc__) && defined(XP_MACOSX)) || defined(__powerpc64__)
   1185    // ppc mac or powerpc64 linux
   1186    void* pc = *(aBp + 2);
   1187    aBp += 3;
   1188 #  else  // i386 or powerpc32 linux
   1189    void* pc = *(aBp + 1);
   1190    aBp += 2;
   1191 #  endif
   1192 
   1193    // Strip off pointer authentication hash, if present. For now, it looks
   1194    // like only return addresses require stripping, and stack pointers do
   1195    // not. This might change in the future.
   1196    pc = (void*)((uintptr_t)pc & kPointerMask);
   1197 
   1198    if (!skipper.ShouldSkipPC(pc)) {
   1199      // Assume that the SP points to the BP of the function
   1200      // it called. We can't know the exact location of the SP
   1201      // but this should be sufficient for our use the SP
   1202      // to order elements on the stack.
   1203      numFrames++;
   1204      (*aCallback)(numFrames, pc, aBp, aClosure);
   1205      if (aMaxFrames != 0 && numFrames == aMaxFrames) {
   1206        break;
   1207      }
   1208    }
   1209    aBp = next;
   1210  }
   1211 }
   1212 
   1213 namespace mozilla {
   1214 
   1215 MFBT_API void FramePointerStackWalk(MozWalkStackCallback aCallback,
   1216                                    uint32_t aMaxFrames, void* aClosure,
   1217                                    void** aBp, void* aStackEnd) {
   1218  // We don't pass a aFirstFramePC because we start walking the stack from the
   1219  // frame at aBp.
   1220  DoFramePointerStackWalk(aCallback, nullptr, aMaxFrames, aClosure, aBp,
   1221                          aStackEnd);
   1222 }
   1223 
   1224 }  // namespace mozilla
   1225 
   1226 #else
   1227 
   1228 namespace mozilla {
   1229 MFBT_API void FramePointerStackWalk(MozWalkStackCallback aCallback,
   1230                                    uint32_t aMaxFrames, void* aClosure,
   1231                                    void** aBp, void* aStackEnd) {}
   1232 }  // namespace mozilla
   1233 
   1234 #endif
   1235 
   1236 MFBT_API int MozFormatCodeAddressDetails(
   1237    char* aBuffer, uint32_t aBufferSize, uint32_t aFrameNumber, void* aPC,
   1238    const MozCodeAddressDetails* aDetails) {
   1239  return MozFormatCodeAddress(aBuffer, aBufferSize, aFrameNumber, aPC,
   1240                              aDetails->function, aDetails->library,
   1241                              aDetails->loffset, aDetails->filename,
   1242                              aDetails->lineno);
   1243 }
   1244 
   1245 MFBT_API int MozFormatCodeAddress(char* aBuffer, uint32_t aBufferSize,
   1246                                  uint32_t aFrameNumber, const void* aPC,
   1247                                  const char* aFunction, const char* aLibrary,
   1248                                  ptrdiff_t aLOffset, const char* aFileName,
   1249                                  uint32_t aLineNo) {
   1250  const char* function = aFunction && aFunction[0] ? aFunction : "???";
   1251  if (aFileName && aFileName[0]) {
   1252    // We have a filename and (presumably) a line number. Use them.
   1253    return SprintfBuf(aBuffer, aBufferSize, "#%02u: %s (%s:%u)", aFrameNumber,
   1254                      function, aFileName, aLineNo);
   1255  } else if (aLibrary && aLibrary[0]) {
   1256    // We have no filename, but we do have a library name. Use it and the
   1257    // library offset, and print them in a way that `fix_stacks.py` can
   1258    // post-process.
   1259    return SprintfBuf(aBuffer, aBufferSize, "#%02u: %s[%s +0x%" PRIxPTR "]",
   1260                      aFrameNumber, function, aLibrary,
   1261                      static_cast<uintptr_t>(aLOffset));
   1262  } else {
   1263    // We have nothing useful to go on. (The format string is split because
   1264    // '??)' is a trigraph and causes a warning, sigh.)
   1265    return SprintfBuf(aBuffer, aBufferSize,
   1266                      "#%02u: ??? (???:???"
   1267                      ")",
   1268                      aFrameNumber);
   1269  }
   1270 }
   1271 
   1272 static void EnsureWrite(FILE* aStream, const char* aBuf, size_t aLen) {
   1273 #ifdef XP_WIN
   1274  int fd = _fileno(aStream);
   1275 #else
   1276  int fd = fileno(aStream);
   1277 #endif
   1278  while (aLen > 0) {
   1279 #ifdef XP_WIN
   1280    auto written = _write(fd, aBuf, aLen);
   1281 #else
   1282    auto written = write(fd, aBuf, aLen);
   1283 #endif
   1284    if (written <= 0 || size_t(written) > aLen) {
   1285      break;
   1286    }
   1287    aBuf += written;
   1288    aLen -= written;
   1289  }
   1290 }
   1291 
   1292 template <int N>
   1293 static int PrintStackFrameBuf(char (&aBuf)[N], uint32_t aFrameNumber, void* aPC,
   1294                              void* aSP) {
   1295  MozCodeAddressDetails details;
   1296  MozDescribeCodeAddress(aPC, &details);
   1297  int len =
   1298      MozFormatCodeAddressDetails(aBuf, N - 1, aFrameNumber, aPC, &details);
   1299  len = std::min(len, N - 2);
   1300  aBuf[len++] = '\n';
   1301  aBuf[len] = '\0';
   1302  return len;
   1303 }
   1304 
   1305 static void PrintStackFrame(uint32_t aFrameNumber, void* aPC, void* aSP,
   1306                            void* aClosure) {
   1307  FILE* stream = (FILE*)aClosure;
   1308  char buf[1025];  // 1024 + 1 for trailing '\n'
   1309  int len = PrintStackFrameBuf(buf, aFrameNumber, aPC, aSP);
   1310  fflush(stream);
   1311  EnsureWrite(stream, buf, len);
   1312 }
   1313 
   1314 static bool WalkTheStackEnabled() {
   1315  static bool result = [] {
   1316    char* value = getenv("MOZ_DISABLE_WALKTHESTACK");
   1317    return !(value && value[0]);
   1318  }();
   1319  return result;
   1320 }
   1321 
   1322 MFBT_API void MozWalkTheStack(FILE* aStream, const void* aFirstFramePC,
   1323                              uint32_t aMaxFrames) {
   1324  if (WalkTheStackEnabled()) {
   1325    MozStackWalk(PrintStackFrame, aFirstFramePC ? aFirstFramePC : CallerPC(),
   1326                 aMaxFrames, aStream);
   1327  }
   1328 }
   1329 
   1330 static void WriteStackFrame(uint32_t aFrameNumber, void* aPC, void* aSP,
   1331                            void* aClosure) {
   1332  auto writer = (void (*)(const char*))aClosure;
   1333  char buf[1024];
   1334  PrintStackFrameBuf(buf, aFrameNumber, aPC, aSP);
   1335  writer(buf);
   1336 }
   1337 
   1338 MFBT_API void MozWalkTheStackWithWriter(void (*aWriter)(const char*),
   1339                                        const void* aFirstFramePC,
   1340                                        uint32_t aMaxFrames) {
   1341  if (WalkTheStackEnabled()) {
   1342    MozStackWalk(WriteStackFrame, aFirstFramePC ? aFirstFramePC : CallerPC(),
   1343                 aMaxFrames, (void*)aWriter);
   1344  }
   1345 }