summaryrefslogtreecommitdiff
path: root/src/VBox/Runtime/common/dbg/dbgstackdumpself.cpp
blob: c41f5fd40cf02473965e3734a548c1a7884aec76 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
/* $Id$ */
/** @file
 * IPRT - Dump current thread stack to buffer.
 */

/*
 * Copyright (C) 2006-2022 Oracle and/or its affiliates.
 *
 * This file is part of VirtualBox base platform packages, as
 * available from https://www.virtualbox.org.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation, in version 3 of the
 * License.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <https://www.gnu.org/licenses>.
 *
 * The contents of this file may alternatively be used under the terms
 * of the Common Development and Distribution License Version 1.0
 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
 * in the VirtualBox distribution, in which case the provisions of the
 * CDDL are applicable instead of those of the GPL.
 *
 * You may elect to license modified versions of this file under the
 * terms and conditions of either the GPL or the CDDL or both.
 *
 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
 */


/*********************************************************************************************************************************
*   Header Files                                                                                                                 *
*********************************************************************************************************************************/
#include "internal/iprt.h"
#include <iprt/dbg.h>

#include <iprt/errcore.h>
#include <iprt/string.h>
#include <iprt/ldr.h>
#include <iprt/list.h>
#include <iprt/mem.h>
#include <iprt/path.h>
#include <iprt/string.h>

#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
# include <iprt/x86.h>
#else
# error "PORTME"
#endif

#ifdef RT_OS_WINDOWS
# include <iprt/param.h>
# include <iprt/utf16.h>
# include <iprt/win/windows.h>
#elif defined(RT_OS_LINUX) || defined(RT_OS_DARWIN)
# include <dlfcn.h>
#endif


/*********************************************************************************************************************************
*   Structures and Typedefs                                                                                                      *
*********************************************************************************************************************************/
/**
 * Cached module.
 */
typedef struct RTDBGSTACKSELFMOD
{
    /** List entry. */
    RTLISTNODE  ListEntry;
    /** The mapping address. */
    uintptr_t   uMapping;
    /** The size of the mapping. */
    size_t      cbMapping;
    /** The loader module handle. */
    RTLDRMOD    hLdrMod;
    /** The debug module handle, if available. */
    RTDBGMOD    hDbgMod;
    /** Offset into szFilename of the name part. */
    size_t      offName;
    /** the module filename. */
    char        szFilename[RTPATH_MAX];
} RTDBGSTACKSELFMOD;
/** Pointer to a cached module. */
typedef RTDBGSTACKSELFMOD *PRTDBGSTACKSELFMOD;


/**
 * Symbol search state.
 */
typedef struct RTDBGSTACKSELFSYMSEARCH
{
    /** The address (not RVA) we're searching for a symbol for. */
    uintptr_t    uSearch;
    /** The distance of the current hit.  This is ~(uintptr_t)0 if no hit. */
    uintptr_t    offBestDist;
    /** Where to return symbol information. */
    PRTDBGSYMBOL pSymInfo;
} RTDBGSTACKSELFSYMSEARCH;
typedef RTDBGSTACKSELFSYMSEARCH *PRTDBGSTACKSELFSYMSEARCH;


/*********************************************************************************************************************************
*   Internal Functions                                                                                                           *
*********************************************************************************************************************************/
/* Wanted to use DECLHIDDEN(DECLASM(size_t)) here, but early solaris 11 doesn't like it. */
RT_C_DECLS_BEGIN
DECL_HIDDEN_CALLBACK(size_t) rtDbgStackDumpSelfWorker(char *pszStack, size_t cbStack, uint32_t fFlags, PCRTCCUINTREG pauRegs);
RT_C_DECLS_END


/**
 * Worker for stack and module reader callback.
 *
 * @returns IPRT status code
 * @param   pvDst               Where to put the request memory.
 * @param   cbToRead            How much to read.
 * @param   uSrcAddr            Where to read the memory from.
 */
static int rtDbgStackDumpSelfSafeMemoryReader(void *pvDst, size_t cbToRead, uintptr_t uSrcAddr)
{
# ifdef RT_OS_WINDOWS
#  if 1
    __try
    {
        memcpy(pvDst, (void const *)uSrcAddr, cbToRead);
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        return VERR_ACCESS_DENIED;
    }
#  else
    SIZE_T cbActual = 0;
    if (ReadProcessMemory(GetCurrentProcess(), (void const *)uSrcAddr, pvDst, cbToRead, &cbActual))
    {
        if (cbActual >= cbToRead)
            return VINF_SUCCESS;
        memset((uint8_t *)pvDst + cbActual, 0, cbToRead - cbActual);
        return VINF_SUCCESS;
    }
#  endif
# else
    /** @todo secure this from SIGSEGV.  */
    memcpy(pvDst, (void const *)uSrcAddr, cbToRead);
# endif
    return VINF_SUCCESS;
}


#if defined(RT_OS_WINDOWS) && 0
/**
 * @callback_method_impl{FNRTLDRRDRMEMREAD}
 */
static DECLCALLBACK(int) rtDbgStackDumpSelfModReader(void *pvBuf, size_t cb, size_t off, void *pvUser)
{
    PRTDBGSTACKSELFMOD pMod = (PRTDBGSTACKSELFMOD)pvUser;
    return rtDbgStackDumpSelfSafeMemoryReader(pvBuf, cb, pMod->uMapping + off);
}
#endif


/**
 * @interface_method_impl{RTDBGUNWINDSTATE,pfnReadStack}
 */
static DECLCALLBACK(int) rtDbgStackDumpSelfReader(PRTDBGUNWINDSTATE pThis, RTUINTPTR uSp, size_t cbToRead, void *pvDst)
{
    RT_NOREF(pThis);
    return rtDbgStackDumpSelfSafeMemoryReader(pvDst, cbToRead, uSp);
}


#ifdef RT_OS_WINDOWS
/**
 * Figure the size of a loaded PE image.
 * @returns The size.
 * @param   uBase       The image base address.
 */
static size_t rtDbgStackDumpSelfGetPeImageSize(uintptr_t uBase)
{
    union
    {
        IMAGE_DOS_HEADER DosHdr;
        IMAGE_NT_HEADERS NtHdrs;
    } uBuf;
    int rc = rtDbgStackDumpSelfSafeMemoryReader(&uBuf, sizeof(uBuf.DosHdr), uBase);
    if (RT_SUCCESS(rc))
    {
        if (   uBuf.DosHdr.e_magic == IMAGE_DOS_SIGNATURE
            && uBuf.DosHdr.e_lfanew < _2M)
        {
            rc = rtDbgStackDumpSelfSafeMemoryReader(&uBuf, sizeof(uBuf.NtHdrs), uBase + uBuf.DosHdr.e_lfanew);
            if (RT_SUCCESS(rc))
            {
                if (uBuf.NtHdrs.Signature == IMAGE_NT_SIGNATURE)
                    return uBuf.NtHdrs.OptionalHeader.SizeOfImage;
            }
        }
    }
    return _64K;
}
#endif


/**
 * Works the module cache.
 *
 * @returns Pointer to module cache entry on success, NULL otherwise.
 * @param   uPc             The PC to locate a module for.
 * @param   pCachedModules  The module cache.
 */
static PRTDBGSTACKSELFMOD rtDbgStackDumpSelfQueryModForPC(uintptr_t uPc, PRTLISTANCHOR pCachedModules)
{
    /*
     * Search the list first.
     */
    PRTDBGSTACKSELFMOD pMod;
    RTListForEach(pCachedModules, pMod, RTDBGSTACKSELFMOD, ListEntry)
    {
        if (uPc - pMod->uMapping < pMod->cbMapping)
            return pMod;
    }

    /*
     * Try figure out the module using the native loader or similar.
     */
#ifdef RT_OS_WINDOWS
    HMODULE hmod = NULL;
    static bool volatile                 s_fResolvedSymbols = false;
    static decltype(GetModuleHandleExW) *g_pfnGetModuleHandleExW = NULL;
    decltype(GetModuleHandleExW) *pfnGetModuleHandleExW;
    if (s_fResolvedSymbols)
        pfnGetModuleHandleExW = g_pfnGetModuleHandleExW;
    else
    {
        pfnGetModuleHandleExW = (decltype(GetModuleHandleExW) *)GetProcAddress(GetModuleHandleW(L"kernel32.dll"),
                                                                               "GetModuleHandleExW");
        g_pfnGetModuleHandleExW = pfnGetModuleHandleExW;
        s_fResolvedSymbols = true;
    }
    if (   pfnGetModuleHandleExW
        && pfnGetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
                                 (LPCWSTR)uPc, &hmod))
    {
        WCHAR wszFilename[RTPATH_MAX];
        DWORD cwcFilename = GetModuleFileNameW(hmod, wszFilename, RT_ELEMENTS(wszFilename));
        if (cwcFilename > 0)
        {
            pMod = (PRTDBGSTACKSELFMOD)RTMemAllocZ(sizeof(*pMod));
            if (pMod)
            {
                char *pszDst = pMod->szFilename;
                int rc = RTUtf16ToUtf8Ex(wszFilename, cwcFilename, &pszDst, sizeof(pMod->szFilename), NULL);
                if (RT_SUCCESS(rc))
                {
                    const char *pszFilename = RTPathFilename(pMod->szFilename);
                    pMod->offName   = pszFilename ? pszFilename - &pMod->szFilename[0] : 0;
                    pMod->uMapping  = (uintptr_t)hmod & ~(uintptr_t)(PAGE_SIZE - 1);
                    pMod->cbMapping = rtDbgStackDumpSelfGetPeImageSize(pMod->uMapping);
                    pMod->hLdrMod   = NIL_RTLDRMOD;
                    pMod->hDbgMod   = NIL_RTDBGMOD;

# if 0 /* this ain't reliable, trouble enumerate symbols in VBoxRT. But why bother when we can load it off the disk. */
                    rc = RTLdrOpenInMemory(&pMod->szFilename[pMod->offName], RTLDR_O_FOR_DEBUG, RTLdrGetHostArch(),
                                           pMod->cbMapping, rtDbgStackDumpSelfModReader, NULL /*pfnDtor*/, pMod /*pvUser*/,
                                           &pMod->hLdrMod, NULL /*pErrInfo*/);
# else
                    rc = RTLdrOpen(pMod->szFilename, RTLDR_O_FOR_DEBUG, RTLdrGetHostArch(), &pMod->hLdrMod);
# endif
                    if (RT_SUCCESS(rc))
                    {
                        pMod->cbMapping = RTLdrSize(pMod->hLdrMod);

                        /* Try open debug info for the module. */
                        uint32_t uTimeDateStamp = 0;
                        RTLdrQueryProp(pMod->hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &uTimeDateStamp, sizeof(uTimeDateStamp));
                        rc = RTDbgModCreateFromPeImage(&pMod->hDbgMod, pMod->szFilename, &pMod->szFilename[pMod->offName],
                                                       &pMod->hLdrMod, (uint32_t)pMod->cbMapping, uTimeDateStamp, NIL_RTDBGCFG);
                        RTListPrepend(pCachedModules, &pMod->ListEntry);
                        return pMod;
                    }
                }
                RTMemFree(pMod);
            }
        }
    }
#elif defined(RT_OS_LINUX) || defined(RT_OS_DARWIN)
    Dl_info Info = { NULL, NULL, NULL, NULL };
    int rc = dladdr((const void *)uPc, &Info);
    if (rc != 0 && Info.dli_fname)
    {
        pMod = (PRTDBGSTACKSELFMOD)RTMemAllocZ(sizeof(*pMod));
        if (pMod)
        {
            /** @todo better filename translation... */
            rc = RTStrCopy(pMod->szFilename, sizeof(pMod->szFilename), Info.dli_fname);
            if (RT_SUCCESS(rc))
            {
                RTStrPurgeEncoding(pMod->szFilename);

                const char *pszFilename = RTPathFilename(pMod->szFilename);
                pMod->offName   = pszFilename ? pszFilename - &pMod->szFilename[0] : 0;
                pMod->uMapping  = (uintptr_t)Info.dli_fbase;
                pMod->cbMapping = 0;
                pMod->hLdrMod   = NIL_RTLDRMOD;
                pMod->hDbgMod   = NIL_RTDBGMOD;

                rc = RTLdrOpen(pMod->szFilename, RTLDR_O_FOR_DEBUG, RTLdrGetHostArch(), &pMod->hLdrMod);
                if (RT_SUCCESS(rc))
                {
                    pMod->cbMapping = RTLdrSize(pMod->hLdrMod);

                    /* Try open debug info for the module. */
                    //uint32_t uTimeDateStamp = 0;
                    //RTLdrQueryProp(pMod->hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &uTimeDateStamp, sizeof(uTimeDateStamp));
                    //RTDbgModCreateFromImage()??
                    //rc = RTDbgModCreateFromPeImage(&pMod->hDbgMod, pMod->szFilename, &pMod->szFilename[pMod->offName],
                    //                               &pMod->hLdrMod, (uint32_t)pMod->cbMapping, uTimeDateStamp, NIL_RTDBGCFG);

                    RTListPrepend(pCachedModules, &pMod->ListEntry);
                    return pMod;
                }
            }
            RTMemFree(pMod);
        }
    }
#endif
    return NULL;
}


/**
 * @callback_method_impl{FNRTLDRENUMSYMS}
 */
static DECLCALLBACK(int) rtDbgStackdumpSelfSymbolSearchCallback(RTLDRMOD hLdrMod, const char *pszSymbol,
                                                                unsigned uSymbol, RTLDRADDR Value, void *pvUser)
{
    PRTDBGSTACKSELFSYMSEARCH pSearch = (PRTDBGSTACKSELFSYMSEARCH)pvUser;
    if (Value >= pSearch->uSearch)
    {
        uintptr_t const offDist = (uintptr_t)Value - pSearch->uSearch;
        if (offDist < pSearch->offBestDist)
        {
            pSearch->offBestDist = offDist;

            PRTDBGSYMBOL pSymInfo = pSearch->pSymInfo;
            pSymInfo->Value    = Value;
            pSymInfo->offSeg   = Value;
            pSymInfo->iSeg     = RTDBGSEGIDX_ABS;
            pSymInfo->iOrdinal = uSymbol;
            pSymInfo->fFlags   = 0;
            if (pszSymbol)
                RTStrCopy(pSymInfo->szName, sizeof(pSymInfo->szName), pszSymbol);
            else
                RTStrPrintf(pSymInfo->szName, sizeof(pSymInfo->szName), "Ordinal#%u", uSymbol);

            if (offDist < 8)
                return VINF_CALLBACK_RETURN;
        }
    }
    RT_NOREF(hLdrMod);
    return VINF_SUCCESS;
}


/**
 * Searches for a symbol close to @a uRva.
 *
 * @returns true if found, false if not.
 * @param   pMod        The module cache entry to search in.
 * @param   uRva        The RVA to locate a symbol for.
 * @param   poffDisp    Where to return the distance between uRva and the returned symbol.
 * @param   pSymInfo    Where to return the symbol information.
 */
static bool rtDbgStackDumpSelfQuerySymbol(PRTDBGSTACKSELFMOD pMod, uintptr_t uRva, PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo)
{
    if (pMod->hDbgMod != NIL_RTDBGMOD)
    {
        int rc = RTDbgModSymbolByAddr(pMod->hDbgMod, RTDBGSEGIDX_RVA, uRva, RTDBGSYMADDR_FLAGS_LESS_OR_EQUAL,
                                      poffDisp, pSymInfo);
        if (RT_SUCCESS(rc))
            return true;
    }

    if (pMod->hLdrMod != NIL_RTLDRMOD)
    {
        RTDBGSTACKSELFSYMSEARCH SearchInfo = { pMod->uMapping + uRva, ~(uintptr_t)0, pSymInfo };
        int rc = RTLdrEnumSymbols(pMod->hLdrMod, 0, (const void *)pMod->uMapping, pMod->uMapping,
                                  rtDbgStackdumpSelfSymbolSearchCallback, &SearchInfo);
        if (RT_SUCCESS(rc) && SearchInfo.offBestDist != ~(uintptr_t)0)
        {
            *poffDisp = SearchInfo.offBestDist;
            return true;
        }
    }

    return false;
}


/**
 * Does the grunt work for RTDbgStackDumpSelf.
 *
 * Called thru an assembly wrapper that collects the necessary register state.
 *
 * @returns Length of the string returned in pszStack.
 * @param   pszStack    Where to output the stack dump.
 * @param   cbStack     The size of the @a pszStack buffer.
 * @param   fFlags      Flags, MBZ.
 * @param   pauRegs     Register state.  For AMD64 and x86 this starts with the
 *                      PC and us followed by the general purpose registers.
 */
DECL_HIDDEN_CALLBACK(size_t) rtDbgStackDumpSelfWorker(char *pszStack, size_t cbStack, uint32_t fFlags, PCRTCCUINTREG pauRegs)
{
    RT_NOREF(fFlags);

    /*
     * Initialize the unwind state.
     */
    RTDBGUNWINDSTATE UnwindState;
    RT_ZERO(UnwindState);

    UnwindState.u32Magic        = RTDBGUNWINDSTATE_MAGIC;
    UnwindState.pfnReadStack    = rtDbgStackDumpSelfReader;
#ifdef RT_ARCH_AMD64
    UnwindState.enmArch         = RTLDRARCH_AMD64;
    UnwindState.uPc             = pauRegs[0];
    UnwindState.enmRetType      = RTDBGRETURNTYPE_NEAR64;
    for (unsigned i = 0; i < 16; i++)
        UnwindState.u.x86.auRegs[i] = pauRegs[i + 1];
#elif defined(RT_ARCH_X86)
    UnwindState.enmArch         = RTLDRARCH_X86_32;
    UnwindState.uPc             = pauRegs[0];
    UnwindState.enmRetType      = RTDBGRETURNTYPE_NEAR32;
    for (unsigned i = 0; i < 8; i++)
        UnwindState.u.x86.auRegs[i] = pauRegs[i + 1];
#else
# error "PORTME"
#endif

    /*
     * We cache modules.
     */
    PRTDBGSTACKSELFMOD  pCurModule = NULL;
    RTLISTANCHOR        CachedModules;
    RTListInit(&CachedModules);

    /*
     * Work the stack.
     */
    size_t offDst = 0;
    while (offDst + 64 < cbStack)
    {
        /* Try locate the module containing the current PC. */
        if (   !pCurModule
            || UnwindState.uPc - pCurModule->uMapping >= pCurModule->cbMapping)
            pCurModule = rtDbgStackDumpSelfQueryModForPC(UnwindState.uPc, &CachedModules);
        bool fManualUnwind = true;
        if (!pCurModule)
            offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, "%p\n", UnwindState.uPc);
        else
        {
            uintptr_t const uRva = UnwindState.uPc - pCurModule->uMapping;

            /*
             * Add a call stack entry with the symbol if we can.
             */
            union
            {
                RTDBGSYMBOL SymbolInfo;
                RTDBGLINE   LineInfo;
            } uBuf;
            RTINTPTR offDisp = 0;
            if (!rtDbgStackDumpSelfQuerySymbol(pCurModule, uRva, &offDisp, &uBuf.SymbolInfo))
                offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, "%p %s + %#zx\n",
                                      UnwindState.uPc, &pCurModule->szFilename[pCurModule->offName], (size_t)uRva);
            else if (offDisp == 0)
                offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, "%p %s!%s (rva:%#zx)\n", UnwindState.uPc,
                                      &pCurModule->szFilename[pCurModule->offName], uBuf.SymbolInfo.szName, (size_t)uRva);
            else
                offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, "%p %s!%s%c%#zx (rva:%#zx)\n",
                                      UnwindState.uPc, &pCurModule->szFilename[pCurModule->offName], uBuf.SymbolInfo.szName,
                                      offDisp >= 0 ? '+' : '-',  (size_t)RT_ABS(offDisp), (size_t)uRva);

            /*
             * Try supply the line number.
             */
            if (pCurModule->hDbgMod != NIL_RTDBGMOD)
            {
                offDisp = 0;
                int rc = RTDbgModLineByAddr(pCurModule->hDbgMod, RTDBGSEGIDX_RVA, uRva, &offDisp, &uBuf.LineInfo);
                if (RT_SUCCESS(rc) && offDisp)
                    offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, "  [%s:%u]\n",
                                          uBuf.LineInfo.szFilename, uBuf.LineInfo.uLineNo);
                else if (RT_SUCCESS(rc))
                    offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, "  [%s:%u (%c%#zx)]\n", uBuf.LineInfo.szFilename,
                                          uBuf.LineInfo.uLineNo, offDisp >= 0 ? '+' : '-',  (size_t)RT_ABS(offDisp));
            }

            /*
             * Try unwind using the module info.
             */
            int rc;
            if (pCurModule->hDbgMod != NIL_RTDBGMOD)
                rc = RTDbgModUnwindFrame(pCurModule->hDbgMod, RTDBGSEGIDX_RVA, uRva, &UnwindState);
            else
                rc = RTLdrUnwindFrame(pCurModule->hLdrMod, (void const *)pCurModule->uMapping, UINT32_MAX, uRva, &UnwindState);
            if (RT_SUCCESS(rc))
                fManualUnwind = false;
        }
        if (fManualUnwind)
        {
            break;
        }
    }

    /*
     * Destroy the cache.
     */
    PRTDBGSTACKSELFMOD pNextModule;
    RTListForEachSafe(&CachedModules, pCurModule, pNextModule, RTDBGSTACKSELFMOD, ListEntry)
    {
        if (pCurModule->hDbgMod != NIL_RTDBGMOD)
        {
            RTDbgModRelease(pCurModule->hDbgMod);
            pCurModule->hDbgMod = NIL_RTDBGMOD;
        }
        if (pCurModule->hLdrMod != NIL_RTLDRMOD)
        {
            RTLdrClose(pCurModule->hLdrMod);
            pCurModule->hLdrMod = NIL_RTLDRMOD;
        }
        RTMemFree(pCurModule);
    }

    return offDst;
}