summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/dbg/dbgmod.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/common/dbg/dbgmod.cpp')
-rw-r--r--src/VBox/Runtime/common/dbg/dbgmod.cpp2256
1 files changed, 2256 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/dbg/dbgmod.cpp b/src/VBox/Runtime/common/dbg/dbgmod.cpp
new file mode 100644
index 00000000..b8f984f6
--- /dev/null
+++ b/src/VBox/Runtime/common/dbg/dbgmod.cpp
@@ -0,0 +1,2256 @@
+/* $Id: dbgmod.cpp $ */
+/** @file
+ * IPRT - Debug Module Interpreter.
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE 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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DBG
+#include <iprt/dbg.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloca.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/avl.h>
+#include <iprt/err.h>
+#include <iprt/initterm.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/once.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/semaphore.h>
+#include <iprt/strcache.h>
+#include <iprt/string.h>
+#include <iprt/uuid.h>
+#include "internal/dbgmod.h"
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Debug info interpreter registration record. */
+typedef struct RTDBGMODREGDBG
+{
+ /** Pointer to the next record. */
+ struct RTDBGMODREGDBG *pNext;
+ /** Pointer to the virtual function table for the interpreter. */
+ PCRTDBGMODVTDBG pVt;
+ /** Usage counter. */
+ uint32_t volatile cUsers;
+} RTDBGMODREGDBG;
+typedef RTDBGMODREGDBG *PRTDBGMODREGDBG;
+
+/** Image interpreter registration record. */
+typedef struct RTDBGMODREGIMG
+{
+ /** Pointer to the next record. */
+ struct RTDBGMODREGIMG *pNext;
+ /** Pointer to the virtual function table for the interpreter. */
+ PCRTDBGMODVTIMG pVt;
+ /** Usage counter. */
+ uint32_t volatile cUsers;
+} RTDBGMODREGIMG;
+typedef RTDBGMODREGIMG *PRTDBGMODREGIMG;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Validates a debug module handle and returns rc if not valid. */
+#define RTDBGMOD_VALID_RETURN_RC(pDbgMod, rc) \
+ do { \
+ AssertPtrReturn((pDbgMod), (rc)); \
+ AssertReturn((pDbgMod)->u32Magic == RTDBGMOD_MAGIC, (rc)); \
+ AssertReturn((pDbgMod)->cRefs > 0, (rc)); \
+ } while (0)
+
+/** Locks the debug module. */
+#define RTDBGMOD_LOCK(pDbgMod) \
+ do { \
+ int rcLock = RTCritSectEnter(&(pDbgMod)->CritSect); \
+ AssertRC(rcLock); \
+ } while (0)
+
+/** Unlocks the debug module. */
+#define RTDBGMOD_UNLOCK(pDbgMod) \
+ do { \
+ int rcLock = RTCritSectLeave(&(pDbgMod)->CritSect); \
+ AssertRC(rcLock); \
+ } while (0)
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Init once object for lazy registration of the built-in image and debug
+ * info interpreters. */
+static RTONCE g_rtDbgModOnce = RTONCE_INITIALIZER;
+/** Read/Write semaphore protecting the list of registered interpreters. */
+static RTSEMRW g_hDbgModRWSem = NIL_RTSEMRW;
+/** List of registered image interpreters. */
+static PRTDBGMODREGIMG g_pImgHead;
+/** List of registered debug infor interpreters. */
+static PRTDBGMODREGDBG g_pDbgHead;
+/** String cache for the debug info interpreters.
+ * RTSTRCACHE is thread safe. */
+DECLHIDDEN(RTSTRCACHE) g_hDbgModStrCache = NIL_RTSTRCACHE;
+
+
+
+
+
+/**
+ * Cleanup debug info interpreter globals.
+ *
+ * @param enmReason The cause of the termination.
+ * @param iStatus The meaning of this depends on enmReason.
+ * @param pvUser User argument, unused.
+ */
+static DECLCALLBACK(void) rtDbgModTermCallback(RTTERMREASON enmReason, int32_t iStatus, void *pvUser)
+{
+ NOREF(iStatus); NOREF(pvUser);
+ if (enmReason == RTTERMREASON_UNLOAD)
+ {
+ RTSemRWDestroy(g_hDbgModRWSem);
+ g_hDbgModRWSem = NIL_RTSEMRW;
+
+ RTStrCacheDestroy(g_hDbgModStrCache);
+ g_hDbgModStrCache = NIL_RTSTRCACHE;
+
+ PRTDBGMODREGDBG pDbg = g_pDbgHead;
+ g_pDbgHead = NULL;
+ while (pDbg)
+ {
+ PRTDBGMODREGDBG pNext = pDbg->pNext;
+ AssertMsg(pDbg->cUsers == 0, ("%#x %s\n", pDbg->cUsers, pDbg->pVt->pszName));
+ RTMemFree(pDbg);
+ pDbg = pNext;
+ }
+
+ PRTDBGMODREGIMG pImg = g_pImgHead;
+ g_pImgHead = NULL;
+ while (pImg)
+ {
+ PRTDBGMODREGIMG pNext = pImg->pNext;
+ AssertMsg(pImg->cUsers == 0, ("%#x %s\n", pImg->cUsers, pImg->pVt->pszName));
+ RTMemFree(pImg);
+ pImg = pNext;
+ }
+ }
+}
+
+
+/**
+ * Internal worker for register a debug interpreter.
+ *
+ * Called while owning the write lock or when locking isn't required.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_NO_MEMORY
+ * @retval VERR_ALREADY_EXISTS
+ *
+ * @param pVt The virtual function table of the debug
+ * module interpreter.
+ */
+static int rtDbgModDebugInterpreterRegister(PCRTDBGMODVTDBG pVt)
+{
+ /*
+ * Search or duplicate registration.
+ */
+ PRTDBGMODREGDBG pPrev = NULL;
+ for (PRTDBGMODREGDBG pCur = g_pDbgHead; pCur; pCur = pCur->pNext)
+ {
+ if (pCur->pVt == pVt)
+ return VERR_ALREADY_EXISTS;
+ if (!strcmp(pCur->pVt->pszName, pVt->pszName))
+ return VERR_ALREADY_EXISTS;
+ pPrev = pCur;
+ }
+
+ /*
+ * Create a new record and add it to the end of the list.
+ */
+ PRTDBGMODREGDBG pReg = (PRTDBGMODREGDBG)RTMemAlloc(sizeof(*pReg));
+ if (!pReg)
+ return VERR_NO_MEMORY;
+ pReg->pVt = pVt;
+ pReg->cUsers = 0;
+ pReg->pNext = NULL;
+ if (pPrev)
+ pPrev->pNext = pReg;
+ else
+ g_pDbgHead = pReg;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Internal worker for register a image interpreter.
+ *
+ * Called while owning the write lock or when locking isn't required.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_NO_MEMORY
+ * @retval VERR_ALREADY_EXISTS
+ *
+ * @param pVt The virtual function table of the image
+ * interpreter.
+ */
+static int rtDbgModImageInterpreterRegister(PCRTDBGMODVTIMG pVt)
+{
+ /*
+ * Search or duplicate registration.
+ */
+ PRTDBGMODREGIMG pPrev = NULL;
+ for (PRTDBGMODREGIMG pCur = g_pImgHead; pCur; pCur = pCur->pNext)
+ {
+ if (pCur->pVt == pVt)
+ return VERR_ALREADY_EXISTS;
+ if (!strcmp(pCur->pVt->pszName, pVt->pszName))
+ return VERR_ALREADY_EXISTS;
+ pPrev = pCur;
+ }
+
+ /*
+ * Create a new record and add it to the end of the list.
+ */
+ PRTDBGMODREGIMG pReg = (PRTDBGMODREGIMG)RTMemAlloc(sizeof(*pReg));
+ if (!pReg)
+ return VERR_NO_MEMORY;
+ pReg->pVt = pVt;
+ pReg->cUsers = 0;
+ pReg->pNext = NULL;
+ if (pPrev)
+ pPrev->pNext = pReg;
+ else
+ g_pImgHead = pReg;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Do-once callback that initializes the read/write semaphore and registers
+ * the built-in interpreters.
+ *
+ * @returns IPRT status code.
+ * @param pvUser NULL.
+ */
+static DECLCALLBACK(int) rtDbgModInitOnce(void *pvUser)
+{
+ NOREF(pvUser);
+
+ /*
+ * Create the semaphore and string cache.
+ */
+ int rc = RTSemRWCreate(&g_hDbgModRWSem);
+ AssertRCReturn(rc, rc);
+
+ rc = RTStrCacheCreate(&g_hDbgModStrCache, "RTDBGMOD");
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Register the interpreters.
+ */
+ rc = rtDbgModDebugInterpreterRegister(&g_rtDbgModVtDbgNm);
+ if (RT_SUCCESS(rc))
+ rc = rtDbgModDebugInterpreterRegister(&g_rtDbgModVtDbgMapSym);
+ if (RT_SUCCESS(rc))
+ rc = rtDbgModDebugInterpreterRegister(&g_rtDbgModVtDbgDwarf);
+ if (RT_SUCCESS(rc))
+ rc = rtDbgModDebugInterpreterRegister(&g_rtDbgModVtDbgCodeView);
+#ifdef RT_OS_WINDOWS
+ if (RT_SUCCESS(rc))
+ rc = rtDbgModDebugInterpreterRegister(&g_rtDbgModVtDbgDbgHelp);
+#endif
+ if (RT_SUCCESS(rc))
+ rc = rtDbgModImageInterpreterRegister(&g_rtDbgModVtImgLdr);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Finally, register the IPRT cleanup callback.
+ */
+ rc = RTTermRegisterCallback(rtDbgModTermCallback, NULL);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+
+ /* bail out: use the termination callback. */
+ }
+ }
+ else
+ g_hDbgModStrCache = NIL_RTSTRCACHE;
+ rtDbgModTermCallback(RTTERMREASON_UNLOAD, 0, NULL);
+ return rc;
+}
+
+
+/**
+ * Performs lazy init of our global variables.
+ * @returns IPRT status code.
+ */
+DECLINLINE(int) rtDbgModLazyInit(void)
+{
+ return RTOnce(&g_rtDbgModOnce, rtDbgModInitOnce, NULL);
+}
+
+
+RTDECL(int) RTDbgModCreate(PRTDBGMOD phDbgMod, const char *pszName, RTUINTPTR cbSeg, uint32_t fFlags)
+{
+ /*
+ * Input validation and lazy initialization.
+ */
+ AssertPtrReturn(phDbgMod, VERR_INVALID_POINTER);
+ *phDbgMod = NIL_RTDBGMOD;
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ AssertReturn(*pszName, VERR_INVALID_PARAMETER);
+ AssertReturn(fFlags == 0 || fFlags == RTDBGMOD_F_NOT_DEFERRED, VERR_INVALID_PARAMETER);
+
+ int rc = rtDbgModLazyInit();
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Allocate a new module instance.
+ */
+ PRTDBGMODINT pDbgMod = (PRTDBGMODINT)RTMemAllocZ(sizeof(*pDbgMod));
+ if (!pDbgMod)
+ return VERR_NO_MEMORY;
+ pDbgMod->u32Magic = RTDBGMOD_MAGIC;
+ pDbgMod->cRefs = 1;
+ rc = RTCritSectInit(&pDbgMod->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ pDbgMod->pszImgFileSpecified = RTStrCacheEnter(g_hDbgModStrCache, pszName);
+ pDbgMod->pszName = RTStrCacheEnterLower(g_hDbgModStrCache, RTPathFilenameEx(pszName, RTPATH_STR_F_STYLE_DOS));
+ if (pDbgMod->pszName)
+ {
+ rc = rtDbgModContainerCreate(pDbgMod, cbSeg);
+ if (RT_SUCCESS(rc))
+ {
+ *phDbgMod = pDbgMod;
+ return rc;
+ }
+ RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFile);
+ RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszName);
+ }
+ RTCritSectDelete(&pDbgMod->CritSect);
+ }
+
+ RTMemFree(pDbgMod);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgModCreate);
+
+
+RTDECL(int) RTDbgModCreateFromMap(PRTDBGMOD phDbgMod, const char *pszFilename, const char *pszName,
+ RTUINTPTR uSubtrahend, RTDBGCFG hDbgCfg)
+{
+ RT_NOREF_PV(hDbgCfg);
+
+ /*
+ * Input validation and lazy initialization.
+ */
+ AssertPtrReturn(phDbgMod, VERR_INVALID_POINTER);
+ *phDbgMod = NIL_RTDBGMOD;
+ AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
+ AssertReturn(*pszFilename, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pszName, VERR_INVALID_POINTER);
+ AssertReturn(uSubtrahend == 0, VERR_NOT_IMPLEMENTED); /** @todo implement uSubtrahend. */
+
+ int rc = rtDbgModLazyInit();
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if (!pszName)
+ pszName = RTPathFilenameEx(pszFilename, RTPATH_STR_F_STYLE_DOS);
+
+ /*
+ * Allocate a new module instance.
+ */
+ PRTDBGMODINT pDbgMod = (PRTDBGMODINT)RTMemAllocZ(sizeof(*pDbgMod));
+ if (!pDbgMod)
+ return VERR_NO_MEMORY;
+ pDbgMod->u32Magic = RTDBGMOD_MAGIC;
+ pDbgMod->cRefs = 1;
+ rc = RTCritSectInit(&pDbgMod->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ pDbgMod->pszName = RTStrCacheEnterLower(g_hDbgModStrCache, pszName);
+ if (pDbgMod->pszName)
+ {
+ pDbgMod->pszDbgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename);
+ if (pDbgMod->pszDbgFile)
+ {
+ /*
+ * Try the map file readers.
+ */
+ rc = RTSemRWRequestRead(g_hDbgModRWSem, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VERR_DBG_NO_MATCHING_INTERPRETER;
+ for (PRTDBGMODREGDBG pCur = g_pDbgHead; pCur; pCur = pCur->pNext)
+ {
+ if (pCur->pVt->fSupports & RT_DBGTYPE_MAP)
+ {
+ pDbgMod->pDbgVt = pCur->pVt;
+ pDbgMod->pvDbgPriv = NULL;
+ rc = pCur->pVt->pfnTryOpen(pDbgMod, RTLDRARCH_WHATEVER);
+ if (RT_SUCCESS(rc))
+ {
+ ASMAtomicIncU32(&pCur->cUsers);
+ RTSemRWReleaseRead(g_hDbgModRWSem);
+
+ *phDbgMod = pDbgMod;
+ return rc;
+ }
+ }
+ }
+
+ /* bail out */
+ RTSemRWReleaseRead(g_hDbgModRWSem);
+ }
+ RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszName);
+ }
+ else
+ rc = VERR_NO_STR_MEMORY;
+ RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszDbgFile);
+ }
+ else
+ rc = VERR_NO_STR_MEMORY;
+ RTCritSectDelete(&pDbgMod->CritSect);
+ }
+
+ RTMemFree(pDbgMod);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgModCreateFromMap);
+
+
+
+/*
+ *
+ * E x e c u t a b l e I m a g e F i l e s
+ * E x e c u t a b l e I m a g e F i l e s
+ * E x e c u t a b l e I m a g e F i l e s
+ *
+ */
+
+
+/**
+ * Opens debug information for an image.
+ *
+ * @returns IPRT status code
+ * @param pDbgMod The debug module structure.
+ *
+ * @note This will generally not look for debug info stored in external
+ * files. rtDbgModFromPeImageExtDbgInfoCallback can help with that.
+ */
+static int rtDbgModOpenDebugInfoInsideImage(PRTDBGMODINT pDbgMod)
+{
+ AssertReturn(!pDbgMod->pDbgVt, VERR_DBG_MOD_IPE);
+ AssertReturn(pDbgMod->pImgVt, VERR_DBG_MOD_IPE);
+
+ int rc = RTSemRWRequestRead(g_hDbgModRWSem, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc))
+ {
+ for (PRTDBGMODREGDBG pDbg = g_pDbgHead; pDbg; pDbg = pDbg->pNext)
+ {
+ pDbgMod->pDbgVt = pDbg->pVt;
+ pDbgMod->pvDbgPriv = NULL;
+ rc = pDbg->pVt->pfnTryOpen(pDbgMod, pDbgMod->pImgVt->pfnGetArch(pDbgMod));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * That's it!
+ */
+ ASMAtomicIncU32(&pDbg->cUsers);
+ RTSemRWReleaseRead(g_hDbgModRWSem);
+ return VINF_SUCCESS;
+ }
+
+ pDbgMod->pDbgVt = NULL;
+ Assert(pDbgMod->pvDbgPriv == NULL);
+ }
+ RTSemRWReleaseRead(g_hDbgModRWSem);
+ }
+
+ return VERR_DBG_NO_MATCHING_INTERPRETER;
+}
+
+
+/** @callback_method_impl{FNRTDBGCFGOPEN} */
+static DECLCALLBACK(int) rtDbgModExtDbgInfoOpenCallback(RTDBGCFG hDbgCfg, const char *pszFilename, void *pvUser1, void *pvUser2)
+{
+ PRTDBGMODINT pDbgMod = (PRTDBGMODINT)pvUser1;
+ PCRTLDRDBGINFO pDbgInfo = (PCRTLDRDBGINFO)pvUser2;
+ RT_NOREF_PV(pDbgInfo); /** @todo consider a more direct search for a interpreter. */
+ RT_NOREF_PV(hDbgCfg);
+
+ Assert(!pDbgMod->pDbgVt);
+ Assert(!pDbgMod->pvDbgPriv);
+ Assert(!pDbgMod->pszDbgFile);
+ Assert(pDbgMod->pImgVt);
+
+ /*
+ * Set the debug file name and try possible interpreters.
+ */
+ pDbgMod->pszDbgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename);
+
+ int rc = RTSemRWRequestRead(g_hDbgModRWSem, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc))
+ {
+ for (PRTDBGMODREGDBG pDbg = g_pDbgHead; pDbg; pDbg = pDbg->pNext)
+ {
+ pDbgMod->pDbgVt = pDbg->pVt;
+ pDbgMod->pvDbgPriv = NULL;
+ rc = pDbg->pVt->pfnTryOpen(pDbgMod, pDbgMod->pImgVt->pfnGetArch(pDbgMod));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Got it!
+ */
+ ASMAtomicIncU32(&pDbg->cUsers);
+ RTSemRWReleaseRead(g_hDbgModRWSem);
+ return VINF_CALLBACK_RETURN;
+ }
+
+ pDbgMod->pDbgVt = NULL;
+ Assert(pDbgMod->pvDbgPriv == NULL);
+ }
+ RTSemRWReleaseRead(g_hDbgModRWSem);
+ }
+
+ /* No joy. */
+ RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszDbgFile);
+ pDbgMod->pszDbgFile = NULL;
+ return rc;
+}
+
+
+/**
+ * Argument package used by rtDbgModOpenDebugInfoExternalToImage.
+ */
+typedef struct RTDBGMODOPENDIETI
+{
+ PRTDBGMODINT pDbgMod;
+ RTDBGCFG hDbgCfg;
+} RTDBGMODOPENDIETI;
+
+
+/** @callback_method_impl{FNRTLDRENUMDBG} */
+static DECLCALLBACK(int)
+rtDbgModOpenDebugInfoExternalToImageCallback(RTLDRMOD hLdrMod, PCRTLDRDBGINFO pDbgInfo, void *pvUser)
+{
+ RTDBGMODOPENDIETI *pArgs = (RTDBGMODOPENDIETI *)pvUser;
+ RT_NOREF_PV(hLdrMod);
+
+ Assert(pDbgInfo->enmType > RTLDRDBGINFOTYPE_INVALID && pDbgInfo->enmType < RTLDRDBGINFOTYPE_END);
+ const char *pszExtFile = pDbgInfo->pszExtFile;
+ if (!pszExtFile)
+ {
+ /*
+ * If a external debug type comes without a file name, calculate a
+ * likely debug filename for it. (Hack for NT4 drivers.)
+ */
+ const char *pszExt = NULL;
+ if (pDbgInfo->enmType == RTLDRDBGINFOTYPE_CODEVIEW_DBG)
+ pszExt = ".dbg";
+ else if ( pDbgInfo->enmType == RTLDRDBGINFOTYPE_CODEVIEW_PDB20
+ || pDbgInfo->enmType == RTLDRDBGINFOTYPE_CODEVIEW_PDB70)
+ pszExt = ".pdb";
+ if (pszExt && pArgs->pDbgMod->pszName)
+ {
+ size_t cchName = strlen(pArgs->pDbgMod->pszName);
+ char *psz = (char *)alloca(cchName + strlen(pszExt) + 1);
+ if (psz)
+ {
+ memcpy(psz, pArgs->pDbgMod->pszName, cchName + 1);
+ RTPathStripSuffix(psz);
+ pszExtFile = strcat(psz, pszExt);
+ }
+ }
+
+ if (!pszExtFile)
+ {
+ Log2(("rtDbgModOpenDebugInfoExternalToImageCallback: enmType=%d\n", pDbgInfo->enmType));
+ return VINF_SUCCESS;
+ }
+ }
+
+ /*
+ * Switch on type and call the appropriate search function.
+ */
+ int rc;
+ switch (pDbgInfo->enmType)
+ {
+ case RTLDRDBGINFOTYPE_CODEVIEW_PDB70:
+ rc = RTDbgCfgOpenPdb70(pArgs->hDbgCfg, pszExtFile,
+ &pDbgInfo->u.Pdb70.Uuid,
+ pDbgInfo->u.Pdb70.uAge,
+ rtDbgModExtDbgInfoOpenCallback, pArgs->pDbgMod, (void *)pDbgInfo);
+ break;
+
+ case RTLDRDBGINFOTYPE_CODEVIEW_PDB20:
+ rc = RTDbgCfgOpenPdb20(pArgs->hDbgCfg, pszExtFile,
+ pDbgInfo->u.Pdb20.cbImage,
+ pDbgInfo->u.Pdb20.uTimestamp,
+ pDbgInfo->u.Pdb20.uAge,
+ rtDbgModExtDbgInfoOpenCallback, pArgs->pDbgMod, (void *)pDbgInfo);
+ break;
+
+ case RTLDRDBGINFOTYPE_CODEVIEW_DBG:
+ rc = RTDbgCfgOpenDbg(pArgs->hDbgCfg, pszExtFile,
+ pDbgInfo->u.Dbg.cbImage,
+ pDbgInfo->u.Dbg.uTimestamp,
+ rtDbgModExtDbgInfoOpenCallback, pArgs->pDbgMod, (void *)pDbgInfo);
+ break;
+
+ case RTLDRDBGINFOTYPE_DWARF_DWO:
+ rc = RTDbgCfgOpenDwo(pArgs->hDbgCfg, pszExtFile,
+ pDbgInfo->u.Dwo.uCrc32,
+ rtDbgModExtDbgInfoOpenCallback, pArgs->pDbgMod, (void *)pDbgInfo);
+ break;
+
+ default:
+ Log(("rtDbgModOpenDebugInfoExternalToImageCallback: Don't know how to handle enmType=%d and pszFileExt=%s\n",
+ pDbgInfo->enmType, pszExtFile));
+ return VERR_DBG_TODO;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ LogFlow(("RTDbgMod: Successfully opened external debug info '%s' for '%s'\n",
+ pArgs->pDbgMod->pszDbgFile, pArgs->pDbgMod->pszImgFile));
+ return VINF_CALLBACK_RETURN;
+ }
+ Log(("rtDbgModOpenDebugInfoExternalToImageCallback: '%s' (enmType=%d) for '%s' -> %Rrc\n",
+ pszExtFile, pDbgInfo->enmType, pArgs->pDbgMod->pszImgFile, rc));
+ return rc;
+}
+
+
+/**
+ * Opens debug info listed in the image that is stored in a separate file.
+ *
+ * @returns IPRT status code
+ * @param pDbgMod The debug module.
+ * @param hDbgCfg The debug config. Can be NIL.
+ */
+static int rtDbgModOpenDebugInfoExternalToImage(PRTDBGMODINT pDbgMod, RTDBGCFG hDbgCfg)
+{
+ Assert(!pDbgMod->pDbgVt);
+
+ RTDBGMODOPENDIETI Args;
+ Args.pDbgMod = pDbgMod;
+ Args.hDbgCfg = hDbgCfg;
+ int rc = pDbgMod->pImgVt->pfnEnumDbgInfo(pDbgMod, rtDbgModOpenDebugInfoExternalToImageCallback, &Args);
+ if (RT_SUCCESS(rc) && pDbgMod->pDbgVt)
+ return VINF_SUCCESS;
+
+ LogFlow(("rtDbgModOpenDebugInfoExternalToImage: rc=%Rrc\n", rc));
+ return VERR_NOT_FOUND;
+}
+
+
+/** @callback_method_impl{FNRTDBGCFGOPEN} */
+static DECLCALLBACK(int) rtDbgModExtDbgInfoOpenCallback2(RTDBGCFG hDbgCfg, const char *pszFilename, void *pvUser1, void *pvUser2)
+{
+ PRTDBGMODINT pDbgMod = (PRTDBGMODINT)pvUser1;
+ RT_NOREF_PV(pvUser2); /** @todo image matching string or smth. */
+ RT_NOREF_PV(hDbgCfg);
+
+ Assert(!pDbgMod->pDbgVt);
+ Assert(!pDbgMod->pvDbgPriv);
+ Assert(!pDbgMod->pszDbgFile);
+ Assert(pDbgMod->pImgVt);
+
+ /*
+ * Set the debug file name and try possible interpreters.
+ */
+ pDbgMod->pszDbgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename);
+
+ int rc = RTSemRWRequestRead(g_hDbgModRWSem, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc))
+ {
+ for (PRTDBGMODREGDBG pDbg = g_pDbgHead; pDbg; pDbg = pDbg->pNext)
+ {
+ pDbgMod->pDbgVt = pDbg->pVt;
+ pDbgMod->pvDbgPriv = NULL;
+ rc = pDbg->pVt->pfnTryOpen(pDbgMod, pDbgMod->pImgVt->pfnGetArch(pDbgMod));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Got it!
+ */
+ ASMAtomicIncU32(&pDbg->cUsers);
+ RTSemRWReleaseRead(g_hDbgModRWSem);
+ return VINF_CALLBACK_RETURN;
+ }
+ pDbgMod->pDbgVt = NULL;
+ Assert(pDbgMod->pvDbgPriv == NULL);
+ }
+ }
+
+ /* No joy. */
+ RTSemRWReleaseRead(g_hDbgModRWSem);
+ RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszDbgFile);
+ pDbgMod->pszDbgFile = NULL;
+ return rc;
+}
+
+
+/**
+ * Opens external debug info that is not listed in the image.
+ *
+ * @returns IPRT status code
+ * @param pDbgMod The debug module.
+ * @param hDbgCfg The debug config. Can be NIL.
+ */
+static int rtDbgModOpenDebugInfoExternalToImage2(PRTDBGMODINT pDbgMod, RTDBGCFG hDbgCfg)
+{
+ int rc;
+ Assert(!pDbgMod->pDbgVt);
+ Assert(pDbgMod->pImgVt);
+
+ /*
+ * Figure out what to search for based on the image format.
+ */
+ const char *pszzExts = NULL;
+ RTLDRFMT enmFmt = pDbgMod->pImgVt->pfnGetFormat(pDbgMod);
+ switch (enmFmt)
+ {
+ case RTLDRFMT_MACHO:
+ {
+ RTUUID Uuid;
+ PRTUUID pUuid = &Uuid;
+ rc = pDbgMod->pImgVt->pfnQueryProp(pDbgMod, RTLDRPROP_UUID, &Uuid, sizeof(Uuid), NULL);
+ if (RT_FAILURE(rc))
+ pUuid = NULL;
+
+ rc = RTDbgCfgOpenDsymBundle(hDbgCfg, pDbgMod->pszImgFile, pUuid,
+ rtDbgModExtDbgInfoOpenCallback2, pDbgMod, NULL /*pvUser2*/);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+ break;
+ }
+
+#if 0 /* Will be links in the image if these apply. .map readers for PE or ELF we don't have. */
+ case RTLDRFMT_ELF:
+ pszzExts = ".debug\0.dwo\0";
+ break;
+ case RTLDRFMT_PE:
+ pszzExts = ".map\0";
+ break;
+#endif
+#if 0 /* Haven't implemented .sym or .map file readers for OS/2 yet. */
+ case RTLDRFMT_LX:
+ pszzExts = ".sym\0.map\0";
+ break;
+#endif
+ default:
+ rc = VERR_NOT_IMPLEMENTED;
+ break;
+ }
+
+ NOREF(pszzExts);
+#if 0 /* Later */
+ if (pszzExts)
+ {
+
+ }
+#endif
+
+ LogFlow(("rtDbgModOpenDebugInfoExternalToImage2: rc=%Rrc\n", rc));
+ return VERR_NOT_FOUND;
+}
+
+
+RTDECL(int) RTDbgModCreateFromImage(PRTDBGMOD phDbgMod, const char *pszFilename, const char *pszName,
+ RTLDRARCH enmArch, RTDBGCFG hDbgCfg)
+{
+ /*
+ * Input validation and lazy initialization.
+ */
+ AssertPtrReturn(phDbgMod, VERR_INVALID_POINTER);
+ *phDbgMod = NIL_RTDBGMOD;
+ AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
+ AssertReturn(*pszFilename, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pszName, VERR_INVALID_POINTER);
+ AssertReturn(enmArch > RTLDRARCH_INVALID && enmArch < RTLDRARCH_END, VERR_INVALID_PARAMETER);
+
+ int rc = rtDbgModLazyInit();
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if (!pszName)
+ pszName = RTPathFilenameEx(pszFilename, RTPATH_STR_F_STYLE_DOS);
+
+ /*
+ * Allocate a new module instance.
+ */
+ PRTDBGMODINT pDbgMod = (PRTDBGMODINT)RTMemAllocZ(sizeof(*pDbgMod));
+ if (!pDbgMod)
+ return VERR_NO_MEMORY;
+ pDbgMod->u32Magic = RTDBGMOD_MAGIC;
+ pDbgMod->cRefs = 1;
+ rc = RTCritSectInit(&pDbgMod->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ pDbgMod->pszName = RTStrCacheEnterLower(g_hDbgModStrCache, pszName);
+ if (pDbgMod->pszName)
+ {
+ pDbgMod->pszImgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename);
+ if (pDbgMod->pszImgFile)
+ {
+ RTStrCacheRetain(pDbgMod->pszImgFile);
+ pDbgMod->pszImgFileSpecified = pDbgMod->pszImgFile;
+
+ /*
+ * Find an image reader which groks the file.
+ */
+ rc = RTSemRWRequestRead(g_hDbgModRWSem, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VERR_DBG_NO_MATCHING_INTERPRETER;
+ PRTDBGMODREGIMG pImg;
+ for (pImg = g_pImgHead; pImg; pImg = pImg->pNext)
+ {
+ pDbgMod->pImgVt = pImg->pVt;
+ pDbgMod->pvImgPriv = NULL;
+ /** @todo need to specify some arch stuff here. */
+ rc = pImg->pVt->pfnTryOpen(pDbgMod, enmArch);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Image detected, but found no debug info we were
+ * able to understand.
+ */
+ /** @todo some generic way of matching image and debug info, flexible signature
+ * of some kind. Apple uses UUIDs, microsoft uses a UUID+age or a
+ * size+timestamp, and GNU a CRC32 (last time I checked). */
+ rc = rtDbgModOpenDebugInfoExternalToImage(pDbgMod, hDbgCfg);
+ if (RT_FAILURE(rc))
+ rc = rtDbgModOpenDebugInfoInsideImage(pDbgMod);
+ if (RT_FAILURE(rc))
+ rc = rtDbgModOpenDebugInfoExternalToImage2(pDbgMod, hDbgCfg);
+ if (RT_FAILURE(rc))
+ rc = rtDbgModCreateForExports(pDbgMod);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * We're done!
+ */
+ ASMAtomicIncU32(&pImg->cUsers);
+ RTSemRWReleaseRead(g_hDbgModRWSem);
+
+ *phDbgMod = pDbgMod;
+ return VINF_SUCCESS;
+ }
+
+ /* Failed, close up the shop. */
+ pDbgMod->pImgVt->pfnClose(pDbgMod);
+ pDbgMod->pImgVt = NULL;
+ pDbgMod->pvImgPriv = NULL;
+ break;
+ }
+ }
+
+ /*
+ * Could it be a file containing raw debug info?
+ */
+ if (!pImg)
+ {
+ pDbgMod->pImgVt = NULL;
+ pDbgMod->pvImgPriv = NULL;
+ pDbgMod->pszDbgFile = pDbgMod->pszImgFile;
+ pDbgMod->pszImgFile = NULL;
+
+ for (PRTDBGMODREGDBG pDbg = g_pDbgHead; pDbg; pDbg = pDbg->pNext)
+ {
+ pDbgMod->pDbgVt = pDbg->pVt;
+ pDbgMod->pvDbgPriv = NULL;
+ rc = pDbg->pVt->pfnTryOpen(pDbgMod, enmArch);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * That's it!
+ */
+ ASMAtomicIncU32(&pDbg->cUsers);
+ RTSemRWReleaseRead(g_hDbgModRWSem);
+
+ *phDbgMod = pDbgMod;
+ return rc;
+ }
+ }
+
+ pDbgMod->pszImgFile = pDbgMod->pszDbgFile;
+ pDbgMod->pszDbgFile = NULL;
+ }
+
+ /* bail out */
+ RTSemRWReleaseRead(g_hDbgModRWSem);
+ }
+ RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFileSpecified);
+ RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFile);
+ }
+ else
+ rc = VERR_NO_STR_MEMORY;
+ RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszName);
+ }
+ else
+ rc = VERR_NO_STR_MEMORY;
+ RTCritSectDelete(&pDbgMod->CritSect);
+ }
+
+ RTMemFree(pDbgMod);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgModCreateFromImage);
+
+
+
+
+
+/*
+ *
+ * P E I M A G E
+ * P E I M A G E
+ * P E I M A G E
+ *
+ */
+
+
+
+/** @callback_method_impl{FNRTDBGCFGOPEN} */
+static DECLCALLBACK(int) rtDbgModFromPeImageOpenCallback(RTDBGCFG hDbgCfg, const char *pszFilename, void *pvUser1, void *pvUser2)
+{
+ PRTDBGMODINT pDbgMod = (PRTDBGMODINT)pvUser1;
+ PRTDBGMODDEFERRED pDeferred = (PRTDBGMODDEFERRED)pvUser2;
+ LogFlow(("rtDbgModFromPeImageOpenCallback: %s\n", pszFilename));
+ RT_NOREF_PV(hDbgCfg);
+
+ Assert(pDbgMod->pImgVt == NULL);
+ Assert(pDbgMod->pvImgPriv == NULL);
+ Assert(pDbgMod->pDbgVt == NULL);
+ Assert(pDbgMod->pvDbgPriv == NULL);
+
+ /*
+ * Replace the image file name while probing it.
+ */
+ const char *pszNewImgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename);
+ if (!pszNewImgFile)
+ return VERR_NO_STR_MEMORY;
+ const char *pszOldImgFile = pDbgMod->pszImgFile;
+ pDbgMod->pszImgFile = pszNewImgFile;
+
+ /*
+ * Find an image reader which groks the file.
+ */
+ int rc = RTSemRWRequestRead(g_hDbgModRWSem, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VERR_DBG_NO_MATCHING_INTERPRETER;
+ PRTDBGMODREGIMG pImg;
+ for (pImg = g_pImgHead; pImg; pImg = pImg->pNext)
+ {
+ pDbgMod->pImgVt = pImg->pVt;
+ pDbgMod->pvImgPriv = NULL;
+ rc = pImg->pVt->pfnTryOpen(pDbgMod, RTLDRARCH_WHATEVER);
+ if (RT_SUCCESS(rc))
+ break;
+ pDbgMod->pImgVt = NULL;
+ Assert(pDbgMod->pvImgPriv == NULL);
+ }
+ RTSemRWReleaseRead(g_hDbgModRWSem);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Check the deferred info.
+ */
+ RTUINTPTR cbImage = pDbgMod->pImgVt->pfnImageSize(pDbgMod);
+ if ( pDeferred->cbImage == 0
+ || pDeferred->cbImage == cbImage)
+ {
+ uint32_t uTimestamp = pDeferred->u.PeImage.uTimestamp; /** @todo add method for getting the timestamp. */
+ if ( pDeferred->u.PeImage.uTimestamp == 0
+ || pDeferred->u.PeImage.uTimestamp == uTimestamp)
+ {
+ Log(("RTDbgMod: Found matching PE image '%s'\n", pszFilename));
+
+ /*
+ * We found the executable image we need, now go find any
+ * debug info associated with it. For PE images, this is
+ * generally found in an external file, so we do a sweep
+ * for that first.
+ *
+ * Then try open debug inside the module, and finally
+ * falling back on exports.
+ */
+ rc = rtDbgModOpenDebugInfoExternalToImage(pDbgMod, pDeferred->hDbgCfg);
+ if (RT_FAILURE(rc))
+ rc = rtDbgModOpenDebugInfoInsideImage(pDbgMod);
+ if (RT_FAILURE(rc))
+ rc = rtDbgModCreateForExports(pDbgMod);
+ if (RT_SUCCESS(rc))
+ {
+ RTStrCacheRelease(g_hDbgModStrCache, pszOldImgFile);
+ return VINF_CALLBACK_RETURN;
+ }
+
+ /* Something bad happened, just give up. */
+ Log(("rtDbgModFromPeImageOpenCallback: rtDbgModCreateForExports failed: %Rrc\n", rc));
+ }
+ else
+ {
+ LogFlow(("rtDbgModFromPeImageOpenCallback: uTimestamp mismatch (found %#x, expected %#x) - %s\n",
+ uTimestamp, pDeferred->u.PeImage.uTimestamp, pszFilename));
+ rc = VERR_DBG_FILE_MISMATCH;
+ }
+ }
+ else
+ {
+ LogFlow(("rtDbgModFromPeImageOpenCallback: cbImage mismatch (found %#x, expected %#x) - %s\n",
+ cbImage, pDeferred->cbImage, pszFilename));
+ rc = VERR_DBG_FILE_MISMATCH;
+ }
+
+ pDbgMod->pImgVt->pfnClose(pDbgMod);
+ pDbgMod->pImgVt = NULL;
+ pDbgMod->pvImgPriv = NULL;
+ }
+ else
+ LogFlow(("rtDbgModFromPeImageOpenCallback: Failed %Rrc - %s\n", rc, pszFilename));
+ }
+
+ /* Restore image name. */
+ pDbgMod->pszImgFile = pszOldImgFile;
+ RTStrCacheRelease(g_hDbgModStrCache, pszNewImgFile);
+ return rc;
+}
+
+
+/** @callback_method_impl{FNRTDBGMODDEFERRED} */
+static DECLCALLBACK(int) rtDbgModFromPeImageDeferredCallback(PRTDBGMODINT pDbgMod, PRTDBGMODDEFERRED pDeferred)
+{
+ int rc;
+
+ Assert(pDbgMod->pszImgFile);
+ if (!pDbgMod->pImgVt)
+ rc = RTDbgCfgOpenPeImage(pDeferred->hDbgCfg, pDbgMod->pszImgFile,
+ pDeferred->cbImage, pDeferred->u.PeImage.uTimestamp,
+ rtDbgModFromPeImageOpenCallback, pDbgMod, pDeferred);
+ else
+ {
+ rc = rtDbgModOpenDebugInfoExternalToImage(pDbgMod, pDeferred->hDbgCfg);
+ if (RT_FAILURE(rc))
+ rc = rtDbgModOpenDebugInfoInsideImage(pDbgMod);
+ if (RT_FAILURE(rc))
+ rc = rtDbgModCreateForExports(pDbgMod);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTDbgModCreateFromPeImage(PRTDBGMOD phDbgMod, const char *pszFilename, const char *pszName,
+ PRTLDRMOD phLdrMod, uint32_t cbImage, uint32_t uTimestamp, RTDBGCFG hDbgCfg)
+{
+ /*
+ * Input validation and lazy initialization.
+ */
+ AssertPtrReturn(phDbgMod, VERR_INVALID_POINTER);
+ *phDbgMod = NIL_RTDBGMOD;
+ AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
+ AssertReturn(*pszFilename, VERR_INVALID_PARAMETER);
+ if (!pszName)
+ pszName = RTPathFilenameEx(pszFilename, RTPATH_STR_F_STYLE_DOS);
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(phLdrMod, VERR_INVALID_POINTER);
+ RTLDRMOD hLdrMod = phLdrMod ? *phLdrMod : NIL_RTLDRMOD;
+ AssertReturn(hLdrMod == NIL_RTLDRMOD || RTLdrSize(hLdrMod) != ~(size_t)0, VERR_INVALID_HANDLE);
+
+ int rc = rtDbgModLazyInit();
+ if (RT_FAILURE(rc))
+ return rc;
+
+ uint64_t fDbgCfg = 0;
+ if (hDbgCfg)
+ {
+ rc = RTDbgCfgQueryUInt(hDbgCfg, RTDBGCFGPROP_FLAGS, &fDbgCfg);
+ AssertRCReturn(rc, rc);
+ }
+
+ /*
+ * Allocate a new module instance.
+ */
+ PRTDBGMODINT pDbgMod = (PRTDBGMODINT)RTMemAllocZ(sizeof(*pDbgMod));
+ if (!pDbgMod)
+ return VERR_NO_MEMORY;
+ pDbgMod->u32Magic = RTDBGMOD_MAGIC;
+ pDbgMod->cRefs = 1;
+ rc = RTCritSectInit(&pDbgMod->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ pDbgMod->pszName = RTStrCacheEnterLower(g_hDbgModStrCache, pszName);
+ if (pDbgMod->pszName)
+ {
+ pDbgMod->pszImgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename);
+ if (pDbgMod->pszImgFile)
+ {
+ RTStrCacheRetain(pDbgMod->pszImgFile);
+ pDbgMod->pszImgFileSpecified = pDbgMod->pszImgFile;
+
+ /*
+ * If we have a loader module, we must instantiate the loader
+ * side of things regardless of the deferred setting.
+ */
+ if (hLdrMod != NIL_RTLDRMOD)
+ {
+ if (!cbImage)
+ cbImage = (uint32_t)RTLdrSize(hLdrMod);
+ pDbgMod->pImgVt = &g_rtDbgModVtImgLdr;
+
+ rc = rtDbgModLdrOpenFromHandle(pDbgMod, hLdrMod);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /* We now own the loader handle, so clear the caller variable. */
+ if (phLdrMod)
+ *phLdrMod = NIL_RTLDRMOD;
+
+ /*
+ * Do it now or procrastinate?
+ */
+ if (!(fDbgCfg & RTDBGCFG_FLAGS_DEFERRED) || !cbImage)
+ {
+ RTDBGMODDEFERRED Deferred;
+ Deferred.cbImage = cbImage;
+ Deferred.hDbgCfg = hDbgCfg;
+ Deferred.u.PeImage.uTimestamp = uTimestamp;
+ rc = rtDbgModFromPeImageDeferredCallback(pDbgMod, &Deferred);
+ }
+ else
+ {
+ PRTDBGMODDEFERRED pDeferred;
+ rc = rtDbgModDeferredCreate(pDbgMod, rtDbgModFromPeImageDeferredCallback, cbImage, hDbgCfg, 0,
+ &pDeferred);
+ if (RT_SUCCESS(rc))
+ pDeferred->u.PeImage.uTimestamp = uTimestamp;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ *phDbgMod = pDbgMod;
+ return VINF_SUCCESS;
+ }
+
+ /* Failed, bail out. */
+ if (hLdrMod != NIL_RTLDRMOD)
+ {
+ Assert(pDbgMod->pImgVt);
+ pDbgMod->pImgVt->pfnClose(pDbgMod);
+ }
+ }
+ RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszName);
+ }
+ else
+ rc = VERR_NO_STR_MEMORY;
+ RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFileSpecified);
+ RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFile);
+ }
+ else
+ rc = VERR_NO_STR_MEMORY;
+ RTCritSectDelete(&pDbgMod->CritSect);
+ }
+
+ RTMemFree(pDbgMod);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgModCreateFromPeImage);
+
+
+
+
+/*
+ *
+ * M a c h - O I M A G E
+ * M a c h - O I M A G E
+ * M a c h - O I M A G E
+ *
+ */
+
+
+/**
+ * Argument package used when opening Mach-O images and .dSYMs files.
+ */
+typedef struct RTDBGMODMACHOARGS
+{
+ /** For use more internal use in file locator callbacks. */
+ RTLDRARCH enmArch;
+ /** For use more internal use in file locator callbacks. */
+ PCRTUUID pUuid;
+ /** For use more internal use in file locator callbacks. */
+ bool fOpenImage;
+} RTDBGMODMACHOARGS;
+/** Pointer to a const segment package. */
+typedef RTDBGMODMACHOARGS const *PCRTDBGMODMACHOARGS;
+
+
+
+/** @callback_method_impl{FNRTDBGCFGOPEN} */
+static DECLCALLBACK(int)
+rtDbgModFromMachOImageOpenDsymMachOCallback(RTDBGCFG hDbgCfg, const char *pszFilename, void *pvUser1, void *pvUser2)
+{
+ PRTDBGMODINT pDbgMod = (PRTDBGMODINT)pvUser1;
+ PCRTDBGMODMACHOARGS pArgs = (PCRTDBGMODMACHOARGS)pvUser2;
+ RT_NOREF_PV(hDbgCfg);
+
+ Assert(!pDbgMod->pDbgVt);
+ Assert(!pDbgMod->pvDbgPriv);
+ Assert(!pDbgMod->pszDbgFile);
+ Assert(!pDbgMod->pImgVt);
+ Assert(!pDbgMod->pvDbgPriv);
+ Assert(pDbgMod->pszImgFile);
+ Assert(pDbgMod->pszImgFileSpecified);
+
+ const char *pszImgFileOrg = pDbgMod->pszImgFile;
+ pDbgMod->pszImgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename);
+ if (!pDbgMod->pszImgFile)
+ return VERR_NO_STR_MEMORY;
+ RTStrCacheRetain(pDbgMod->pszImgFile);
+ pDbgMod->pszDbgFile = pDbgMod->pszImgFile;
+
+ /*
+ * Try image interpreters as the dwarf file inside the dSYM bundle is a
+ * Mach-O file with dwarf debug sections insides it and no code or data.
+ */
+ int rc = RTSemRWRequestRead(g_hDbgModRWSem, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VERR_DBG_NO_MATCHING_INTERPRETER;
+ PRTDBGMODREGIMG pImg;
+ for (pImg = g_pImgHead; pImg; pImg = pImg->pNext)
+ {
+ pDbgMod->pImgVt = pImg->pVt;
+ pDbgMod->pvImgPriv = NULL;
+ rc = pImg->pVt->pfnTryOpen(pDbgMod, pArgs->enmArch);
+ if (RT_SUCCESS(rc))
+ break;
+ pDbgMod->pImgVt = NULL;
+ Assert(pDbgMod->pvImgPriv == NULL);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Check the UUID if one was given.
+ */
+ if (pArgs->pUuid)
+ {
+ RTUUID UuidOpened;
+ rc = pDbgMod->pImgVt->pfnQueryProp(pDbgMod, RTLDRPROP_UUID, &UuidOpened, sizeof(UuidOpened), NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if (RTUuidCompare(&UuidOpened, pArgs->pUuid) != 0)
+ rc = VERR_DBG_FILE_MISMATCH;
+ }
+ else if (rc == VERR_NOT_FOUND || rc == VERR_NOT_IMPLEMENTED)
+ rc = VERR_DBG_FILE_MISMATCH;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Pass it to the DWARF reader(s). Careful to restrict this or
+ * the dbghelp wrapper may end up being overly helpful.
+ */
+ for (PRTDBGMODREGDBG pDbg = g_pDbgHead; pDbg; pDbg = pDbg->pNext)
+ {
+ if (pDbg->pVt->fSupports & (RT_DBGTYPE_DWARF | RT_DBGTYPE_STABS | RT_DBGTYPE_WATCOM))
+
+ {
+ pDbgMod->pDbgVt = pDbg->pVt;
+ pDbgMod->pvDbgPriv = NULL;
+ rc = pDbg->pVt->pfnTryOpen(pDbgMod, pDbgMod->pImgVt->pfnGetArch(pDbgMod));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Got it!
+ */
+ ASMAtomicIncU32(&pDbg->cUsers);
+ RTSemRWReleaseRead(g_hDbgModRWSem);
+ RTStrCacheRelease(g_hDbgModStrCache, pszImgFileOrg);
+ return VINF_CALLBACK_RETURN;
+ }
+ pDbgMod->pDbgVt = NULL;
+ Assert(pDbgMod->pvDbgPriv == NULL);
+ }
+ }
+
+ /*
+ * Likely fallback for when opening image.
+ */
+ if (pArgs->fOpenImage)
+ {
+ rc = rtDbgModCreateForExports(pDbgMod);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Done.
+ */
+ RTSemRWReleaseRead(g_hDbgModRWSem);
+ RTStrCacheRelease(g_hDbgModStrCache, pszImgFileOrg);
+ return VINF_CALLBACK_RETURN;
+ }
+ }
+ }
+
+ pDbgMod->pImgVt->pfnClose(pDbgMod);
+ pDbgMod->pImgVt = NULL;
+ pDbgMod->pvImgPriv = NULL;
+ }
+ }
+
+ /* No joy. */
+ RTSemRWReleaseRead(g_hDbgModRWSem);
+ RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFile);
+ pDbgMod->pszImgFile = pszImgFileOrg;
+ RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszDbgFile);
+ pDbgMod->pszDbgFile = NULL;
+ return rc;
+}
+
+
+static int rtDbgModFromMachOImageWorker(PRTDBGMODINT pDbgMod, RTLDRARCH enmArch, uint32_t cbImage,
+ uint32_t cSegs, PCRTDBGSEGMENT paSegs, PCRTUUID pUuid, RTDBGCFG hDbgCfg)
+{
+ RT_NOREF_PV(cbImage); RT_NOREF_PV(cSegs); RT_NOREF_PV(paSegs);
+
+ RTDBGMODMACHOARGS Args;
+ Args.enmArch = enmArch;
+ Args.pUuid = pUuid && RTUuidIsNull(pUuid) ? pUuid : NULL;
+ Args.fOpenImage = false;
+
+ /*
+ * Search for the .dSYM bundle first, since that's generally all we need.
+ */
+ int rc = RTDbgCfgOpenDsymBundle(hDbgCfg, pDbgMod->pszImgFile, pUuid,
+ rtDbgModFromMachOImageOpenDsymMachOCallback, pDbgMod, &Args);
+ if (RT_FAILURE(rc))
+ {
+ /*
+ * If we cannot get at the .dSYM, try the executable image.
+ */
+ Args.fOpenImage = true;
+ rc = RTDbgCfgOpenMachOImage(hDbgCfg, pDbgMod->pszImgFile, pUuid,
+ rtDbgModFromMachOImageOpenDsymMachOCallback, pDbgMod, &Args);
+ }
+ return rc;
+}
+
+
+/** @callback_method_impl{FNRTDBGMODDEFERRED} */
+static DECLCALLBACK(int) rtDbgModFromMachOImageDeferredCallback(PRTDBGMODINT pDbgMod, PRTDBGMODDEFERRED pDeferred)
+{
+ return rtDbgModFromMachOImageWorker(pDbgMod, pDeferred->u.MachO.enmArch, pDeferred->cbImage,
+ pDeferred->u.MachO.cSegs, pDeferred->u.MachO.aSegs,
+ &pDeferred->u.MachO.Uuid, pDeferred->hDbgCfg);
+}
+
+
+RTDECL(int) RTDbgModCreateFromMachOImage(PRTDBGMOD phDbgMod, const char *pszFilename, const char *pszName,
+ RTLDRARCH enmArch, uint32_t cbImage, uint32_t cSegs, PCRTDBGSEGMENT paSegs,
+ PCRTUUID pUuid, RTDBGCFG hDbgCfg, uint32_t fFlags)
+{
+ /*
+ * Input validation and lazy initialization.
+ */
+ AssertPtrReturn(phDbgMod, VERR_INVALID_POINTER);
+ *phDbgMod = NIL_RTDBGMOD;
+ AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
+ AssertReturn(*pszFilename, VERR_INVALID_PARAMETER);
+ if (!pszName)
+ pszName = RTPathFilenameEx(pszFilename, RTPATH_STR_F_STYLE_HOST);
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ if (cSegs)
+ {
+ AssertReturn(cSegs < 1024, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(paSegs, VERR_INVALID_POINTER);
+ AssertReturn(!cbImage, VERR_INVALID_PARAMETER);
+ }
+ AssertReturn(cbImage || cSegs, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pUuid, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~(RTDBGMOD_F_NOT_DEFERRED)), VERR_INVALID_PARAMETER);
+
+ int rc = rtDbgModLazyInit();
+ if (RT_FAILURE(rc))
+ return rc;
+
+ uint64_t fDbgCfg = 0;
+ if (hDbgCfg)
+ {
+ rc = RTDbgCfgQueryUInt(hDbgCfg, RTDBGCFGPROP_FLAGS, &fDbgCfg);
+ AssertRCReturn(rc, rc);
+ }
+
+ /*
+ * Allocate a new module instance.
+ */
+ PRTDBGMODINT pDbgMod = (PRTDBGMODINT)RTMemAllocZ(sizeof(*pDbgMod));
+ if (!pDbgMod)
+ return VERR_NO_MEMORY;
+ pDbgMod->u32Magic = RTDBGMOD_MAGIC;
+ pDbgMod->cRefs = 1;
+ rc = RTCritSectInit(&pDbgMod->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ pDbgMod->pszName = RTStrCacheEnterLower(g_hDbgModStrCache, pszName);
+ if (pDbgMod->pszName)
+ {
+ pDbgMod->pszImgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename);
+ if (pDbgMod->pszImgFile)
+ {
+ RTStrCacheRetain(pDbgMod->pszImgFile);
+ pDbgMod->pszImgFileSpecified = pDbgMod->pszImgFile;
+
+ /*
+ * Load it immediately?
+ */
+ if ( !(fDbgCfg & RTDBGCFG_FLAGS_DEFERRED)
+ || cSegs /* for the time being. */
+ || (!cbImage && !cSegs)
+ || (fFlags & RTDBGMOD_F_NOT_DEFERRED) )
+ rc = rtDbgModFromMachOImageWorker(pDbgMod, enmArch, cbImage, cSegs, paSegs, pUuid, hDbgCfg);
+ else
+ {
+ /*
+ * Procrastinate. Need image size atm.
+ */
+ PRTDBGMODDEFERRED pDeferred;
+ rc = rtDbgModDeferredCreate(pDbgMod, rtDbgModFromMachOImageDeferredCallback, cbImage, hDbgCfg,
+ RT_UOFFSETOF_DYN(RTDBGMODDEFERRED, u.MachO.aSegs[cSegs]),
+ &pDeferred);
+ if (RT_SUCCESS(rc))
+ {
+ pDeferred->u.MachO.Uuid = *pUuid;
+ pDeferred->u.MachO.enmArch = enmArch;
+ pDeferred->u.MachO.cSegs = cSegs;
+ if (cSegs)
+ memcpy(&pDeferred->u.MachO.aSegs, paSegs, cSegs * sizeof(paSegs[0]));
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ *phDbgMod = pDbgMod;
+ return VINF_SUCCESS;
+ }
+
+ /* Failed, bail out. */
+ RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszName);
+ }
+ else
+ rc = VERR_NO_STR_MEMORY;
+ RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFileSpecified);
+ RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFile);
+ }
+ else
+ rc = VERR_NO_STR_MEMORY;
+ RTCritSectDelete(&pDbgMod->CritSect);
+ }
+
+ RTMemFree(pDbgMod);
+ return rc;
+}
+
+
+
+RT_EXPORT_SYMBOL(RTDbgModCreateFromMachOImage);
+
+
+
+/**
+ * Destroys an module after the reference count has reached zero.
+ *
+ * @param pDbgMod The module instance.
+ */
+static void rtDbgModDestroy(PRTDBGMODINT pDbgMod)
+{
+ /*
+ * Close the debug info interpreter first, then the image interpret.
+ */
+ RTCritSectEnter(&pDbgMod->CritSect); /* paranoia */
+
+ if (pDbgMod->pDbgVt)
+ {
+ pDbgMod->pDbgVt->pfnClose(pDbgMod);
+ pDbgMod->pDbgVt = NULL;
+ pDbgMod->pvDbgPriv = NULL;
+ }
+
+ if (pDbgMod->pImgVt)
+ {
+ pDbgMod->pImgVt->pfnClose(pDbgMod);
+ pDbgMod->pImgVt = NULL;
+ pDbgMod->pvImgPriv = NULL;
+ }
+
+ /*
+ * Free the resources.
+ */
+ ASMAtomicWriteU32(&pDbgMod->u32Magic, ~RTDBGMOD_MAGIC);
+ RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszName);
+ RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFile);
+ RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFileSpecified);
+ RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszDbgFile);
+ RTCritSectLeave(&pDbgMod->CritSect); /* paranoia */
+ RTCritSectDelete(&pDbgMod->CritSect);
+ RTMemFree(pDbgMod);
+}
+
+
+RTDECL(uint32_t) RTDbgModRetain(RTDBGMOD hDbgMod)
+{
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, UINT32_MAX);
+ return ASMAtomicIncU32(&pDbgMod->cRefs);
+}
+RT_EXPORT_SYMBOL(RTDbgModRetain);
+
+
+RTDECL(uint32_t) RTDbgModRelease(RTDBGMOD hDbgMod)
+{
+ if (hDbgMod == NIL_RTDBGMOD)
+ return 0;
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pDbgMod->cRefs);
+ if (!cRefs)
+ rtDbgModDestroy(pDbgMod);
+ return cRefs;
+}
+RT_EXPORT_SYMBOL(RTDbgModRelease);
+
+
+RTDECL(const char *) RTDbgModName(RTDBGMOD hDbgMod)
+{
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, NULL);
+ return pDbgMod->pszName;
+}
+RT_EXPORT_SYMBOL(RTDbgModName);
+
+
+RTDECL(const char *) RTDbgModDebugFile(RTDBGMOD hDbgMod)
+{
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, NULL);
+ if (pDbgMod->fDeferred || pDbgMod->fExports)
+ return NULL;
+ return pDbgMod->pszDbgFile;
+}
+RT_EXPORT_SYMBOL(RTDbgModDebugFile);
+
+
+RTDECL(const char *) RTDbgModImageFile(RTDBGMOD hDbgMod)
+{
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, NULL);
+ return pDbgMod->pszImgFileSpecified;
+}
+RT_EXPORT_SYMBOL(RTDbgModImageFile);
+
+
+RTDECL(const char *) RTDbgModImageFileUsed(RTDBGMOD hDbgMod)
+{
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, NULL);
+ return pDbgMod->pszImgFile == pDbgMod->pszImgFileSpecified ? NULL : pDbgMod->pszImgFile;
+}
+RT_EXPORT_SYMBOL(RTDbgModImageFileUsed);
+
+
+RTDECL(bool) RTDbgModIsDeferred(RTDBGMOD hDbgMod)
+{
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, false);
+ return pDbgMod->fDeferred;
+}
+
+
+RTDECL(bool) RTDbgModIsExports(RTDBGMOD hDbgMod)
+{
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, false);
+ return pDbgMod->fExports;
+}
+
+
+RTDECL(int) RTDbgModRemoveAll(RTDBGMOD hDbgMod, bool fLeaveSegments)
+{
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE);
+
+ RTDBGMOD_LOCK(pDbgMod);
+
+ /* Only possible on container modules. */
+ int rc = VINF_SUCCESS;
+ if (pDbgMod->pDbgVt != &g_rtDbgModVtDbgContainer)
+ {
+ if (fLeaveSegments)
+ {
+ rc = rtDbgModContainer_LineRemoveAll(pDbgMod);
+ if (RT_SUCCESS(rc))
+ rc = rtDbgModContainer_SymbolRemoveAll(pDbgMod);
+ }
+ else
+ rc = rtDbgModContainer_RemoveAll(pDbgMod);
+ }
+ else
+ rc = VERR_ACCESS_DENIED;
+
+ RTDBGMOD_UNLOCK(pDbgMod);
+ return rc;
+}
+
+
+RTDECL(RTDBGSEGIDX) RTDbgModRvaToSegOff(RTDBGMOD hDbgMod, RTUINTPTR uRva, PRTUINTPTR poffSeg)
+{
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, NIL_RTDBGSEGIDX);
+ RTDBGMOD_LOCK(pDbgMod);
+
+ RTDBGSEGIDX iSeg = pDbgMod->pDbgVt->pfnRvaToSegOff(pDbgMod, uRva, poffSeg);
+
+ RTDBGMOD_UNLOCK(pDbgMod);
+ return iSeg;
+}
+RT_EXPORT_SYMBOL(RTDbgModRvaToSegOff);
+
+
+RTDECL(uint64_t) RTDbgModGetTag(RTDBGMOD hDbgMod)
+{
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, 0);
+ return pDbgMod->uTag;
+}
+RT_EXPORT_SYMBOL(RTDbgModGetTag);
+
+
+RTDECL(int) RTDbgModSetTag(RTDBGMOD hDbgMod, uint64_t uTag)
+{
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE);
+ RTDBGMOD_LOCK(pDbgMod);
+
+ pDbgMod->uTag = uTag;
+
+ RTDBGMOD_UNLOCK(pDbgMod);
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTDbgModSetTag);
+
+
+RTDECL(RTUINTPTR) RTDbgModImageSize(RTDBGMOD hDbgMod)
+{
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, RTUINTPTR_MAX);
+ RTDBGMOD_LOCK(pDbgMod);
+
+ RTUINTPTR cbImage = pDbgMod->pDbgVt->pfnImageSize(pDbgMod);
+
+ RTDBGMOD_UNLOCK(pDbgMod);
+ return cbImage;
+}
+RT_EXPORT_SYMBOL(RTDbgModImageSize);
+
+
+RTDECL(RTLDRFMT) RTDbgModImageGetFormat(RTDBGMOD hDbgMod)
+{
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, RTLDRFMT_INVALID);
+ RTDBGMOD_LOCK(pDbgMod);
+
+ RTLDRFMT enmFmt;
+ if ( pDbgMod->pImgVt
+ && pDbgMod->pImgVt->pfnGetFormat)
+ enmFmt = pDbgMod->pImgVt->pfnGetFormat(pDbgMod);
+ else
+ enmFmt = RTLDRFMT_INVALID;
+
+ RTDBGMOD_UNLOCK(pDbgMod);
+ return enmFmt;
+}
+RT_EXPORT_SYMBOL(RTDbgModImageGetFormat);
+
+
+RTDECL(RTLDRARCH) RTDbgModImageGetArch(RTDBGMOD hDbgMod)
+{
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, RTLDRARCH_INVALID);
+ RTDBGMOD_LOCK(pDbgMod);
+
+ RTLDRARCH enmArch;
+ if ( pDbgMod->pImgVt
+ && pDbgMod->pImgVt->pfnGetArch)
+ enmArch = pDbgMod->pImgVt->pfnGetArch(pDbgMod);
+ else
+ enmArch = RTLDRARCH_WHATEVER;
+
+ RTDBGMOD_UNLOCK(pDbgMod);
+ return enmArch;
+}
+RT_EXPORT_SYMBOL(RTDbgModImageGetArch);
+
+
+RTDECL(int) RTDbgModImageQueryProp(RTDBGMOD hDbgMod, RTLDRPROP enmProp, void *pvBuf, size_t cbBuf, size_t *pcbRet)
+{
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE);
+ AssertPtrNullReturn(pcbRet, VERR_INVALID_POINTER);
+ RTDBGMOD_LOCK(pDbgMod);
+
+ int rc;
+ if ( pDbgMod->pImgVt
+ && pDbgMod->pImgVt->pfnQueryProp)
+ rc = pDbgMod->pImgVt->pfnQueryProp(pDbgMod, enmProp, pvBuf, cbBuf, pcbRet);
+ else
+ rc = VERR_NOT_FOUND;
+
+ RTDBGMOD_UNLOCK(pDbgMod);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgModImageQueryProp);
+
+
+RTDECL(int) RTDbgModSegmentAdd(RTDBGMOD hDbgMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName,
+ uint32_t fFlags, PRTDBGSEGIDX piSeg)
+{
+ /*
+ * Validate input.
+ */
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE);
+ AssertMsgReturn(uRva + cb >= uRva, ("uRva=%RTptr cb=%RTptr\n", uRva, cb), VERR_DBG_ADDRESS_WRAP);
+ Assert(*pszName);
+ size_t cchName = strlen(pszName);
+ AssertReturn(cchName > 0, VERR_DBG_SEGMENT_NAME_OUT_OF_RANGE);
+ AssertReturn(cchName < RTDBG_SEGMENT_NAME_LENGTH, VERR_DBG_SEGMENT_NAME_OUT_OF_RANGE);
+ AssertMsgReturn(!fFlags, ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+ AssertPtrNull(piSeg);
+ AssertMsgReturn(!piSeg || *piSeg == NIL_RTDBGSEGIDX || *piSeg <= RTDBGSEGIDX_LAST, ("%#x\n", *piSeg), VERR_DBG_SPECIAL_SEGMENT);
+
+ /*
+ * Do the deed.
+ */
+ RTDBGMOD_LOCK(pDbgMod);
+ int rc = pDbgMod->pDbgVt->pfnSegmentAdd(pDbgMod, uRva, cb, pszName, cchName, fFlags, piSeg);
+ RTDBGMOD_UNLOCK(pDbgMod);
+
+ return rc;
+
+}
+RT_EXPORT_SYMBOL(RTDbgModSegmentAdd);
+
+
+RTDECL(RTDBGSEGIDX) RTDbgModSegmentCount(RTDBGMOD hDbgMod)
+{
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, NIL_RTDBGSEGIDX);
+ RTDBGMOD_LOCK(pDbgMod);
+
+ RTDBGSEGIDX cSegs = pDbgMod->pDbgVt->pfnSegmentCount(pDbgMod);
+
+ RTDBGMOD_UNLOCK(pDbgMod);
+ return cSegs;
+}
+RT_EXPORT_SYMBOL(RTDbgModSegmentCount);
+
+
+RTDECL(int) RTDbgModSegmentByIndex(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo)
+{
+ AssertMsgReturn(iSeg <= RTDBGSEGIDX_LAST, ("%#x\n", iSeg), VERR_DBG_SPECIAL_SEGMENT);
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE);
+ RTDBGMOD_LOCK(pDbgMod);
+
+ int rc = pDbgMod->pDbgVt->pfnSegmentByIndex(pDbgMod, iSeg, pSegInfo);
+
+ RTDBGMOD_UNLOCK(pDbgMod);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgModSegmentByIndex);
+
+
+RTDECL(RTUINTPTR) RTDbgModSegmentSize(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg)
+{
+ if (iSeg == RTDBGSEGIDX_RVA)
+ return RTDbgModImageSize(hDbgMod);
+ RTDBGSEGMENT SegInfo;
+ int rc = RTDbgModSegmentByIndex(hDbgMod, iSeg, &SegInfo);
+ return RT_SUCCESS(rc) ? SegInfo.cb : RTUINTPTR_MAX;
+}
+RT_EXPORT_SYMBOL(RTDbgModSegmentSize);
+
+
+RTDECL(RTUINTPTR) RTDbgModSegmentRva(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg)
+{
+ RTDBGSEGMENT SegInfo;
+ int rc = RTDbgModSegmentByIndex(hDbgMod, iSeg, &SegInfo);
+ return RT_SUCCESS(rc) ? SegInfo.uRva : RTUINTPTR_MAX;
+}
+RT_EXPORT_SYMBOL(RTDbgModSegmentRva);
+
+
+RTDECL(int) RTDbgModSymbolAdd(RTDBGMOD hDbgMod, const char *pszSymbol, RTDBGSEGIDX iSeg, RTUINTPTR off,
+ RTUINTPTR cb, uint32_t fFlags, uint32_t *piOrdinal)
+{
+ /*
+ * Validate input.
+ */
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pszSymbol, VERR_INVALID_POINTER);
+ size_t cchSymbol = strlen(pszSymbol);
+ AssertReturn(cchSymbol, VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE);
+ AssertReturn(cchSymbol < RTDBG_SYMBOL_NAME_LENGTH, VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE);
+ AssertMsgReturn( iSeg <= RTDBGSEGIDX_LAST
+ || ( iSeg >= RTDBGSEGIDX_SPECIAL_FIRST
+ && iSeg <= RTDBGSEGIDX_SPECIAL_LAST),
+ ("%#x\n", iSeg),
+ VERR_DBG_INVALID_SEGMENT_INDEX);
+ AssertMsgReturn(off + cb >= off, ("off=%RTptr cb=%RTptr\n", off, cb), VERR_DBG_ADDRESS_WRAP);
+ AssertReturn(!(fFlags & ~RTDBGSYMBOLADD_F_VALID_MASK), VERR_INVALID_FLAGS);
+
+ RTDBGMOD_LOCK(pDbgMod);
+
+ /*
+ * Convert RVAs.
+ */
+ if (iSeg == RTDBGSEGIDX_RVA)
+ {
+ iSeg = pDbgMod->pDbgVt->pfnRvaToSegOff(pDbgMod, off, &off);
+ if (iSeg == NIL_RTDBGSEGIDX)
+ {
+ RTDBGMOD_UNLOCK(pDbgMod);
+ return VERR_DBG_INVALID_RVA;
+ }
+ }
+
+ /*
+ * Get down to business.
+ */
+ int rc = pDbgMod->pDbgVt->pfnSymbolAdd(pDbgMod, pszSymbol, cchSymbol, iSeg, off, cb, fFlags, piOrdinal);
+
+ RTDBGMOD_UNLOCK(pDbgMod);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgModSymbolAdd);
+
+
+RTDECL(uint32_t) RTDbgModSymbolCount(RTDBGMOD hDbgMod)
+{
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, UINT32_MAX);
+ RTDBGMOD_LOCK(pDbgMod);
+
+ uint32_t cSymbols = pDbgMod->pDbgVt->pfnSymbolCount(pDbgMod);
+
+ RTDBGMOD_UNLOCK(pDbgMod);
+ return cSymbols;
+}
+RT_EXPORT_SYMBOL(RTDbgModSymbolCount);
+
+
+RTDECL(int) RTDbgModSymbolByOrdinal(RTDBGMOD hDbgMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo)
+{
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE);
+ RTDBGMOD_LOCK(pDbgMod);
+
+ int rc = pDbgMod->pDbgVt->pfnSymbolByOrdinal(pDbgMod, iOrdinal, pSymInfo);
+
+ RTDBGMOD_UNLOCK(pDbgMod);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgModSymbolByOrdinal);
+
+
+RTDECL(int) RTDbgModSymbolByOrdinalA(RTDBGMOD hDbgMod, uint32_t iOrdinal, PRTDBGSYMBOL *ppSymInfo)
+{
+ AssertPtr(ppSymInfo);
+ *ppSymInfo = NULL;
+
+ PRTDBGSYMBOL pSymInfo = RTDbgSymbolAlloc();
+ if (!pSymInfo)
+ return VERR_NO_MEMORY;
+
+ int rc = RTDbgModSymbolByOrdinal(hDbgMod, iOrdinal, pSymInfo);
+
+ if (RT_SUCCESS(rc))
+ *ppSymInfo = pSymInfo;
+ else
+ RTDbgSymbolFree(pSymInfo);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgModSymbolByOrdinalA);
+
+
+/**
+ * Return a segment number/name as symbol if we couldn't find any
+ * valid symbols within the segment.
+ */
+DECL_NO_INLINE(static, int)
+rtDbgModSymbolByAddrTrySegments(PRTDBGMODINT pDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR off,
+ PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo)
+{
+ Assert(iSeg <= RTDBGSEGIDX_LAST);
+ RTDBGSEGMENT SegInfo;
+ int rc = pDbgMod->pDbgVt->pfnSegmentByIndex(pDbgMod, iSeg, &SegInfo);
+ if (RT_SUCCESS(rc))
+ {
+ pSymInfo->Value = 0;
+ pSymInfo->cb = SegInfo.cb;
+ pSymInfo->offSeg = 0;
+ pSymInfo->iSeg = iSeg;
+ pSymInfo->fFlags = 0;
+ if (SegInfo.szName[0])
+ RTStrPrintf(pSymInfo->szName, sizeof(pSymInfo->szName), "start_seg%u_%s", SegInfo.iSeg, SegInfo.szName);
+ else
+ RTStrPrintf(pSymInfo->szName, sizeof(pSymInfo->szName), "start_seg%u", SegInfo.iSeg);
+ if (poffDisp)
+ *poffDisp = off;
+ return VINF_SUCCESS;
+ }
+ return VERR_SYMBOL_NOT_FOUND;
+}
+
+
+RTDECL(int) RTDbgModSymbolByAddr(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags,
+ PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo)
+{
+ /*
+ * Validate input.
+ */
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE);
+ AssertPtrNull(poffDisp);
+ AssertPtr(pSymInfo);
+ AssertReturn(!(fFlags & ~RTDBGSYMADDR_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ RTDBGMOD_LOCK(pDbgMod);
+
+ /*
+ * Convert RVAs.
+ */
+ if (iSeg == RTDBGSEGIDX_RVA)
+ {
+ iSeg = pDbgMod->pDbgVt->pfnRvaToSegOff(pDbgMod, off, &off);
+ if (iSeg == NIL_RTDBGSEGIDX)
+ {
+ RTDBGMOD_UNLOCK(pDbgMod);
+ return VERR_DBG_INVALID_RVA;
+ }
+ }
+
+ /*
+ * Get down to business.
+ */
+ int rc = pDbgMod->pDbgVt->pfnSymbolByAddr(pDbgMod, iSeg, off, fFlags, poffDisp, pSymInfo);
+
+ /* If we failed to locate a symbol, try use the specified segment as a reference. */
+ if ( rc == VERR_SYMBOL_NOT_FOUND
+ && iSeg <= RTDBGSEGIDX_LAST
+ && !(fFlags & RTDBGSYMADDR_FLAGS_GREATER_OR_EQUAL))
+ rc = rtDbgModSymbolByAddrTrySegments(pDbgMod, iSeg, off, poffDisp, pSymInfo);
+
+ RTDBGMOD_UNLOCK(pDbgMod);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgModSymbolByAddr);
+
+
+RTDECL(int) RTDbgModSymbolByAddrA(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags,
+ PRTINTPTR poffDisp, PRTDBGSYMBOL *ppSymInfo)
+{
+ AssertPtr(ppSymInfo);
+ *ppSymInfo = NULL;
+
+ PRTDBGSYMBOL pSymInfo = RTDbgSymbolAlloc();
+ if (!pSymInfo)
+ return VERR_NO_MEMORY;
+
+ int rc = RTDbgModSymbolByAddr(hDbgMod, iSeg, off, fFlags, poffDisp, pSymInfo);
+
+ if (RT_SUCCESS(rc))
+ *ppSymInfo = pSymInfo;
+ else
+ RTDbgSymbolFree(pSymInfo);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgModSymbolByAddrA);
+
+
+RTDECL(int) RTDbgModSymbolByName(RTDBGMOD hDbgMod, const char *pszSymbol, PRTDBGSYMBOL pSymInfo)
+{
+ /*
+ * Validate input.
+ */
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE);
+ AssertPtr(pszSymbol);
+ size_t cchSymbol = strlen(pszSymbol);
+ AssertReturn(cchSymbol, VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE);
+ AssertReturn(cchSymbol < RTDBG_SYMBOL_NAME_LENGTH, VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE);
+ AssertPtr(pSymInfo);
+
+ /*
+ * Make the query.
+ */
+ RTDBGMOD_LOCK(pDbgMod);
+ int rc = pDbgMod->pDbgVt->pfnSymbolByName(pDbgMod, pszSymbol, cchSymbol, pSymInfo);
+ RTDBGMOD_UNLOCK(pDbgMod);
+
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgModSymbolByName);
+
+
+RTDECL(int) RTDbgModSymbolByNameA(RTDBGMOD hDbgMod, const char *pszSymbol, PRTDBGSYMBOL *ppSymInfo)
+{
+ AssertPtr(ppSymInfo);
+ *ppSymInfo = NULL;
+
+ PRTDBGSYMBOL pSymInfo = RTDbgSymbolAlloc();
+ if (!pSymInfo)
+ return VERR_NO_MEMORY;
+
+ int rc = RTDbgModSymbolByName(hDbgMod, pszSymbol, pSymInfo);
+
+ if (RT_SUCCESS(rc))
+ *ppSymInfo = pSymInfo;
+ else
+ RTDbgSymbolFree(pSymInfo);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgModSymbolByNameA);
+
+
+RTDECL(int) RTDbgModLineAdd(RTDBGMOD hDbgMod, const char *pszFile, uint32_t uLineNo,
+ RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t *piOrdinal)
+{
+ /*
+ * Validate input.
+ */
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE);
+ AssertPtr(pszFile);
+ size_t cchFile = strlen(pszFile);
+ AssertReturn(cchFile, VERR_DBG_FILE_NAME_OUT_OF_RANGE);
+ AssertReturn(cchFile < RTDBG_FILE_NAME_LENGTH, VERR_DBG_FILE_NAME_OUT_OF_RANGE);
+ AssertMsgReturn( iSeg <= RTDBGSEGIDX_LAST
+ || iSeg == RTDBGSEGIDX_RVA,
+ ("%#x\n", iSeg),
+ VERR_DBG_INVALID_SEGMENT_INDEX);
+ AssertReturn(uLineNo > 0 && uLineNo < UINT32_MAX, VERR_INVALID_PARAMETER);
+
+ RTDBGMOD_LOCK(pDbgMod);
+
+ /*
+ * Convert RVAs.
+ */
+ if (iSeg == RTDBGSEGIDX_RVA)
+ {
+ iSeg = pDbgMod->pDbgVt->pfnRvaToSegOff(pDbgMod, off, &off);
+ if (iSeg == NIL_RTDBGSEGIDX)
+ {
+ RTDBGMOD_UNLOCK(pDbgMod);
+ return VERR_DBG_INVALID_RVA;
+ }
+ }
+
+ /*
+ * Get down to business.
+ */
+ int rc = pDbgMod->pDbgVt->pfnLineAdd(pDbgMod, pszFile, cchFile, uLineNo, iSeg, off, piOrdinal);
+
+ RTDBGMOD_UNLOCK(pDbgMod);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgModLineAdd);
+
+
+RTDECL(uint32_t) RTDbgModLineCount(RTDBGMOD hDbgMod)
+{
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, UINT32_MAX);
+ RTDBGMOD_LOCK(pDbgMod);
+
+ uint32_t cLineNumbers = pDbgMod->pDbgVt->pfnLineCount(pDbgMod);
+
+ RTDBGMOD_UNLOCK(pDbgMod);
+ return cLineNumbers;
+}
+RT_EXPORT_SYMBOL(RTDbgModLineCount);
+
+
+RTDECL(int) RTDbgModLineByOrdinal(RTDBGMOD hDbgMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo)
+{
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE);
+ RTDBGMOD_LOCK(pDbgMod);
+
+ int rc = pDbgMod->pDbgVt->pfnLineByOrdinal(pDbgMod, iOrdinal, pLineInfo);
+
+ RTDBGMOD_UNLOCK(pDbgMod);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgModLineByOrdinal);
+
+
+RTDECL(int) RTDbgModLineByOrdinalA(RTDBGMOD hDbgMod, uint32_t iOrdinal, PRTDBGLINE *ppLineInfo)
+{
+ AssertPtr(ppLineInfo);
+ *ppLineInfo = NULL;
+
+ PRTDBGLINE pLineInfo = RTDbgLineAlloc();
+ if (!pLineInfo)
+ return VERR_NO_MEMORY;
+
+ int rc = RTDbgModLineByOrdinal(hDbgMod, iOrdinal, pLineInfo);
+
+ if (RT_SUCCESS(rc))
+ *ppLineInfo = pLineInfo;
+ else
+ RTDbgLineFree(pLineInfo);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgModLineByOrdinalA);
+
+
+RTDECL(int) RTDbgModLineByAddr(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTINTPTR poffDisp, PRTDBGLINE pLineInfo)
+{
+ /*
+ * Validate input.
+ */
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE);
+ AssertPtrNull(poffDisp);
+ AssertPtr(pLineInfo);
+
+ RTDBGMOD_LOCK(pDbgMod);
+
+ /*
+ * Convert RVAs.
+ */
+ if (iSeg == RTDBGSEGIDX_RVA)
+ {
+ iSeg = pDbgMod->pDbgVt->pfnRvaToSegOff(pDbgMod, off, &off);
+ if (iSeg == NIL_RTDBGSEGIDX)
+ {
+ RTDBGMOD_UNLOCK(pDbgMod);
+ return VERR_DBG_INVALID_RVA;
+ }
+ }
+
+ int rc = pDbgMod->pDbgVt->pfnLineByAddr(pDbgMod, iSeg, off, poffDisp, pLineInfo);
+
+ RTDBGMOD_UNLOCK(pDbgMod);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgModLineByAddr);
+
+
+RTDECL(int) RTDbgModLineByAddrA(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTINTPTR poffDisp, PRTDBGLINE *ppLineInfo)
+{
+ AssertPtr(ppLineInfo);
+ *ppLineInfo = NULL;
+
+ PRTDBGLINE pLineInfo = RTDbgLineAlloc();
+ if (!pLineInfo)
+ return VERR_NO_MEMORY;
+
+ int rc = RTDbgModLineByAddr(hDbgMod, iSeg, off, poffDisp, pLineInfo);
+
+ if (RT_SUCCESS(rc))
+ *ppLineInfo = pLineInfo;
+ else
+ RTDbgLineFree(pLineInfo);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgModLineByAddrA);
+
+
+RTDECL(int) RTDbgModUnwindFrame(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState)
+{
+ /*
+ * Validate input.
+ */
+ PRTDBGMODINT pDbgMod = hDbgMod;
+ RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE);
+ AssertPtr(pState);
+ AssertReturn(pState->u32Magic == RTDBGUNWINDSTATE_MAGIC, VERR_INVALID_MAGIC);
+
+ RTDBGMOD_LOCK(pDbgMod);
+
+ /*
+ * Convert RVAs.
+ */
+ if (iSeg == RTDBGSEGIDX_RVA)
+ {
+ iSeg = pDbgMod->pDbgVt->pfnRvaToSegOff(pDbgMod, off, &off);
+ if (iSeg == NIL_RTDBGSEGIDX)
+ {
+ RTDBGMOD_UNLOCK(pDbgMod);
+ return VERR_DBG_INVALID_RVA;
+ }
+ }
+
+ /*
+ * Try the debug module first, then the image.
+ */
+ int rc = VERR_DBG_NO_UNWIND_INFO;
+ if (pDbgMod->pDbgVt->pfnUnwindFrame)
+ rc = pDbgMod->pDbgVt->pfnUnwindFrame(pDbgMod, iSeg, off, pState);
+ if ( ( rc == VERR_DBG_NO_UNWIND_INFO
+ || rc == VERR_DBG_UNWIND_INFO_NOT_FOUND)
+ && pDbgMod->pImgVt
+ && pDbgMod->pImgVt->pfnUnwindFrame)
+ {
+ if (rc == VERR_DBG_NO_UNWIND_INFO)
+ rc = pDbgMod->pImgVt->pfnUnwindFrame(pDbgMod, iSeg, off, pState);
+ else
+ {
+ rc = pDbgMod->pImgVt->pfnUnwindFrame(pDbgMod, iSeg, off, pState);
+ if (rc == VERR_DBG_NO_UNWIND_INFO)
+ rc = VERR_DBG_UNWIND_INFO_NOT_FOUND;
+ }
+ }
+
+ RTDBGMOD_UNLOCK(pDbgMod);
+ return rc;
+
+}
+RT_EXPORT_SYMBOL(RTDbgModUnwindFrame);
+