summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/dbg
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/common/dbg')
-rw-r--r--src/VBox/Runtime/common/dbg/Makefile.kup0
-rw-r--r--src/VBox/Runtime/common/dbg/dbg.cpp122
-rw-r--r--src/VBox/Runtime/common/dbg/dbgas.cpp1513
-rw-r--r--src/VBox/Runtime/common/dbg/dbgcfg.cpp2525
-rw-r--r--src/VBox/Runtime/common/dbg/dbgmod.cpp2317
-rw-r--r--src/VBox/Runtime/common/dbg/dbgmodcodeview.cpp3197
-rw-r--r--src/VBox/Runtime/common/dbg/dbgmodcontainer.cpp1050
-rw-r--r--src/VBox/Runtime/common/dbg/dbgmoddbghelp.cpp537
-rw-r--r--src/VBox/Runtime/common/dbg/dbgmoddeferred.cpp730
-rw-r--r--src/VBox/Runtime/common/dbg/dbgmoddwarf.cpp6287
-rw-r--r--src/VBox/Runtime/common/dbg/dbgmodexports.cpp183
-rw-r--r--src/VBox/Runtime/common/dbg/dbgmodghidra.cpp526
-rw-r--r--src/VBox/Runtime/common/dbg/dbgmodldr.cpp286
-rw-r--r--src/VBox/Runtime/common/dbg/dbgmodmapsym.cpp622
-rw-r--r--src/VBox/Runtime/common/dbg/dbgmodnm.cpp581
-rw-r--r--src/VBox/Runtime/common/dbg/dbgstackdumpself-amd64-x86.asm157
-rw-r--r--src/VBox/Runtime/common/dbg/dbgstackdumpself.cpp543
17 files changed, 21176 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/dbg/Makefile.kup b/src/VBox/Runtime/common/dbg/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/common/dbg/Makefile.kup
diff --git a/src/VBox/Runtime/common/dbg/dbg.cpp b/src/VBox/Runtime/common/dbg/dbg.cpp
new file mode 100644
index 00000000..95b95353
--- /dev/null
+++ b/src/VBox/Runtime/common/dbg/dbg.cpp
@@ -0,0 +1,122 @@
+/* $Id: dbg.cpp $ */
+/** @file
+ * IPRT - Debug Misc.
+ */
+
+/*
+ * Copyright (C) 2009-2023 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 <iprt/dbg.h>
+#include "internal/iprt.h"
+
+#include <iprt/mem.h>
+
+
+
+/**
+ * Allocate a new symbol structure.
+ *
+ * @returns Pointer to a new structure on success, NULL on failure.
+ */
+RTDECL(PRTDBGSYMBOL) RTDbgSymbolAlloc(void)
+{
+ return (PRTDBGSYMBOL)RTMemAllocZ(sizeof(RTDBGSYMBOL));
+}
+RT_EXPORT_SYMBOL(RTDbgSymbolAlloc);
+
+
+/**
+ * Duplicates a symbol structure.
+ *
+ * @returns Pointer to duplicate on success, NULL on failure.
+ *
+ * @param pSymInfo The symbol info to duplicate.
+ */
+RTDECL(PRTDBGSYMBOL) RTDbgSymbolDup(PCRTDBGSYMBOL pSymInfo)
+{
+ return (PRTDBGSYMBOL)RTMemDup(pSymInfo, sizeof(*pSymInfo));
+}
+RT_EXPORT_SYMBOL(RTDbgSymbolDup);
+
+
+/**
+ * Free a symbol structure previously allocated by a RTDbg method.
+ *
+ * @param pSymInfo The symbol info to free. NULL is ignored.
+ */
+RTDECL(void) RTDbgSymbolFree(PRTDBGSYMBOL pSymInfo)
+{
+ RTMemFree(pSymInfo);
+}
+RT_EXPORT_SYMBOL(RTDbgSymbolFree);
+
+
+/**
+ * Allocate a new line number structure.
+ *
+ * @returns Pointer to a new structure on success, NULL on failure.
+ */
+RTDECL(PRTDBGLINE) RTDbgLineAlloc(void)
+{
+ return (PRTDBGLINE)RTMemAllocZ(sizeof(RTDBGLINE));
+}
+RT_EXPORT_SYMBOL(RTDbgLineAlloc);
+
+
+/**
+ * Duplicates a line number structure.
+ *
+ * @returns Pointer to duplicate on success, NULL on failure.
+ *
+ * @param pLine The line number to duplicate.
+ */
+RTDECL(PRTDBGLINE) RTDbgLineDup(PCRTDBGLINE pLine)
+{
+ return (PRTDBGLINE)RTMemDup(pLine, sizeof(*pLine));
+}
+RT_EXPORT_SYMBOL(RTDbgLineDup);
+
+
+/**
+ * Free a line number structure previously allocated by a RTDbg method.
+ *
+ * @param pLine The line number to free. NULL is ignored.
+ */
+RTDECL(void) RTDbgLineFree(PRTDBGLINE pLine)
+{
+ RTMemFree(pLine);
+}
+RT_EXPORT_SYMBOL(RTDbgLineFree);
+
diff --git a/src/VBox/Runtime/common/dbg/dbgas.cpp b/src/VBox/Runtime/common/dbg/dbgas.cpp
new file mode 100644
index 00000000..cd45a676
--- /dev/null
+++ b/src/VBox/Runtime/common/dbg/dbgas.cpp
@@ -0,0 +1,1513 @@
+/* $Id: dbgas.cpp $ */
+/** @file
+ * IPRT - Debug Address Space.
+ */
+
+/*
+ * Copyright (C) 2009-2023 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 <iprt/dbg.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/avl.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/param.h>
+#include <iprt/string.h>
+#include <iprt/semaphore.h>
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Pointer to a module table entry. */
+typedef struct RTDBGASMOD *PRTDBGASMOD;
+/** Pointer to an address space mapping node. */
+typedef struct RTDBGASMAP *PRTDBGASMAP;
+/** Pointer to a name head. */
+typedef struct RTDBGASNAME *PRTDBGASNAME;
+
+/**
+ * Module entry.
+ */
+typedef struct RTDBGASMOD
+{
+ /** Node core, the module handle is the key. */
+ AVLPVNODECORE Core;
+ /** Pointer to the first mapping of the module or a segment within it. */
+ PRTDBGASMAP pMapHead;
+ /** Pointer to the next module with an identical name. */
+ PRTDBGASMOD pNextName;
+ /** The index into RTDBGASINT::papModules. */
+ uint32_t iOrdinal;
+} RTDBGASMOD;
+
+/**
+ * An address space mapping, either of a full module or a segment.
+ */
+typedef struct RTDBGASMAP
+{
+ /** The AVL node core. Contains the address range. */
+ AVLRUINTPTRNODECORE Core;
+ /** Pointer to the next mapping of the module. */
+ PRTDBGASMAP pNext;
+ /** Pointer to the module. */
+ PRTDBGASMOD pMod;
+ /** Which segment in the module.
+ * This is NIL_RTDBGSEGIDX when the entire module is mapped. */
+ RTDBGSEGIDX iSeg;
+} RTDBGASMAP;
+
+/**
+ * Name in the address space.
+ */
+typedef struct RTDBGASNAME
+{
+ /** The string space node core.*/
+ RTSTRSPACECORE StrCore;
+ /** The list of nodes */
+ PRTDBGASMOD pHead;
+} RTDBGASNAME;
+
+/**
+ * Debug address space instance.
+ */
+typedef struct RTDBGASINT
+{
+ /** Magic value (RTDBGAS_MAGIC). */
+ uint32_t u32Magic;
+ /** The number of reference to this address space. */
+ uint32_t volatile cRefs;
+ /** Handle of the read-write lock. */
+ RTSEMRW hLock;
+ /** Number of modules in the module address space. */
+ uint32_t cModules;
+ /** Pointer to the module table.
+ * The valid array length is given by cModules. */
+ PRTDBGASMOD *papModules;
+ /** AVL tree translating module handles to module entries. */
+ AVLPVTREE ModTree;
+ /** AVL tree mapping addresses to modules. */
+ AVLRUINTPTRTREE MapTree;
+ /** Names of the modules in the name space. */
+ RTSTRSPACE NameSpace;
+ /** The first address the AS. */
+ RTUINTPTR FirstAddr;
+ /** The last address in the AS. */
+ RTUINTPTR LastAddr;
+ /** The name of the address space. (variable length) */
+ char szName[1];
+} RTDBGASINT;
+/** Pointer to an a debug address space instance. */
+typedef RTDBGASINT *PRTDBGASINT;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Validates an address space handle and returns rc if not valid. */
+#define RTDBGAS_VALID_RETURN_RC(pDbgAs, rc) \
+ do { \
+ AssertPtrReturn((pDbgAs), (rc)); \
+ AssertReturn((pDbgAs)->u32Magic == RTDBGAS_MAGIC, (rc)); \
+ AssertReturn((pDbgAs)->cRefs > 0, (rc)); \
+ } while (0)
+
+/** Locks the address space for reading. */
+#define RTDBGAS_LOCK_READ(pDbgAs) \
+ do { \
+ int rcLock = RTSemRWRequestRead((pDbgAs)->hLock, RT_INDEFINITE_WAIT); \
+ AssertRC(rcLock); \
+ } while (0)
+
+/** Unlocks the address space after reading. */
+#define RTDBGAS_UNLOCK_READ(pDbgAs) \
+ do { \
+ int rcLock = RTSemRWReleaseRead((pDbgAs)->hLock); \
+ AssertRC(rcLock); \
+ } while (0)
+
+/** Locks the address space for writing. */
+#define RTDBGAS_LOCK_WRITE(pDbgAs) \
+ do { \
+ int rcLock = RTSemRWRequestWrite((pDbgAs)->hLock, RT_INDEFINITE_WAIT); \
+ AssertRC(rcLock); \
+ } while (0)
+
+/** Unlocks the address space after writing. */
+#define RTDBGAS_UNLOCK_WRITE(pDbgAs) \
+ do { \
+ int rcLock = RTSemRWReleaseWrite((pDbgAs)->hLock); \
+ AssertRC(rcLock); \
+ } while (0)
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static void rtDbgAsModuleUnlinkMod(PRTDBGASINT pDbgAs, PRTDBGASMOD pMod);
+static void rtDbgAsModuleUnlinkByMap(PRTDBGASINT pDbgAs, PRTDBGASMAP pMap);
+
+
+RTDECL(int) RTDbgAsCreate(PRTDBGAS phDbgAs, RTUINTPTR FirstAddr, RTUINTPTR LastAddr, const char *pszName)
+{
+ /*
+ * Input validation.
+ */
+ AssertPtrReturn(phDbgAs, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ AssertReturn(FirstAddr < LastAddr, VERR_INVALID_PARAMETER);
+
+ /*
+ * Allocate memory for the instance data.
+ */
+ size_t cchName = strlen(pszName);
+ PRTDBGASINT pDbgAs = (PRTDBGASINT)RTMemAllocVar(RT_UOFFSETOF_DYN(RTDBGASINT, szName[cchName + 1]));
+ if (!pDbgAs)
+ return VERR_NO_MEMORY;
+
+ /* initialize it. */
+ pDbgAs->u32Magic = RTDBGAS_MAGIC;
+ pDbgAs->cRefs = 1;
+ pDbgAs->hLock = NIL_RTSEMRW;
+ pDbgAs->cModules = 0;
+ pDbgAs->papModules = NULL;
+ pDbgAs->ModTree = NULL;
+ pDbgAs->MapTree = NULL;
+ pDbgAs->NameSpace = NULL;
+ pDbgAs->FirstAddr = FirstAddr;
+ pDbgAs->LastAddr = LastAddr;
+ memcpy(pDbgAs->szName, pszName, cchName + 1);
+ int rc = RTSemRWCreate(&pDbgAs->hLock);
+ if (RT_SUCCESS(rc))
+ {
+ *phDbgAs = pDbgAs;
+ return VINF_SUCCESS;
+ }
+
+ pDbgAs->u32Magic = 0;
+ RTMemFree(pDbgAs);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgAsCreate);
+
+
+RTDECL(int) RTDbgAsCreateV(PRTDBGAS phDbgAs, RTUINTPTR FirstAddr, RTUINTPTR LastAddr, const char *pszNameFmt, va_list va)
+{
+ AssertPtrReturn(pszNameFmt, VERR_INVALID_POINTER);
+
+ char *pszName;
+ RTStrAPrintfV(&pszName, pszNameFmt, va);
+ if (!pszName)
+ return VERR_NO_MEMORY;
+
+ int rc = RTDbgAsCreate(phDbgAs, FirstAddr, LastAddr, pszName);
+
+ RTStrFree(pszName);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgAsCreateV);
+
+
+RTDECL(int) RTDbgAsCreateF(PRTDBGAS phDbgAs, RTUINTPTR FirstAddr, RTUINTPTR LastAddr, const char *pszNameFmt, ...)
+{
+ va_list va;
+ va_start(va, pszNameFmt);
+ int rc = RTDbgAsCreateV(phDbgAs, FirstAddr, LastAddr, pszNameFmt, va);
+ va_end(va);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgAsCreateF);
+
+
+/**
+ * Callback used by RTDbgAsDestroy to free all mapping nodes.
+ *
+ * @returns 0
+ * @param pNode The map node.
+ * @param pvUser NULL.
+ */
+static DECLCALLBACK(int) rtDbgAsDestroyMapCallback(PAVLRUINTPTRNODECORE pNode, void *pvUser)
+{
+ RTMemFree(pNode);
+ NOREF(pvUser);
+ return 0;
+}
+
+
+/**
+ * Callback used by RTDbgAsDestroy to free all name space nodes.
+ *
+ * @returns 0
+ * @param pStr The name node.
+ * @param pvUser NULL.
+ */
+static DECLCALLBACK(int) rtDbgAsDestroyNameCallback(PRTSTRSPACECORE pStr, void *pvUser)
+{
+ RTMemFree(pStr);
+ NOREF(pvUser);
+ return 0;
+}
+
+
+/**
+ * Destroys the address space.
+ *
+ * This means unlinking all the modules it currently contains, potentially
+ * causing some or all of them to be destroyed as they are managed by
+ * reference counting.
+ *
+ * @param pDbgAs The address space instance to be destroyed.
+ */
+static void rtDbgAsDestroy(PRTDBGASINT pDbgAs)
+{
+ /*
+ * Mark the address space invalid and release all the modules.
+ */
+ ASMAtomicWriteU32(&pDbgAs->u32Magic, ~RTDBGAS_MAGIC);
+
+ RTAvlrUIntPtrDestroy(&pDbgAs->MapTree, rtDbgAsDestroyMapCallback, NULL);
+ RTStrSpaceDestroy(&pDbgAs->NameSpace, rtDbgAsDestroyNameCallback, NULL);
+
+ uint32_t i = pDbgAs->cModules;
+ while (i-- > 0)
+ {
+ PRTDBGASMOD pMod = pDbgAs->papModules[i];
+ AssertPtr(pMod);
+ if (RT_VALID_PTR(pMod))
+ {
+ Assert(pMod->iOrdinal == i);
+ RTDbgModRelease((RTDBGMOD)pMod->Core.Key);
+ pMod->Core.Key = NIL_RTDBGMOD;
+ pMod->iOrdinal = UINT32_MAX;
+ RTMemFree(pMod);
+ }
+ pDbgAs->papModules[i] = NULL;
+ }
+ RTSemRWDestroy(pDbgAs->hLock);
+ pDbgAs->hLock = NIL_RTSEMRW;
+ RTMemFree(pDbgAs->papModules);
+ pDbgAs->papModules = NULL;
+
+ RTMemFree(pDbgAs);
+}
+
+
+RTDECL(uint32_t) RTDbgAsRetain(RTDBGAS hDbgAs)
+{
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, UINT32_MAX);
+ return ASMAtomicIncU32(&pDbgAs->cRefs);
+}
+RT_EXPORT_SYMBOL(RTDbgAsRetain);
+
+
+RTDECL(uint32_t) RTDbgAsRelease(RTDBGAS hDbgAs)
+{
+ if (hDbgAs == NIL_RTDBGAS)
+ return 0;
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pDbgAs->cRefs);
+ if (!cRefs)
+ rtDbgAsDestroy(pDbgAs);
+ return cRefs;
+}
+RT_EXPORT_SYMBOL(RTDbgAsRelease);
+
+
+RTDECL(int) RTDbgAsLockExcl(RTDBGAS hDbgAs)
+{
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE);
+ RTDBGAS_LOCK_WRITE(pDbgAs);
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTDbgAsLockExcl);
+
+
+RTDECL(int) RTDbgAsUnlockExcl(RTDBGAS hDbgAs)
+{
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE);
+ RTDBGAS_UNLOCK_WRITE(pDbgAs);
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTDbgAsUnlockExcl);
+
+
+RTDECL(const char *) RTDbgAsName(RTDBGAS hDbgAs)
+{
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, NULL);
+ return pDbgAs->szName;
+}
+RT_EXPORT_SYMBOL(RTDbgAsName);
+
+
+RTDECL(RTUINTPTR) RTDbgAsFirstAddr(RTDBGAS hDbgAs)
+{
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, 0);
+ return pDbgAs->FirstAddr;
+}
+RT_EXPORT_SYMBOL(RTDbgAsFirstAddr);
+
+
+RTDECL(RTUINTPTR) RTDbgAsLastAddr(RTDBGAS hDbgAs)
+{
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, 0);
+ return pDbgAs->LastAddr;
+}
+RT_EXPORT_SYMBOL(RTDbgAsLastAddr);
+
+
+RTDECL(uint32_t) RTDbgAsModuleCount(RTDBGAS hDbgAs)
+{
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, 0);
+ return pDbgAs->cModules;
+}
+RT_EXPORT_SYMBOL(RTDbgAsModuleCount);
+
+
+/**
+ * Common worker for RTDbgAsModuleLink and RTDbgAsModuleLinkSeg.
+ *
+ * @returns IPRT status code.
+ * @param pDbgAs Pointer to the address space instance data.
+ * @param hDbgMod The module to link.
+ * @param iSeg The segment to link or NIL if all.
+ * @param Addr The address we're linking it at.
+ * @param cb The size of what we're linking.
+ * @param pszName The name of the module.
+ * @param fFlags See RTDBGASLINK_FLAGS_*.
+ *
+ * @remarks The caller must have locked the address space for writing.
+ */
+int rtDbgAsModuleLinkCommon(PRTDBGASINT pDbgAs, RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg,
+ RTUINTPTR Addr, RTUINTPTR cb, const char *pszName, uint32_t fFlags)
+{
+ /*
+ * Check that the requested space is undisputed.
+ */
+ for (;;)
+ {
+ PRTDBGASMAP pAdjMod = (PRTDBGASMAP)RTAvlrUIntPtrGetBestFit(&pDbgAs->MapTree, Addr, false /* fAbove */);
+ if ( pAdjMod
+ && pAdjMod->Core.KeyLast >= Addr)
+ {
+ if (!(fFlags & RTDBGASLINK_FLAGS_REPLACE))
+ return VERR_ADDRESS_CONFLICT;
+ rtDbgAsModuleUnlinkByMap(pDbgAs, pAdjMod);
+ continue;
+ }
+ pAdjMod = (PRTDBGASMAP)RTAvlrUIntPtrGetBestFit(&pDbgAs->MapTree, Addr, true /* fAbove */);
+ if ( pAdjMod
+ && pAdjMod->Core.Key <= Addr + cb - 1)
+ {
+ if (!(fFlags & RTDBGASLINK_FLAGS_REPLACE))
+ return VERR_ADDRESS_CONFLICT;
+ rtDbgAsModuleUnlinkByMap(pDbgAs, pAdjMod);
+ continue;
+ }
+ break;
+ }
+
+ /*
+ * First, create or find the module table entry.
+ */
+ PRTDBGASMOD pMod = (PRTDBGASMOD)RTAvlPVGet(&pDbgAs->ModTree, hDbgMod);
+ if (!pMod)
+ {
+ /*
+ * Ok, we need a new entry. Grow the table if necessary.
+ */
+ if (!(pDbgAs->cModules % 32))
+ {
+ void *pvNew = RTMemRealloc(pDbgAs->papModules, sizeof(pDbgAs->papModules[0]) * (pDbgAs->cModules + 32));
+ if (!pvNew)
+ return VERR_NO_MEMORY;
+ pDbgAs->papModules = (PRTDBGASMOD *)pvNew;
+ }
+ pMod = (PRTDBGASMOD)RTMemAlloc(sizeof(*pMod));
+ if (!pMod)
+ return VERR_NO_MEMORY;
+ pMod->Core.Key = hDbgMod;
+ pMod->pMapHead = NULL;
+ pMod->pNextName = NULL;
+ if (RT_UNLIKELY(!RTAvlPVInsert(&pDbgAs->ModTree, &pMod->Core)))
+ {
+ AssertFailed();
+ pDbgAs->cModules--;
+ RTMemFree(pMod);
+ return VERR_INTERNAL_ERROR;
+ }
+ pMod->iOrdinal = pDbgAs->cModules;
+ pDbgAs->papModules[pDbgAs->cModules] = pMod;
+ pDbgAs->cModules++;
+ RTDbgModRetain(hDbgMod);
+
+ /*
+ * Add it to the name space.
+ */
+ PRTDBGASNAME pName = (PRTDBGASNAME)RTStrSpaceGet(&pDbgAs->NameSpace, pszName);
+ if (!pName)
+ {
+ size_t cchName = strlen(pszName);
+ pName = (PRTDBGASNAME)RTMemAlloc(sizeof(*pName) + cchName + 1);
+ if (!pName)
+ {
+ RTDbgModRelease(hDbgMod);
+ pDbgAs->cModules--;
+ RTAvlPVRemove(&pDbgAs->ModTree, hDbgMod);
+ RTMemFree(pMod);
+ return VERR_NO_MEMORY;
+ }
+ pName->StrCore.cchString = cchName;
+ pName->StrCore.pszString = (char *)memcpy(pName + 1, pszName, cchName + 1);
+ pName->pHead = pMod;
+ if (!RTStrSpaceInsert(&pDbgAs->NameSpace, &pName->StrCore))
+ AssertFailed();
+ }
+ else
+ {
+ /* quick, but unfair. */
+ pMod->pNextName = pName->pHead;
+ pName->pHead = pMod;
+ }
+ }
+
+ /*
+ * Create a mapping node.
+ */
+ int rc;
+ PRTDBGASMAP pMap = (PRTDBGASMAP)RTMemAlloc(sizeof(*pMap));
+ if (pMap)
+ {
+ pMap->Core.Key = Addr;
+ pMap->Core.KeyLast = Addr + cb - 1;
+ pMap->pMod = pMod;
+ pMap->iSeg = iSeg;
+ if (RTAvlrUIntPtrInsert(&pDbgAs->MapTree, &pMap->Core))
+ {
+ PRTDBGASMAP *pp = &pMod->pMapHead;
+ while (*pp && (*pp)->Core.Key < Addr)
+ pp = &(*pp)->pNext;
+ pMap->pNext = *pp;
+ *pp = pMap;
+ return VINF_SUCCESS;
+ }
+
+ AssertFailed();
+ RTMemFree(pMap);
+ rc = VERR_ADDRESS_CONFLICT;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ /*
+ * Unlink the module if this was the only mapping.
+ */
+ if (!pMod->pMapHead)
+ rtDbgAsModuleUnlinkMod(pDbgAs, pMod);
+ return rc;
+}
+
+
+RTDECL(int) RTDbgAsModuleLink(RTDBGAS hDbgAs, RTDBGMOD hDbgMod, RTUINTPTR ImageAddr, uint32_t fFlags)
+{
+ /*
+ * Validate input.
+ */
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE);
+ const char *pszName = RTDbgModName(hDbgMod);
+ if (!pszName)
+ return VERR_INVALID_HANDLE;
+ RTUINTPTR cb = RTDbgModImageSize(hDbgMod);
+ if (!cb)
+ return VERR_OUT_OF_RANGE;
+ if ( ImageAddr < pDbgAs->FirstAddr
+ || ImageAddr > pDbgAs->LastAddr
+ || ImageAddr + cb - 1 < pDbgAs->FirstAddr
+ || ImageAddr + cb - 1 > pDbgAs->LastAddr
+ || ImageAddr + cb - 1 < ImageAddr)
+ return VERR_OUT_OF_RANGE;
+ AssertReturn(!(fFlags & ~RTDBGASLINK_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ /*
+ * Invoke worker common with RTDbgAsModuleLinkSeg.
+ */
+ RTDBGAS_LOCK_WRITE(pDbgAs);
+ int rc = rtDbgAsModuleLinkCommon(pDbgAs, hDbgMod, NIL_RTDBGSEGIDX, ImageAddr, cb, pszName, fFlags);
+ RTDBGAS_UNLOCK_WRITE(pDbgAs);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgAsModuleLink);
+
+
+RTDECL(int) RTDbgAsModuleLinkSeg(RTDBGAS hDbgAs, RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR SegAddr, uint32_t fFlags)
+{
+ /*
+ * Validate input.
+ */
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE);
+ const char *pszName = RTDbgModName(hDbgMod);
+ if (!pszName)
+ return VERR_INVALID_HANDLE;
+ RTUINTPTR cb = RTDbgModSegmentSize(hDbgMod, iSeg);
+ if (!cb)
+ return VERR_OUT_OF_RANGE;
+ if ( SegAddr < pDbgAs->FirstAddr
+ || SegAddr > pDbgAs->LastAddr
+ || SegAddr + cb - 1 < pDbgAs->FirstAddr
+ || SegAddr + cb - 1 > pDbgAs->LastAddr
+ || SegAddr + cb - 1 < SegAddr)
+ return VERR_OUT_OF_RANGE;
+ AssertReturn(!(fFlags & ~RTDBGASLINK_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ /*
+ * Invoke worker common with RTDbgAsModuleLinkSeg.
+ */
+ RTDBGAS_LOCK_WRITE(pDbgAs);
+ int rc = rtDbgAsModuleLinkCommon(pDbgAs, hDbgMod, iSeg, SegAddr, cb, pszName, fFlags);
+ RTDBGAS_UNLOCK_WRITE(pDbgAs);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgAsModuleLinkSeg);
+
+
+/**
+ * Worker for RTDbgAsModuleUnlink, RTDbgAsModuleUnlinkByAddr and rtDbgAsModuleLinkCommon.
+ *
+ * @param pDbgAs Pointer to the address space instance data.
+ * @param pMod The module to unlink.
+ *
+ * @remarks The caller must have locked the address space for writing.
+ */
+static void rtDbgAsModuleUnlinkMod(PRTDBGASINT pDbgAs, PRTDBGASMOD pMod)
+{
+ Assert(!pMod->pMapHead);
+
+ /*
+ * Unlink it from the name.
+ */
+ const char *pszName = RTDbgModName((RTDBGMOD)pMod->Core.Key);
+ PRTDBGASNAME pName = (PRTDBGASNAME)RTStrSpaceGet(&pDbgAs->NameSpace, pszName);
+ AssertReturnVoid(pName);
+
+ if (pName->pHead == pMod)
+ pName->pHead = pMod->pNextName;
+ else
+ for (PRTDBGASMOD pCur = pName->pHead; pCur; pCur = pCur->pNextName)
+ if (pCur->pNextName == pMod)
+ {
+ pCur->pNextName = pMod->pNextName;
+ break;
+ }
+ pMod->pNextName = NULL;
+
+ /*
+ * Free the name if this was the last reference to it.
+ */
+ if (!pName->pHead)
+ {
+ pName = (PRTDBGASNAME)RTStrSpaceRemove(&pDbgAs->NameSpace, pName->StrCore.pszString);
+ Assert(pName);
+ RTMemFree(pName);
+ }
+
+ /*
+ * Remove it from the module handle tree.
+ */
+ PAVLPVNODECORE pNode = RTAvlPVRemove(&pDbgAs->ModTree, pMod->Core.Key);
+ Assert(pNode == &pMod->Core); NOREF(pNode);
+
+ /*
+ * Remove it from the module table by replacing it by the last entry.
+ */
+ pDbgAs->cModules--;
+ uint32_t iMod = pMod->iOrdinal;
+ Assert(iMod <= pDbgAs->cModules);
+ if (iMod != pDbgAs->cModules)
+ {
+ PRTDBGASMOD pTailMod = pDbgAs->papModules[pDbgAs->cModules];
+ pTailMod->iOrdinal = iMod;
+ pDbgAs->papModules[iMod] = pTailMod;
+ }
+ pMod->iOrdinal = UINT32_MAX;
+
+ /*
+ * Free it.
+ */
+ RTMemFree(pMod);
+}
+
+
+/**
+ * Worker for RTDbgAsModuleUnlink and RTDbgAsModuleUnlinkByAddr.
+ *
+ * @param pDbgAs Pointer to the address space instance data.
+ * @param pMap The map to unlink and free.
+ *
+ * @remarks The caller must have locked the address space for writing.
+ */
+static void rtDbgAsModuleUnlinkMap(PRTDBGASINT pDbgAs, PRTDBGASMAP pMap)
+{
+ /* remove from the tree */
+ PAVLRUINTPTRNODECORE pNode = RTAvlrUIntPtrRemove(&pDbgAs->MapTree, pMap->Core.Key);
+ Assert(pNode == &pMap->Core); NOREF(pNode);
+
+ /* unlink */
+ PRTDBGASMOD pMod = pMap->pMod;
+ if (pMod->pMapHead == pMap)
+ pMod->pMapHead = pMap->pNext;
+ else
+ {
+ bool fFound = false;
+ for (PRTDBGASMAP pCur = pMod->pMapHead; pCur; pCur = pCur->pNext)
+ if (pCur->pNext == pMap)
+ {
+ pCur->pNext = pMap->pNext;
+ fFound = true;
+ break;
+ }
+ Assert(fFound);
+ }
+
+ /* free it */
+ pMap->Core.Key = pMap->Core.KeyLast = 0;
+ pMap->pNext = NULL;
+ pMap->pMod = NULL;
+ RTMemFree(pMap);
+}
+
+
+/**
+ * Worker for RTDbgAsModuleUnlinkByAddr and rtDbgAsModuleLinkCommon that
+ * unlinks a single mapping and releases the module if it's the last one.
+ *
+ * @param pDbgAs The address space instance.
+ * @param pMap The mapping to unlink.
+ *
+ * @remarks The caller must have locked the address space for writing.
+ */
+static void rtDbgAsModuleUnlinkByMap(PRTDBGASINT pDbgAs, PRTDBGASMAP pMap)
+{
+ /*
+ * Unlink it from the address space.
+ * Unlink the module as well if it's the last mapping it has.
+ */
+ PRTDBGASMOD pMod = pMap->pMod;
+ rtDbgAsModuleUnlinkMap(pDbgAs, pMap);
+ if (!pMod->pMapHead)
+ rtDbgAsModuleUnlinkMod(pDbgAs, pMod);
+}
+
+
+RTDECL(int) RTDbgAsModuleUnlink(RTDBGAS hDbgAs, RTDBGMOD hDbgMod)
+{
+ /*
+ * Validate input.
+ */
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE);
+ if (hDbgMod == NIL_RTDBGMOD)
+ return VINF_SUCCESS;
+
+ RTDBGAS_LOCK_WRITE(pDbgAs);
+ PRTDBGASMOD pMod = (PRTDBGASMOD)RTAvlPVGet(&pDbgAs->ModTree, hDbgMod);
+ if (!pMod)
+ {
+ RTDBGAS_UNLOCK_WRITE(pDbgAs);
+ return VERR_NOT_FOUND;
+ }
+
+ /*
+ * Unmap all everything and release the module.
+ */
+ while (pMod->pMapHead)
+ rtDbgAsModuleUnlinkMap(pDbgAs, pMod->pMapHead);
+ rtDbgAsModuleUnlinkMod(pDbgAs, pMod);
+
+ RTDBGAS_UNLOCK_WRITE(pDbgAs);
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTDbgAsModuleUnlink);
+
+
+RTDECL(int) RTDbgAsModuleUnlinkByAddr(RTDBGAS hDbgAs, RTUINTPTR Addr)
+{
+ /*
+ * Validate input.
+ */
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE);
+
+ RTDBGAS_LOCK_WRITE(pDbgAs);
+ PRTDBGASMAP pMap = (PRTDBGASMAP)RTAvlrUIntPtrRangeGet(&pDbgAs->MapTree, Addr);
+ if (!pMap)
+ {
+ RTDBGAS_UNLOCK_WRITE(pDbgAs);
+ return VERR_NOT_FOUND;
+ }
+
+ /*
+ * Hand it to
+ */
+ rtDbgAsModuleUnlinkByMap(pDbgAs, pMap);
+
+ RTDBGAS_UNLOCK_WRITE(pDbgAs);
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTDbgAsModuleUnlinkByAddr);
+
+
+RTDECL(RTDBGMOD) RTDbgAsModuleByIndex(RTDBGAS hDbgAs, uint32_t iModule)
+{
+ /*
+ * Validate input.
+ */
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, NIL_RTDBGMOD);
+
+ RTDBGAS_LOCK_READ(pDbgAs);
+ if (iModule >= pDbgAs->cModules)
+ {
+ RTDBGAS_UNLOCK_READ(pDbgAs);
+ return NIL_RTDBGMOD;
+ }
+
+ /*
+ * Get, retain and return it.
+ */
+ RTDBGMOD hMod = (RTDBGMOD)pDbgAs->papModules[iModule]->Core.Key;
+ RTDbgModRetain(hMod);
+
+ RTDBGAS_UNLOCK_READ(pDbgAs);
+ return hMod;
+}
+RT_EXPORT_SYMBOL(RTDbgAsModuleByIndex);
+
+
+RTDECL(int) RTDbgAsModuleByAddr(RTDBGAS hDbgAs, RTUINTPTR Addr, PRTDBGMOD phMod, PRTUINTPTR pAddr, PRTDBGSEGIDX piSeg)
+{
+ /*
+ * Validate input.
+ */
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE);
+
+ RTDBGAS_LOCK_READ(pDbgAs);
+ PRTDBGASMAP pMap = (PRTDBGASMAP)RTAvlrUIntPtrRangeGet(&pDbgAs->MapTree, Addr);
+ if (!pMap)
+ {
+ RTDBGAS_UNLOCK_READ(pDbgAs);
+ return VERR_NOT_FOUND;
+ }
+
+ /*
+ * Set up the return values.
+ */
+ if (phMod)
+ {
+ RTDBGMOD hMod = (RTDBGMOD)pMap->pMod->Core.Key;
+ RTDbgModRetain(hMod);
+ *phMod = hMod;
+ }
+ if (pAddr)
+ *pAddr = pMap->Core.Key;
+ if (piSeg)
+ *piSeg = pMap->iSeg;
+
+ RTDBGAS_UNLOCK_READ(pDbgAs);
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTDbgAsModuleByAddr);
+
+
+RTDECL(int) RTDbgAsModuleByName(RTDBGAS hDbgAs, const char *pszName, uint32_t iName, PRTDBGMOD phMod)
+{
+ /*
+ * Validate input.
+ */
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE);
+ AssertPtrReturn(phMod, VERR_INVALID_POINTER);
+
+ RTDBGAS_LOCK_READ(pDbgAs);
+ PRTDBGASNAME pName = (PRTDBGASNAME)RTStrSpaceGet(&pDbgAs->NameSpace, pszName);
+ if (!pName)
+ {
+ RTDBGAS_UNLOCK_READ(pDbgAs);
+ return VERR_NOT_FOUND;
+ }
+
+ PRTDBGASMOD pMod = pName->pHead;
+ while (iName-- > 0)
+ {
+ pMod = pMod->pNextName;
+ if (!pMod)
+ {
+ RTDBGAS_UNLOCK_READ(pDbgAs);
+ return VERR_OUT_OF_RANGE;
+ }
+ }
+
+ /*
+ * Get, retain and return it.
+ */
+ RTDBGMOD hMod = (RTDBGMOD)pMod->Core.Key;
+ RTDbgModRetain(hMod);
+ *phMod = hMod;
+
+ RTDBGAS_UNLOCK_READ(pDbgAs);
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTDbgAsModuleByName);
+
+
+RTDECL(int) RTDbgAsModuleQueryMapByIndex(RTDBGAS hDbgAs, uint32_t iModule, PRTDBGASMAPINFO paMappings, uint32_t *pcMappings, uint32_t fFlags)
+{
+ /*
+ * Validate input.
+ */
+ uint32_t const cMappings = *pcMappings;
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE);
+ AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
+
+ RTDBGAS_LOCK_READ(pDbgAs);
+ if (iModule >= pDbgAs->cModules)
+ {
+ RTDBGAS_UNLOCK_READ(pDbgAs);
+ return VERR_OUT_OF_RANGE;
+ }
+
+ /*
+ * Copy the mapping information about the module.
+ */
+ int rc = VINF_SUCCESS;
+ PRTDBGASMAP pMap = pDbgAs->papModules[iModule]->pMapHead;
+ uint32_t cMaps = 0;
+ while (pMap)
+ {
+ if (cMaps >= cMappings)
+ {
+ rc = VINF_BUFFER_OVERFLOW;
+ break;
+ }
+ paMappings[cMaps].Address = pMap->Core.Key;
+ paMappings[cMaps].iSeg = pMap->iSeg;
+ cMaps++;
+ pMap = pMap->pNext;
+ }
+
+ RTDBGAS_UNLOCK_READ(pDbgAs);
+ *pcMappings = cMaps;
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgAsModuleQueryMapByIndex);
+
+
+/**
+ * Internal worker that looks up and retains a module.
+ *
+ * @returns Module handle, NIL_RTDBGMOD if not found.
+ * @param pDbgAs The address space instance data.
+ * @param Addr Address within the module.
+ * @param piSeg where to return the segment index.
+ * @param poffSeg Where to return the segment offset.
+ * @param pMapAddr The mapping address (RTDBGASMAP::Core.Key).
+ */
+DECLINLINE(RTDBGMOD) rtDbgAsModuleByAddr(PRTDBGASINT pDbgAs, RTUINTPTR Addr, PRTDBGSEGIDX piSeg, PRTUINTPTR poffSeg, PRTUINTPTR pMapAddr)
+{
+ RTDBGMOD hMod = NIL_RTDBGMOD;
+
+ RTDBGAS_LOCK_READ(pDbgAs);
+ PRTDBGASMAP pMap = (PRTDBGASMAP)RTAvlrUIntPtrRangeGet(&pDbgAs->MapTree, Addr);
+ if (pMap)
+ {
+ hMod = (RTDBGMOD)pMap->pMod->Core.Key;
+ RTDbgModRetain(hMod);
+ *piSeg = pMap->iSeg != NIL_RTDBGSEGIDX ? pMap->iSeg : RTDBGSEGIDX_RVA;
+ *poffSeg = Addr - pMap->Core.Key;
+ if (pMapAddr)
+ *pMapAddr = pMap->Core.Key;
+ }
+ RTDBGAS_UNLOCK_READ(pDbgAs);
+
+ return hMod;
+}
+
+
+/**
+ * Adjusts the address to correspond to the mapping of the module/segment.
+ *
+ * @param pAddr The address to adjust (in/out).
+ * @param iSeg The related segment.
+ * @param hDbgMod The module handle.
+ * @param MapAddr The mapping address.
+ * @param iMapSeg The segment that's mapped, NIL_RTDBGSEGIDX or
+ * RTDBGSEGIDX_RVA if the whole module is mapped here.
+ */
+DECLINLINE(void) rtDbgAsAdjustAddressByMapping(PRTUINTPTR pAddr, RTDBGSEGIDX iSeg,
+ RTDBGMOD hDbgMod, RTUINTPTR MapAddr, RTDBGSEGIDX iMapSeg)
+{
+ if (iSeg == RTDBGSEGIDX_ABS)
+ return;
+
+ if (iSeg == RTDBGSEGIDX_RVA)
+ {
+ if ( iMapSeg == RTDBGSEGIDX_RVA
+ || iMapSeg == NIL_RTDBGSEGIDX)
+ *pAddr += MapAddr;
+ else
+ {
+ RTUINTPTR SegRva = RTDbgModSegmentRva(hDbgMod, iMapSeg);
+ AssertReturnVoid(SegRva != RTUINTPTR_MAX);
+ AssertMsg(SegRva <= *pAddr, ("SegRva=%RTptr *pAddr=%RTptr\n", SegRva, *pAddr));
+ *pAddr += MapAddr - SegRva;
+ }
+ }
+ else
+ {
+ if ( iMapSeg != RTDBGSEGIDX_RVA
+ && iMapSeg != NIL_RTDBGSEGIDX)
+ {
+ Assert(iMapSeg == iSeg);
+ *pAddr += MapAddr;
+ }
+ else
+ {
+ RTUINTPTR SegRva = RTDbgModSegmentRva(hDbgMod, iSeg);
+ AssertReturnVoid(SegRva != RTUINTPTR_MAX);
+ *pAddr += MapAddr + SegRva;
+ }
+ }
+}
+
+
+/**
+ * Adjusts the symbol value to correspond to the mapping of the module/segment.
+ *
+ * @param pSymbol The returned symbol info.
+ * @param hDbgMod The module handle.
+ * @param MapAddr The mapping address.
+ * @param iMapSeg The segment that's mapped, NIL_RTDBGSEGIDX if the
+ * whole module is mapped here.
+ */
+DECLINLINE(void) rtDbgAsAdjustSymbolValue(PRTDBGSYMBOL pSymbol, RTDBGMOD hDbgMod, RTUINTPTR MapAddr, RTDBGSEGIDX iMapSeg)
+{
+ Assert(pSymbol->iSeg != NIL_RTDBGSEGIDX);
+ Assert(pSymbol->offSeg == pSymbol->Value);
+ rtDbgAsAdjustAddressByMapping(&pSymbol->Value, pSymbol->iSeg, hDbgMod, MapAddr, iMapSeg);
+}
+
+
+/**
+ * Adjusts the line number address to correspond to the mapping of the module/segment.
+ *
+ * @param pLine The returned line number info.
+ * @param hDbgMod The module handle.
+ * @param MapAddr The mapping address.
+ * @param iMapSeg The segment that's mapped, NIL_RTDBGSEGIDX if the
+ * whole module is mapped here.
+ */
+DECLINLINE(void) rtDbgAsAdjustLineAddress(PRTDBGLINE pLine, RTDBGMOD hDbgMod, RTUINTPTR MapAddr, RTDBGSEGIDX iMapSeg)
+{
+ Assert(pLine->iSeg != NIL_RTDBGSEGIDX);
+ Assert(pLine->offSeg == pLine->Address);
+ rtDbgAsAdjustAddressByMapping(&pLine->Address, pLine->iSeg, hDbgMod, MapAddr, iMapSeg);
+}
+
+
+RTDECL(int) RTDbgAsSymbolAdd(RTDBGAS hDbgAs, const char *pszSymbol, RTUINTPTR Addr, RTUINTPTR cb, uint32_t fFlags, uint32_t *piOrdinal)
+{
+ /*
+ * Validate input and resolve the address.
+ */
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE);
+
+ RTDBGSEGIDX iSeg = NIL_RTDBGSEGIDX; /* shut up gcc */
+ RTUINTPTR offSeg = 0;
+ RTDBGMOD hMod = rtDbgAsModuleByAddr(pDbgAs, Addr, &iSeg, &offSeg, NULL);
+ if (hMod == NIL_RTDBGMOD)
+ return VERR_NOT_FOUND;
+
+ /*
+ * Forward the call.
+ */
+ int rc = RTDbgModSymbolAdd(hMod, pszSymbol, iSeg, offSeg, cb, fFlags, piOrdinal);
+ RTDbgModRelease(hMod);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgAsSymbolAdd);
+
+
+/**
+ * Creates a snapshot of the module table on the temporary heap.
+ *
+ * The caller must release all the module handles before freeing the table
+ * using RTMemTmpFree.
+ *
+ * @returns Module table snaphot.
+ * @param pDbgAs The address space instance data.
+ * @param pcModules Where to return the number of modules.
+ */
+static PRTDBGMOD rtDbgAsSnapshotModuleTable(PRTDBGASINT pDbgAs, uint32_t *pcModules)
+{
+ RTDBGAS_LOCK_READ(pDbgAs);
+
+ uint32_t iMod = *pcModules = pDbgAs->cModules;
+ PRTDBGMOD pahModules = (PRTDBGMOD)RTMemTmpAlloc(sizeof(pahModules[0]) * RT_MAX(iMod, 1));
+ if (pahModules)
+ {
+ while (iMod-- > 0)
+ {
+ RTDBGMOD hMod = (RTDBGMOD)pDbgAs->papModules[iMod]->Core.Key;
+ pahModules[iMod] = hMod;
+ RTDbgModRetain(hMod);
+ }
+ }
+
+ RTDBGAS_UNLOCK_READ(pDbgAs);
+ return pahModules;
+}
+
+
+RTDECL(int) RTDbgAsSymbolByAddr(RTDBGAS hDbgAs, RTUINTPTR Addr, uint32_t fFlags,
+ PRTINTPTR poffDisp, PRTDBGSYMBOL pSymbol, PRTDBGMOD phMod)
+{
+ /*
+ * Validate input and resolve the address.
+ */
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE);
+ if (phMod)
+ *phMod = NIL_RTDBGMOD;
+
+ RTDBGSEGIDX iSeg = NIL_RTDBGSEGIDX; /* shut up gcc */
+ RTUINTPTR offSeg = 0;
+ RTUINTPTR MapAddr = 0;
+ RTDBGMOD hMod = rtDbgAsModuleByAddr(pDbgAs, Addr, &iSeg, &offSeg, &MapAddr);
+ if (hMod == NIL_RTDBGMOD)
+ {
+ /*
+ * Check for absolute symbols. Requires iterating all modules.
+ */
+ if (fFlags & RTDBGSYMADDR_FLAGS_SKIP_ABS)
+ return VERR_NOT_FOUND;
+
+ uint32_t cModules;
+ PRTDBGMOD pahModules = rtDbgAsSnapshotModuleTable(pDbgAs, &cModules);
+ if (!pahModules)
+ return VERR_NO_TMP_MEMORY;
+
+ int rc;
+ RTINTPTR offBestDisp = RTINTPTR_MAX;
+ uint32_t iBest = UINT32_MAX;
+ for (uint32_t i = 0; i < cModules; i++)
+ {
+ RTINTPTR offDisp;
+ rc = RTDbgModSymbolByAddr(pahModules[i], RTDBGSEGIDX_ABS, Addr, fFlags, &offDisp, pSymbol);
+ if (RT_SUCCESS(rc) && RT_ABS(offDisp) < offBestDisp)
+ {
+ offBestDisp = RT_ABS(offDisp);
+ iBest = i;
+ }
+ }
+
+ if (iBest == UINT32_MAX)
+ rc = VERR_NOT_FOUND;
+ else
+ {
+ hMod = pahModules[iBest];
+ rc = RTDbgModSymbolByAddr(hMod, RTDBGSEGIDX_ABS, Addr, fFlags, poffDisp, pSymbol);
+ if (RT_SUCCESS(rc))
+ {
+ rtDbgAsAdjustSymbolValue(pSymbol, hMod, MapAddr, iSeg);
+ if (phMod)
+ RTDbgModRetain(*phMod = hMod);
+ }
+ }
+
+ for (uint32_t i = 0; i < cModules; i++)
+ RTDbgModRelease(pahModules[i]);
+ RTMemTmpFree(pahModules);
+ return rc;
+ }
+
+ /*
+ * Forward the call.
+ */
+ int rc = RTDbgModSymbolByAddr(hMod, iSeg, offSeg, fFlags, poffDisp, pSymbol);
+ if (RT_SUCCESS(rc))
+ rtDbgAsAdjustSymbolValue(pSymbol, hMod, MapAddr, iSeg);
+ if (phMod)
+ *phMod = hMod;
+ else
+ RTDbgModRelease(hMod);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgAsSymbolByAddr);
+
+
+RTDECL(int) RTDbgAsSymbolByAddrA(RTDBGAS hDbgAs, RTUINTPTR Addr, uint32_t fFlags,
+ PRTINTPTR poffDisp, PRTDBGSYMBOL *ppSymInfo, PRTDBGMOD phMod)
+{
+ /*
+ * Validate input and resolve the address.
+ */
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE);
+
+ RTDBGSEGIDX iSeg = NIL_RTDBGSEGIDX;
+ RTUINTPTR offSeg = 0;
+ RTUINTPTR MapAddr = 0;
+ RTDBGMOD hMod = rtDbgAsModuleByAddr(pDbgAs, Addr, &iSeg, &offSeg, &MapAddr);
+ if (hMod == NIL_RTDBGMOD)
+ {
+ if (phMod)
+ *phMod = NIL_RTDBGMOD;
+ return VERR_NOT_FOUND;
+ }
+
+ /*
+ * Forward the call.
+ */
+ int rc = RTDbgModSymbolByAddrA(hMod, iSeg, offSeg, fFlags, poffDisp, ppSymInfo);
+ if (RT_SUCCESS(rc))
+ rtDbgAsAdjustSymbolValue(*ppSymInfo, hMod, MapAddr, iSeg);
+ if (phMod)
+ *phMod = hMod;
+ else
+ RTDbgModRelease(hMod);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgAsSymbolByAddrA);
+
+
+/**
+ * Attempts to find a mapping of the specified symbol/module and
+ * adjust it's Value field accordingly.
+ *
+ * @returns true / false success indicator.
+ * @param pDbgAs The address space.
+ * @param hDbgMod The module handle.
+ * @param pSymbol The symbol info.
+ */
+static bool rtDbgAsFindMappingAndAdjustSymbolValue(PRTDBGASINT pDbgAs, RTDBGMOD hDbgMod, PRTDBGSYMBOL pSymbol)
+{
+ /*
+ * Absolute segments needs no fixing.
+ */
+ RTDBGSEGIDX const iSeg = pSymbol->iSeg;
+ if (iSeg == RTDBGSEGIDX_ABS)
+ return true;
+
+ RTDBGAS_LOCK_READ(pDbgAs);
+
+ /*
+ * Lookup up the module by it's handle and iterate the mappings looking for one
+ * that either encompasses the entire module or the segment in question.
+ */
+ PRTDBGASMOD pMod = (PRTDBGASMOD)RTAvlPVGet(&pDbgAs->ModTree, hDbgMod);
+ if (pMod)
+ {
+ for (PRTDBGASMAP pMap = pMod->pMapHead; pMap; pMap = pMap->pNext)
+ {
+ /* Exact segment match or full-mapping. */
+ if ( iSeg == pMap->iSeg
+ || pMap->iSeg == NIL_RTDBGSEGIDX)
+ {
+ RTUINTPTR MapAddr = pMap->Core.Key;
+ RTDBGSEGIDX iMapSeg = pMap->iSeg;
+
+ RTDBGAS_UNLOCK_READ(pDbgAs);
+ rtDbgAsAdjustSymbolValue(pSymbol, hDbgMod, MapAddr, iMapSeg);
+ return true;
+ }
+
+ /* Symbol uses RVA and the mapping doesn't, see if it's in the mapped segment. */
+ if (iSeg == RTDBGSEGIDX_RVA)
+ {
+ Assert(pMap->iSeg != NIL_RTDBGSEGIDX);
+ RTUINTPTR SegRva = RTDbgModSegmentRva(hDbgMod, pMap->iSeg);
+ Assert(SegRva != RTUINTPTR_MAX);
+ RTUINTPTR cbSeg = RTDbgModSegmentSize(hDbgMod, pMap->iSeg);
+ if (SegRva - pSymbol->Value < cbSeg)
+ {
+ RTUINTPTR MapAddr = pMap->Core.Key;
+ RTDBGSEGIDX iMapSeg = pMap->iSeg;
+
+ RTDBGAS_UNLOCK_READ(pDbgAs);
+ rtDbgAsAdjustSymbolValue(pSymbol, hDbgMod, MapAddr, iMapSeg);
+ return true;
+ }
+ }
+ }
+ }
+ /* else: Unmapped while we were searching. */
+
+ RTDBGAS_UNLOCK_READ(pDbgAs);
+ return false;
+}
+
+
+RTDECL(int) RTDbgAsSymbolByName(RTDBGAS hDbgAs, const char *pszSymbol, PRTDBGSYMBOL pSymbol, PRTDBGMOD phMod)
+{
+ /*
+ * Validate input.
+ */
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pszSymbol, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSymbol, VERR_INVALID_POINTER);
+
+ /*
+ * Look for module pattern.
+ */
+ const char *pachModPat = NULL;
+ size_t cchModPat = 0;
+ const char *pszBang = strchr(pszSymbol, '!');
+ if (pszBang)
+ {
+ pachModPat = pszSymbol;
+ cchModPat = pszBang - pszSymbol;
+ pszSymbol = pszBang + 1;
+ if (!*pszSymbol)
+ return VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE;
+ /* Note! Zero length module -> no pattern -> escape for symbol with '!'. */
+ }
+
+ /*
+ * Iterate the modules, looking for the symbol.
+ */
+ uint32_t cModules;
+ PRTDBGMOD pahModules = rtDbgAsSnapshotModuleTable(pDbgAs, &cModules);
+ if (!pahModules)
+ return VERR_NO_TMP_MEMORY;
+
+ for (uint32_t i = 0; i < cModules; i++)
+ {
+ if ( cchModPat == 0
+ || RTStrSimplePatternNMatch(pachModPat, cchModPat, RTDbgModName(pahModules[i]), RTSTR_MAX))
+ {
+ int rc = RTDbgModSymbolByName(pahModules[i], pszSymbol, pSymbol);
+ if (RT_SUCCESS(rc))
+ {
+ if (rtDbgAsFindMappingAndAdjustSymbolValue(pDbgAs, pahModules[i], pSymbol))
+ {
+ if (phMod)
+ RTDbgModRetain(*phMod = pahModules[i]);
+ for (; i < cModules; i++)
+ RTDbgModRelease(pahModules[i]);
+ RTMemTmpFree(pahModules);
+ return rc;
+ }
+ }
+ }
+ RTDbgModRelease(pahModules[i]);
+ }
+
+ RTMemTmpFree(pahModules);
+ return VERR_SYMBOL_NOT_FOUND;
+}
+RT_EXPORT_SYMBOL(RTDbgAsSymbolByName);
+
+
+RTDECL(int) RTDbgAsSymbolByNameA(RTDBGAS hDbgAs, const char *pszSymbol, PRTDBGSYMBOL *ppSymbol, PRTDBGMOD phMod)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(ppSymbol, VERR_INVALID_POINTER);
+ *ppSymbol = NULL;
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pszSymbol, VERR_INVALID_POINTER);
+
+ /*
+ * Look for module pattern.
+ */
+ const char *pachModPat = NULL;
+ size_t cchModPat = 0;
+ const char *pszBang = strchr(pszSymbol, '!');
+ if (pszBang)
+ {
+ pachModPat = pszSymbol;
+ cchModPat = pszBang - pszSymbol;
+ pszSymbol = pszBang + 1;
+ if (!*pszSymbol)
+ return VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE;
+ /* Note! Zero length module -> no pattern -> escape for symbol with '!'. */
+ }
+
+ /*
+ * Iterate the modules, looking for the symbol.
+ */
+ uint32_t cModules;
+ PRTDBGMOD pahModules = rtDbgAsSnapshotModuleTable(pDbgAs, &cModules);
+ if (!pahModules)
+ return VERR_NO_TMP_MEMORY;
+
+ for (uint32_t i = 0; i < cModules; i++)
+ {
+ if ( cchModPat == 0
+ || RTStrSimplePatternNMatch(pachModPat, cchModPat, RTDbgModName(pahModules[i]), RTSTR_MAX))
+ {
+ int rc = RTDbgModSymbolByNameA(pahModules[i], pszSymbol, ppSymbol);
+ if (RT_SUCCESS(rc))
+ {
+ if (rtDbgAsFindMappingAndAdjustSymbolValue(pDbgAs, pahModules[i], *ppSymbol))
+ {
+ if (phMod)
+ RTDbgModRetain(*phMod = pahModules[i]);
+ for (; i < cModules; i++)
+ RTDbgModRelease(pahModules[i]);
+ RTMemTmpFree(pahModules);
+ return rc;
+ }
+ }
+ }
+ RTDbgModRelease(pahModules[i]);
+ }
+
+ RTMemTmpFree(pahModules);
+ return VERR_SYMBOL_NOT_FOUND;
+}
+RT_EXPORT_SYMBOL(RTDbgAsSymbolByNameA);
+
+
+RTDECL(int) RTDbgAsLineAdd(RTDBGAS hDbgAs, const char *pszFile, uint32_t uLineNo, RTUINTPTR Addr, uint32_t *piOrdinal)
+{
+ /*
+ * Validate input and resolve the address.
+ */
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE);
+
+ RTDBGSEGIDX iSeg = NIL_RTDBGSEGIDX; /* shut up gcc */
+ RTUINTPTR offSeg = 0; /* ditto */
+ RTDBGMOD hMod = rtDbgAsModuleByAddr(pDbgAs, Addr, &iSeg, &offSeg, NULL);
+ if (hMod == NIL_RTDBGMOD)
+ return VERR_NOT_FOUND;
+
+ /*
+ * Forward the call.
+ */
+ int rc = RTDbgModLineAdd(hMod, pszFile, uLineNo, iSeg, offSeg, piOrdinal);
+ RTDbgModRelease(hMod);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgAsLineAdd);
+
+
+RTDECL(int) RTDbgAsLineByAddr(RTDBGAS hDbgAs, RTUINTPTR Addr, PRTINTPTR poffDisp, PRTDBGLINE pLine, PRTDBGMOD phMod)
+{
+ /*
+ * Validate input and resolve the address.
+ */
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE);
+
+ RTDBGSEGIDX iSeg = NIL_RTDBGSEGIDX; /* shut up gcc */
+ RTUINTPTR offSeg = 0;
+ RTUINTPTR MapAddr = 0;
+ RTDBGMOD hMod = rtDbgAsModuleByAddr(pDbgAs, Addr, &iSeg, &offSeg, &MapAddr);
+ if (hMod == NIL_RTDBGMOD)
+ return VERR_NOT_FOUND;
+
+ /*
+ * Forward the call.
+ */
+ int rc = RTDbgModLineByAddr(hMod, iSeg, offSeg, poffDisp, pLine);
+ if (RT_SUCCESS(rc))
+ {
+ rtDbgAsAdjustLineAddress(pLine, hMod, MapAddr, iSeg);
+ if (phMod)
+ *phMod = hMod;
+ else
+ RTDbgModRelease(hMod);
+ }
+ else
+ RTDbgModRelease(hMod);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgAsLineByAddr);
+
+
+RTDECL(int) RTDbgAsLineByAddrA(RTDBGAS hDbgAs, RTUINTPTR Addr, PRTINTPTR poffDisp, PRTDBGLINE *ppLine, PRTDBGMOD phMod)
+{
+ /*
+ * Validate input and resolve the address.
+ */
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE);
+
+ RTDBGSEGIDX iSeg = NIL_RTDBGSEGIDX; /* shut up gcc */
+ RTUINTPTR offSeg = 0;
+ RTUINTPTR MapAddr = 0;
+ RTDBGMOD hMod = rtDbgAsModuleByAddr(pDbgAs, Addr, &iSeg, &offSeg, &MapAddr);
+ if (hMod == NIL_RTDBGMOD)
+ return VERR_NOT_FOUND;
+
+ /*
+ * Forward the call.
+ */
+ int rc = RTDbgModLineByAddrA(hMod, iSeg, offSeg, poffDisp, ppLine);
+ if (RT_SUCCESS(rc))
+ {
+ rtDbgAsAdjustLineAddress(*ppLine, hMod, MapAddr, iSeg);
+ if (phMod)
+ *phMod = hMod;
+ else
+ RTDbgModRelease(hMod);
+ }
+ else
+ RTDbgModRelease(hMod);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTDbgAsLineByAddrA);
+
diff --git a/src/VBox/Runtime/common/dbg/dbgcfg.cpp b/src/VBox/Runtime/common/dbg/dbgcfg.cpp
new file mode 100644
index 00000000..f680f123
--- /dev/null
+++ b/src/VBox/Runtime/common/dbg/dbgcfg.cpp
@@ -0,0 +1,2525 @@
+/* $Id: dbgcfg.cpp $ */
+/** @file
+ * IPRT - Debugging Configuration.
+ */
+
+/*
+ * Copyright (C) 2013-2023 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 *
+*********************************************************************************************************************************/
+#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/critsect.h>
+#include <iprt/ctype.h>
+#include <iprt/dir.h>
+#include <iprt/err.h>
+#include <iprt/env.h>
+#include <iprt/file.h>
+#ifdef IPRT_WITH_HTTP
+# include <iprt/http.h>
+#endif
+#include <iprt/list.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/process.h>
+#include <iprt/semaphore.h>
+#include <iprt/string.h>
+#include <iprt/uuid.h>
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * String list entry.
+ */
+typedef struct RTDBGCFGSTR
+{
+ /** List entry. */
+ RTLISTNODE ListEntry;
+ /** Domain specific flags. */
+ uint16_t fFlags;
+ /** The length of the string. */
+ uint16_t cch;
+ /** The string. */
+ char sz[1];
+} RTDBGCFGSTR;
+/** Pointer to a string list entry. */
+typedef RTDBGCFGSTR *PRTDBGCFGSTR;
+
+
+/**
+ * Configuration instance.
+ */
+typedef struct RTDBGCFGINT
+{
+ /** The magic value (RTDBGCFG_MAGIC). */
+ uint32_t u32Magic;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+ /** Flags, see RTDBGCFG_FLAGS_XXX. */
+ uint64_t fFlags;
+
+ /** List of paths to search for debug files and executable images. */
+ RTLISTANCHOR PathList;
+ /** List of debug file suffixes. */
+ RTLISTANCHOR SuffixList;
+ /** List of paths to search for source files. */
+ RTLISTANCHOR SrcPathList;
+
+#ifdef RT_OS_WINDOWS
+ /** The _NT_ALT_SYMBOL_PATH and _NT_SYMBOL_PATH combined. */
+ RTLISTANCHOR NtSymbolPathList;
+ /** The _NT_EXECUTABLE_PATH. */
+ RTLISTANCHOR NtExecutablePathList;
+ /** The _NT_SOURCE_PATH. */
+ RTLISTANCHOR NtSourcePath;
+#endif
+
+ /** Log callback function. */
+ PFNRTDBGCFGLOG pfnLogCallback;
+ /** User argument to pass to the log callback. */
+ void *pvLogUser;
+
+ /** Critical section protecting the instance data. */
+ RTCRITSECTRW CritSect;
+} *PRTDBGCFGINT;
+
+/**
+ * Mnemonics map entry for a 64-bit unsigned property value.
+ */
+typedef struct RTDBGCFGU64MNEMONIC
+{
+ /** The flags to set or clear. */
+ uint64_t fFlags;
+ /** The mnemonic. */
+ const char *pszMnemonic;
+ /** The length of the mnemonic. */
+ uint8_t cchMnemonic;
+ /** If @c true, the bits in fFlags will be set, if @c false they will be
+ * cleared. */
+ bool fSet;
+} RTDBGCFGU64MNEMONIC;
+/** Pointer to a read only mnemonic map entry for a uint64_t property. */
+typedef RTDBGCFGU64MNEMONIC const *PCRTDBGCFGU64MNEMONIC;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Validates a debug module handle and returns rc if not valid. */
+#define RTDBGCFG_VALID_RETURN_RC(pThis, rc) \
+ do { \
+ AssertPtrReturn((pThis), (rc)); \
+ AssertReturn((pThis)->u32Magic == RTDBGCFG_MAGIC, (rc)); \
+ AssertReturn((pThis)->cRefs > 0, (rc)); \
+ } while (0)
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Mnemonics map for RTDBGCFGPROP_FLAGS. */
+static const RTDBGCFGU64MNEMONIC g_aDbgCfgFlags[] =
+{
+ { RTDBGCFG_FLAGS_DEFERRED, RT_STR_TUPLE("deferred"), true },
+ { RTDBGCFG_FLAGS_DEFERRED, RT_STR_TUPLE("nodeferred"), false },
+ { RTDBGCFG_FLAGS_NO_SYM_SRV, RT_STR_TUPLE("symsrv"), false },
+ { RTDBGCFG_FLAGS_NO_SYM_SRV, RT_STR_TUPLE("nosymsrv"), true },
+ { RTDBGCFG_FLAGS_NO_SYSTEM_PATHS, RT_STR_TUPLE("syspaths"), false },
+ { RTDBGCFG_FLAGS_NO_SYSTEM_PATHS, RT_STR_TUPLE("nosyspaths"), true },
+ { RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH, RT_STR_TUPLE("rec"), false },
+ { RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH, RT_STR_TUPLE("norec"), true },
+ { RTDBGCFG_FLAGS_NO_RECURSIV_SRC_SEARCH, RT_STR_TUPLE("recsrc"), false },
+ { RTDBGCFG_FLAGS_NO_RECURSIV_SRC_SEARCH, RT_STR_TUPLE("norecsrc"), true },
+ { 0, NULL, 0, false }
+};
+
+
+/** Interesting bundle suffixes. */
+static const char * const g_apszBundleSuffixes[] =
+{
+ ".kext",
+ ".app",
+ ".framework",
+ ".component",
+ ".action",
+ ".caction",
+ ".bundle",
+ ".sourcebundle",
+ ".menu",
+ ".plugin",
+ ".ppp",
+ ".monitorpanel",
+ ".scripting",
+ ".prefPane",
+ ".qlgenerator",
+ ".brailledriver",
+ ".saver",
+ ".SpeechVoice",
+ ".SpeechRecognizer",
+ ".SpeechSynthesizer",
+ ".mdimporter",
+ ".spreporter",
+ ".xpc",
+ NULL
+};
+
+/** Debug bundle suffixes. (Same as above + .dSYM) */
+static const char * const g_apszDSymBundleSuffixes[] =
+{
+ ".dSYM",
+ ".kext.dSYM",
+ ".app.dSYM",
+ ".framework.dSYM",
+ ".component.dSYM",
+ ".action.dSYM",
+ ".caction.dSYM",
+ ".bundle.dSYM",
+ ".sourcebundle.dSYM",
+ ".menu.dSYM",
+ ".plugin.dSYM",
+ ".ppp.dSYM",
+ ".monitorpanel.dSYM",
+ ".scripting.dSYM",
+ ".prefPane.dSYM",
+ ".qlgenerator.dSYM",
+ ".brailledriver.dSYM",
+ ".saver.dSYM",
+ ".SpeechVoice.dSYM",
+ ".SpeechRecognizer.dSYM",
+ ".SpeechSynthesizer.dSYM",
+ ".mdimporter.dSYM",
+ ".spreporter.dSYM",
+ ".xpc.dSYM",
+ NULL
+};
+
+
+
+/**
+ * Runtime logging, level 1.
+ *
+ * @param pThis The debug config instance data.
+ * @param pszFormat The message format string.
+ * @param ... Arguments references in the format string.
+ */
+static void rtDbgCfgLog1(PRTDBGCFGINT pThis, const char *pszFormat, ...)
+{
+ if (LogIsEnabled() || (pThis && pThis->pfnLogCallback))
+ {
+ va_list va;
+ va_start(va, pszFormat);
+ char *pszMsg = RTStrAPrintf2V(pszFormat, va);
+ va_end(va);
+
+ Log(("RTDbgCfg: %s", pszMsg));
+ if (pThis && pThis->pfnLogCallback)
+ pThis->pfnLogCallback(pThis, 1, pszMsg, pThis->pvLogUser);
+ RTStrFree(pszMsg);
+ }
+}
+
+
+/**
+ * Runtime logging, level 2.
+ *
+ * @param pThis The debug config instance data.
+ * @param pszFormat The message format string.
+ * @param ... Arguments references in the format string.
+ */
+static void rtDbgCfgLog2(PRTDBGCFGINT pThis, const char *pszFormat, ...)
+{
+ if (LogIs2Enabled() || (pThis && pThis->pfnLogCallback))
+ {
+ va_list va;
+ va_start(va, pszFormat);
+ char *pszMsg = RTStrAPrintf2V(pszFormat, va);
+ va_end(va);
+
+ Log(("RTDbgCfg: %s", pszMsg));
+ if (pThis && pThis->pfnLogCallback)
+ pThis->pfnLogCallback(pThis, 2, pszMsg, pThis->pvLogUser);
+ RTStrFree(pszMsg);
+ }
+}
+
+
+/**
+ * Checks if the file system at the given path is case insensitive or not.
+ *
+ * @returns true / false
+ * @param pszPath The path to query about.
+ */
+static int rtDbgCfgIsFsCaseInsensitive(const char *pszPath)
+{
+ RTFSPROPERTIES Props;
+ int rc = RTFsQueryProperties(pszPath, &Props);
+ if (RT_FAILURE(rc))
+ return RT_OPSYS == RT_OPSYS_DARWIN
+ || RT_OPSYS == RT_OPSYS_DOS
+ || RT_OPSYS == RT_OPSYS_OS2
+ || RT_OPSYS == RT_OPSYS_NT
+ || RT_OPSYS == RT_OPSYS_WINDOWS;
+ return !Props.fCaseSensitive;
+}
+
+
+/**
+ * Worker that does case sensitive file/dir searching.
+ *
+ * @returns true / false.
+ * @param pszPath The path buffer containing an existing directory and
+ * at @a offLastComp the name we're looking for.
+ * RTPATH_MAX in size. On success, this last component
+ * will have the correct case. On failure, the last
+ * component is stripped off.
+ * @param offLastComp The offset of the last component (for chopping it
+ * off).
+ * @param enmType What kind of thing we're looking for.
+ */
+static bool rtDbgCfgIsXxxxAndFixCaseWorker(char *pszPath, size_t offLastComp, RTDIRENTRYTYPE enmType)
+{
+ /** @todo IPRT should generalize this so we can use host specific tricks to
+ * speed it up. */
+
+ char *pszName = &pszPath[offLastComp];
+
+ /* Return straight away if the name isn't case foldable. */
+ if (!RTStrIsCaseFoldable(pszName))
+ {
+ *pszName = '\0';
+ return false;
+ }
+
+ /*
+ * Try some simple case folding games.
+ */
+ RTStrToLower(pszName);
+ if (RTFileExists(pszPath))
+ return true;
+
+ RTStrToUpper(pszName);
+ if (RTFileExists(pszPath))
+ return true;
+
+ /*
+ * Open the directory and check each entry in it.
+ */
+ char chSaved = *pszName;
+ *pszName = '\0';
+
+ RTDIR hDir;
+ int rc = RTDirOpen(&hDir, pszPath);
+ if (RT_FAILURE(rc))
+ return false;
+
+ *pszName = chSaved;
+
+ for (;;)
+ {
+ /* Read the next entry. */
+ union
+ {
+ RTDIRENTRY Entry;
+ uint8_t ab[_4K];
+ } u;
+ size_t cbBuf = sizeof(u);
+ rc = RTDirRead(hDir, &u.Entry, &cbBuf);
+ if (RT_FAILURE(rc))
+ break;
+
+ if ( !RTStrICmp(pszName, u.Entry.szName)
+ && ( u.Entry.enmType == enmType
+ || u.Entry.enmType == RTDIRENTRYTYPE_UNKNOWN
+ || u.Entry.enmType == RTDIRENTRYTYPE_SYMLINK) )
+ {
+ strcpy(pszName, u.Entry.szName);
+ if (u.Entry.enmType != enmType)
+ RTDirQueryUnknownType(pszPath, true /*fFollowSymlinks*/, &u.Entry.enmType);
+ if (u.Entry.enmType == enmType)
+ {
+ RTDirClose(hDir);
+ return true;
+ }
+ }
+ }
+
+ RTDirClose(hDir);
+ *pszName = '\0';
+
+ return false;
+}
+
+
+/**
+ * Appends @a pszSubDir to @a pszPath and check whether it exists and is a
+ * directory.
+ *
+ * If @a fCaseInsensitive is set, we will do a case insensitive search for a
+ * matching sub directory.
+ *
+ * @returns true / false
+ * @param pszPath The path buffer containing an existing
+ * directory. RTPATH_MAX in size.
+ * @param pszSubDir The sub directory to append.
+ * @param fCaseInsensitive Whether case insensitive searching is required.
+ */
+static bool rtDbgCfgIsDirAndFixCase(char *pszPath, const char *pszSubDir, bool fCaseInsensitive)
+{
+ /* Save the length of the input path so we can restore it in the case
+ insensitive branch further down. */
+ size_t const cchPath = strlen(pszPath);
+
+ /*
+ * Append the sub directory and check if we got a hit.
+ */
+ int rc = RTPathAppend(pszPath, RTPATH_MAX, pszSubDir);
+ if (RT_FAILURE(rc))
+ return false;
+
+ if (RTDirExists(pszPath))
+ return true;
+
+ /*
+ * Do case insensitive lookup if requested.
+ */
+ if (fCaseInsensitive)
+ return rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, RTDIRENTRYTYPE_DIRECTORY);
+
+ pszPath[cchPath] = '\0';
+ return false;
+}
+
+
+/**
+ * Appends @a pszSubDir1 and @a pszSuffix to @a pszPath and check whether it
+ * exists and is a directory.
+ *
+ * If @a fCaseInsensitive is set, we will do a case insensitive search for a
+ * matching sub directory.
+ *
+ * @returns true / false
+ * @param pszPath The path buffer containing an existing
+ * directory. RTPATH_MAX in size.
+ * @param pszSubDir The sub directory to append.
+ * @param pszSuffix The suffix to append.
+ * @param fCaseInsensitive Whether case insensitive searching is required.
+ */
+static bool rtDbgCfgIsDirAndFixCase2(char *pszPath, const char *pszSubDir, const char *pszSuffix, bool fCaseInsensitive)
+{
+ Assert(!strpbrk(pszSuffix, ":/\\"));
+
+ /* Save the length of the input path so we can restore it in the case
+ insensitive branch further down. */
+ size_t const cchPath = strlen(pszPath);
+
+ /*
+ * Append the subdirectory and suffix, then check if we got a hit.
+ */
+ int rc = RTPathAppend(pszPath, RTPATH_MAX, pszSubDir);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTStrCat(pszPath, RTPATH_MAX, pszSuffix);
+ if (RT_SUCCESS(rc))
+ {
+ if (RTDirExists(pszPath))
+ return true;
+
+ /*
+ * Do case insensitive lookup if requested.
+ */
+ if (fCaseInsensitive)
+ return rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, RTDIRENTRYTYPE_DIRECTORY);
+ }
+ }
+
+ pszPath[cchPath] = '\0';
+ return false;
+}
+
+
+/**
+ * Appends @a pszFilename to @a pszPath and check whether it exists and is a
+ * directory.
+ *
+ * If @a fCaseInsensitive is set, we will do a case insensitive search for a
+ * matching filename.
+ *
+ * @returns true / false
+ * @param pszPath The path buffer containing an existing
+ * directory. RTPATH_MAX in size.
+ * @param pszFilename The filename to append.
+ * @param pszSuffix Optional filename suffix to append.
+ * @param fCaseInsensitive Whether case insensitive searching is required.
+ * @param fMsCompressed Whether to look for the MS compressed file name
+ * variant.
+ * @param pfProbablyCompressed This is set to true if a MS compressed
+ * filename variant is returned. Optional.
+ */
+static bool rtDbgCfgIsFileAndFixCase(char *pszPath, const char *pszFilename, const char *pszSuffix, bool fCaseInsensitive,
+ bool fMsCompressed, bool *pfProbablyCompressed)
+{
+ /* Save the length of the input path so we can restore it in the case
+ insensitive branch further down. */
+ size_t cchPath = strlen(pszPath);
+ if (pfProbablyCompressed)
+ *pfProbablyCompressed = false;
+
+ /*
+ * Append the filename and optionally suffix, then check if we got a hit.
+ */
+ int rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename);
+ if (RT_FAILURE(rc))
+ return false;
+ if (pszSuffix)
+ {
+ Assert(!fMsCompressed);
+ rc = RTStrCat(pszPath, RTPATH_MAX, pszSuffix);
+ if (RT_FAILURE(rc))
+ return false;
+ }
+
+ if (RTFileExists(pszPath))
+ return true;
+
+ /*
+ * Do case insensitive file lookup if requested.
+ */
+ if (fCaseInsensitive)
+ {
+ if (rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, RTDIRENTRYTYPE_FILE))
+ return true;
+ }
+
+ /*
+ * Look for MS compressed file if requested.
+ */
+ if ( fMsCompressed
+ && (unsigned char)pszFilename[strlen(pszFilename) - 1] < 0x7f)
+ {
+ pszPath[cchPath] = '\0';
+ rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename);
+ AssertRCReturn(rc, false);
+ pszPath[strlen(pszPath) - 1] = '_';
+
+ if (pfProbablyCompressed)
+ *pfProbablyCompressed = true;
+
+ if ( RTFileExists(pszPath)
+ || ( fCaseInsensitive
+ && rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, RTDIRENTRYTYPE_FILE) ))
+ return true;
+
+ if (pfProbablyCompressed)
+ *pfProbablyCompressed = false;
+ }
+
+ pszPath[cchPath] = '\0';
+ return false;
+}
+
+
+static int rtDbgCfgTryOpenDir(PRTDBGCFGINT pThis, char *pszPath, PRTPATHSPLIT pSplitFn, uint32_t fFlags,
+ PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
+{
+ int rcRet = VWRN_NOT_FOUND;
+ int rc2;
+
+ /* If the directory doesn't exist, just quit immediately.
+ Note! Our case insensitivity doesn't extend to the search dirs themselfs,
+ only to the bits under neath them. */
+ if (!RTDirExists(pszPath))
+ {
+ rtDbgCfgLog2(pThis, "Dir does not exist: '%s'\n", pszPath);
+ return rcRet;
+ }
+
+ /* Figure out whether we have to do a case sensitive search or not.
+ Note! As a simplification, we don't ask for case settings in each
+ directory under the user specified path, we assume the file
+ systems that mounted there have compatible settings. Faster
+ that way. */
+ bool const fCaseInsensitive = (fFlags & RTDBGCFG_O_CASE_INSENSITIVE)
+ && !rtDbgCfgIsFsCaseInsensitive(pszPath);
+
+ size_t const cchPath = strlen(pszPath);
+
+ /*
+ * Look for the file with less and less of the original path given.
+ */
+ for (unsigned i = RTPATH_PROP_HAS_ROOT_SPEC(pSplitFn->fProps); i < pSplitFn->cComps; i++)
+ {
+ pszPath[cchPath] = '\0';
+
+ rc2 = VINF_SUCCESS;
+ for (unsigned j = i; j < pSplitFn->cComps - 1U && RT_SUCCESS(rc2); j++)
+ if (!rtDbgCfgIsDirAndFixCase(pszPath, pSplitFn->apszComps[i], fCaseInsensitive))
+ rc2 = VERR_FILE_NOT_FOUND;
+
+ if (RT_SUCCESS(rc2))
+ {
+ if (rtDbgCfgIsFileAndFixCase(pszPath, pSplitFn->apszComps[pSplitFn->cComps - 1], NULL /*pszSuffix*/,
+ fCaseInsensitive, false, NULL))
+ {
+ rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
+ rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
+ if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
+ {
+ if (rc2 == VINF_CALLBACK_RETURN)
+ rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath);
+ else
+ rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
+ return rc2;
+ }
+ rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath);
+ if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
+ rcRet = rc2;
+ }
+ }
+ }
+
+ /*
+ * Do a recursive search if requested.
+ */
+ if ( (fFlags & RTDBGCFG_O_RECURSIVE)
+ && pThis
+ && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH) )
+ {
+ /** @todo Recursive searching will be done later. */
+ }
+
+ return rcRet;
+}
+
+static int rtDbgCfgUnpackMsCacheFile(PRTDBGCFGINT pThis, char *pszPath, const char *pszFilename)
+{
+ rtDbgCfgLog2(pThis, "Unpacking '%s'...\n", pszPath);
+
+ /*
+ * Duplicate the source file path, just for simplicity and restore the
+ * final character in the orignal. We cheerfully ignorining any
+ * possibility of multibyte UTF-8 sequences just like the caller did when
+ * setting it to '_'.
+ */
+ char *pszSrcArchive = RTStrDup(pszPath);
+ if (!pszSrcArchive)
+ return VERR_NO_STR_MEMORY;
+
+ pszPath[strlen(pszPath) - 1] = RT_C_TO_LOWER(pszFilename[strlen(pszFilename) - 1]);
+
+
+ /*
+ * Figuring out the argument list for the platform specific unpack util.
+ */
+#ifdef RT_OS_WINDOWS
+ RTPathChangeToDosSlashes(pszSrcArchive, false /*fForce*/);
+ RTPathChangeToDosSlashes(pszPath, false /*fForce*/);
+ const char *papszArgs[] =
+ {
+ "expand.exe",
+ pszSrcArchive,
+ pszPath,
+ NULL
+ };
+
+#else
+ char szExtractDir[RTPATH_MAX];
+ strcpy(szExtractDir, pszPath);
+ RTPathStripFilename(szExtractDir);
+
+ const char *papszArgs[] =
+ {
+ "cabextract",
+ "-L", /* Lower case extracted files. */
+ "-d", szExtractDir, /* Extraction path */
+ pszSrcArchive,
+ NULL
+ };
+#endif
+
+ /*
+ * Do the unpacking.
+ */
+ RTPROCESS hChild;
+ int rc = RTProcCreate(papszArgs[0], papszArgs, RTENV_DEFAULT,
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ RTPROC_FLAGS_NO_WINDOW | RTPROC_FLAGS_HIDDEN | RTPROC_FLAGS_SEARCH_PATH,
+#else
+ RTPROC_FLAGS_SEARCH_PATH,
+#endif
+ &hChild);
+ if (RT_SUCCESS(rc))
+ {
+ RTPROCSTATUS ProcStatus;
+ rc = RTProcWait(hChild, RTPROCWAIT_FLAGS_BLOCK, &ProcStatus);
+ if (RT_SUCCESS(rc))
+ {
+ if ( ProcStatus.enmReason == RTPROCEXITREASON_NORMAL
+ && ProcStatus.iStatus == 0)
+ {
+ if (RTPathExists(pszPath))
+ {
+ rtDbgCfgLog1(pThis, "Successfully unpacked '%s' to '%s'.\n", pszSrcArchive, pszPath);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ rtDbgCfgLog1(pThis, "Successfully ran unpacker on '%s', but '%s' is missing!\n", pszSrcArchive, pszPath);
+ rc = VERR_ZIP_ERROR;
+ }
+ }
+ else
+ {
+ rtDbgCfgLog2(pThis, "Unpacking '%s' failed: iStatus=%d enmReason=%d\n",
+ pszSrcArchive, ProcStatus.iStatus, ProcStatus.enmReason);
+ rc = VERR_ZIP_CORRUPTED;
+ }
+ }
+ else
+ rtDbgCfgLog1(pThis, "Error waiting for process: %Rrc\n", rc);
+
+ }
+ else
+ rtDbgCfgLog1(pThis, "Error starting unpack process '%s': %Rrc\n", papszArgs[0], rc);
+
+ return rc;
+}
+
+
+static int rtDbgCfgTryDownloadAndOpen(PRTDBGCFGINT pThis, const char *pszServer, char *pszPath,
+ const char *pszCacheSubDir, const char *pszUuidMappingSubDir,
+ PRTPATHSPLIT pSplitFn, const char *pszCacheSuffix, uint32_t fFlags,
+ PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
+{
+ RT_NOREF_PV(pszUuidMappingSubDir); /** @todo do we bother trying pszUuidMappingSubDir? */
+ RT_NOREF_PV(pszCacheSuffix); /** @todo do we bother trying pszUuidMappingSubDir? */
+ RT_NOREF_PV(fFlags);
+
+ if (pThis->fFlags & RTDBGCFG_FLAGS_NO_SYM_SRV)
+ return VWRN_NOT_FOUND;
+ if (!pszCacheSubDir || !*pszCacheSubDir)
+ return VWRN_NOT_FOUND;
+ if ( !(fFlags & RTDBGCFG_O_SYMSRV)
+ && !(fFlags & RTDBGCFG_O_DEBUGINFOD))
+ return VWRN_NOT_FOUND;
+
+ /*
+ * Create the path.
+ */
+ size_t cchTmp = strlen(pszPath);
+
+ int rc = RTDirCreateFullPath(pszPath, 0766);
+ if (!RTDirExists(pszPath))
+ {
+ Log(("Error creating cache dir '%s': %Rrc\n", pszPath, rc));
+ return rc;
+ }
+
+ const char *pszFilename = pSplitFn->apszComps[pSplitFn->cComps - 1];
+ rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename);
+ if (RT_FAILURE(rc))
+ return rc;
+ RTStrToLower(&pszPath[cchTmp]);
+ if (!RTDirExists(pszPath))
+ {
+ rc = RTDirCreate(pszPath, 0766, 0);
+ if (RT_FAILURE(rc))
+ {
+ Log(("RTDirCreate(%s) -> %Rrc\n", pszPath, rc));
+ }
+ }
+
+ rc = RTPathAppend(pszPath, RTPATH_MAX, pszCacheSubDir);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (!RTDirExists(pszPath))
+ {
+ rc = RTDirCreate(pszPath, 0766, 0);
+ if (RT_FAILURE(rc))
+ {
+ Log(("RTDirCreate(%s) -> %Rrc\n", pszPath, rc));
+ }
+ }
+
+ /* Prepare the destination file name while we're here. */
+ cchTmp = strlen(pszPath);
+ RTStrToLower(&pszPath[cchTmp]);
+ rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Download/copy the file.
+ */
+ char szUrl[_2K];
+ /* Download URL? */
+ if ( RTStrIStartsWith(pszServer, "http://")
+ || RTStrIStartsWith(pszServer, "https://")
+ || RTStrIStartsWith(pszServer, "ftp://") )
+ {
+#ifdef IPRT_WITH_HTTP
+ RTHTTP hHttp;
+ rc = RTHttpCreate(&hHttp);
+ if (RT_SUCCESS(rc))
+ {
+ RTHttpUseSystemProxySettings(hHttp);
+ RTHttpSetFollowRedirects(hHttp, 8);
+
+ static const char * const s_apszHeadersMsSymSrv[] =
+ {
+ "User-Agent: Microsoft-Symbol-Server/6.6.0999.9",
+ "Pragma: no-cache",
+ };
+
+ static const char * const s_apszHeadersDebuginfod[] =
+ {
+ "User-Agent: IPRT DbgCfg 1.0",
+ "Pragma: no-cache",
+ };
+
+ if (fFlags & RTDBGCFG_O_SYMSRV)
+ rc = RTHttpSetHeaders(hHttp, RT_ELEMENTS(s_apszHeadersMsSymSrv), s_apszHeadersMsSymSrv);
+ else /* Must be debuginfod. */
+ rc = RTHttpSetHeaders(hHttp, RT_ELEMENTS(s_apszHeadersDebuginfod), s_apszHeadersDebuginfod);
+ if (RT_SUCCESS(rc))
+ {
+ if (fFlags & RTDBGCFG_O_SYMSRV)
+ RTStrPrintf(szUrl, sizeof(szUrl), "%s/%s/%s/%s", pszServer, pszFilename, pszCacheSubDir, pszFilename);
+ else
+ RTStrPrintf(szUrl, sizeof(szUrl), "%s/buildid/%s/debuginfo", pszServer, pszCacheSubDir);
+
+ /** @todo Use some temporary file name and rename it after the operation
+ * since not all systems support read-deny file sharing
+ * settings. */
+ rtDbgCfgLog2(pThis, "Downloading '%s' to '%s'...\n", szUrl, pszPath);
+ rc = RTHttpGetFile(hHttp, szUrl, pszPath);
+ if (RT_FAILURE(rc))
+ {
+ RTFileDelete(pszPath);
+ rtDbgCfgLog1(pThis, "%Rrc on URL '%s'\n", rc, szUrl);
+ }
+ if ( rc == VERR_HTTP_NOT_FOUND
+ && (fFlags & RTDBGCFG_O_SYMSRV))
+ {
+ /* Try the compressed version of the file. */
+ pszPath[strlen(pszPath) - 1] = '_';
+ szUrl[strlen(szUrl) - 1] = '_';
+ rtDbgCfgLog2(pThis, "Downloading '%s' to '%s'...\n", szUrl, pszPath);
+ rc = RTHttpGetFile(hHttp, szUrl, pszPath);
+ if (RT_SUCCESS(rc))
+ rc = rtDbgCfgUnpackMsCacheFile(pThis, pszPath, pszFilename);
+ else
+ {
+ rtDbgCfgLog1(pThis, "%Rrc on URL '%s'\n", rc, pszPath);
+ RTFileDelete(pszPath);
+ }
+ }
+ }
+
+ RTHttpDestroy(hHttp);
+ }
+#else
+ rc = VWRN_NOT_FOUND;
+#endif
+ }
+ /* No download, assume dir on server share. */
+ else
+ {
+ if (RTStrIStartsWith(pszServer, "file:///"))
+ pszServer += 4 + 1 + 3 - 1;
+
+ /* Compose the path to the uncompressed file on the server. */
+ rc = RTPathJoin(szUrl, sizeof(szUrl), pszServer, pszFilename);
+ if (RT_SUCCESS(rc))
+ rc = RTPathAppend(szUrl, sizeof(szUrl), pszCacheSubDir);
+ if (RT_SUCCESS(rc))
+ rc = RTPathAppend(szUrl, sizeof(szUrl), pszFilename);
+ if (RT_SUCCESS(rc))
+ {
+ rtDbgCfgLog2(pThis, "Copying '%s' to '%s'...\n", szUrl, pszPath);
+ rc = RTFileCopy(szUrl, pszPath);
+ if (RT_FAILURE(rc))
+ {
+ RTFileDelete(pszPath);
+ rtDbgCfgLog1(pThis, "%Rrc on '%s'\n", rc, szUrl);
+
+ /* Try the compressed version. */
+ pszPath[strlen(pszPath) - 1] = '_';
+ szUrl[strlen(szUrl) - 1] = '_';
+ rtDbgCfgLog2(pThis, "Copying '%s' to '%s'...\n", szUrl, pszPath);
+ rc = RTFileCopy(szUrl, pszPath);
+ if (RT_SUCCESS(rc))
+ rc = rtDbgCfgUnpackMsCacheFile(pThis, pszPath, pszFilename);
+ else
+ {
+ rtDbgCfgLog1(pThis, "%Rrc on '%s'\n", rc, pszPath);
+ RTFileDelete(pszPath);
+ }
+ }
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Succeeded in downloading it. Add UUID mapping?
+ */
+ if (pszUuidMappingSubDir)
+ {
+ /** @todo UUID mapping when downloading. */
+ }
+
+ /*
+ * Give the file a try.
+ */
+ Assert(RTFileExists(pszPath));
+ rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
+ rc = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
+ if (rc == VINF_CALLBACK_RETURN)
+ rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath);
+ else if (rc == VERR_CALLBACK_RETURN)
+ rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
+ else
+ rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc, pszPath);
+ }
+
+ return rc;
+}
+
+
+static int rtDbgCfgCopyFileToCache(PRTDBGCFGINT pThis, char const *pszSrc, const char *pchCache, size_t cchCache,
+ const char *pszCacheSubDir, const char *pszUuidMappingSubDir, PRTPATHSPLIT pSplitFn)
+{
+ RT_NOREF_PV(pThis); RT_NOREF_PV(pszSrc); RT_NOREF_PV(pchCache); RT_NOREF_PV(cchCache);
+ RT_NOREF_PV(pszUuidMappingSubDir); RT_NOREF_PV(pSplitFn);
+
+ if (!pszCacheSubDir || !*pszCacheSubDir)
+ return VINF_SUCCESS;
+
+ /** @todo copy to cache */
+ return VINF_SUCCESS;
+}
+
+
+static int rtDbgCfgTryOpenCache(PRTDBGCFGINT pThis, char *pszPath, size_t cchCachePath,
+ const char *pszCacheSubDir, const char *pszUuidMappingSubDir,
+ PCRTPATHSPLIT pSplitFn, const char *pszCacheSuffix, uint32_t fFlags,
+ PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
+{
+ Assert(pszPath[cchCachePath] == '\0');
+
+ /*
+ * If the cache doesn't exist, fail right away.
+ */
+ if (!pszCacheSubDir || !*pszCacheSubDir)
+ return VWRN_NOT_FOUND;
+ if (!RTDirExists(pszPath))
+ {
+ rtDbgCfgLog2(pThis, "Cache does not exist: '%s'\n", pszPath);
+ return VWRN_NOT_FOUND;
+ }
+
+ /*
+ * If we got a UUID mapping option, try it first as we can hopefully
+ * dispense with case folding.
+ */
+ if (pszUuidMappingSubDir)
+ {
+ int rc = RTPathAppend(pszPath, RTPATH_MAX, pszUuidMappingSubDir);
+ if ( RT_SUCCESS(rc)
+ && RTFileExists(pszPath))
+ {
+ /* Try resolve the path before presenting it to the client, a
+ 12 digit filename is of little worth. */
+ char szBackup[RTPATH_MAX];
+ strcpy(szBackup, pszPath);
+ rc = RTPathAbs(szBackup, pszPath, RTPATH_MAX);
+ if (RT_FAILURE(rc))
+ strcpy(pszPath, szBackup);
+
+ /* Do the callback thing. */
+ rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
+ int rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
+ if (rc2 == VINF_CALLBACK_RETURN)
+ rtDbgCfgLog1(pThis, "Found '%s' via uuid mapping.\n", pszPath);
+ else if (rc2 == VERR_CALLBACK_RETURN)
+ rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
+ else
+ rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath);
+ if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
+ return rc2;
+
+ /* Failed, restore the cache path. */
+ memcpy(pszPath, szBackup, cchCachePath);
+ }
+ pszPath[cchCachePath] = '\0';
+ }
+
+ /*
+ * Carefully construct the cache path with case insensitivity in mind.
+ */
+ bool const fCaseInsensitive = (fFlags & RTDBGCFG_O_CASE_INSENSITIVE)
+ && !rtDbgCfgIsFsCaseInsensitive(pszPath);
+ const char *pszFilename = pSplitFn->apszComps[pSplitFn->cComps - 1];
+
+ if (!rtDbgCfgIsDirAndFixCase(pszPath, pszFilename, fCaseInsensitive))
+ return VWRN_NOT_FOUND;
+
+ if (!rtDbgCfgIsDirAndFixCase(pszPath, pszCacheSubDir, fCaseInsensitive))
+ return VWRN_NOT_FOUND;
+
+ bool fProbablyCompressed = false;
+ if (!rtDbgCfgIsFileAndFixCase(pszPath, pszFilename, pszCacheSuffix, fCaseInsensitive,
+ RT_BOOL(fFlags & RTDBGCFG_O_MAYBE_COMPRESSED_MS), &fProbablyCompressed))
+ return VWRN_NOT_FOUND;
+ if (fProbablyCompressed)
+ {
+ int rc = rtDbgCfgUnpackMsCacheFile(pThis, pszPath, pszFilename);
+ if (RT_FAILURE(rc))
+ return VWRN_NOT_FOUND;
+ }
+
+ rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
+ int rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
+ if (rc2 == VINF_CALLBACK_RETURN)
+ rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath);
+ else if (rc2 == VERR_CALLBACK_RETURN)
+ rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
+ else
+ rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath);
+ return rc2;
+}
+
+
+static int rtDbgCfgTryOpenList(PRTDBGCFGINT pThis, PRTLISTANCHOR pList, PRTPATHSPLIT pSplitFn, const char *pszCacheSubDir,
+ const char *pszUuidMappingSubDir, uint32_t fFlags, char *pszPath,
+ PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
+{
+ int rcRet = VWRN_NOT_FOUND;
+ int rc2 = VINF_SUCCESS;
+
+ const char *pchCache = NULL;
+ size_t cchCache = 0;
+ int rcCache = VWRN_NOT_FOUND;
+
+ PRTDBGCFGSTR pCur;
+ RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry)
+ {
+ size_t cchDir = pCur->cch;
+ const char *pszDir = pCur->sz;
+ rtDbgCfgLog2(pThis, "Path list entry: '%s'\n", pszDir);
+
+ /* This is very simplistic, but we have a unreasonably large path
+ buffer, so it'll work just fine and simplify things greatly below. */
+ if (cchDir >= RTPATH_MAX - 8U)
+ {
+ if (RT_SUCCESS_NP(rcRet))
+ rcRet = VERR_FILENAME_TOO_LONG;
+ continue;
+ }
+
+ /*
+ * Process the path according to it's type.
+ */
+ if (!RTStrNICmp(pszDir, RT_STR_TUPLE("srv*")))
+ {
+ /*
+ * Symbol server.
+ */
+ pszDir += sizeof("srv*") - 1;
+ cchDir -= sizeof("srv*") - 1;
+ bool fSearchCache = false;
+ const char *pszServer = (const char *)memchr(pszDir, '*', cchDir);
+ if (!pszServer)
+ pszServer = pszDir;
+ else if (pszServer == pszDir)
+ continue;
+ else
+ {
+ fSearchCache = true;
+ pchCache = pszDir;
+ cchCache = pszServer - pszDir;
+ pszServer++;
+ }
+
+ /* We don't have any default cache directory, so skip if the cache is missing. */
+ if (cchCache == 0)
+ continue;
+
+ /* Search the cache first (if we haven't already done so). */
+ if (fSearchCache)
+ {
+ memcpy(pszPath, pchCache, cchCache);
+ pszPath[cchCache] = '\0';
+ RTPathChangeToUnixSlashes(pszPath, false);
+
+ rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, cchCache, pszCacheSubDir, pszUuidMappingSubDir,
+ pSplitFn, NULL /*pszCacheSuffix*/, fFlags, pfnCallback, pvUser1, pvUser2);
+ if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
+ return rc2;
+ }
+
+ /* Try downloading the file. */
+ if (rcCache == VWRN_NOT_FOUND)
+ {
+ memcpy(pszPath, pchCache, cchCache);
+ pszPath[cchCache] = '\0';
+ RTPathChangeToUnixSlashes(pszPath, false);
+
+ rc2 = rtDbgCfgTryDownloadAndOpen(pThis, pszServer, pszPath, pszCacheSubDir, pszUuidMappingSubDir,
+ pSplitFn, NULL /*pszCacheSuffix*/, fFlags, pfnCallback, pvUser1, pvUser2);
+ if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
+ return rc2;
+ }
+ }
+ else if (!RTStrNICmp(pszDir, RT_STR_TUPLE("cache*")))
+ {
+ /*
+ * Cache directory.
+ */
+ pszDir += sizeof("cache*") - 1;
+ cchDir -= sizeof("cache*") - 1;
+ if (!cchDir)
+ continue;
+ pchCache = pszDir;
+ cchCache = cchDir;
+
+ memcpy(pszPath, pchCache, cchCache);
+ pszPath[cchCache] = '\0';
+ RTPathChangeToUnixSlashes(pszPath, false);
+
+ rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, cchCache, pszCacheSubDir, pszUuidMappingSubDir,
+ pSplitFn, NULL /*pszCacheSuffix*/, fFlags, pfnCallback, pvUser1, pvUser2);
+ if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
+ return rc2;
+ }
+ else
+ {
+ /*
+ * Normal directory. Check for our own 'rec*' and 'norec*' prefix
+ * flags governing recursive searching.
+ */
+ uint32_t fFlagsDir = fFlags;
+ if (!RTStrNICmp(pszDir, RT_STR_TUPLE("rec*")))
+ {
+ pszDir += sizeof("rec*") - 1;
+ cchDir -= sizeof("rec*") - 1;
+ fFlagsDir |= RTDBGCFG_O_RECURSIVE;
+ }
+ else if (!RTStrNICmp(pszDir, RT_STR_TUPLE("norec*")))
+ {
+ pszDir += sizeof("norec*") - 1;
+ cchDir -= sizeof("norec*") - 1;
+ fFlagsDir &= ~RTDBGCFG_O_RECURSIVE;
+ }
+
+ /* Copy the path into the buffer and do the searching. */
+ memcpy(pszPath, pszDir, cchDir);
+ pszPath[cchDir] = '\0';
+ RTPathChangeToUnixSlashes(pszPath, false);
+
+ rc2 = rtDbgCfgTryOpenDir(pThis, pszPath, pSplitFn, fFlagsDir, pfnCallback, pvUser1, pvUser2);
+ if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
+ {
+ if ( rc2 == VINF_CALLBACK_RETURN
+ && cchCache > 0)
+ rtDbgCfgCopyFileToCache(pThis, pszPath, pchCache, cchCache,
+ pszCacheSubDir, pszUuidMappingSubDir, pSplitFn);
+ return rc2;
+ }
+ }
+
+ /* Propagate errors. */
+ if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
+ rcRet = rc2;
+ }
+
+ return rcRet;
+}
+
+
+/**
+ * Common worker routine for Image and debug info opening.
+ *
+ * This will not search using for suffixes.
+ *
+ * @returns IPRT status code.
+ * @param hDbgCfg The debugging configuration handle.
+ * NIL_RTDBGCFG is accepted, but the result is
+ * that no paths will be searched beyond the
+ * given and the current directory.
+ * @param pszFilename The filename to search for. This may or may
+ * not include a full or partial path.
+ * @param pszCacheSubDir The cache subdirectory to look in.
+ * @param pszUuidMappingSubDir UUID mapping subdirectory to check, NULL if
+ * no mapping wanted.
+ * @param fFlags Flags and hints.
+ * @param pfnCallback The open callback routine.
+ * @param pvUser1 User parameter 1.
+ * @param pvUser2 User parameter 2.
+ */
+static int rtDbgCfgOpenWithSubDir(RTDBGCFG hDbgCfg, const char *pszFilename, const char *pszCacheSubDir,
+ const char *pszUuidMappingSubDir, uint32_t fFlags,
+ PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
+{
+ int rcRet = VINF_SUCCESS;
+ int rc2;
+
+ /*
+ * Do a little validating first.
+ */
+ PRTDBGCFGINT pThis = hDbgCfg;
+ if (pThis != NIL_RTDBGCFG)
+ RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
+ else
+ pThis = NULL;
+ AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszCacheSubDir, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTDBGCFG_O_VALID_MASK), VERR_INVALID_FLAGS);
+
+ /*
+ * Do some guessing as to the way we should parse the filename and whether
+ * it's case exact or not.
+ */
+ bool fDosPath = RT_OPSYS_USES_DOS_PATHS(fFlags & RTDBGCFG_O_OPSYS_MASK)
+ || (fFlags & RTDBGCFG_O_CASE_INSENSITIVE)
+ || strchr(pszFilename, ':') != NULL
+ || strchr(pszFilename, '\\') != NULL;
+ if (fDosPath)
+ fFlags |= RTDBGCFG_O_CASE_INSENSITIVE;
+
+ rtDbgCfgLog2(pThis, "Looking for '%s' w/ cache subdir '%s' and %#x flags...\n", pszFilename, pszCacheSubDir, fFlags);
+
+ PRTPATHSPLIT pSplitFn;
+ rc2 = RTPathSplitA(pszFilename, &pSplitFn, fDosPath ? RTPATH_STR_F_STYLE_DOS : RTPATH_STR_F_STYLE_UNIX);
+ if (RT_FAILURE(rc2))
+ return rc2;
+ AssertReturnStmt(pSplitFn->fProps & RTPATH_PROP_FILENAME, RTPathSplitFree(pSplitFn), VERR_IS_A_DIRECTORY);
+
+ /*
+ * Try the stored file name first if it has a kind of absolute path.
+ */
+ char szPath[RTPATH_MAX];
+ if (RTPATH_PROP_HAS_ROOT_SPEC(pSplitFn->fProps))
+ {
+ rc2 = RTPathSplitReassemble(pSplitFn, RTPATH_STR_F_STYLE_HOST, szPath, sizeof(szPath));
+ if (RT_SUCCESS(rc2) && RTFileExists(szPath))
+ {
+ RTPathChangeToUnixSlashes(szPath, false);
+ rtDbgCfgLog1(pThis, "Trying '%s'...\n", szPath);
+ rc2 = pfnCallback(pThis, szPath, pvUser1, pvUser2);
+ if (rc2 == VINF_CALLBACK_RETURN)
+ rtDbgCfgLog1(pThis, "Found '%s'.\n", szPath);
+ else if (rc2 == VERR_CALLBACK_RETURN)
+ rtDbgCfgLog1(pThis, "Error opening '%s'.\n", szPath);
+ else
+ rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, szPath);
+ }
+ }
+ if ( rc2 != VINF_CALLBACK_RETURN
+ && rc2 != VERR_CALLBACK_RETURN)
+ {
+ /*
+ * Try the current directory (will take cover relative paths
+ * skipped above).
+ */
+ rc2 = RTPathGetCurrent(szPath, sizeof(szPath));
+ if (RT_FAILURE(rc2))
+ strcpy(szPath, ".");
+ RTPathChangeToUnixSlashes(szPath, false);
+
+ rc2 = rtDbgCfgTryOpenDir(pThis, szPath, pSplitFn, fFlags, pfnCallback, pvUser1, pvUser2);
+ if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
+ rcRet = rc2;
+
+ if ( rc2 != VINF_CALLBACK_RETURN
+ && rc2 != VERR_CALLBACK_RETURN
+ && pThis)
+ {
+ rc2 = RTCritSectRwEnterShared(&pThis->CritSect);
+ if (RT_SUCCESS(rc2))
+ {
+ /*
+ * Run the applicable lists.
+ */
+ rc2 = rtDbgCfgTryOpenList(pThis, &pThis->PathList, pSplitFn, pszCacheSubDir,
+ pszUuidMappingSubDir, fFlags, szPath, pfnCallback, pvUser1, pvUser2);
+ if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
+ rcRet = rc2;
+
+#ifdef RT_OS_WINDOWS
+ if ( rc2 != VINF_CALLBACK_RETURN
+ && rc2 != VERR_CALLBACK_RETURN
+ && (fFlags & RTDBGCFG_O_EXECUTABLE_IMAGE)
+ && !(fFlags & RTDBGCFG_O_NO_SYSTEM_PATHS)
+ && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_SYSTEM_PATHS) )
+ {
+ rc2 = rtDbgCfgTryOpenList(pThis, &pThis->NtExecutablePathList, pSplitFn, pszCacheSubDir,
+ pszUuidMappingSubDir, fFlags, szPath, pfnCallback, pvUser1, pvUser2);
+ if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
+ rcRet = rc2;
+ }
+
+ if ( rc2 != VINF_CALLBACK_RETURN
+ && rc2 != VERR_CALLBACK_RETURN
+ && !(fFlags & RTDBGCFG_O_NO_SYSTEM_PATHS)
+ && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_SYSTEM_PATHS) )
+ {
+ rc2 = rtDbgCfgTryOpenList(pThis, &pThis->NtSymbolPathList, pSplitFn, pszCacheSubDir,
+ pszUuidMappingSubDir, fFlags, szPath, pfnCallback, pvUser1, pvUser2);
+ if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
+ rcRet = rc2;
+ }
+#endif
+ RTCritSectRwLeaveShared(&pThis->CritSect);
+ }
+ else if (RT_SUCCESS(rcRet))
+ rcRet = rc2;
+ }
+ }
+
+ RTPathSplitFree(pSplitFn);
+ if ( rc2 == VINF_CALLBACK_RETURN
+ || rc2 == VERR_CALLBACK_RETURN)
+ rcRet = rc2;
+ else if (RT_SUCCESS(rcRet))
+ rcRet = VERR_NOT_FOUND;
+ return rcRet;
+}
+
+
+RTDECL(int) RTDbgCfgOpenEx(RTDBGCFG hDbgCfg, const char *pszFilename, const char *pszCacheSubDir,
+ const char *pszUuidMappingSubDir, uint32_t fFlags,
+ PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
+{
+ return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, pszCacheSubDir, pszUuidMappingSubDir, fFlags,
+ pfnCallback, pvUser1, pvUser2);
+}
+
+
+
+
+RTDECL(int) RTDbgCfgOpenPeImage(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp,
+ PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
+{
+ char szSubDir[32];
+ RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, cbImage);
+ return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL,
+ RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
+ | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXECUTABLE_IMAGE,
+ pfnCallback, pvUser1, pvUser2);
+}
+
+
+RTDECL(int) RTDbgCfgOpenPdb70(RTDBGCFG hDbgCfg, const char *pszFilename, PCRTUUID pUuid, uint32_t uAge,
+ PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
+{
+ char szSubDir[64];
+ if (!pUuid)
+ szSubDir[0] = '\0';
+ else
+ {
+ /* Stringify the UUID and remove the dashes. */
+ int rc2 = RTUuidToStr(pUuid, szSubDir, sizeof(szSubDir));
+ AssertRCReturn(rc2, rc2);
+
+ char *pszSrc = szSubDir;
+ char *pszDst = szSubDir;
+ char ch;
+ while ((ch = *pszSrc++))
+ if (ch != '-')
+ *pszDst++ = RT_C_TO_UPPER(ch);
+
+ RTStrPrintf(pszDst, &szSubDir[sizeof(szSubDir)] - pszDst, "%X", uAge);
+ }
+
+ return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL,
+ RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
+ | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXT_DEBUG_FILE,
+ pfnCallback, pvUser1, pvUser2);
+}
+
+
+RTDECL(int) RTDbgCfgOpenPdb20(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp, uint32_t uAge,
+ PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
+{
+ RT_NOREF_PV(cbImage);
+ /** @todo test this! */
+ char szSubDir[32];
+ RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, uAge);
+ return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL,
+ RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
+ | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXT_DEBUG_FILE,
+ pfnCallback, pvUser1, pvUser2);
+}
+
+
+RTDECL(int) RTDbgCfgOpenDbg(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp,
+ PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
+{
+ char szSubDir[32];
+ RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, cbImage);
+ return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL,
+ RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE
+ | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXT_DEBUG_FILE,
+ pfnCallback, pvUser1, pvUser2);
+}
+
+
+RTDECL(int) RTDbgCfgOpenDwo(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t uCrc32,
+ PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
+{
+ char szSubDir[32];
+ RTStrPrintf(szSubDir, sizeof(szSubDir), "%08x", uCrc32);
+ return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL,
+ RT_OPSYS_UNKNOWN | RTDBGCFG_O_EXT_DEBUG_FILE,
+ pfnCallback, pvUser1, pvUser2);
+}
+
+
+RTDECL(int) RTDbgCfgOpenDwoBuildId(RTDBGCFG hDbgCfg, const char *pszFilename, const uint8_t *pbBuildId,
+ size_t cbBuildId, PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
+{
+ char *pszSubDir = NULL;
+ int rc = RTStrAPrintf(&pszSubDir, "%#.*Rhxs", cbBuildId, pbBuildId);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, pszSubDir, NULL,
+ RTDBGCFG_O_DEBUGINFOD | RT_OPSYS_UNKNOWN | RTDBGCFG_O_EXT_DEBUG_FILE,
+ pfnCallback, pvUser1, pvUser2);
+ RTStrFree(pszSubDir);
+ }
+
+ return rc;
+}
+
+
+
+/*
+ *
+ * D a r w i n . d S Y M b u n d l e s
+ * D a r w i n . d S Y M b u n d l e s
+ * D a r w i n . d S Y M b u n d l e s
+ *
+ */
+
+/**
+ * Very similar to rtDbgCfgTryOpenDir.
+ */
+static int rtDbgCfgTryOpenDsymBundleInDir(PRTDBGCFGINT pThis, char *pszPath, PRTPATHSPLIT pSplitFn,
+ const char * const *papszSuffixes, uint32_t fFlags,
+ PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
+{
+ int rcRet = VWRN_NOT_FOUND;
+ int rc2;
+
+ /* If the directory doesn't exist, just quit immediately.
+ Note! Our case insensitivity doesn't extend to the search dirs themselfs,
+ only to the bits under neath them. */
+ if (!RTDirExists(pszPath))
+ {
+ rtDbgCfgLog2(pThis, "Dir does not exist: '%s'\n", pszPath);
+ return rcRet;
+ }
+
+ /* Figure out whether we have to do a case sensitive search or not.
+ Note! As a simplification, we don't ask for case settings in each
+ directory under the user specified path, we assume the file
+ systems that mounted there have compatible settings. Faster
+ that way. */
+ bool const fCaseInsensitive = (fFlags & RTDBGCFG_O_CASE_INSENSITIVE)
+ && !rtDbgCfgIsFsCaseInsensitive(pszPath);
+
+ size_t const cchPath = strlen(pszPath);
+
+ /*
+ * Look for the file with less and less of the original path given.
+ * Also try out typical bundle extension variations.
+ */
+ const char *pszName = pSplitFn->apszComps[pSplitFn->cComps - 1];
+ for (unsigned i = RTPATH_PROP_HAS_ROOT_SPEC(pSplitFn->fProps); i < pSplitFn->cComps; i++)
+ {
+ pszPath[cchPath] = '\0';
+
+ rc2 = VINF_SUCCESS;
+ for (unsigned j = i; j < pSplitFn->cComps - 1U && RT_SUCCESS(rc2); j++)
+ if (!rtDbgCfgIsDirAndFixCase(pszPath, pSplitFn->apszComps[i], fCaseInsensitive))
+ rc2 = VERR_FILE_NOT_FOUND;
+ if (RT_SUCCESS(rc2))
+ {
+ for (uint32_t iSuffix = 0; papszSuffixes[iSuffix]; iSuffix++)
+ {
+ if ( !rtDbgCfgIsDirAndFixCase2(pszPath, pszName, papszSuffixes[iSuffix], fCaseInsensitive)
+ && !rtDbgCfgIsDirAndFixCase(pszPath, "Contents", fCaseInsensitive)
+ && !rtDbgCfgIsDirAndFixCase(pszPath, "Resources", fCaseInsensitive)
+ && !rtDbgCfgIsDirAndFixCase(pszPath, "DWARF", fCaseInsensitive))
+ {
+ if (rtDbgCfgIsFileAndFixCase(pszPath, pszName, NULL /*pszSuffix*/, fCaseInsensitive, false, NULL))
+ {
+ rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath);
+ rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2);
+ if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
+ {
+ if (rc2 == VINF_CALLBACK_RETURN)
+ rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath);
+ else
+ rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath);
+ return rc2;
+ }
+ rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath);
+ if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
+ rcRet = rc2;
+ }
+ }
+ }
+ }
+ rc2 = VERR_FILE_NOT_FOUND;
+ }
+
+ /*
+ * Do a recursive search if requested.
+ */
+ if ( (fFlags & RTDBGCFG_O_RECURSIVE)
+ && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH) )
+ {
+ /** @todo Recursive searching will be done later. */
+ }
+
+ return rcRet;
+}
+
+
+/**
+ * Very similar to rtDbgCfgTryOpenList.
+ */
+static int rtDbgCfgTryOpenBundleInList(PRTDBGCFGINT pThis, PRTLISTANCHOR pList, PRTPATHSPLIT pSplitFn,
+ const char * const *papszSuffixes, const char *pszCacheSubDir,
+ const char *pszCacheSuffix, const char *pszUuidMappingSubDir,
+ uint32_t fFlags, char *pszPath,
+ PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
+{
+ int rcRet = VWRN_NOT_FOUND;
+ int rc2;
+
+ const char *pchCache = NULL;
+ size_t cchCache = 0;
+ int rcCache = VWRN_NOT_FOUND;
+
+ PRTDBGCFGSTR pCur;
+ RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry)
+ {
+ size_t cchDir = pCur->cch;
+ const char *pszDir = pCur->sz;
+ rtDbgCfgLog2(pThis, "Path list entry: '%s'\n", pszDir);
+
+ /* This is very simplistic, but we have a unreasonably large path
+ buffer, so it'll work just fine and simplify things greatly below. */
+ if (cchDir >= RTPATH_MAX - 8U)
+ {
+ if (RT_SUCCESS_NP(rcRet))
+ rcRet = VERR_FILENAME_TOO_LONG;
+ continue;
+ }
+
+ /*
+ * Process the path according to it's type.
+ */
+ rc2 = VINF_SUCCESS;
+ if (!RTStrNICmp(pszDir, RT_STR_TUPLE("srv*")))
+ {
+ /*
+ * Symbol server.
+ */
+ pszDir += sizeof("srv*") - 1;
+ cchDir -= sizeof("srv*") - 1;
+ bool fSearchCache = false;
+ const char *pszServer = (const char *)memchr(pszDir, '*', cchDir);
+ if (!pszServer)
+ pszServer = pszDir;
+ else if (pszServer == pszDir)
+ continue;
+ else
+ {
+ fSearchCache = true;
+ pchCache = pszDir;
+ cchCache = pszServer - pszDir;
+ pszServer++;
+ }
+
+ /* We don't have any default cache directory, so skip if the cache is missing. */
+ if (cchCache == 0)
+ continue;
+
+ /* Search the cache first (if we haven't already done so). */
+ if (fSearchCache)
+ {
+ memcpy(pszPath, pchCache, cchCache);
+ pszPath[cchCache] = '\0';
+ RTPathChangeToUnixSlashes(pszPath, false);
+
+ rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, cchCache, pszCacheSubDir, pszUuidMappingSubDir,
+ pSplitFn, pszCacheSuffix, fFlags, pfnCallback, pvUser1, pvUser2);
+ if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
+ return rc2;
+ }
+
+ /* Try downloading the file. */
+ if (rcCache == VWRN_NOT_FOUND)
+ {
+ memcpy(pszPath, pchCache, cchCache);
+ pszPath[cchCache] = '\0';
+ RTPathChangeToUnixSlashes(pszPath, false);
+
+ rc2 = rtDbgCfgTryDownloadAndOpen(pThis, pszServer, pszPath, pszCacheSubDir, pszUuidMappingSubDir,
+ pSplitFn, pszCacheSuffix, fFlags, pfnCallback, pvUser1, pvUser2);
+ if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
+ return rc2;
+ }
+ }
+ else if (!RTStrNICmp(pszDir, RT_STR_TUPLE("cache*")))
+ {
+ /*
+ * Cache directory.
+ */
+ pszDir += sizeof("cache*") - 1;
+ cchDir -= sizeof("cache*") - 1;
+ if (!cchDir)
+ continue;
+ pchCache = pszDir;
+ cchCache = cchDir;
+
+ memcpy(pszPath, pchCache, cchCache);
+ pszPath[cchCache] = '\0';
+ RTPathChangeToUnixSlashes(pszPath, false);
+
+ rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, cchCache, pszCacheSubDir, pszUuidMappingSubDir,
+ pSplitFn, pszCacheSuffix, fFlags, pfnCallback, pvUser1, pvUser2);
+ if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
+ return rc2;
+ }
+ else
+ {
+ /*
+ * Normal directory. Check for our own 'rec*' and 'norec*' prefix
+ * flags governing recursive searching.
+ */
+ uint32_t fFlagsDir = fFlags;
+ if (!RTStrNICmp(pszDir, RT_STR_TUPLE("rec*")))
+ {
+ pszDir += sizeof("rec*") - 1;
+ cchDir -= sizeof("rec*") - 1;
+ fFlagsDir |= RTDBGCFG_O_RECURSIVE;
+ }
+ else if (!RTStrNICmp(pszDir, RT_STR_TUPLE("norec*")))
+ {
+ pszDir += sizeof("norec*") - 1;
+ cchDir -= sizeof("norec*") - 1;
+ fFlagsDir &= ~RTDBGCFG_O_RECURSIVE;
+ }
+
+ /* Copy the path into the buffer and do the searching. */
+ memcpy(pszPath, pszDir, cchDir);
+ pszPath[cchDir] = '\0';
+ RTPathChangeToUnixSlashes(pszPath, false);
+
+ rc2 = rtDbgCfgTryOpenDsymBundleInDir(pThis, pszPath, pSplitFn, papszSuffixes, fFlagsDir,
+ pfnCallback, pvUser1, pvUser2);
+ if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN)
+ {
+ if ( rc2 == VINF_CALLBACK_RETURN
+ && cchCache > 0)
+ rtDbgCfgCopyFileToCache(pThis, pszPath, pchCache, cchCache,
+ pszCacheSubDir, pszUuidMappingSubDir, pSplitFn);
+ return rc2;
+ }
+ }
+
+ /* Propagate errors. */
+ if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
+ rcRet = rc2;
+ }
+
+ return rcRet;
+}
+
+
+/**
+ * Creating a UUID mapping subdirectory path for use in caches.
+ *
+ * @returns IPRT status code.
+ * @param pszSubDir The output buffer.
+ * @param cbSubDir The size of the output buffer. (Top dir length +
+ * slash + UUID string len + extra dash.)
+ * @param pszTopDir The top level cache directory name. No slashes
+ * or other directory separators, please.
+ * @param pUuid The UUID.
+ */
+static int rtDbgCfgConstructUuidMappingSubDir(char *pszSubDir, size_t cbSubDir, const char *pszTopDir, PCRTUUID pUuid)
+{
+ Assert(!strpbrk(pszTopDir, ":/\\"));
+
+ size_t cchTopDir = strlen(pszTopDir);
+ if (cchTopDir + 1 + 1 + RTUUID_STR_LENGTH + 1 > cbSubDir)
+ return VERR_BUFFER_OVERFLOW;
+ memcpy(pszSubDir, pszTopDir, cchTopDir);
+
+ pszSubDir += cchTopDir;
+ *pszSubDir++ = RTPATH_SLASH;
+ cbSubDir -= cchTopDir + 1;
+
+ /* ed5a8336-35c2-4892-9122-21d5572924a3 -> ED5A/8336/35C2/4892/9122/21D5572924A3 */
+ int rc = RTUuidToStr(pUuid, pszSubDir + 1, cbSubDir - 1); AssertRCReturn(rc, rc);
+ RTStrToUpper(pszSubDir + 1);
+ memmove(pszSubDir, pszSubDir + 1, 4);
+ pszSubDir += 4;
+ *pszSubDir = RTPATH_SLASH;
+ pszSubDir += 5;
+ *pszSubDir = RTPATH_SLASH;
+ pszSubDir += 5;
+ *pszSubDir = RTPATH_SLASH;
+ pszSubDir += 5;
+ *pszSubDir = RTPATH_SLASH;
+ pszSubDir += 5;
+ *pszSubDir = RTPATH_SLASH;
+
+ return VINF_SUCCESS;
+}
+
+
+static int rtDbgCfgOpenBundleFile(RTDBGCFG hDbgCfg, const char *pszImage, const char * const *papszSuffixes,
+ const char *pszBundleSubDir, PCRTUUID pUuid, const char *pszUuidMapDirName,
+ const char *pszCacheSuffix, bool fOpenImage,
+ PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
+{
+ /*
+ * Bundles are directories, means we can forget about sharing code much
+ * with the other RTDbgCfgOpenXXX methods. Thus we're duplicating a lot of
+ * code from rtDbgCfgOpenWithSubDir with .dSYM/.kext/.dylib/.app/.* related
+ * adjustments, so, a bug found here or there probably means the other
+ * version needs updating.
+ */
+ int rcRet = VINF_SUCCESS;
+ int rc2;
+
+ /*
+ * Do a little validating first.
+ */
+ PRTDBGCFGINT pThis = hDbgCfg;
+ if (pThis != NIL_RTDBGCFG)
+ RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
+ else
+ pThis = NULL;
+ AssertPtrReturn(pszImage, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
+
+ /*
+ * Set up rtDbgCfgOpenWithSubDir and uuid map parameters.
+ */
+ uint32_t fFlags = RTDBGCFG_O_EXT_DEBUG_FILE | RT_OPSYS_DARWIN;
+ const char *pszCacheSubDir = NULL;
+ char szCacheSubDir[RTUUID_STR_LENGTH];
+ const char *pszUuidMappingSubDir = NULL;
+ char szUuidMappingSubDir[RTUUID_STR_LENGTH + 16];
+ if (pUuid)
+ {
+ /* Since Mac debuggers uses UUID mappings, we just the slashing default
+ UUID string representation instead of stripping dashes like for PDB. */
+ RTUuidToStr(pUuid, szCacheSubDir, sizeof(szCacheSubDir));
+ pszCacheSubDir = szCacheSubDir;
+
+ rc2 = rtDbgCfgConstructUuidMappingSubDir(szUuidMappingSubDir, sizeof(szUuidMappingSubDir), pszUuidMapDirName, pUuid);
+ AssertRCReturn(rc2, rc2);
+ pszUuidMappingSubDir = szUuidMappingSubDir;
+ }
+
+ /*
+ * Do some guessing as to the way we should parse the filename and whether
+ * it's case exact or not.
+ */
+ bool fDosPath = strchr(pszImage, ':') != NULL
+ || strchr(pszImage, '\\') != NULL
+ || RT_OPSYS_USES_DOS_PATHS(fFlags & RTDBGCFG_O_OPSYS_MASK)
+ || (fFlags & RTDBGCFG_O_CASE_INSENSITIVE);
+ if (fDosPath)
+ fFlags |= RTDBGCFG_O_CASE_INSENSITIVE;
+
+ rtDbgCfgLog2(pThis, "Looking for '%s' with %#x flags...\n", pszImage, fFlags);
+
+ PRTPATHSPLIT pSplitFn;
+ rc2 = RTPathSplitA(pszImage, &pSplitFn, fDosPath ? RTPATH_STR_F_STYLE_DOS : RTPATH_STR_F_STYLE_UNIX);
+ if (RT_FAILURE(rc2))
+ return rc2;
+ AssertReturnStmt(pSplitFn->fProps & RTPATH_PROP_FILENAME, RTPathSplitFree(pSplitFn), VERR_IS_A_DIRECTORY);
+
+ /*
+ * Try the image directory first.
+ */
+ char szPath[RTPATH_MAX];
+ if (pSplitFn->cComps > 0)
+ {
+ rc2 = RTPathSplitReassemble(pSplitFn, RTPATH_STR_F_STYLE_HOST, szPath, sizeof(szPath));
+ if (fOpenImage && RT_SUCCESS(rc2))
+ {
+ rc2 = RTStrCat(szPath, sizeof(szPath), papszSuffixes[0]);
+ if (RT_SUCCESS(rc2))
+ rc2 = RTStrCat(szPath, sizeof(szPath), pszBundleSubDir);
+ if (RT_SUCCESS(rc2))
+ rc2 = RTPathAppend(szPath, sizeof(szPath), pSplitFn->apszComps[pSplitFn->cComps - 1]);
+ }
+ if (RT_SUCCESS(rc2) && RTPathExists(szPath))
+ {
+ RTPathChangeToUnixSlashes(szPath, false);
+ rtDbgCfgLog1(pThis, "Trying '%s'...\n", szPath);
+ rc2 = pfnCallback(pThis, szPath, pvUser1, pvUser2);
+ if (rc2 == VINF_CALLBACK_RETURN)
+ rtDbgCfgLog1(pThis, "Found '%s'.\n", szPath);
+ else if (rc2 == VERR_CALLBACK_RETURN)
+ rtDbgCfgLog1(pThis, "Error opening '%s'.\n", szPath);
+ else
+ rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, szPath);
+ }
+ }
+ if ( rc2 != VINF_CALLBACK_RETURN
+ && rc2 != VERR_CALLBACK_RETURN)
+ {
+ /*
+ * Try the current directory (will take cover relative paths
+ * skipped above).
+ */
+ rc2 = RTPathGetCurrent(szPath, sizeof(szPath));
+ if (RT_FAILURE(rc2))
+ strcpy(szPath, ".");
+ RTPathChangeToUnixSlashes(szPath, false);
+
+ rc2 = rtDbgCfgTryOpenDsymBundleInDir(pThis, szPath, pSplitFn, g_apszDSymBundleSuffixes,
+ fFlags, pfnCallback, pvUser1, pvUser2);
+ if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
+ rcRet = rc2;
+
+ if ( rc2 != VINF_CALLBACK_RETURN
+ && rc2 != VERR_CALLBACK_RETURN
+ && pThis)
+ {
+ rc2 = RTCritSectRwEnterShared(&pThis->CritSect);
+ if (RT_SUCCESS(rc2))
+ {
+ /*
+ * Run the applicable lists.
+ */
+ rc2 = rtDbgCfgTryOpenBundleInList(pThis, &pThis->PathList, pSplitFn, g_apszDSymBundleSuffixes,
+ pszCacheSubDir, pszCacheSuffix,
+ pszUuidMappingSubDir, fFlags, szPath,
+ pfnCallback, pvUser1, pvUser2);
+ if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet))
+ rcRet = rc2;
+
+ RTCritSectRwLeaveShared(&pThis->CritSect);
+ }
+ else if (RT_SUCCESS(rcRet))
+ rcRet = rc2;
+ }
+ }
+
+ RTPathSplitFree(pSplitFn);
+ if ( rc2 == VINF_CALLBACK_RETURN
+ || rc2 == VERR_CALLBACK_RETURN)
+ rcRet = rc2;
+ else if (RT_SUCCESS(rcRet))
+ rcRet = VERR_NOT_FOUND;
+ return rcRet;
+}
+
+
+RTDECL(int) RTDbgCfgOpenDsymBundle(RTDBGCFG hDbgCfg, const char *pszImage, PCRTUUID pUuid,
+ PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
+{
+ return rtDbgCfgOpenBundleFile(hDbgCfg, pszImage, g_apszDSymBundleSuffixes,
+ "Contents" RTPATH_SLASH_STR "Resources" RTPATH_SLASH_STR "DWARF",
+ pUuid, RTDBG_CACHE_UUID_MAP_DIR_DSYMS, RTDBG_CACHE_DSYM_FILE_SUFFIX, false /* fOpenImage */,
+ pfnCallback, pvUser1, pvUser2);
+}
+
+
+RTDECL(int) RTDbgCfgOpenMachOImage(RTDBGCFG hDbgCfg, const char *pszImage, PCRTUUID pUuid,
+ PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2)
+{
+ return rtDbgCfgOpenBundleFile(hDbgCfg, pszImage, g_apszBundleSuffixes,
+ "Contents" RTPATH_SLASH_STR "MacOS",
+ pUuid, RTDBG_CACHE_UUID_MAP_DIR_IMAGES, NULL /*pszCacheSuffix*/, true /* fOpenImage */,
+ pfnCallback, pvUser1, pvUser2);
+}
+
+
+
+RTDECL(int) RTDbgCfgSetLogCallback(RTDBGCFG hDbgCfg, PFNRTDBGCFGLOG pfnCallback, void *pvUser)
+{
+ PRTDBGCFGINT pThis = hDbgCfg;
+ RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
+ AssertPtrNullReturn(pfnCallback, VERR_INVALID_POINTER);
+
+ int rc = RTCritSectRwEnterExcl(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ if ( pThis->pfnLogCallback == NULL
+ || pfnCallback == NULL
+ || pfnCallback == pThis->pfnLogCallback)
+ {
+ pThis->pfnLogCallback = NULL;
+ pThis->pvLogUser = NULL;
+ ASMCompilerBarrier(); /* paranoia */
+ pThis->pvLogUser = pvUser;
+ pThis->pfnLogCallback = pfnCallback;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_ACCESS_DENIED;
+ RTCritSectRwLeaveExcl(&pThis->CritSect);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Frees a string list.
+ *
+ * @param pList The list to free.
+ */
+static void rtDbgCfgFreeStrList(PRTLISTANCHOR pList)
+{
+ PRTDBGCFGSTR pCur;
+ PRTDBGCFGSTR pNext;
+ RTListForEachSafe(pList, pCur, pNext, RTDBGCFGSTR, ListEntry)
+ {
+ RTListNodeRemove(&pCur->ListEntry);
+ RTMemFree(pCur);
+ }
+}
+
+
+/**
+ * Make changes to a string list, given a semicolon separated input string.
+ *
+ * @returns VINF_SUCCESS, VERR_FILENAME_TOO_LONG, VERR_NO_MEMORY
+ * @param pThis The config instance.
+ * @param enmOp The change operation.
+ * @param pszValue The input strings separated by semicolon.
+ * @param fPaths Indicates that this is a path list and that we
+ * should look for srv and cache prefixes.
+ * @param pList The string list anchor.
+ */
+static int rtDbgCfgChangeStringList(PRTDBGCFGINT pThis, RTDBGCFGOP enmOp, const char *pszValue, bool fPaths,
+ PRTLISTANCHOR pList)
+{
+ RT_NOREF_PV(pThis); RT_NOREF_PV(fPaths);
+
+ if (enmOp == RTDBGCFGOP_SET)
+ rtDbgCfgFreeStrList(pList);
+
+ PRTLISTNODE pPrependTo = pList;
+ while (*pszValue)
+ {
+ /* Skip separators. */
+ while (*pszValue == ';')
+ pszValue++;
+ if (!*pszValue)
+ break;
+
+ /* Find the end of this path. */
+ const char *pchPath = pszValue++;
+ char ch;
+ while ((ch = *pszValue) && ch != ';')
+ pszValue++;
+ size_t cchPath = pszValue - pchPath;
+ if (cchPath >= UINT16_MAX)
+ return VERR_FILENAME_TOO_LONG;
+
+ if (enmOp == RTDBGCFGOP_REMOVE)
+ {
+ /*
+ * Remove all occurences.
+ */
+ PRTDBGCFGSTR pCur;
+ PRTDBGCFGSTR pNext;
+ RTListForEachSafe(pList, pCur, pNext, RTDBGCFGSTR, ListEntry)
+ {
+ if ( pCur->cch == cchPath
+ && !memcmp(pCur->sz, pchPath, cchPath))
+ {
+ RTListNodeRemove(&pCur->ListEntry);
+ RTMemFree(pCur);
+ }
+ }
+ }
+ else
+ {
+ /*
+ * We're adding a new one.
+ */
+ PRTDBGCFGSTR pNew = (PRTDBGCFGSTR)RTMemAlloc(RT_UOFFSETOF_DYN(RTDBGCFGSTR, sz[cchPath + 1]));
+ if (!pNew)
+ return VERR_NO_MEMORY;
+ pNew->cch = (uint16_t)cchPath;
+ pNew->fFlags = 0;
+ memcpy(pNew->sz, pchPath, cchPath);
+ pNew->sz[cchPath] = '\0';
+
+ if (enmOp == RTDBGCFGOP_PREPEND)
+ {
+ RTListNodeInsertAfter(pPrependTo, &pNew->ListEntry);
+ pPrependTo = &pNew->ListEntry;
+ }
+ else
+ RTListAppend(pList, &pNew->ListEntry);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Make changes to a 64-bit value
+ *
+ * @returns VINF_SUCCESS, VERR_DBG_CFG_INVALID_VALUE.
+ * @param pThis The config instance.
+ * @param enmOp The change operation.
+ * @param pszValue The input value.
+ * @param paMnemonics The mnemonics map for this value.
+ * @param puValue The value to change.
+ */
+static int rtDbgCfgChangeStringU64(PRTDBGCFGINT pThis, RTDBGCFGOP enmOp, const char *pszValue,
+ PCRTDBGCFGU64MNEMONIC paMnemonics, uint64_t *puValue)
+{
+ RT_NOREF_PV(pThis);
+
+ uint64_t uNew = enmOp == RTDBGCFGOP_SET ? 0 : *puValue;
+ char ch;
+ while ((ch = *pszValue))
+ {
+ /* skip whitespace and separators */
+ while (RT_C_IS_SPACE(ch) || RT_C_IS_CNTRL(ch) || ch == ';' || ch == ':')
+ ch = *++pszValue;
+ if (!ch)
+ break;
+
+ if (RT_C_IS_DIGIT(ch))
+ {
+ uint64_t uTmp;
+ int rc = RTStrToUInt64Ex(pszValue, (char **)&pszValue, 0, &uTmp);
+ if (RT_FAILURE(rc) || rc == VWRN_NUMBER_TOO_BIG)
+ return VERR_DBG_CFG_INVALID_VALUE;
+
+ if (enmOp != RTDBGCFGOP_REMOVE)
+ uNew |= uTmp;
+ else
+ uNew &= ~uTmp;
+ }
+ else
+ {
+ /* A mnemonic, find the end of it. */
+ const char *pszMnemonic = pszValue - 1;
+ do
+ ch = *++pszValue;
+ while (ch && !RT_C_IS_SPACE(ch) && !RT_C_IS_CNTRL(ch) && ch != ';' && ch != ':');
+ size_t cchMnemonic = pszValue - pszMnemonic;
+
+ /* Look it up in the map and apply it. */
+ unsigned i = 0;
+ while (paMnemonics[i].pszMnemonic)
+ {
+ if ( cchMnemonic == paMnemonics[i].cchMnemonic
+ && !memcmp(pszMnemonic, paMnemonics[i].pszMnemonic, cchMnemonic))
+ {
+ if (paMnemonics[i].fSet ? enmOp != RTDBGCFGOP_REMOVE : enmOp == RTDBGCFGOP_REMOVE)
+ uNew |= paMnemonics[i].fFlags;
+ else
+ uNew &= ~paMnemonics[i].fFlags;
+ break;
+ }
+ i++;
+ }
+
+ if (!paMnemonics[i].pszMnemonic)
+ return VERR_DBG_CFG_INVALID_VALUE;
+ }
+ }
+
+ *puValue = uNew;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTDbgCfgChangeString(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, RTDBGCFGOP enmOp, const char *pszValue)
+{
+ PRTDBGCFGINT pThis = hDbgCfg;
+ RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
+ AssertReturn(enmOp > RTDBGCFGOP_INVALID && enmOp < RTDBGCFGOP_END, VERR_INVALID_PARAMETER);
+ if (!pszValue)
+ pszValue = "";
+ else
+ AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
+
+ int rc = RTCritSectRwEnterExcl(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ switch (enmProp)
+ {
+ case RTDBGCFGPROP_FLAGS:
+ rc = rtDbgCfgChangeStringU64(pThis, enmOp, pszValue, g_aDbgCfgFlags, &pThis->fFlags);
+ break;
+ case RTDBGCFGPROP_PATH:
+ rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, true, &pThis->PathList);
+ break;
+ case RTDBGCFGPROP_SUFFIXES:
+ rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, false, &pThis->SuffixList);
+ break;
+ case RTDBGCFGPROP_SRC_PATH:
+ rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, true, &pThis->SrcPathList);
+ break;
+ default:
+ AssertFailed();
+ rc = VERR_INTERNAL_ERROR_3;
+ }
+
+ RTCritSectRwLeaveExcl(&pThis->CritSect);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTDbgCfgChangeUInt(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, RTDBGCFGOP enmOp, uint64_t uValue)
+{
+ PRTDBGCFGINT pThis = hDbgCfg;
+ RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
+ AssertReturn(enmOp > RTDBGCFGOP_INVALID && enmOp < RTDBGCFGOP_END, VERR_INVALID_PARAMETER);
+
+ int rc = RTCritSectRwEnterExcl(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ uint64_t *puValue = NULL;
+ switch (enmProp)
+ {
+ case RTDBGCFGPROP_FLAGS:
+ puValue = &pThis->fFlags;
+ break;
+ default:
+ rc = VERR_DBG_CFG_NOT_UINT_PROP;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ switch (enmOp)
+ {
+ case RTDBGCFGOP_SET:
+ *puValue = uValue;
+ break;
+ case RTDBGCFGOP_APPEND:
+ case RTDBGCFGOP_PREPEND:
+ *puValue |= uValue;
+ break;
+ case RTDBGCFGOP_REMOVE:
+ *puValue &= ~uValue;
+ break;
+ default:
+ AssertFailed();
+ rc = VERR_INTERNAL_ERROR_2;
+ }
+ }
+
+ RTCritSectRwLeaveExcl(&pThis->CritSect);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Querys a string list as a single string (semicolon separators).
+ *
+ * @returns VINF_SUCCESS, VERR_BUFFER_OVERFLOW.
+ * @param hDbgCfg The config instance handle.
+ * @param pList The string list anchor.
+ * @param pszValue The output buffer.
+ * @param cbValue The size of the output buffer.
+ */
+static int rtDbgCfgQueryStringList(RTDBGCFG hDbgCfg, PRTLISTANCHOR pList,
+ char *pszValue, size_t cbValue)
+{
+ RT_NOREF_PV(hDbgCfg);
+
+ /*
+ * Check the length first.
+ */
+ size_t cbReq = 1;
+ PRTDBGCFGSTR pCur;
+ RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry)
+ cbReq += pCur->cch + 1;
+ if (cbReq > cbValue)
+ return VERR_BUFFER_OVERFLOW;
+
+ /*
+ * Construct the string list in the buffer.
+ */
+ char *psz = pszValue;
+ RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry)
+ {
+ if (psz != pszValue)
+ *psz++ = ';';
+ memcpy(psz, pCur->sz, pCur->cch);
+ psz += pCur->cch;
+ }
+ *psz = '\0';
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Querys the string value of a 64-bit unsigned int.
+ *
+ * @returns VINF_SUCCESS, VERR_BUFFER_OVERFLOW.
+ * @param hDbgCfg The config instance handle.
+ * @param uValue The value to query.
+ * @param paMnemonics The mnemonics map for this value.
+ * @param pszValue The output buffer.
+ * @param cbValue The size of the output buffer.
+ */
+static int rtDbgCfgQueryStringU64(RTDBGCFG hDbgCfg, uint64_t uValue, PCRTDBGCFGU64MNEMONIC paMnemonics,
+ char *pszValue, size_t cbValue)
+{
+ RT_NOREF_PV(hDbgCfg);
+
+ /*
+ * If no mnemonics, just return the hex value.
+ */
+ if (!paMnemonics || paMnemonics[0].pszMnemonic)
+ {
+ char szTmp[64];
+ size_t cch = RTStrPrintf(szTmp, sizeof(szTmp), "%#x", uValue);
+ if (cch + 1 > cbValue)
+ return VERR_BUFFER_OVERFLOW;
+ memcpy(pszValue, szTmp, cbValue);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Check that there is sufficient buffer space first.
+ */
+ size_t cbReq = 1;
+ for (unsigned i = 0; paMnemonics[i].pszMnemonic; i++)
+ if ( paMnemonics[i].fSet
+ ? (paMnemonics[i].fFlags & uValue)
+ : !(paMnemonics[i].fFlags & uValue))
+ cbReq += (cbReq != 1) + paMnemonics[i].cchMnemonic;
+ if (cbReq > cbValue)
+ return VERR_BUFFER_OVERFLOW;
+
+ /*
+ * Construct the string.
+ */
+ char *psz = pszValue;
+ for (unsigned i = 0; paMnemonics[i].pszMnemonic; i++)
+ if ( paMnemonics[i].fSet
+ ? (paMnemonics[i].fFlags & uValue)
+ : !(paMnemonics[i].fFlags & uValue))
+ {
+ if (psz != pszValue)
+ *psz++ = ' ';
+ memcpy(psz, paMnemonics[i].pszMnemonic, paMnemonics[i].cchMnemonic);
+ psz += paMnemonics[i].cchMnemonic;
+ }
+ *psz = '\0';
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTDbgCfgQueryString(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, char *pszValue, size_t cbValue)
+{
+ PRTDBGCFGINT pThis = hDbgCfg;
+ RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
+
+ int rc = RTCritSectRwEnterShared(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ switch (enmProp)
+ {
+ case RTDBGCFGPROP_FLAGS:
+ rc = rtDbgCfgQueryStringU64(pThis, pThis->fFlags, g_aDbgCfgFlags, pszValue, cbValue);
+ break;
+ case RTDBGCFGPROP_PATH:
+ rc = rtDbgCfgQueryStringList(pThis, &pThis->PathList, pszValue, cbValue);
+ break;
+ case RTDBGCFGPROP_SUFFIXES:
+ rc = rtDbgCfgQueryStringList(pThis, &pThis->SuffixList, pszValue, cbValue);
+ break;
+ case RTDBGCFGPROP_SRC_PATH:
+ rc = rtDbgCfgQueryStringList(pThis, &pThis->SrcPathList, pszValue, cbValue);
+ break;
+ default:
+ AssertFailed();
+ rc = VERR_INTERNAL_ERROR_3;
+ }
+
+ RTCritSectRwLeaveShared(&pThis->CritSect);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTDbgCfgQueryUInt(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, uint64_t *puValue)
+{
+ PRTDBGCFGINT pThis = hDbgCfg;
+ RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(puValue, VERR_INVALID_POINTER);
+
+ int rc = RTCritSectRwEnterShared(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ switch (enmProp)
+ {
+ case RTDBGCFGPROP_FLAGS:
+ *puValue = pThis->fFlags;
+ break;
+ default:
+ rc = VERR_DBG_CFG_NOT_UINT_PROP;
+ }
+
+ RTCritSectRwLeaveShared(&pThis->CritSect);
+ }
+
+ return rc;
+}
+
+RTDECL(uint32_t) RTDbgCfgRetain(RTDBGCFG hDbgCfg)
+{
+ PRTDBGCFGINT pThis = hDbgCfg;
+ RTDBGCFG_VALID_RETURN_RC(pThis, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs < UINT32_MAX / 2);
+ return cRefs;
+}
+
+
+RTDECL(uint32_t) RTDbgCfgRelease(RTDBGCFG hDbgCfg)
+{
+ if (hDbgCfg == NIL_RTDBGCFG)
+ return 0;
+
+ PRTDBGCFGINT pThis = hDbgCfg;
+ RTDBGCFG_VALID_RETURN_RC(pThis, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ if (!cRefs)
+ {
+ /*
+ * Last reference - free all memory.
+ */
+ ASMAtomicWriteU32(&pThis->u32Magic, ~RTDBGCFG_MAGIC);
+ rtDbgCfgFreeStrList(&pThis->PathList);
+ rtDbgCfgFreeStrList(&pThis->SuffixList);
+ rtDbgCfgFreeStrList(&pThis->SrcPathList);
+#ifdef RT_OS_WINDOWS
+ rtDbgCfgFreeStrList(&pThis->NtSymbolPathList);
+ rtDbgCfgFreeStrList(&pThis->NtExecutablePathList);
+ rtDbgCfgFreeStrList(&pThis->NtSourcePath);
+#endif
+ RTCritSectRwDelete(&pThis->CritSect);
+ RTMemFree(pThis);
+ }
+ else
+ Assert(cRefs < UINT32_MAX / 2);
+ return cRefs;
+}
+
+
+RTDECL(int) RTDbgCfgCreate(PRTDBGCFG phDbgCfg, const char *pszEnvVarPrefix, bool fNativePaths)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(phDbgCfg, VERR_INVALID_POINTER);
+ if (pszEnvVarPrefix)
+ {
+ AssertPtrReturn(pszEnvVarPrefix, VERR_INVALID_POINTER);
+ AssertReturn(*pszEnvVarPrefix, VERR_INVALID_PARAMETER);
+ }
+
+ /*
+ * Allocate and initialize a new instance.
+ */
+ PRTDBGCFGINT pThis = (PRTDBGCFGINT)RTMemAllocZ(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ pThis->u32Magic = RTDBGCFG_MAGIC;
+ pThis->cRefs = 1;
+ RTListInit(&pThis->PathList);
+ RTListInit(&pThis->SuffixList);
+ RTListInit(&pThis->SrcPathList);
+#ifdef RT_OS_WINDOWS
+ RTListInit(&pThis->NtSymbolPathList);
+ RTListInit(&pThis->NtExecutablePathList);
+ RTListInit(&pThis->NtSourcePath);
+#endif
+
+ int rc = RTCritSectRwInit(&pThis->CritSect);
+ if (RT_FAILURE(rc))
+ {
+ RTMemFree(pThis);
+ return rc;
+ }
+
+ /*
+ * Read configurtion from the environment if requested to do so.
+ */
+ if (pszEnvVarPrefix || fNativePaths)
+ {
+ const size_t cbEnvVar = 256;
+ const size_t cbEnvVal = 65536 - cbEnvVar;
+ char *pszEnvVar = (char *)RTMemTmpAlloc(cbEnvVar + cbEnvVal);
+ if (pszEnvVar)
+ {
+ char *pszEnvVal = pszEnvVar + cbEnvVar;
+
+ if (pszEnvVarPrefix)
+ {
+ static struct
+ {
+ RTDBGCFGPROP enmProp;
+ const char *pszVar;
+ } const s_aProps[] =
+ {
+ { RTDBGCFGPROP_FLAGS, "FLAGS" },
+ { RTDBGCFGPROP_PATH, "PATH" },
+ { RTDBGCFGPROP_SUFFIXES, "SUFFIXES" },
+ { RTDBGCFGPROP_SRC_PATH, "SRC_PATH" },
+ };
+
+ for (unsigned i = 0; i < RT_ELEMENTS(s_aProps); i++)
+ {
+ size_t cchEnvVar = RTStrPrintf(pszEnvVar, cbEnvVar, "%s_%s", pszEnvVarPrefix, s_aProps[i].pszVar);
+ if (cchEnvVar >= cbEnvVar - 1)
+ {
+ rc = VERR_BUFFER_OVERFLOW;
+ break;
+ }
+
+ rc = RTEnvGetEx(RTENV_DEFAULT, pszEnvVar, pszEnvVal, cbEnvVal, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTDbgCfgChangeString(pThis, s_aProps[i].enmProp, RTDBGCFGOP_SET, pszEnvVal);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ else if (rc != VERR_ENV_VAR_NOT_FOUND)
+ break;
+ else
+ rc = VINF_SUCCESS;
+ }
+ }
+
+ /*
+ * Pick up system specific search paths.
+ */
+ if (RT_SUCCESS(rc) && fNativePaths)
+ {
+ struct
+ {
+ PRTLISTANCHOR pList;
+ const char *pszVar;
+ char chSep;
+ } aNativePaths[] =
+ {
+#ifdef RT_OS_WINDOWS
+ { &pThis->NtExecutablePathList, "_NT_EXECUTABLE_PATH", ';' },
+ { &pThis->NtSymbolPathList, "_NT_ALT_SYMBOL_PATH", ';' },
+ { &pThis->NtSymbolPathList, "_NT_SYMBOL_PATH", ';' },
+ { &pThis->NtSourcePath, "_NT_SOURCE_PATH", ';' },
+#endif
+ { NULL, NULL, 0 }
+ };
+ for (unsigned i = 0; aNativePaths[i].pList; i++)
+ {
+ Assert(aNativePaths[i].chSep == ';'); /* fix when needed */
+ rc = RTEnvGetEx(RTENV_DEFAULT, aNativePaths[i].pszVar, pszEnvVal, cbEnvVal, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtDbgCfgChangeStringList(pThis, RTDBGCFGOP_APPEND, pszEnvVal, true, aNativePaths[i].pList);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ else if (rc != VERR_ENV_VAR_NOT_FOUND)
+ break;
+ else
+ rc = VINF_SUCCESS;
+ }
+ }
+ RTMemTmpFree(pszEnvVar);
+ }
+ else
+ rc = VERR_NO_TMP_MEMORY;
+ if (RT_FAILURE(rc))
+ {
+ /*
+ * Error, bail out.
+ */
+ RTDbgCfgRelease(pThis);
+ return rc;
+ }
+ }
+
+ /*
+ * Returns successfully.
+ */
+ *phDbgCfg = pThis;
+
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/common/dbg/dbgmod.cpp b/src/VBox/Runtime/common/dbg/dbgmod.cpp
new file mode 100644
index 00000000..ac08f297
--- /dev/null
+++ b/src/VBox/Runtime/common/dbg/dbgmod.cpp
@@ -0,0 +1,2317 @@
+/* $Id: dbgmod.cpp $ */
+/** @file
+ * IPRT - Debug Module Interpreter.
+ */
+
+/*
+ * Copyright (C) 2009-2023 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 *
+*********************************************************************************************************************************/
+#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. */
+DECL_HIDDEN_DATA(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 IPRT_WITH_GHIDRA_DBG_MOD
+ if (RT_SUCCESS(rc))
+ rc = rtDbgModDebugInterpreterRegister(&g_rtDbgModVtDbgGhidra);
+#endif
+#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_FLAGS);
+
+ 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))
+ {
+ 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 */
+ rc = VERR_DBG_NO_MATCHING_INTERPRETER;
+ 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))
+ {
+ 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, 0 /*fLdrFlags*/);
+ 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 */
+ rc = VERR_DBG_NO_MATCHING_INTERPRETER;
+ 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;
+ int rc2 = pImg->pVt->pfnTryOpen(pDbgMod, RTLDRARCH_WHATEVER, 0 /*fLdrFlags*/);
+ if (RT_SUCCESS(rc2))
+ {
+ rc = rc2;
+ 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 /*cbDeferred*/, 0 /*fFlags*/, &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;
+ /** RTDBGMOD_F_XXX. */
+ uint32_t fFlags;
+} 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;
+ int rc2 = pImg->pVt->pfnTryOpen(pDbgMod, pArgs->enmArch,
+ pArgs->fFlags & RTDBGMOD_F_MACHO_LOAD_LINKEDIT ? RTLDR_O_MACHO_LOAD_LINKEDIT : 0);
+ if (RT_SUCCESS(rc2))
+ {
+ rc = rc2;
+ 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, uint32_t fFlags)
+{
+ 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;
+ Args.fFlags = fFlags;
+
+ /*
+ * 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, pDeferred->fFlags);
+}
+
+
+RTDECL(int) RTDbgModCreateFromMachOImage(PRTDBGMOD phDbgMod, const char *pszFilename, const char *pszName, RTLDRARCH enmArch,
+ PRTLDRMOD phLdrModIn, 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);
+ }
+ AssertPtrNullReturn(pUuid, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTDBGMOD_F_VALID_MASK), VERR_INVALID_FLAGS);
+
+ AssertPtrNullReturn(phLdrModIn, VERR_INVALID_POINTER);
+ RTLDRMOD hLdrModIn = phLdrModIn ? *phLdrModIn : NIL_RTLDRMOD;
+ AssertReturn(hLdrModIn == NIL_RTLDRMOD || RTLdrSize(hLdrModIn) != ~(size_t)0, VERR_INVALID_HANDLE);
+
+ AssertReturn(cbImage || cSegs || hLdrModIn != NIL_RTLDRMOD, 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);
+ }
+
+ /*
+ * If we got no UUID but the caller passed in a module handle, try
+ * query the UUID from it.
+ */
+ RTUUID UuidFromImage = RTUUID_INITIALIZE_NULL;
+ if ((!pUuid || RTUuidIsNull(pUuid)) && hLdrModIn != NIL_RTLDRMOD)
+ {
+ rc = RTLdrQueryProp(hLdrModIn, RTLDRPROP_UUID, &UuidFromImage, sizeof(UuidFromImage));
+ if (RT_SUCCESS(rc))
+ pUuid = &UuidFromImage;
+ }
+
+ /*
+ * 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)
+ || hLdrModIn != NIL_RTLDRMOD)
+ {
+ rc = rtDbgModFromMachOImageWorker(pDbgMod, enmArch, cbImage, cSegs, paSegs, pUuid, hDbgCfg, fFlags);
+ if (RT_FAILURE(rc) && hLdrModIn != NIL_RTLDRMOD)
+ {
+ /*
+ * Create module based on exports from hLdrModIn.
+ */
+ if (!cbImage)
+ cbImage = (uint32_t)RTLdrSize(hLdrModIn);
+ pDbgMod->pImgVt = &g_rtDbgModVtImgLdr;
+
+ rc = rtDbgModLdrOpenFromHandle(pDbgMod, hLdrModIn);
+ if (RT_SUCCESS(rc))
+ {
+ /* We now own the loader handle, so clear the caller variable. */
+ if (phLdrModIn)
+ *phLdrModIn = NIL_RTLDRMOD;
+
+ /** @todo delayed exports stuff */
+ rc = rtDbgModCreateForExports(pDbgMod);
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Procrastinate. Need image size atm.
+ */
+ PRTDBGMODDEFERRED pDeferred;
+ rc = rtDbgModDeferredCreate(pDbgMod, rtDbgModFromMachOImageDeferredCallback, cbImage, hDbgCfg,
+ RT_UOFFSETOF_DYN(RTDBGMODDEFERRED, u.MachO.aSegs[cSegs]),
+ 0 /*fFlags*/, &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);
+
diff --git a/src/VBox/Runtime/common/dbg/dbgmodcodeview.cpp b/src/VBox/Runtime/common/dbg/dbgmodcodeview.cpp
new file mode 100644
index 00000000..5ce270d0
--- /dev/null
+++ b/src/VBox/Runtime/common/dbg/dbgmodcodeview.cpp
@@ -0,0 +1,3197 @@
+/* $Id: dbgmodcodeview.cpp $ */
+/** @file
+ * IPRT - Debug Module Reader For Microsoft CodeView and COFF.
+ *
+ * Based on the following documentation (plus guess work and googling):
+ *
+ * - "Tools Interface Standard (TIS) Formats Specification for Windows",
+ * dated February 1993, version 1.0.
+ *
+ * - "Visual C++ 5.0 Symbolic Debug Information Specification" chapter of
+ * SPECS.CHM from MSDN Library October 2001.
+ *
+ * - "High Level Languages Debug Table Documentation", aka HLLDBG.HTML, aka
+ * IBMHLL.HTML, last changed 1996-07-08.
+ *
+ * Testcases using RTLdrFlt:
+ * - VBoxPcBios.sym at 0xf0000.
+ * - NT4 kernel PE image (coff syms).
+ */
+
+/*
+ * Copyright (C) 2013-2023 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 *
+*********************************************************************************************************************************/
+#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/err.h>
+#include <iprt/file.h>
+#include <iprt/latin1.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/sort.h>
+#include <iprt/string.h>
+#include <iprt/strcache.h>
+#include "internal/dbgmod.h"
+#include "internal/magics.h"
+
+#include <iprt/formats/codeview.h>
+#include <iprt/formats/pecoff.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * File type.
+ */
+typedef enum RTCVFILETYPE
+{
+ RTCVFILETYPE_INVALID = 0,
+ /** Executable image. */
+ RTCVFILETYPE_IMAGE,
+ /** A DBG-file with a IMAGE_SEPARATE_DEBUG_HEADER. */
+ RTCVFILETYPE_DBG,
+ /** A PDB file. */
+ RTCVFILETYPE_PDB,
+ /** Some other kind of file with CV at the end. */
+ RTCVFILETYPE_OTHER_AT_END,
+ /** The end of the valid values. */
+ RTCVFILETYPE_END,
+ /** Type blowup. */
+ RTCVFILETYPE_32BIT_HACK = 0x7fffffff
+} RTCVFILETYPE;
+
+
+/**
+ * CodeView debug info reader instance.
+ */
+typedef struct RTDBGMODCV
+{
+ /** Using a container for managing the debug info. */
+ RTDBGMOD hCnt;
+
+ /** @name Codeview details
+ * @{ */
+ /** The code view magic (used as format indicator). */
+ uint32_t u32CvMagic;
+ /** The offset of the CV debug info in the file. */
+ uint32_t offBase;
+ /** The size of the CV debug info. */
+ uint32_t cbDbgInfo;
+ /** The offset of the subsection directory (relative to offBase). */
+ uint32_t offDir;
+ /** @} */
+
+ /** @name COFF details.
+ * @{ */
+ /** Offset of the COFF header. */
+ uint32_t offCoffDbgInfo;
+ /** The size of the COFF debug info. */
+ uint32_t cbCoffDbgInfo;
+ /** The COFF debug info header. */
+ IMAGE_COFF_SYMBOLS_HEADER CoffHdr;
+ /** @} */
+
+ /** The file type. */
+ RTCVFILETYPE enmType;
+ /** The file handle (if external). */
+ RTFILE hFile;
+ /** Pointer to the module (no reference retained). */
+ PRTDBGMODINT pMod;
+
+ /** The image size, if we know it. This is 0 if we don't know it. */
+ uint32_t cbImage;
+
+ /** Indicates that we've loaded segments intot he container already. */
+ bool fHaveLoadedSegments;
+ /** Alternative address translation method for DOS frames. */
+ bool fHaveDosFrames;
+
+ /** @name Codeview Parsing state.
+ * @{ */
+ /** Number of directory entries. */
+ uint32_t cDirEnts;
+ /** The directory (converted to 32-bit). */
+ PRTCVDIRENT32 paDirEnts;
+ /** Current debugging style when parsing modules. */
+ uint16_t uCurStyle;
+ /** Current debugging style version (HLL only). */
+ uint16_t uCurStyleVer;
+
+ /** The segment map (if present). */
+ PRTCVSEGMAP pSegMap;
+ /** Segment names. */
+ char *pszzSegNames;
+ /** The size of the segment names. */
+ uint32_t cbSegNames;
+
+ /** Size of the block pchSrcStrings points to. */
+ size_t cbSrcStrings;
+ /** Buffer space allocated for the source string table. */
+ size_t cbSrcStringsAlloc;
+ /** Copy of the last CV8 source string table. */
+ char *pchSrcStrings;
+
+ /** The size of the current source information table. */
+ size_t cbSrcInfo;
+ /** Buffer space allocated for the source information table. */
+ size_t cbSrcInfoAlloc;
+ /** Copy of the last CV8 source information table. */
+ uint8_t *pbSrcInfo;
+
+ /** @} */
+
+} RTDBGMODCV;
+/** Pointer to a codeview debug info reader instance. */
+typedef RTDBGMODCV *PRTDBGMODCV;
+/** Pointer to a const codeview debug info reader instance. */
+typedef RTDBGMODCV *PCRTDBGMODCV;
+
+
+
+/**
+ * Subsection callback.
+ *
+ * @returns IPRT status code.
+ * @param pThis The CodeView debug info reader instance.
+ * @param pvSubSect Pointer to the subsection data.
+ * @param cbSubSect The size of the subsection data.
+ * @param pDirEnt The directory entry.
+ */
+typedef DECLCALLBACKTYPE(int, FNDBGMODCVSUBSECTCALLBACK,(PRTDBGMODCV pThis, void const *pvSubSect, size_t cbSubSect,
+ PCRTCVDIRENT32 pDirEnt));
+/** Pointer to a subsection callback. */
+typedef FNDBGMODCVSUBSECTCALLBACK *PFNDBGMODCVSUBSECTCALLBACK;
+
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Light weight assert + return w/ fixed status code. */
+#define RTDBGMODCV_CHECK_RET_BF(a_Expr, a_LogArgs) \
+ do { \
+ if (!(a_Expr)) \
+ { \
+ Log(("RTDbgCv: Check failed on line %d: " #a_Expr "\n", __LINE__)); \
+ Log(a_LogArgs); \
+ /*AssertFailed();*/ \
+ return VERR_CV_BAD_FORMAT; \
+ } \
+ } while (0)
+
+
+/** Light weight assert + return w/ fixed status code. */
+#define RTDBGMODCV_CHECK_NOMSG_RET_BF(a_Expr) \
+ do { \
+ if (!(a_Expr)) \
+ { \
+ Log(("RTDbgCv: Check failed on line %d: " #a_Expr "\n", __LINE__)); \
+ /*AssertFailed();*/ \
+ return VERR_CV_BAD_FORMAT; \
+ } \
+ } while (0)
+
+
+
+
+
+/**
+ * Reads CodeView information.
+ *
+ * @returns IPRT status code.
+ * @param pThis The CodeView reader instance.
+ * @param off The offset to start reading at, relative to the
+ * CodeView base header.
+ * @param pvBuf The buffer to read into.
+ * @param cb How many bytes to read.
+ */
+static int rtDbgModCvReadAt(PRTDBGMODCV pThis, uint32_t off, void *pvBuf, size_t cb)
+{
+ int rc;
+ if (pThis->hFile == NIL_RTFILE)
+ rc = pThis->pMod->pImgVt->pfnReadAt(pThis->pMod, UINT32_MAX, off + pThis->offBase, pvBuf, cb);
+ else
+ rc = RTFileReadAt(pThis->hFile, off + pThis->offBase, pvBuf, cb, NULL);
+ return rc;
+}
+
+
+/**
+ * Reads CodeView information into an allocated buffer.
+ *
+ * @returns IPRT status code.
+ * @param pThis The CodeView reader instance.
+ * @param off The offset to start reading at, relative to the
+ * CodeView base header.
+ * @param ppvBuf Where to return the allocated buffer on success.
+ * @param cb How many bytes to read.
+ */
+static int rtDbgModCvReadAtAlloc(PRTDBGMODCV pThis, uint32_t off, void **ppvBuf, size_t cb)
+{
+ int rc;
+ void *pvBuf = *ppvBuf = RTMemAlloc(cb);
+ if (pvBuf)
+ {
+ if (pThis->hFile == NIL_RTFILE)
+ rc = pThis->pMod->pImgVt->pfnReadAt(pThis->pMod, UINT32_MAX, off + pThis->offBase, pvBuf, cb);
+ else
+ rc = RTFileReadAt(pThis->hFile, off + pThis->offBase, pvBuf, cb, NULL);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+
+ RTMemFree(pvBuf);
+ *ppvBuf = NULL;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+
+#ifdef LOG_ENABLED
+/**
+ * Gets a name string for a subsection type.
+ *
+ * @returns Section name (read only).
+ * @param uSubSectType The subsection type.
+ */
+static const char *rtDbgModCvGetSubSectionName(uint16_t uSubSectType)
+{
+ switch (uSubSectType)
+ {
+ case kCvSst_OldModule: return "sstOldModule";
+ case kCvSst_OldPublic: return "sstOldPublic";
+ case kCvSst_OldTypes: return "sstOldTypes";
+ case kCvSst_OldSymbols: return "sstOldSymbols";
+ case kCvSst_OldSrcLines: return "sstOldSrcLines";
+ case kCvSst_OldLibraries: return "sstOldLibraries";
+ case kCvSst_OldImports: return "sstOldImports";
+ case kCvSst_OldCompacted: return "sstOldCompacted";
+ case kCvSst_OldSrcLnSeg: return "sstOldSrcLnSeg";
+ case kCvSst_OldSrcLines3: return "sstOldSrcLines3";
+
+ case kCvSst_Module: return "sstModule";
+ case kCvSst_Types: return "sstTypes";
+ case kCvSst_Public: return "sstPublic";
+ case kCvSst_PublicSym: return "sstPublicSym";
+ case kCvSst_Symbols: return "sstSymbols";
+ case kCvSst_AlignSym: return "sstAlignSym";
+ case kCvSst_SrcLnSeg: return "sstSrcLnSeg";
+ case kCvSst_SrcModule: return "sstSrcModule";
+ case kCvSst_Libraries: return "sstLibraries";
+ case kCvSst_GlobalSym: return "sstGlobalSym";
+ case kCvSst_GlobalPub: return "sstGlobalPub";
+ case kCvSst_GlobalTypes: return "sstGlobalTypes";
+ case kCvSst_MPC: return "sstMPC";
+ case kCvSst_SegMap: return "sstSegMap";
+ case kCvSst_SegName: return "sstSegName";
+ case kCvSst_PreComp: return "sstPreComp";
+ case kCvSst_PreCompMap: return "sstPreCompMap";
+ case kCvSst_OffsetMap16: return "sstOffsetMap16";
+ case kCvSst_OffsetMap32: return "sstOffsetMap32";
+ case kCvSst_FileIndex: return "sstFileIndex";
+ case kCvSst_StaticSym: return "sstStaticSym";
+ }
+ static char s_sz[32];
+ RTStrPrintf(s_sz, sizeof(s_sz), "Unknown%#x", uSubSectType);
+ return s_sz;
+}
+#endif /* LOG_ENABLED */
+
+
+#ifdef LOG_ENABLED
+/**
+ * Gets a name string for a symbol type.
+ *
+ * @returns symbol type name (read only).
+ * @param enmSymType The symbol type to name.
+ */
+static const char *rtDbgModCvSsSymTypeName(RTCVSYMTYPE enmSymType)
+{
+ switch (enmSymType)
+ {
+# define CASE_RET_STR(Name) case kCvSymType_##Name: return #Name;
+ CASE_RET_STR(Compile);
+ CASE_RET_STR(Register);
+ CASE_RET_STR(Constant);
+ CASE_RET_STR(UDT);
+ CASE_RET_STR(SSearch);
+ CASE_RET_STR(End);
+ CASE_RET_STR(Skip);
+ CASE_RET_STR(CVReserve);
+ CASE_RET_STR(ObjName);
+ CASE_RET_STR(EndArg);
+ CASE_RET_STR(CobolUDT);
+ CASE_RET_STR(ManyReg);
+ CASE_RET_STR(Return);
+ CASE_RET_STR(EntryThis);
+ CASE_RET_STR(BpRel16);
+ CASE_RET_STR(LData16);
+ CASE_RET_STR(GData16);
+ CASE_RET_STR(Pub16);
+ CASE_RET_STR(LProc16);
+ CASE_RET_STR(GProc16);
+ CASE_RET_STR(Thunk16);
+ CASE_RET_STR(BLock16);
+ CASE_RET_STR(With16);
+ CASE_RET_STR(Label16);
+ CASE_RET_STR(CExModel16);
+ CASE_RET_STR(VftPath16);
+ CASE_RET_STR(RegRel16);
+ CASE_RET_STR(BpRel32);
+ CASE_RET_STR(LData32);
+ CASE_RET_STR(GData32);
+ CASE_RET_STR(Pub32);
+ CASE_RET_STR(LProc32);
+ CASE_RET_STR(GProc32);
+ CASE_RET_STR(Thunk32);
+ CASE_RET_STR(Block32);
+ CASE_RET_STR(With32);
+ CASE_RET_STR(Label32);
+ CASE_RET_STR(CExModel32);
+ CASE_RET_STR(VftPath32);
+ CASE_RET_STR(RegRel32);
+ CASE_RET_STR(LThread32);
+ CASE_RET_STR(GThread32);
+ CASE_RET_STR(LProcMips);
+ CASE_RET_STR(GProcMips);
+ CASE_RET_STR(ProcRef);
+ CASE_RET_STR(DataRef);
+ CASE_RET_STR(Align);
+ CASE_RET_STR(LProcRef);
+ CASE_RET_STR(V2_Register);
+ CASE_RET_STR(V2_Constant);
+ CASE_RET_STR(V2_Udt);
+ CASE_RET_STR(V2_CobolUdt);
+ CASE_RET_STR(V2_ManyReg);
+ CASE_RET_STR(V2_BpRel);
+ CASE_RET_STR(V2_LData);
+ CASE_RET_STR(V2_GData);
+ CASE_RET_STR(V2_Pub);
+ CASE_RET_STR(V2_LProc);
+ CASE_RET_STR(V2_GProc);
+ CASE_RET_STR(V2_VftTable);
+ CASE_RET_STR(V2_RegRel);
+ CASE_RET_STR(V2_LThread);
+ CASE_RET_STR(V2_GThread);
+ CASE_RET_STR(V2_Unknown_1010);
+ CASE_RET_STR(V2_Unknown_1011);
+ CASE_RET_STR(V2_FrameInfo);
+ CASE_RET_STR(V2_Compliand);
+ CASE_RET_STR(V3_Compliand);
+ CASE_RET_STR(V3_Thunk);
+ CASE_RET_STR(V3_Block);
+ CASE_RET_STR(V3_Unknown_1104);
+ CASE_RET_STR(V3_Label);
+ CASE_RET_STR(V3_Register);
+ CASE_RET_STR(V3_Constant);
+ CASE_RET_STR(V3_Udt);
+ CASE_RET_STR(V3_Unknown_1109);
+ CASE_RET_STR(V3_Unknown_110a);
+ CASE_RET_STR(V3_BpRel);
+ CASE_RET_STR(V3_LData);
+ CASE_RET_STR(V3_GData);
+ CASE_RET_STR(V3_Pub);
+ CASE_RET_STR(V3_LProc);
+ CASE_RET_STR(V3_GProc);
+ CASE_RET_STR(V3_RegRel);
+ CASE_RET_STR(V3_LThread);
+ CASE_RET_STR(V3_GThread);
+ CASE_RET_STR(V3_Unknown_1114);
+ CASE_RET_STR(V3_Unknown_1115);
+ CASE_RET_STR(V3_MSTool);
+ CASE_RET_STR(V3_PubFunc1);
+ CASE_RET_STR(V3_PubFunc2);
+ CASE_RET_STR(V3_SectInfo);
+ CASE_RET_STR(V3_SubSectInfo);
+ CASE_RET_STR(V3_Entrypoint);
+ CASE_RET_STR(V3_Unknown_1139);
+ CASE_RET_STR(V3_SecuCookie);
+ CASE_RET_STR(V3_Unknown_113b);
+ CASE_RET_STR(V3_MsToolInfo);
+ CASE_RET_STR(V3_MsToolEnv);
+ CASE_RET_STR(VS2013_Local);
+ CASE_RET_STR(VS2013_FpOff);
+ CASE_RET_STR(VS2013_LProc32);
+ CASE_RET_STR(VS2013_GProc32);
+#undef CASE_RET_STR
+ case kCvSymType_EndOfValues: break;
+ }
+ return "<unknown type>";
+}
+#endif /* LOG_ENABLED */
+
+
+/**
+ * Adds a string to the g_hDbgModStrCache after sanitizing it.
+ *
+ * IPRT only deals with UTF-8 strings, so the string will be forced to UTF-8
+ * encoding. Also, codeview generally have length prefixed
+ *
+ * @returns String cached copy of the string.
+ * @param pch The string to copy to the cache.
+ * @param cch The length of the string. RTSTR_MAX if zero
+ * terminated.
+ */
+static const char *rtDbgModCvAddSanitizedStringToCache(const char *pch, size_t cch)
+{
+ /*
+ * If the string is valid UTF-8 and or the right length, we're good.
+ * This is usually the case.
+ */
+ const char *pszRet;
+ int rc;
+ if (cch != RTSTR_MAX)
+ rc = RTStrValidateEncodingEx(pch, cch, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
+ else
+ rc = RTStrValidateEncodingEx(pch, cch, 0);
+ if (RT_SUCCESS(rc))
+ pszRet = RTStrCacheEnterN(g_hDbgModStrCache, pch, cch);
+ else
+ {
+ /*
+ * Need to sanitize the string, so make a copy of it.
+ */
+ char *pszCopy = (char *)RTMemDupEx(pch, cch, 1);
+ AssertPtrReturn(pszCopy, NULL);
+
+ /* Deal with anyembedded zero chars. */
+ char *psz = RTStrEnd(pszCopy, cch);
+ while (psz)
+ {
+ *psz = '_';
+ psz = RTStrEnd(psz, cch - (psz - pszCopy));
+ }
+
+ /* Force valid UTF-8 encoding. */
+ RTStrPurgeEncoding(pszCopy);
+ Assert(strlen(pszCopy) == cch);
+
+ /* Enter it into the cache and free the temp copy. */
+ pszRet = RTStrCacheEnterN(g_hDbgModStrCache, pszCopy, cch);
+ RTMemFree(pszCopy);
+ }
+ return pszRet;
+}
+
+
+/**
+ * Translates a codeview segment and offset into our segment layout.
+ *
+ * @returns
+ * @param pThis .
+ * @param piSeg .
+ * @param poff .
+ */
+DECLINLINE(int) rtDbgModCvAdjustSegAndOffset(PRTDBGMODCV pThis, uint32_t *piSeg, uint64_t *poff)
+{
+ uint32_t iSeg = *piSeg;
+ if (iSeg == 0)
+ iSeg = RTDBGSEGIDX_ABS;
+ else if (pThis->pSegMap)
+ {
+ if (pThis->fHaveDosFrames)
+ {
+ if ( iSeg > pThis->pSegMap->Hdr.cSegs
+ || iSeg == 0)
+ return VERR_CV_BAD_FORMAT;
+ if (*poff <= pThis->pSegMap->aDescs[iSeg - 1].cb + pThis->pSegMap->aDescs[iSeg - 1].off)
+ *poff -= pThis->pSegMap->aDescs[iSeg - 1].off;
+ else
+ {
+ /* Workaround for VGABIOS where _DATA symbols like vgafont8 are
+ reported in the VGAROM segment. */
+ uint64_t uAddrSym = *poff + ((uint32_t)pThis->pSegMap->aDescs[iSeg - 1].iFrame << 4);
+ uint16_t j = pThis->pSegMap->Hdr.cSegs;
+ while (j-- > 0)
+ {
+ uint64_t uAddrFirst = (uint64_t)pThis->pSegMap->aDescs[j].off
+ + ((uint32_t)pThis->pSegMap->aDescs[j].iFrame << 4);
+ if (uAddrSym - uAddrFirst < pThis->pSegMap->aDescs[j].cb)
+ {
+ Log(("CV addr fix: %04x:%08x -> %04x:%08x\n", iSeg, *poff, j + 1, uAddrSym - uAddrFirst));
+ *poff = uAddrSym - uAddrFirst;
+ iSeg = j + 1;
+ break;
+ }
+ }
+ if (j == UINT16_MAX)
+ return VERR_CV_BAD_FORMAT;
+ }
+ }
+ else
+ {
+ if ( iSeg > pThis->pSegMap->Hdr.cSegs
+ || iSeg == 0
+ || *poff > pThis->pSegMap->aDescs[iSeg - 1].cb)
+ return VERR_CV_BAD_FORMAT;
+ *poff += pThis->pSegMap->aDescs[iSeg - 1].off;
+ }
+ if (pThis->pSegMap->aDescs[iSeg - 1].fFlags & RTCVSEGMAPDESC_F_ABS)
+ iSeg = RTDBGSEGIDX_ABS;
+ else
+ iSeg = pThis->pSegMap->aDescs[iSeg - 1].iGroup;
+ }
+ *piSeg = iSeg;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Adds a symbol to the container.
+ *
+ * @returns IPRT status code
+ * @param pThis The CodeView debug info reader instance.
+ * @param iSeg Segment number.
+ * @param off Offset into the segment
+ * @param pchName The symbol name (not necessarily terminated).
+ * @param cchName The symbol name length.
+ * @param fFlags Flags reserved for future exploits, MBZ.
+ * @param cbSym Symbol size, 0 if not available.
+ */
+static int rtDbgModCvAddSymbol(PRTDBGMODCV pThis, uint32_t iSeg, uint64_t off, const char *pchName,
+ uint32_t cchName, uint32_t fFlags, uint32_t cbSym)
+{
+ RT_NOREF_PV(fFlags);
+ const char *pszName = rtDbgModCvAddSanitizedStringToCache(pchName, cchName);
+ int rc;
+ if (pszName)
+ {
+#if 1
+ Log2(("CV Sym: %04x:%08x %.*s\n", iSeg, off, cchName, pchName));
+ rc = rtDbgModCvAdjustSegAndOffset(pThis, &iSeg, &off);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTDbgModSymbolAdd(pThis->hCnt, pszName, iSeg, off, cbSym, RTDBGSYMBOLADD_F_ADJUST_SIZES_ON_CONFLICT, NULL);
+
+ /* Simple duplicate symbol mangling, just to get more details. */
+ if (rc == VERR_DBG_DUPLICATE_SYMBOL && cchName < _2K)
+ {
+ char szTmpName[_2K + 96];
+ memcpy(szTmpName, pszName, cchName);
+ szTmpName[cchName] = '_';
+ for (uint32_t i = 1; i < 32; i++)
+ {
+ RTStrFormatU32(&szTmpName[cchName + 1], 80, i, 10, 0, 0, 0);
+ rc = RTDbgModSymbolAdd(pThis->hCnt, szTmpName, iSeg, off, cbSym, 0 /*fFlags*/, NULL);
+ if (rc != VERR_DBG_DUPLICATE_SYMBOL)
+ break;
+ }
+
+ }
+ else if (rc == VERR_DBG_ADDRESS_CONFLICT && cbSym)
+ rc = RTDbgModSymbolAdd(pThis->hCnt, pszName, iSeg, off, cbSym,
+ RTDBGSYMBOLADD_F_REPLACE_SAME_ADDR | RTDBGSYMBOLADD_F_ADJUST_SIZES_ON_CONFLICT, NULL);
+
+ Log(("Symbol: %04x:%08x %.*s [%Rrc]\n", iSeg, off, cchName, pchName, rc));
+ if (rc == VERR_DBG_ADDRESS_CONFLICT || rc == VERR_DBG_DUPLICATE_SYMBOL)
+ rc = VINF_SUCCESS;
+ }
+ else
+ Log(("Invalid segment index/offset %#06x:%08x for symbol %.*s\n", iSeg, off, cchName, pchName));
+
+#else
+ Log(("Symbol: %04x:%08x %.*s\n", iSeg, off, cchName, pchName));
+ rc = VINF_SUCCESS;
+#endif
+ RTStrCacheRelease(g_hDbgModStrCache, pszName);
+ }
+ else
+ rc = VERR_NO_STR_MEMORY;
+ return rc;
+}
+
+
+/**
+ * Validates the a zero terminated string.
+ *
+ * @returns String length if valid, UINT16_MAX if invalid.
+ * @param pszString The string to validate.
+ * @param pvRec The pointer to the record containing the string.
+ * @param cbRec The record length.
+ */
+static uint16_t rtDbgModCvValidateZeroString(const char *pszString, void const *pvRec, uint16_t cbRec)
+{
+ size_t offStrMember = (uintptr_t)pszString - (uintptr_t)pvRec;
+ AssertReturn(offStrMember < _1K, UINT16_MAX);
+ AssertReturn(offStrMember <= cbRec, UINT16_MAX);
+ cbRec -= (uint16_t)offStrMember;
+
+ const char *pchEnd = RTStrEnd(pszString, cbRec);
+ AssertReturn(pchEnd, UINT16_MAX);
+
+ int rc = RTStrValidateEncoding(pszString);
+ AssertRCReturn(rc, UINT16_MAX);
+
+ return (uint16_t)(pchEnd - pszString);
+}
+
+
+/**
+ * Parses a CV4 symbol table, adding symbols to the container.
+ *
+ * @returns IPRT status code
+ * @param pThis The CodeView debug info reader instance.
+ * @param pvSymTab The symbol table.
+ * @param cbSymTab The size of the symbol table.
+ * @param fFlags Flags reserved for future exploits, MBZ.
+ */
+static int rtDbgModCvSsProcessV4PlusSymTab(PRTDBGMODCV pThis, void const *pvSymTab, size_t cbSymTab, uint32_t fFlags)
+{
+ int rc = VINF_SUCCESS;
+ RTCPTRUNION uCursor;
+ uCursor.pv = pvSymTab;
+
+ RT_NOREF_PV(fFlags);
+
+ while (cbSymTab > 0 && RT_SUCCESS(rc))
+ {
+ uint8_t const * const pbRecStart = uCursor.pu8;
+ uint16_t cbRec = *uCursor.pu16++;
+ if (cbRec >= 2)
+ {
+ uint16_t uSymType = *uCursor.pu16++;
+
+ Log3((" %p: uSymType=%#06x LB %#x %s\n",
+ pbRecStart - (uint8_t *)pvSymTab, uSymType, cbRec, rtDbgModCvSsSymTypeName((RTCVSYMTYPE)uSymType)));
+ RTDBGMODCV_CHECK_RET_BF(cbRec >= 2 && cbRec <= cbSymTab, ("cbRec=%#x cbSymTab=%#x\n", cbRec, cbSymTab));
+
+ switch (uSymType)
+ {
+ case kCvSymType_LData16:
+ case kCvSymType_GData16:
+ case kCvSymType_Pub16:
+ {
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec > 2 + 2+2+2+1);
+ uint16_t off = *uCursor.pu16++;
+ uint16_t iSeg = *uCursor.pu16++;
+ /*uint16_t iType =*/ *uCursor.pu16++;
+ uint8_t cchName = *uCursor.pu8++;
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(cchName > 0);
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= 2 + 2+2+2+1 + cchName);
+
+ rc = rtDbgModCvAddSymbol(pThis, iSeg, off, uCursor.pch, cchName, 0, 0);
+ break;
+ }
+
+ case kCvSymType_LData32:
+ case kCvSymType_GData32:
+ case kCvSymType_Pub32:
+ {
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec > 2 + 4+2+2+1);
+ uint32_t off = *uCursor.pu32++;
+ uint16_t iSeg = *uCursor.pu16++;
+ /*uint16_t iType =*/ *uCursor.pu16++;
+ uint8_t cchName = *uCursor.pu8++;
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(cchName > 0);
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= 2 + 4+2+2+1 + cchName);
+
+ rc = rtDbgModCvAddSymbol(pThis, iSeg, off, uCursor.pch, cchName, 0, 0);
+ break;
+ }
+
+ case kCvSymType_LProc16:
+ case kCvSymType_GProc16:
+ {
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec > 2 + 4+4+4+2+2+2+2+2+2+1+1);
+ /*uint32_t uParent =*/ *uCursor.pu32++;
+ /*uint32_t uEnd =*/ *uCursor.pu32++;
+ /*uint32_t uNext =*/ *uCursor.pu32++;
+ uint16_t cbProc = *uCursor.pu16++;
+ /*uint16_t offDebugStart =*/ *uCursor.pu16++;
+ /*uint16_t offDebugEnd =*/ *uCursor.pu16++;
+ uint16_t off = *uCursor.pu16++;
+ uint16_t iSeg = *uCursor.pu16++;
+ /*uint16_t iProcType =*/ *uCursor.pu16++;
+ /*uint8_t fbType =*/ *uCursor.pu8++;
+ uint8_t cchName = *uCursor.pu8++;
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(cchName > 0);
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= 2 + 4+4+4+2+2+2+2+2+2+1+1 + cchName);
+
+ rc = rtDbgModCvAddSymbol(pThis, iSeg, off, uCursor.pch, cchName, 0, cbProc);
+ break;
+ }
+
+ case kCvSymType_LProc32:
+ case kCvSymType_GProc32:
+ {
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec > 2 + 4+4+4+4+4+4+4+2+2+1+1);
+ /*uint32_t uParent =*/ *uCursor.pu32++;
+ /*uint32_t uEnd =*/ *uCursor.pu32++;
+ /*uint32_t uNext =*/ *uCursor.pu32++;
+ /*uint32_t cbProc =*/ *uCursor.pu32++;
+ /*uint32_t offDebugStart =*/ *uCursor.pu32++;
+ /*uint32_t offDebugEnd =*/ *uCursor.pu32++;
+ uint32_t off = *uCursor.pu32++;
+ uint16_t iSeg = *uCursor.pu16++;
+ /*uint16_t iProcType =*/ *uCursor.pu16++;
+ /*uint8_t fbType =*/ *uCursor.pu8++;
+ uint8_t cchName = *uCursor.pu8++;
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(cchName > 0);
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= 2 + 4+4+4+4+4+4+4+2+2+1+1 + cchName);
+
+ rc = rtDbgModCvAddSymbol(pThis, iSeg, off, uCursor.pch, cchName, 0, 0);
+ break;
+ }
+
+ case kCvSymType_V3_Label:
+ {
+ PCRTCVSYMV3LABEL pLabel = (PCRTCVSYMV3LABEL)uCursor.pv;
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= sizeof(*pLabel));
+ uint16_t cchName = rtDbgModCvValidateZeroString(pLabel->szName, pLabel, cbRec);
+ if (cchName != UINT16_MAX && cchName > 0)
+ rc = rtDbgModCvAddSymbol(pThis, pLabel->iSection, pLabel->offSection, pLabel->szName, cchName, 0, 0);
+ else
+ Log3((" cchName=%#x sec:off=%#x:%#x %.*Rhxs\n",
+ cchName, pLabel->iSection, pLabel->offSection, cbRec, pLabel));
+ break;
+ }
+
+ case kCvSymType_V3_LData:
+ case kCvSymType_V3_GData:
+ case kCvSymType_V3_Pub:
+ {
+ PCRTCVSYMV3TYPEDNAME pData = (PCRTCVSYMV3TYPEDNAME)uCursor.pv;
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= sizeof(*pData));
+ uint16_t cchName = rtDbgModCvValidateZeroString(pData->szName, pData, cbRec);
+ if (cchName != UINT16_MAX && cchName > 0)
+ rc = rtDbgModCvAddSymbol(pThis, pData->iSection, pData->offSection, pData->szName, cchName, 0, 0);
+ else
+ Log3((" cchName=%#x sec:off=%#x:%#x idType=%#x %.*Rhxs\n",
+ cchName, pData->iSection, pData->offSection, pData->idType, cbRec, pData));
+ break;
+ }
+
+ case kCvSymType_V3_LProc:
+ case kCvSymType_V3_GProc:
+ {
+ PCRTCVSYMV3PROC pProc = (PCRTCVSYMV3PROC)uCursor.pv;
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= sizeof(*pProc));
+ uint16_t cchName = rtDbgModCvValidateZeroString(pProc->szName, pProc, cbRec);
+ if (cchName != UINT16_MAX && cchName > 0)
+ rc = rtDbgModCvAddSymbol(pThis, pProc->iSection, pProc->offSection, pProc->szName, cchName,
+ 0, pProc->cbProc);
+ else
+ Log3((" cchName=%#x sec:off=%#x:%#x LB %#x\n",
+ cchName, pProc->iSection, pProc->offSection, pProc->cbProc));
+ break;
+ }
+
+ }
+ }
+ /*else: shorter records can be used for alignment, I guess. */
+
+ /* next */
+ uCursor.pu8 = pbRecStart + cbRec + 2;
+ cbSymTab -= cbRec + 2;
+ }
+ return rc;
+}
+
+
+/**
+ * Makes a copy of the CV8 source string table.
+ *
+ * It will be references in a subsequent source information table, and again by
+ * line number tables thru that.
+ *
+ * @returns IPRT status code
+ * @param pThis The CodeView debug info reader instance.
+ * @param pvSrcStrings The source string table.
+ * @param cbSrcStrings The size of the source strings.
+ * @param fFlags Flags reserved for future exploits, MBZ.
+ */
+static int rtDbgModCvSsProcessV8SrcStrings(PRTDBGMODCV pThis, void const *pvSrcStrings, size_t cbSrcStrings, uint32_t fFlags)
+{
+ RT_NOREF_PV(fFlags);
+
+ if (pThis->cbSrcStrings)
+ Log(("\n!!More than one source file string table for this module!!\n\n"));
+
+ if (cbSrcStrings >= pThis->cbSrcStringsAlloc)
+ {
+ void *pvNew = RTMemRealloc(pThis->pchSrcStrings, cbSrcStrings + 1);
+ AssertReturn(pvNew, VERR_NO_MEMORY);
+ pThis->pchSrcStrings = (char *)pvNew;
+ pThis->cbSrcStringsAlloc = cbSrcStrings + 1;
+ }
+ memcpy(pThis->pchSrcStrings, pvSrcStrings, cbSrcStrings);
+ pThis->pchSrcStrings[cbSrcStrings] = '\0';
+ pThis->cbSrcStrings = cbSrcStrings;
+ Log2((" saved %#x bytes of CV8 source strings\n", cbSrcStrings));
+
+ if (LogIs3Enabled())
+ {
+ size_t iFile = 0;
+ size_t off = pThis->pchSrcStrings[0] != '\0' ? 0 : 1;
+ while (off < cbSrcStrings)
+ {
+ size_t cch = strlen(&pThis->pchSrcStrings[off]);
+ Log3((" %010zx #%03zu: %s\n", off, iFile, &pThis->pchSrcStrings[off]));
+ off += cch + 1;
+ iFile++;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Makes a copy of the CV8 source information table.
+ *
+ * It will be references in subsequent line number tables.
+ *
+ * @returns IPRT status code
+ * @param pThis The CodeView debug info reader instance.
+ * @param pvSrcInfo The source information table.
+ * @param cbSrcInfo The size of the source information table (bytes).
+ * @param fFlags Flags reserved for future exploits, MBZ.
+ */
+static int rtDbgModCvSsProcessV8SrcInfo(PRTDBGMODCV pThis, void const *pvSrcInfo, size_t cbSrcInfo, uint32_t fFlags)
+{
+ RT_NOREF_PV(fFlags);
+
+ if (pThis->cbSrcInfo)
+ Log(("\n!!More than one source file info table for this module!!\n\n"));
+
+ if (cbSrcInfo + sizeof(RTCV8SRCINFO) > pThis->cbSrcInfoAlloc)
+ {
+ void *pvNew = RTMemRealloc(pThis->pbSrcInfo, cbSrcInfo + sizeof(RTCV8SRCINFO));
+ AssertReturn(pvNew, VERR_NO_MEMORY);
+ pThis->pbSrcInfo = (uint8_t *)pvNew;
+ pThis->cbSrcInfoAlloc = cbSrcInfo + sizeof(RTCV8SRCINFO);
+ }
+ memcpy(pThis->pbSrcInfo, pvSrcInfo, cbSrcInfo);
+ memset(&pThis->pbSrcInfo[cbSrcInfo], 0, sizeof(RTCV8SRCINFO));
+ pThis->cbSrcInfo = cbSrcInfo;
+ Log2((" saved %#x bytes of CV8 source file info\n", cbSrcInfo));
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Makes a copy of the CV8 source string table.
+ *
+ * It will be references in subsequent line number tables.
+ *
+ * @returns IPRT status code
+ * @param pThis The CodeView debug info reader instance.
+ * @param pvSectLines The section source line table.
+ * @param cbSectLines The size of the section source line table.
+ * @param fFlags Flags reserved for future exploits, MBZ.
+ */
+static int rtDbgModCvSsProcessV8SectLines(PRTDBGMODCV pThis, void const *pvSectLines, size_t cbSectLines, uint32_t fFlags)
+{
+ RT_NOREF_PV(fFlags);
+
+ /*
+ * Starts with header.
+ */
+ PCRTCV8LINESHDR pHdr = (PCRTCV8LINESHDR)pvSectLines;
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSectLines >= sizeof(*pHdr));
+ cbSectLines -= sizeof(*pHdr);
+ Log2(("RTDbgModCv: seg #%u, off %#x LB %#x \n", pHdr->iSection, pHdr->offSection, pHdr->cbSectionCovered));
+
+ RTCPTRUNION uCursor;
+ uCursor.pv = pHdr + 1;
+ while (cbSectLines > 0)
+ {
+ /* Source file header. */
+ PCRTCV8LINESSRCMAP pSrcHdr = (PCRTCV8LINESSRCMAP)uCursor.pv;
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSectLines >= sizeof(*pSrcHdr));
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(pSrcHdr->cb == pSrcHdr->cLines * sizeof(RTCV8LINEPAIR) + sizeof(RTCV8LINESSRCMAP));
+ RTDBGMODCV_CHECK_RET_BF(!(pSrcHdr->offSourceInfo & 3), ("offSourceInfo=%#x\n", pSrcHdr->offSourceInfo));
+ if (pSrcHdr->offSourceInfo + sizeof(uint32_t) <= pThis->cbSrcInfo)
+ {
+ PCRTCV8SRCINFO pSrcInfo = (PCRTCV8SRCINFO)&pThis->pbSrcInfo[pSrcHdr->offSourceInfo];
+ const char *pszName = pSrcInfo->offSourceName < pThis->cbSrcStrings
+ ? &pThis->pchSrcStrings[pSrcInfo->offSourceName] : "unknown.c";
+ pszName = rtDbgModCvAddSanitizedStringToCache(pszName, RTSTR_MAX);
+ Log2(("RTDbgModCv: #%u lines, %#x bytes, %#x=%s\n", pSrcHdr->cLines, pSrcHdr->cb, pSrcInfo->offSourceName, pszName));
+
+ if (pszName)
+ {
+ /* Process the line/offset pairs. */
+ uint32_t cLeft = pSrcHdr->cLines;
+ PCRTCV8LINEPAIR pPair = (PCRTCV8LINEPAIR)(pSrcHdr + 1);
+ while (cLeft-- > 0)
+ {
+ uint32_t idxSeg = pHdr->iSection;
+ uint64_t off = pPair->offSection + pHdr->offSection;
+ int rc = rtDbgModCvAdjustSegAndOffset(pThis, &idxSeg, &off);
+ if (RT_SUCCESS(rc))
+ rc = RTDbgModLineAdd(pThis->hCnt, pszName, pPair->uLineNumber, idxSeg, off, NULL);
+ if (RT_SUCCESS(rc))
+ Log3(("RTDbgModCv: %#x:%#010llx %0u\n", idxSeg, off, pPair->uLineNumber));
+ else
+ Log(( "RTDbgModCv: %#x:%#010llx %0u - rc=%Rrc!! (org: idxSeg=%#x off=%#x)\n",
+ idxSeg, off, pPair->uLineNumber, rc, pHdr->iSection, pPair->offSection));
+
+ /* next */
+ pPair++;
+ }
+ Assert((uintptr_t)pPair - (uintptr_t)pSrcHdr == pSrcHdr->cb);
+ }
+ }
+ else
+ Log(("RTDbgModCv: offSourceInfo=%#x cbSrcInfo=%#x!\n", pSrcHdr->offSourceInfo, pThis->cbSrcInfo));
+
+ /* next */
+ cbSectLines -= pSrcHdr->cb;
+ uCursor.pu8 += pSrcHdr->cb;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+
+/**
+ * Parses a CV8 symbol table, adding symbols to the container.
+ *
+ * @returns IPRT status code
+ * @param pThis The CodeView debug info reader instance.
+ * @param pvSymTab The symbol table.
+ * @param cbSymTab The size of the symbol table.
+ * @param fFlags Flags reserved for future exploits, MBZ.
+ */
+static int rtDbgModCvSsProcessV8SymTab(PRTDBGMODCV pThis, void const *pvSymTab, size_t cbSymTab, uint32_t fFlags)
+{
+ size_t const cbSymTabSaved = cbSymTab;
+ int rc = VINF_SUCCESS;
+
+ /*
+ * First pass looks for source information and source strings tables.
+ * Microsoft puts the 0xf3 and 0xf4 last, usually with 0xf4 first.
+ *
+ * We ASSUME one string and one info table per module!
+ */
+ RTCPTRUNION uCursor;
+ uCursor.pv = pvSymTab;
+ for (;;)
+ {
+ RTDBGMODCV_CHECK_RET_BF(cbSymTab > sizeof(RTCV8SYMBOLSBLOCK), ("cbSymTab=%zu\n", cbSymTab));
+ PCRTCV8SYMBOLSBLOCK pBlockHdr = (PCRTCV8SYMBOLSBLOCK)uCursor.pv;
+ Log3((" %p: pass #1 uType=%#04x LB %#x\n", (uint8_t *)pBlockHdr - (uint8_t *)pvSymTab, pBlockHdr->uType, pBlockHdr->cb));
+ RTDBGMODCV_CHECK_RET_BF(pBlockHdr->cb <= cbSymTab - sizeof(RTCV8SYMBOLSBLOCK),
+ ("cb=%#u cbSymTab=%zu\n", pBlockHdr->cb, cbSymTab));
+
+ switch (pBlockHdr->uType)
+ {
+ case RTCV8SYMBLOCK_TYPE_SRC_STR:
+ rc = rtDbgModCvSsProcessV8SrcStrings(pThis, pBlockHdr + 1, pBlockHdr->cb, fFlags);
+ break;
+
+ case RTCV8SYMBLOCK_TYPE_SRC_INFO:
+ rc = rtDbgModCvSsProcessV8SrcInfo(pThis, pBlockHdr + 1, pBlockHdr->cb, fFlags);
+ break;
+
+ case RTCV8SYMBLOCK_TYPE_SECT_LINES:
+ case RTCV8SYMBLOCK_TYPE_SYMBOLS:
+ break;
+ default:
+ Log(("rtDbgModCvSsProcessV8SymTab: Unknown block type %#x (LB %#x)\n", pBlockHdr->uType, pBlockHdr->cb));
+ break;
+ }
+ uint32_t cbAligned = RT_ALIGN_32(sizeof(*pBlockHdr) + pBlockHdr->cb, 4);
+ if (RT_SUCCESS(rc) && cbSymTab > cbAligned)
+ {
+ uCursor.pu8 += cbAligned;
+ cbSymTab -= cbAligned;
+ }
+ else
+ break;
+ }
+
+ /*
+ * Log the source info now that we've gathered both it and the strings.
+ */
+ if (LogIs3Enabled() && pThis->cbSrcInfo)
+ {
+ Log3((" Source file info table:\n"));
+ size_t iFile = 0;
+ size_t off = 0;
+ while (off + 4 <= pThis->cbSrcInfo)
+ {
+ PCRTCV8SRCINFO pSrcInfo = (PCRTCV8SRCINFO)&pThis->pbSrcInfo[off];
+#ifdef LOG_ENABLED
+ const char *pszName = pSrcInfo->offSourceName < pThis->cbSrcStrings
+ ? &pThis->pchSrcStrings[pSrcInfo->offSourceName] : "out-of-bounds.c!";
+ if (pSrcInfo->uDigestType == RTCV8SRCINFO_DIGEST_TYPE_MD5)
+ Log3((" %010zx #%03zu: %RTuuid %#x=%s\n", off, iFile, &pSrcInfo->Digest.md5, pSrcInfo->offSourceName, pszName));
+ else if (pSrcInfo->uDigestType == RTCV8SRCINFO_DIGEST_TYPE_NONE)
+ Log3((" %010zx #%03zu: <none> %#x=%s\n", off, iFile, pSrcInfo->offSourceName, pszName));
+ else
+ Log3((" %010zx #%03zu: !%#x! %#x=%s\n", off, iFile, pSrcInfo->uDigestType, pSrcInfo->offSourceName, pszName));
+#endif
+ off += pSrcInfo->uDigestType == RTCV8SRCINFO_DIGEST_TYPE_MD5 ? sizeof(*pSrcInfo) : 8;
+ iFile++;
+ }
+ }
+
+ /*
+ * Second pass, process symbols and line numbers.
+ */
+ uCursor.pv = pvSymTab;
+ cbSymTab = cbSymTabSaved;
+ for (;;)
+ {
+ RTDBGMODCV_CHECK_RET_BF(cbSymTab > sizeof(RTCV8SYMBOLSBLOCK), ("cbSymTab=%zu\n", cbSymTab));
+ PCRTCV8SYMBOLSBLOCK pBlockHdr = (PCRTCV8SYMBOLSBLOCK)uCursor.pv;
+ Log3((" %p: pass #2 uType=%#04x LB %#x\n", (uint8_t *)pBlockHdr - (uint8_t *)pvSymTab, pBlockHdr->uType, pBlockHdr->cb));
+ RTDBGMODCV_CHECK_RET_BF(pBlockHdr->cb <= cbSymTab - sizeof(RTCV8SYMBOLSBLOCK),
+ ("cb=%#u cbSymTab=%zu\n", pBlockHdr->cb, cbSymTab));
+
+ switch (pBlockHdr->uType)
+ {
+ case RTCV8SYMBLOCK_TYPE_SYMBOLS:
+ rc = rtDbgModCvSsProcessV4PlusSymTab(pThis, pBlockHdr + 1, pBlockHdr->cb, fFlags);
+ break;
+
+ case RTCV8SYMBLOCK_TYPE_SECT_LINES:
+ rc = rtDbgModCvSsProcessV8SectLines(pThis, pBlockHdr + 1, pBlockHdr->cb, fFlags);
+ break;
+
+ case RTCV8SYMBLOCK_TYPE_SRC_INFO:
+ case RTCV8SYMBLOCK_TYPE_SRC_STR:
+ break;
+ default:
+ Log(("rtDbgModCvSsProcessV8SymTab: Unknown block type %#x (LB %#x)\n", pBlockHdr->uType, pBlockHdr->cb));
+ break;
+ }
+ uint32_t cbAligned = RT_ALIGN_32(sizeof(*pBlockHdr) + pBlockHdr->cb, 4);
+ if (RT_SUCCESS(rc) && cbSymTab > cbAligned)
+ {
+ uCursor.pu8 += cbAligned;
+ cbSymTab -= cbAligned;
+ }
+ else
+ break;
+ }
+ return rc;
+}
+
+
+/** @callback_method_impl{FNDBGMODCVSUBSECTCALLBACK,
+ * Parses kCvSst_GlobalPub\, kCvSst_GlobalSym and kCvSst_StaticSym subsections\,
+ * adding symbols it finds to the container.} */
+static DECLCALLBACK(int)
+rtDbgModCvSs_GlobalPub_GlobalSym_StaticSym(PRTDBGMODCV pThis, void const *pvSubSect, size_t cbSubSect, PCRTCVDIRENT32 pDirEnt)
+{
+ PCRTCVGLOBALSYMTABHDR pHdr = (PCRTCVGLOBALSYMTABHDR)pvSubSect;
+ RT_NOREF_PV(pDirEnt);
+
+ /*
+ * Quick data validation.
+ */
+ Log2(("RTDbgModCv: %s: uSymHash=%#x uAddrHash=%#x cbSymbols=%#x cbSymHash=%#x cbAddrHash=%#x\n",
+ rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType), pHdr->uSymHash,
+ pHdr->uAddrHash, pHdr->cbSymbols, pHdr->cbSymHash, pHdr->cbAddrHash));
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSubSect >= sizeof(RTCVGLOBALSYMTABHDR));
+ RTDBGMODCV_CHECK_NOMSG_RET_BF((uint64_t)pHdr->cbSymbols + pHdr->cbSymHash + pHdr->cbAddrHash <= cbSubSect - sizeof(*pHdr));
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(pHdr->uSymHash < 0x20);
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(pHdr->uAddrHash < 0x20);
+ if (!pHdr->cbSymbols)
+ return VINF_SUCCESS;
+
+ /*
+ * Parse the symbols.
+ */
+ return rtDbgModCvSsProcessV4PlusSymTab(pThis, pHdr + 1, pHdr->cbSymbols, 0);
+}
+
+
+/** @callback_method_impl{FNDBGMODCVSUBSECTCALLBACK,
+ * Parses kCvSst_Module subsection\, storing the debugging style in pThis.} */
+static DECLCALLBACK(int)
+rtDbgModCvSs_Module(PRTDBGMODCV pThis, void const *pvSubSect, size_t cbSubSect, PCRTCVDIRENT32 pDirEnt)
+{
+ RT_NOREF_PV(pDirEnt);
+
+ RTCPTRUNION uCursor;
+ uCursor.pv = pvSubSect;
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSubSect >= 2 + 2 + 2 + 2 + 0 + 1);
+ uint16_t iOverlay = *uCursor.pu16++; NOREF(iOverlay);
+ uint16_t iLib = *uCursor.pu16++; NOREF(iLib);
+ uint16_t cSegs = *uCursor.pu16++;
+ pThis->uCurStyle = *uCursor.pu16++;
+ if (pThis->uCurStyle == 0)
+ pThis->uCurStyle = RT_MAKE_U16('C', 'V');
+ pThis->uCurStyleVer = 0;
+ pThis->cbSrcInfo = 0;
+ pThis->cbSrcStrings = 0;
+ uint8_t cchName = uCursor.pu8[cSegs * 12];
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSubSect >= 2 + 2 + 2 + 2 + cSegs * 12U + 1 + cchName);
+
+#ifdef LOG_ENABLED
+ const char *pchName = (const char *)&uCursor.pu8[cSegs * 12 + 1];
+ Log2(("RTDbgModCv: Module: iOverlay=%#x iLib=%#x cSegs=%#x Style=%c%c (%#x) %.*s\n", iOverlay, iLib, cSegs,
+ RT_BYTE1(pThis->uCurStyle), RT_BYTE2(pThis->uCurStyle), pThis->uCurStyle, cchName, pchName));
+#endif
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(pThis->uCurStyle == RT_MAKE_U16('C', 'V'));
+
+#ifdef LOG_ENABLED
+ PCRTCVMODSEGINFO32 paSegs = (PCRTCVMODSEGINFO32)uCursor.pv;
+ for (uint16_t iSeg = 0; iSeg < cSegs; iSeg++)
+ Log2((" #%02u: %04x:%08x LB %08x\n", iSeg, paSegs[iSeg].iSeg, paSegs[iSeg].off, paSegs[iSeg].cb));
+#endif
+
+ return VINF_SUCCESS;
+}
+
+
+/** @callback_method_impl{FNDBGMODCVSUBSECTCALLBACK,
+ * Parses kCvSst_Symbols\, kCvSst_PublicSym and kCvSst_AlignSym subsections\,
+ * adding symbols it finds to the container.} */
+static DECLCALLBACK(int)
+rtDbgModCvSs_Symbols_PublicSym_AlignSym(PRTDBGMODCV pThis, void const *pvSubSect, size_t cbSubSect, PCRTCVDIRENT32 pDirEnt)
+{
+ RT_NOREF_PV(pDirEnt);
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(pThis->uCurStyle == RT_MAKE_U16('C', 'V'));
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSubSect >= 8);
+
+ uint32_t u32Signature = *(uint32_t const *)pvSubSect;
+ RTDBGMODCV_CHECK_RET_BF(u32Signature == RTCVSYMBOLS_SIGNATURE_CV4 || u32Signature == RTCVSYMBOLS_SIGNATURE_CV8,
+ ("%#x, expected %#x\n", u32Signature, RTCVSYMBOLS_SIGNATURE_CV4));
+ if (u32Signature == RTCVSYMBOLS_SIGNATURE_CV8)
+ return rtDbgModCvSsProcessV8SymTab(pThis, (uint8_t const *)pvSubSect + 4, cbSubSect - 4, 0);
+ return rtDbgModCvSsProcessV4PlusSymTab(pThis, (uint8_t const *)pvSubSect + 4, cbSubSect - 4, 0);
+}
+
+
+/** @callback_method_impl{FNDBGMODCVSUBSECTCALLBACK,
+ * Parses kCvSst_SrcModule adding line numbers it finds to the container.}
+ */
+static DECLCALLBACK(int)
+rtDbgModCvSs_SrcModule(PRTDBGMODCV pThis, void const *pvSubSect, size_t cbSubSect, PCRTCVDIRENT32 pDirEnt)
+{
+ RT_NOREF_PV(pDirEnt);
+ Log(("rtDbgModCvSs_SrcModule: uCurStyle=%#x\n%.*Rhxd\n", pThis->uCurStyle, cbSubSect, pvSubSect));
+
+ /* Check the header. */
+ PCRTCVSRCMODULE pHdr = (PCRTCVSRCMODULE)pvSubSect;
+ AssertReturn(cbSubSect >= RT_UOFFSETOF(RTCVSRCMODULE, aoffSrcFiles), VERR_CV_BAD_FORMAT);
+ size_t cbHdr = sizeof(RTCVSRCMODULE)
+ + pHdr->cFiles * sizeof(uint32_t)
+ + pHdr->cSegs * sizeof(uint32_t) * 2
+ + pHdr->cSegs * sizeof(uint16_t);
+ Log2(("RTDbgModCv: SrcModule: cFiles=%u cSegs=%u\n", pHdr->cFiles, pHdr->cFiles));
+ RTDBGMODCV_CHECK_RET_BF(cbSubSect >= cbHdr, ("cbSubSect=%#x cbHdr=%zx\n", cbSubSect, cbHdr));
+#ifdef LOG_ENABLED
+ if (LogIs2Enabled())
+ {
+ for (uint32_t i = 0; i < pHdr->cFiles; i++)
+ Log2(("RTDbgModCv: source file #%u: %#x\n", i, pHdr->aoffSrcFiles[i]));
+ PCRTCVSRCRANGE paSegRanges = (PCRTCVSRCRANGE)&pHdr->aoffSrcFiles[pHdr->cFiles];
+ uint16_t const *paidxSegs = (uint16_t const *)&paSegRanges[pHdr->cSegs];
+ for (uint32_t i = 0; i < pHdr->cSegs; i++)
+ Log2(("RTDbgModCv: seg #%u: %#010x-%#010x\n", paidxSegs[i], paSegRanges[i].offStart, paSegRanges[i].offEnd));
+ }
+#endif
+
+ /*
+ * Work over the source files.
+ */
+ for (uint32_t i = 0; i < pHdr->cFiles; i++)
+ {
+ uint32_t const offSrcFile = pHdr->aoffSrcFiles[i];
+ RTDBGMODCV_CHECK_RET_BF(cbSubSect - RT_UOFFSETOF(RTCVSRCFILE, aoffSrcLines) >= offSrcFile,
+ ("cbSubSect=%#x (- %#x) aoffSrcFiles[%u]=%#x\n",
+ cbSubSect, RT_UOFFSETOF(RTCVSRCFILE, aoffSrcLines), i, offSrcFile));
+ PCRTCVSRCFILE pSrcFile = (PCRTCVSRCFILE)((uint8_t const *)pvSubSect + offSrcFile);
+ size_t cbSrcFileHdr = RT_UOFFSETOF_DYN(RTCVSRCFILE, aoffSrcLines[pSrcFile->cSegs])
+ + sizeof(RTCVSRCRANGE) * pSrcFile->cSegs
+ + sizeof(uint8_t);
+ RTDBGMODCV_CHECK_RET_BF(cbSubSect >= offSrcFile + cbSrcFileHdr && cbSubSect > cbSrcFileHdr,
+ ("cbSubSect=%#x aoffSrcFiles[%u]=%#x cbSrcFileHdr=%#x\n", cbSubSect, offSrcFile, i, cbSrcFileHdr));
+ PCRTCVSRCRANGE paSegRanges = (PCRTCVSRCRANGE)&pSrcFile->aoffSrcLines[pSrcFile->cSegs];
+ uint8_t const *pcchName = (uint8_t const *)&paSegRanges[pSrcFile->cSegs]; /** @todo TIS NB09 docs say 16-bit length... */
+ const char *pchName = (const char *)(pcchName + 1);
+ RTDBGMODCV_CHECK_RET_BF(cbSubSect >= offSrcFile + cbSrcFileHdr + *pcchName,
+ ("cbSubSect=%#x offSrcFile=%#x cbSubSect=%#x *pcchName=%#x\n",
+ cbSubSect, offSrcFile, cbSubSect, *pcchName));
+ Log2(("RTDbgModCv: source file #%u/%#x: cSegs=%#x '%.*s'\n", i, offSrcFile, pSrcFile->cSegs, *pcchName, pchName));
+ const char *pszName = rtDbgModCvAddSanitizedStringToCache(pchName, *pcchName);
+
+ /*
+ * Work the segments this source file contributes code to.
+ */
+ for (uint32_t iSeg = 0; iSeg < pSrcFile->cSegs; iSeg++)
+ {
+ uint32_t const offSrcLine = pSrcFile->aoffSrcLines[iSeg];
+ RTDBGMODCV_CHECK_RET_BF(cbSubSect - RT_UOFFSETOF(RTCVSRCLINE, aoffLines) >= offSrcLine,
+ ("cbSubSect=%#x (- %#x) aoffSrcFiles[%u]=%#x\n",
+ cbSubSect, RT_UOFFSETOF(RTCVSRCLINE, aoffLines), iSeg, offSrcLine));
+ PCRTCVSRCLINE pSrcLine = (PCRTCVSRCLINE)((uint8_t const *)pvSubSect + offSrcLine);
+ size_t cbSrcLine = RT_UOFFSETOF_DYN(RTCVSRCLINE, aoffLines[pSrcLine->cPairs])
+ + pSrcLine->cPairs * sizeof(uint16_t);
+ RTDBGMODCV_CHECK_RET_BF(cbSubSect >= offSrcLine + cbSrcLine,
+ ("cbSubSect=%#x aoffSrcFiles[%u]=%#x cbSrcLine=%#x\n",
+ cbSubSect, iSeg, offSrcLine, cbSrcLine));
+ uint16_t const *paiLines = (uint16_t const *)&pSrcLine->aoffLines[pSrcLine->cPairs];
+ Log2(("RTDbgModCv: seg #%u, %u pairs (off %#x)\n", pSrcLine->idxSeg, pSrcLine->cPairs, offSrcLine));
+ for (uint32_t iPair = 0; iPair < pSrcLine->cPairs; iPair++)
+ {
+
+ uint32_t idxSeg = pSrcLine->idxSeg;
+ uint64_t off = pSrcLine->aoffLines[iPair];
+ int rc = rtDbgModCvAdjustSegAndOffset(pThis, &idxSeg, &off);
+ if (RT_SUCCESS(rc))
+ rc = RTDbgModLineAdd(pThis->hCnt, pszName, paiLines[iPair], idxSeg, off, NULL);
+ if (RT_SUCCESS(rc))
+ Log3(("RTDbgModCv: %#x:%#010llx %0u\n", idxSeg, off, paiLines[iPair]));
+ /* Note! Wlink produces the sstSrcModule subsections from LINNUM records, however the
+ CVGenLines() function assumes there is only one segment contributing to the
+ line numbers. So, when we do assembly that jumps between segments, it emits
+ the wrong addresses for some line numbers and we end up here, typically with
+ VERR_DBG_ADDRESS_CONFLICT. */
+ else
+ Log(( "RTDbgModCv: %#x:%#010llx %0u - rc=%Rrc!! (org: idxSeg=%#x off=%#x)\n",
+ idxSeg, off, paiLines[iPair], rc, pSrcLine->idxSeg, pSrcLine->aoffLines[iPair]));
+ }
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+static int rtDbgModCvLoadSegmentMap(PRTDBGMODCV pThis)
+{
+ /*
+ * Search for the segment map and segment names. They will be at the end of the directory.
+ */
+ uint32_t iSegMap = UINT32_MAX;
+ uint32_t iSegNames = UINT32_MAX;
+ uint32_t i = pThis->cDirEnts;
+ while (i-- > 0)
+ {
+ if ( pThis->paDirEnts[i].iMod != 0xffff
+ && pThis->paDirEnts[i].iMod != 0x0000)
+ break;
+ if (pThis->paDirEnts[i].uSubSectType == kCvSst_SegMap)
+ iSegMap = i;
+ else if (pThis->paDirEnts[i].uSubSectType == kCvSst_SegName)
+ iSegNames = i;
+ }
+ if (iSegMap == UINT32_MAX)
+ {
+ Log(("RTDbgModCv: No segment map present, using segment indexes as is then...\n"));
+ return VINF_SUCCESS;
+ }
+ RTDBGMODCV_CHECK_RET_BF(pThis->paDirEnts[iSegMap].cb >= sizeof(RTCVSEGMAPHDR),
+ ("Bad sstSegMap entry: cb=%#x\n", pThis->paDirEnts[iSegMap].cb));
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(iSegNames == UINT32_MAX || pThis->paDirEnts[iSegNames].cb > 0);
+
+ /*
+ * Read them into memory.
+ */
+ int rc = rtDbgModCvReadAtAlloc(pThis, pThis->paDirEnts[iSegMap].off, (void **)&pThis->pSegMap,
+ pThis->paDirEnts[iSegMap].cb);
+ if (iSegNames != UINT32_MAX && RT_SUCCESS(rc))
+ {
+ pThis->cbSegNames = pThis->paDirEnts[iSegNames].cb;
+ rc = rtDbgModCvReadAtAlloc(pThis, pThis->paDirEnts[iSegNames].off, (void **)&pThis->pszzSegNames,
+ pThis->paDirEnts[iSegNames].cb);
+ }
+ if (RT_FAILURE(rc))
+ return rc;
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(!pThis->pszzSegNames || !pThis->pszzSegNames[pThis->cbSegNames - 1]); /* must be terminated */
+
+ /* Use local pointers to avoid lots of indirection and typing. */
+ PCRTCVSEGMAPHDR pHdr = &pThis->pSegMap->Hdr;
+ PRTCVSEGMAPDESC paDescs = &pThis->pSegMap->aDescs[0];
+
+ /*
+ * If there are only logical segments, assume a direct mapping.
+ * PE images, like the NT4 kernel, does it like this.
+ */
+ bool const fNoGroups = pHdr->cSegs == pHdr->cLogSegs;
+
+ /*
+ * The PE image has an extra section/segment for the headers, the others
+ * doesn't. PE images doesn't have DOS frames. So, figure the image type now.
+ */
+ RTLDRFMT enmImgFmt = RTLDRFMT_INVALID;
+ if (pThis->pMod->pImgVt)
+ enmImgFmt = pThis->pMod->pImgVt->pfnGetFormat(pThis->pMod);
+
+ /*
+ * Validate and display it all.
+ */
+ Log2(("RTDbgModCv: SegMap: cSegs=%#x cLogSegs=%#x (cbSegNames=%#x)\n", pHdr->cSegs, pHdr->cLogSegs, pThis->cbSegNames));
+ RTDBGMODCV_CHECK_RET_BF(pThis->paDirEnts[iSegMap].cb >= sizeof(*pHdr) + pHdr->cSegs * sizeof(paDescs[0]),
+ ("SegMap is out of bounds: cbSubSect=%#x cSegs=%#x\n", pThis->paDirEnts[iSegMap].cb, pHdr->cSegs));
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(pHdr->cSegs >= pHdr->cLogSegs);
+
+ Log2(("Logical segment descriptors: %u\n", pHdr->cLogSegs));
+
+ bool fHaveDosFrames = false;
+ for (i = 0; i < pHdr->cSegs; i++)
+ {
+ if (i == pHdr->cLogSegs)
+ Log2(("Group/Physical descriptors: %u\n", pHdr->cSegs - pHdr->cLogSegs));
+ char szFlags[16];
+ memset(szFlags, '-', sizeof(szFlags));
+ if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_READ)
+ szFlags[0] = 'R';
+ if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_WRITE)
+ szFlags[1] = 'W';
+ if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_EXECUTE)
+ szFlags[2] = 'X';
+ if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_32BIT)
+ szFlags[3] = '3', szFlags[4] = '2';
+ if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_SEL)
+ szFlags[5] = 'S';
+ if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_ABS)
+ szFlags[6] = 'A';
+ if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_GROUP)
+ szFlags[7] = 'G';
+ szFlags[8] = '\0';
+ if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_RESERVED)
+ szFlags[8] = '!', szFlags[9] = '\0';
+ Log2((" #%02u: %#010x LB %#010x flags=%#06x ovl=%#06x group=%#06x frame=%#06x iSegName=%#06x iClassName=%#06x %s\n",
+ i < pHdr->cLogSegs ? i : i - pHdr->cLogSegs, paDescs[i].off, paDescs[i].cb, paDescs[i].fFlags, paDescs[i].iOverlay,
+ paDescs[i].iGroup, paDescs[i].iFrame, paDescs[i].offSegName, paDescs[i].offClassName, szFlags));
+
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].offSegName == UINT16_MAX || paDescs[i].offSegName < pThis->cbSegNames);
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].offClassName == UINT16_MAX || paDescs[i].offClassName < pThis->cbSegNames);
+ const char *pszName = paDescs[i].offSegName != UINT16_MAX
+ ? pThis->pszzSegNames + paDescs[i].offSegName
+ : NULL;
+ const char *pszClass = paDescs[i].offClassName != UINT16_MAX
+ ? pThis->pszzSegNames + paDescs[i].offClassName
+ : NULL;
+ if (pszName || pszClass)
+ Log2((" pszName=%s pszClass=%s\n", pszName, pszClass));
+
+ /* Validate the group link. */
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].iGroup == 0 || !(paDescs[i].fFlags & RTCVSEGMAPDESC_F_GROUP));
+ RTDBGMODCV_CHECK_NOMSG_RET_BF( paDescs[i].iGroup == 0
+ || ( paDescs[i].iGroup >= pHdr->cLogSegs
+ && paDescs[i].iGroup < pHdr->cSegs));
+ RTDBGMODCV_CHECK_NOMSG_RET_BF( paDescs[i].iGroup == 0
+ || (paDescs[paDescs[i].iGroup].fFlags & RTCVSEGMAPDESC_F_GROUP));
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(!(paDescs[i].fFlags & RTCVSEGMAPDESC_F_GROUP) || paDescs[i].off == 0); /* assumed below */
+
+ if (fNoGroups)
+ {
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].iGroup == 0);
+ if ( !fHaveDosFrames
+ && paDescs[i].iFrame != 0
+ && (paDescs[i].fFlags & (RTCVSEGMAPDESC_F_SEL | RTCVSEGMAPDESC_F_ABS))
+ && paDescs[i].iOverlay == 0
+ && enmImgFmt != RTLDRFMT_PE
+ && pThis->enmType != RTCVFILETYPE_DBG)
+ fHaveDosFrames = true; /* BIOS, only groups with frames. */
+ }
+ }
+
+ /*
+ * Further valiations based on fHaveDosFrames or not.
+ */
+ if (fNoGroups)
+ {
+ if (fHaveDosFrames)
+ for (i = 0; i < pHdr->cSegs; i++)
+ {
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].iOverlay == 0);
+ RTDBGMODCV_CHECK_NOMSG_RET_BF( (paDescs[i].fFlags & (RTCVSEGMAPDESC_F_SEL | RTCVSEGMAPDESC_F_ABS))
+ == RTCVSEGMAPDESC_F_SEL
+ || (paDescs[i].fFlags & (RTCVSEGMAPDESC_F_SEL | RTCVSEGMAPDESC_F_ABS))
+ == RTCVSEGMAPDESC_F_ABS);
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(!(paDescs[i].fFlags & RTCVSEGMAPDESC_F_ABS));
+ }
+ else
+ for (i = 0; i < pHdr->cSegs; i++)
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].off == 0);
+ }
+
+ /*
+ * Modify the groups index to be the loader segment index instead, also
+ * add the segments to the container if we haven't done that already.
+ */
+
+ /* Guess work: Group can be implicit if used. Observed Visual C++ v1.5,
+ omitting the CODE group. */
+ const char *pszGroup0 = NULL;
+ uint64_t cbGroup0 = 0;
+ if (!fNoGroups && !fHaveDosFrames)
+ {
+ for (i = 0; i < pHdr->cSegs; i++)
+ if ( !(paDescs[i].fFlags & (RTCVSEGMAPDESC_F_GROUP | RTCVSEGMAPDESC_F_ABS))
+ && paDescs[i].iGroup == 0)
+ {
+ if (pszGroup0 == NULL && paDescs[i].offClassName != UINT16_MAX)
+ pszGroup0 = pThis->pszzSegNames + paDescs[i].offClassName;
+ uint64_t offEnd = (uint64_t)paDescs[i].off + paDescs[i].cb;
+ if (offEnd > cbGroup0)
+ cbGroup0 = offEnd;
+ }
+ }
+
+ /* Add the segments.
+ Note! The RVAs derived from this exercise are all wrong. :-/
+ Note! We don't have an image loader, so we cannot add any fake sections. */
+ /** @todo Try see if we can figure something out from the frame value later. */
+ if (!pThis->fHaveLoadedSegments)
+ {
+ uint16_t iSeg = 0;
+ if (!fHaveDosFrames)
+ {
+ Assert(!pThis->pMod->pImgVt); Assert(pThis->enmType != RTCVFILETYPE_DBG);
+ uint64_t uRva = 0;
+ if (cbGroup0 && !fNoGroups)
+ {
+ rc = RTDbgModSegmentAdd(pThis->hCnt, 0, cbGroup0, pszGroup0 ? pszGroup0 : "Seg00", 0 /*fFlags*/, NULL);
+ uRva += cbGroup0;
+ iSeg++;
+ }
+
+ for (i = 0; RT_SUCCESS(rc) && i < pHdr->cSegs; i++)
+ if ((paDescs[i].fFlags & RTCVSEGMAPDESC_F_GROUP) || fNoGroups)
+ {
+ char szName[16];
+ char *pszName = szName;
+ if (paDescs[i].offSegName != UINT16_MAX)
+ pszName = pThis->pszzSegNames + paDescs[i].offSegName;
+ else
+ RTStrPrintf(szName, sizeof(szName), "Seg%02u", iSeg);
+ rc = RTDbgModSegmentAdd(pThis->hCnt, uRva, paDescs[i].cb, pszName, 0 /*fFlags*/, NULL);
+ uRva += paDescs[i].cb;
+ iSeg++;
+ }
+ }
+ else
+ {
+ /* The map is not sorted by RVA, very annoying, but I'm countering
+ by being lazy and slow about it. :-) Btw. this is the BIOS case. */
+ Assert(fNoGroups);
+#if 1 /** @todo need more inputs */
+
+ /* Figure image base address. */
+ uint64_t uImageBase = UINT64_MAX;
+ for (i = 0; RT_SUCCESS(rc) && i < pHdr->cSegs; i++)
+ {
+ uint64_t uAddr = (uint64_t)paDescs[i].off + ((uint32_t)paDescs[i].iFrame << 4);
+ if (uAddr < uImageBase)
+ uImageBase = uAddr;
+ }
+
+ /* Add the segments. */
+ uint64_t uMinAddr = uImageBase;
+ for (i = 0; RT_SUCCESS(rc) && i < pHdr->cSegs; i++)
+ {
+ /* Figure out the next one. */
+ uint16_t cOverlaps = 0;
+ uint16_t iBest = UINT16_MAX;
+ uint64_t uBestAddr = UINT64_MAX;
+ for (uint16_t j = 0; j < pHdr->cSegs; j++)
+ {
+ uint64_t uAddr = (uint64_t)paDescs[j].off + ((uint32_t)paDescs[j].iFrame << 4);
+ if (uAddr >= uMinAddr && uAddr < uBestAddr)
+ {
+ uBestAddr = uAddr;
+ iBest = j;
+ }
+ else if (uAddr == uBestAddr)
+ {
+ cOverlaps++;
+ if (paDescs[j].cb > paDescs[iBest].cb)
+ {
+ uBestAddr = uAddr;
+ iBest = j;
+ }
+ }
+ }
+ if (iBest == UINT16_MAX && RT_SUCCESS(rc))
+ {
+ rc = VERR_CV_IPE;
+ break;
+ }
+
+ /* Add it. */
+ char szName[16];
+ char *pszName = szName;
+ if (paDescs[iBest].offSegName != UINT16_MAX)
+ pszName = pThis->pszzSegNames + paDescs[iBest].offSegName;
+ else
+ RTStrPrintf(szName, sizeof(szName), "Seg%02u", iSeg);
+ RTDBGSEGIDX idxDbgSeg = NIL_RTDBGSEGIDX;
+ rc = RTDbgModSegmentAdd(pThis->hCnt, uBestAddr - uImageBase, paDescs[iBest].cb, pszName, 0 /*fFlags*/, &idxDbgSeg);
+ Log(("CV: %#010x LB %#010x %s uRVA=%#010x iBest=%u cOverlaps=%u [idxDbgSeg=%#x iSeg=%#x]\n",
+ uBestAddr, paDescs[iBest].cb, szName, uBestAddr - uImageBase, iBest, cOverlaps, idxDbgSeg, idxDbgSeg));
+
+ /* Update translations. */
+ paDescs[iBest].iGroup = iSeg;
+ if (cOverlaps > 0)
+ {
+ for (uint16_t j = 0; j < pHdr->cSegs; j++)
+ if ((uint64_t)paDescs[j].off + ((uint32_t)paDescs[j].iFrame << 4) == uBestAddr)
+ paDescs[iBest].iGroup = iSeg;
+ i += cOverlaps;
+ }
+
+ /* Advance. */
+ uMinAddr = uBestAddr + 1;
+ iSeg++;
+ }
+
+ pThis->fHaveDosFrames = true;
+#else
+ uint32_t iFrameFirst = UINT32_MAX;
+ uint16_t iSeg = 0;
+ uint32_t iFrameMin = 0;
+ do
+ {
+ /* Find next frame. */
+ uint32_t iFrame = UINT32_MAX;
+ for (uint16_t j = 0; j < pHdr->cSegs; j++)
+ if (paDescs[j].iFrame >= iFrameMin && paDescs[j].iFrame < iFrame)
+ iFrame = paDescs[j].iFrame;
+ if (iFrame == UINT32_MAX)
+ break;
+
+ /* Figure the frame span. */
+ uint32_t offFirst = UINT32_MAX;
+ uint64_t offEnd = 0;
+ for (uint16_t j = 0; j < pHdr->cSegs; j++)
+ if (paDescs[j].iFrame == iFrame)
+ {
+ uint64_t offThisEnd = paDescs[j].off + paDescs[j].cb;
+ if (offThisEnd > offEnd)
+ offEnd = offThisEnd;
+ if (paDescs[j].off < offFirst)
+ offFirst = paDescs[j].off;
+ }
+
+ if (offFirst < offEnd)
+ {
+ /* Add it. */
+ char szName[16];
+ RTStrPrintf(szName, sizeof(szName), "Frame_%04x", iFrame);
+ Log(("CV: %s offEnd=%#x offFirst=%#x\n", szName, offEnd, offFirst));
+ if (iFrameFirst == UINT32_MAX)
+ iFrameFirst = iFrame;
+ rc = RTDbgModSegmentAdd(pThis->hCnt, (iFrame - iFrameFirst) << 4, offEnd, szName, 0 /*fFlags*/, NULL);
+
+ /* Translation updates. */
+ for (uint16_t j = 0; j < pHdr->cSegs; j++)
+ if (paDescs[j].iFrame == iFrame)
+ {
+ paDescs[j].iGroup = iSeg;
+ paDescs[j].off = 0;
+ paDescs[j].cb = offEnd > UINT32_MAX ? UINT32_MAX : (uint32_t)offEnd;
+ }
+
+ iSeg++;
+ }
+
+ iFrameMin = iFrame + 1;
+ } while (RT_SUCCESS(rc));
+#endif
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ Log(("RTDbgModCv: %Rrc while adding segments from SegMap\n", rc));
+ return rc;
+ }
+
+ pThis->fHaveLoadedSegments = true;
+
+ /* Skip the stuff below if we have DOS frames since we did it all above. */
+ if (fHaveDosFrames)
+ return VINF_SUCCESS;
+ }
+
+ /* Pass one: Fixate the group segment indexes. */
+ uint16_t iSeg0 = enmImgFmt == RTLDRFMT_PE || pThis->enmType == RTCVFILETYPE_DBG ? 1 : 0;
+ uint16_t iSeg = iSeg0 + (cbGroup0 > 0); /** @todo probably wrong... */
+ for (i = 0; i < pHdr->cSegs; i++)
+ if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_ABS)
+ paDescs[i].iGroup = (uint16_t)(RTDBGSEGIDX_ABS & UINT16_MAX);
+ else if ((paDescs[i].fFlags & RTCVSEGMAPDESC_F_GROUP) || fNoGroups)
+ paDescs[i].iGroup = iSeg++;
+
+ /* Pass two: Resolve group references in to segment indexes. */
+ Log2(("Mapped segments (both kinds):\n"));
+ for (i = 0; i < pHdr->cSegs; i++)
+ {
+ if (!fNoGroups && !(paDescs[i].fFlags & (RTCVSEGMAPDESC_F_GROUP | RTCVSEGMAPDESC_F_ABS)))
+ paDescs[i].iGroup = paDescs[i].iGroup == 0 ? iSeg0 : paDescs[paDescs[i].iGroup].iGroup;
+
+ Log2((" #%02u: %#010x LB %#010x -> %#06x (flags=%#06x ovl=%#06x frame=%#06x)\n",
+ i, paDescs[i].off, paDescs[i].cb, paDescs[i].iGroup,
+ paDescs[i].fFlags, paDescs[i].iOverlay, paDescs[i].iFrame));
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @callback_method_impl{PFNRTSORTCMP,
+ * Used by rtDbgModCvLoadDirectory to sort the directory.}
+ */
+static DECLCALLBACK(int) rtDbgModCvDirEntCmp(void const *pvElement1, void const *pvElement2, void *pvUser)
+{
+ PRTCVDIRENT32 pEntry1 = (PRTCVDIRENT32)pvElement1;
+ PRTCVDIRENT32 pEntry2 = (PRTCVDIRENT32)pvElement2;
+ if (pEntry1->iMod < pEntry2->iMod)
+ return -1;
+ if (pEntry1->iMod > pEntry2->iMod)
+ return 1;
+ if (pEntry1->uSubSectType < pEntry2->uSubSectType)
+ return -1;
+ if (pEntry1->uSubSectType > pEntry2->uSubSectType)
+ return 1;
+
+ RT_NOREF_PV(pvUser);
+ return 0;
+}
+
+
+/**
+ * Loads the directory into memory (RTDBGMODCV::paDirEnts and
+ * RTDBGMODCV::cDirEnts).
+ *
+ * Converting old format version into the newer format to simplifying the code
+ * using the directory.
+ *
+ *
+ * @returns IPRT status code. (May leave with paDirEnts allocated on failure.)
+ * @param pThis The CV reader instance.
+ */
+static int rtDbgModCvLoadDirectory(PRTDBGMODCV pThis)
+{
+ /*
+ * Read in the CV directory.
+ */
+ int rc;
+ if ( pThis->u32CvMagic == RTCVHDR_MAGIC_NB00
+ || pThis->u32CvMagic == RTCVHDR_MAGIC_NB02)
+ {
+ /*
+ * 16-bit type.
+ */
+ RTCVDIRHDR16 DirHdr;
+ rc = rtDbgModCvReadAt(pThis, pThis->offDir, &DirHdr, sizeof(DirHdr));
+ if (RT_SUCCESS(rc))
+ {
+ if (DirHdr.cEntries > 2 && DirHdr.cEntries < _64K - 32U)
+ {
+ pThis->cDirEnts = DirHdr.cEntries;
+ pThis->paDirEnts = (PRTCVDIRENT32)RTMemAlloc(DirHdr.cEntries * sizeof(pThis->paDirEnts[0]));
+ if (pThis->paDirEnts)
+ {
+ rc = rtDbgModCvReadAt(pThis, pThis->offDir + sizeof(DirHdr),
+ pThis->paDirEnts, DirHdr.cEntries * sizeof(RTCVDIRENT16));
+ if (RT_SUCCESS(rc))
+ {
+ /* Convert the entries (from the end). */
+ uint32_t cLeft = DirHdr.cEntries;
+ RTCVDIRENT32 volatile *pDst = pThis->paDirEnts + cLeft;
+ RTCVDIRENT16 volatile *pSrc = (RTCVDIRENT16 volatile *)pThis->paDirEnts + cLeft;
+ while (cLeft--)
+ {
+ pDst--;
+ pSrc--;
+
+ pDst->cb = pSrc->cb;
+ pDst->off = RT_MAKE_U32(pSrc->offLow, pSrc->offHigh);
+ pDst->iMod = pSrc->iMod;
+ pDst->uSubSectType = pSrc->uSubSectType;
+ }
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ {
+ Log(("Old CV directory count is out of considered valid range: %#x\n", DirHdr.cEntries));
+ rc = VERR_CV_BAD_FORMAT;
+ }
+ }
+ }
+ else
+ {
+ /*
+ * 32-bit type (reading too much for NB04 is no problem).
+ *
+ * Note! The watcom linker (v1.9) seems to overwrite the directory
+ * header and more under some conditions. So, if this code fails
+ * you might be so lucky as to have reproduce that issue...
+ */
+ RTCVDIRHDR32EX DirHdr;
+ rc = rtDbgModCvReadAt(pThis, pThis->offDir, &DirHdr, sizeof(DirHdr));
+ if (RT_SUCCESS(rc))
+ {
+ if ( DirHdr.Core.cbHdr != sizeof(DirHdr.Core)
+ && DirHdr.Core.cbHdr != sizeof(DirHdr))
+ {
+ Log(("Unexpected CV directory size: %#x [wlink screwup?]\n", DirHdr.Core.cbHdr));
+ rc = VERR_CV_BAD_FORMAT;
+ }
+ if ( DirHdr.Core.cbHdr == sizeof(DirHdr)
+ && ( DirHdr.offNextDir != 0
+ || DirHdr.fFlags != 0) )
+ {
+ Log(("Extended CV directory headers fields are not zero: fFlags=%#x offNextDir=%#x [wlink screwup?]\n",
+ DirHdr.fFlags, DirHdr.offNextDir));
+ rc = VERR_CV_BAD_FORMAT;
+ }
+ if (DirHdr.Core.cbEntry != sizeof(RTCVDIRENT32))
+ {
+ Log(("Unexpected CV directory entry size: %#x (expected %#x) [wlink screwup?]\n", DirHdr.Core.cbEntry, sizeof(RTCVDIRENT32)));
+ rc = VERR_CV_BAD_FORMAT;
+ }
+ if (DirHdr.Core.cEntries < 2 || DirHdr.Core.cEntries >= _512K)
+ {
+ Log(("CV directory count is out of considered valid range: %#x [wlink screwup?]\n", DirHdr.Core.cEntries));
+ rc = VERR_CV_BAD_FORMAT;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ pThis->cDirEnts = DirHdr.Core.cEntries;
+ pThis->paDirEnts = (PRTCVDIRENT32)RTMemAlloc(DirHdr.Core.cEntries * sizeof(pThis->paDirEnts[0]));
+ if (pThis->paDirEnts)
+ rc = rtDbgModCvReadAt(pThis, pThis->offDir + DirHdr.Core.cbHdr,
+ pThis->paDirEnts, DirHdr.Core.cEntries * sizeof(RTCVDIRENT32));
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t const cbDbgInfo = pThis->cbDbgInfo;
+ uint32_t const cDirEnts = pThis->cDirEnts;
+
+ /*
+ * Just sort the directory in a way we like, no need to make
+ * complicated demands on the linker output.
+ */
+ RTSortShell(pThis->paDirEnts, cDirEnts, sizeof(pThis->paDirEnts[0]), rtDbgModCvDirEntCmp, NULL);
+
+ /*
+ * Basic info validation.
+ */
+ uint16_t cGlobalMods = 0;
+ uint16_t cNormalMods = 0;
+ uint16_t iModLast = 0;
+ Log2(("RTDbgModCv: %u (%#x) directory entries:\n", cDirEnts, cDirEnts));
+ for (uint32_t i = 0; i < cDirEnts; i++)
+ {
+ PCRTCVDIRENT32 pDirEnt = &pThis->paDirEnts[i];
+ Log2((" #%04u mod=%#06x sst=%#06x at %#010x LB %#07x %s\n",
+ i, pDirEnt->iMod, pDirEnt->uSubSectType, pDirEnt->off, pDirEnt->cb,
+ rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType)));
+
+ if ( pDirEnt->off >= cbDbgInfo
+ || pDirEnt->cb >= cbDbgInfo
+ || pDirEnt->off + pDirEnt->cb > cbDbgInfo)
+ {
+ Log(("CV directory entry #%u is out of bounds: %#x LB %#x, max %#x\n", i, pDirEnt->off, pDirEnt->cb, cbDbgInfo));
+ rc = VERR_CV_BAD_FORMAT;
+ }
+ if ( pDirEnt->iMod == 0
+ && pThis->u32CvMagic != RTCVHDR_MAGIC_NB04
+ && pThis->u32CvMagic != RTCVHDR_MAGIC_NB02
+ && pThis->u32CvMagic != RTCVHDR_MAGIC_NB00)
+ {
+ Log(("CV directory entry #%u uses module index 0 (uSubSectType=%#x)\n", i, pDirEnt->uSubSectType));
+ rc = VERR_CV_BAD_FORMAT;
+ }
+ if (pDirEnt->iMod == 0 || pDirEnt->iMod == 0xffff)
+ cGlobalMods++;
+ else
+ {
+ if (pDirEnt->iMod > iModLast)
+ {
+ if ( pDirEnt->uSubSectType != kCvSst_Module
+ && pDirEnt->uSubSectType != kCvSst_OldModule)
+ {
+ Log(("CV directory entry #%u: expected module subsection first, found %s (%#x)\n",
+ i, rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType), pDirEnt->uSubSectType));
+ rc = VERR_CV_BAD_FORMAT;
+ }
+ if (pDirEnt->iMod != iModLast + 1)
+ {
+ Log(("CV directory entry #%u: skips from mod %#x to %#x modules\n", i, iModLast, pDirEnt->iMod));
+ rc = VERR_CV_BAD_FORMAT;
+ }
+ iModLast = pDirEnt->iMod;
+ }
+ cNormalMods++;
+ }
+ }
+ if (cGlobalMods == 0)
+ {
+ Log(("CV directory contains no global modules\n"));
+ rc = VERR_CV_BAD_FORMAT;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ Log(("CV dir stats: %u total, %u normal, %u special, iModLast=%#x (%u)\n",
+ cDirEnts, cNormalMods, cGlobalMods, iModLast, iModLast));
+
+#if 0 /* skip this stuff */
+ /*
+ * Validate the directory ordering.
+ */
+ uint16_t i = 0;
+
+ /* Normal modules. */
+ if (pThis->enmDirOrder != RTCVDIRORDER_BY_SST_MOD)
+ {
+ uint16_t iEndNormalMods = cNormalMods + (pThis->enmDirOrder == RTCVDIRORDER_BY_MOD_0 ? cGlobalMods : 0);
+ while (i < iEndNormalMods)
+ {
+ if (pThis->paDirEnts[i].iMod == 0 || pThis->paDirEnts[i].iMod == 0xffff)
+ {
+ Log(("CV directory entry #%u: Unexpected global module entry.\n", i));
+ rc = VERR_CV_BAD_FORMAT;
+ }
+ i++;
+ }
+ }
+ else
+ {
+ uint32_t fSeen = RT_BIT_32(kCvSst_Module - kCvSst_Module)
+ | RT_BIT_32(kCvSst_Libraries - kCvSst_Module)
+ | RT_BIT_32(kCvSst_GlobalSym - kCvSst_Module)
+ | RT_BIT_32(kCvSst_GlobalPub - kCvSst_Module)
+ | RT_BIT_32(kCvSst_GlobalTypes - kCvSst_Module)
+ | RT_BIT_32(kCvSst_SegName - kCvSst_Module)
+ | RT_BIT_32(kCvSst_SegMap - kCvSst_Module)
+ | RT_BIT_32(kCvSst_StaticSym - kCvSst_Module)
+ | RT_BIT_32(kCvSst_FileIndex - kCvSst_Module)
+ | RT_BIT_32(kCvSst_MPC - kCvSst_Module);
+ uint16_t iMod = 0;
+ uint16_t uSst = kCvSst_Module;
+ while (i < cNormalMods)
+ {
+ PCRTCVDIRENT32 pDirEnt = &pThis->paDirEnts[i];
+ if ( pDirEnt->iMod > iMod
+ || pDirEnt->iMod == iMod) /* wlink subjected to MSVC 2010 /Z7 files with multiple .debug$S. */
+ {
+ if (pDirEnt->uSubSectType != uSst)
+ {
+ Log(("CV directory entry #%u: Expected %s (%#x), found %s (%#x).\n",
+ i, rtDbgModCvGetSubSectionName(uSst), uSst,
+ rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType), pDirEnt->uSubSectType));
+ rc = VERR_CV_BAD_FORMAT;
+ }
+ }
+ else
+ {
+ uint32_t iBit = pDirEnt->uSubSectType - kCvSst_Module;
+ if (iBit >= 32U || (fSeen & RT_BIT_32(iBit)))
+ {
+ Log(("CV directory entry #%u: SST %s (%#x) has already been seen or is for globals.\n",
+ i, rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType), pDirEnt->uSubSectType));
+ rc = VERR_CV_BAD_FORMAT;
+ }
+ fSeen |= RT_BIT_32(iBit);
+ }
+
+ uSst = pDirEnt->uSubSectType;
+ iMod = pDirEnt->iMod;
+ i++;
+ }
+ }
+
+ /* New style with special modules at the end. */
+ if (pThis->enmDirOrder != RTCVDIRORDER_BY_MOD_0)
+ while (i < cDirEnts)
+ {
+ if (pThis->paDirEnts[i].iMod != 0 && pThis->paDirEnts[i].iMod != 0xffff)
+ {
+ Log(("CV directory entry #%u: Expected global module entry, not %#x.\n", i,
+ pThis->paDirEnts[i].iMod));
+ rc = VERR_CV_BAD_FORMAT;
+ }
+ i++;
+ }
+#endif
+ }
+ }
+
+ return rc;
+}
+
+
+static int rtDbgModCvLoadCodeViewInfo(PRTDBGMODCV pThis)
+{
+ /*
+ * Load the directory, the segment map (if any) and then scan for segments
+ * if necessary.
+ */
+ int rc = rtDbgModCvLoadDirectory(pThis);
+ if (RT_SUCCESS(rc))
+ rc = rtDbgModCvLoadSegmentMap(pThis);
+ if (RT_SUCCESS(rc) && !pThis->fHaveLoadedSegments)
+ {
+ rc = VERR_CV_TODO; /** @todo Scan anything containing address, in particular sstSegMap and sstModule,
+ * and reconstruct the segments from that information. */
+ pThis->cbImage = 0x1000;
+ rc = VINF_SUCCESS;
+ }
+
+ /*
+ * Process the directory.
+ */
+ for (uint32_t i = 0; RT_SUCCESS(rc) && i < pThis->cDirEnts; i++)
+ {
+ PCRTCVDIRENT32 pDirEnt = &pThis->paDirEnts[i];
+ Log3(("Processing module %#06x subsection #%04u %s\n", pDirEnt->iMod, i, rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType)));
+ PFNDBGMODCVSUBSECTCALLBACK pfnCallback = NULL;
+ switch (pDirEnt->uSubSectType)
+ {
+ case kCvSst_GlobalPub:
+ case kCvSst_GlobalSym:
+ case kCvSst_StaticSym:
+ pfnCallback = rtDbgModCvSs_GlobalPub_GlobalSym_StaticSym;
+ break;
+ case kCvSst_Module:
+ pfnCallback = rtDbgModCvSs_Module;
+ break;
+ case kCvSst_PublicSym:
+ case kCvSst_Symbols:
+ case kCvSst_AlignSym:
+ pfnCallback = rtDbgModCvSs_Symbols_PublicSym_AlignSym;
+ break;
+
+ case kCvSst_OldModule:
+ case kCvSst_OldPublic:
+ case kCvSst_OldTypes:
+ case kCvSst_OldSymbols:
+ case kCvSst_OldSrcLines:
+ case kCvSst_OldLibraries:
+ case kCvSst_OldImports:
+ case kCvSst_OldCompacted:
+ case kCvSst_OldSrcLnSeg:
+ case kCvSst_OldSrcLines3:
+ /** @todo implement more. */
+ break;
+
+ case kCvSst_Types:
+ case kCvSst_Public:
+ case kCvSst_SrcLnSeg:
+ /** @todo implement more. */
+ break;
+ case kCvSst_SrcModule:
+ pfnCallback = rtDbgModCvSs_SrcModule;
+ break;
+ case kCvSst_Libraries:
+ case kCvSst_GlobalTypes:
+ case kCvSst_MPC:
+ case kCvSst_PreComp:
+ case kCvSst_PreCompMap:
+ case kCvSst_OffsetMap16:
+ case kCvSst_OffsetMap32:
+ case kCvSst_FileIndex:
+
+ default:
+ /** @todo implement more. */
+ break;
+
+ /* Skip because we've already processed them: */
+ case kCvSst_SegMap:
+ case kCvSst_SegName:
+ pfnCallback = NULL;
+ break;
+ }
+
+ if (pfnCallback)
+ {
+ void *pvSubSect;
+ rc = rtDbgModCvReadAtAlloc(pThis, pDirEnt->off, &pvSubSect, pDirEnt->cb);
+ if (RT_SUCCESS(rc))
+ {
+ rc = pfnCallback(pThis, pvSubSect, pDirEnt->cb, pDirEnt);
+ RTMemFree(pvSubSect);
+ }
+ }
+ }
+
+ /*
+ * Free temporary parsing objects.
+ */
+ if (pThis->pbSrcInfo)
+ {
+ RTMemFree(pThis->pbSrcInfo);
+ pThis->pbSrcInfo = NULL;
+ pThis->cbSrcInfo = 0;
+ pThis->cbSrcInfoAlloc = 0;
+ }
+ if (pThis->pchSrcStrings)
+ {
+ RTMemFree(pThis->pchSrcStrings);
+ pThis->pchSrcStrings = NULL;
+ pThis->cbSrcStrings = 0;
+ pThis->cbSrcStringsAlloc = 0;
+ }
+
+ return rc;
+}
+
+
+/*
+ *
+ * COFF Debug Info Parsing.
+ * COFF Debug Info Parsing.
+ * COFF Debug Info Parsing.
+ *
+ */
+
+#ifdef LOG_ENABLED
+static const char *rtDbgModCvGetCoffStorageClassName(uint8_t bStorageClass)
+{
+ switch (bStorageClass)
+ {
+ case IMAGE_SYM_CLASS_END_OF_FUNCTION: return "END_OF_FUNCTION";
+ case IMAGE_SYM_CLASS_NULL: return "NULL";
+ case IMAGE_SYM_CLASS_AUTOMATIC: return "AUTOMATIC";
+ case IMAGE_SYM_CLASS_EXTERNAL: return "EXTERNAL";
+ case IMAGE_SYM_CLASS_STATIC: return "STATIC";
+ case IMAGE_SYM_CLASS_REGISTER: return "REGISTER";
+ case IMAGE_SYM_CLASS_EXTERNAL_DEF: return "EXTERNAL_DEF";
+ case IMAGE_SYM_CLASS_LABEL: return "LABEL";
+ case IMAGE_SYM_CLASS_UNDEFINED_LABEL: return "UNDEFINED_LABEL";
+ case IMAGE_SYM_CLASS_MEMBER_OF_STRUCT: return "MEMBER_OF_STRUCT";
+ case IMAGE_SYM_CLASS_ARGUMENT: return "ARGUMENT";
+ case IMAGE_SYM_CLASS_STRUCT_TAG: return "STRUCT_TAG";
+ case IMAGE_SYM_CLASS_MEMBER_OF_UNION: return "MEMBER_OF_UNION";
+ case IMAGE_SYM_CLASS_UNION_TAG: return "UNION_TAG";
+ case IMAGE_SYM_CLASS_TYPE_DEFINITION: return "TYPE_DEFINITION";
+ case IMAGE_SYM_CLASS_UNDEFINED_STATIC: return "UNDEFINED_STATIC";
+ case IMAGE_SYM_CLASS_ENUM_TAG: return "ENUM_TAG";
+ case IMAGE_SYM_CLASS_MEMBER_OF_ENUM: return "MEMBER_OF_ENUM";
+ case IMAGE_SYM_CLASS_REGISTER_PARAM: return "REGISTER_PARAM";
+ case IMAGE_SYM_CLASS_BIT_FIELD: return "BIT_FIELD";
+ case IMAGE_SYM_CLASS_FAR_EXTERNAL: return "FAR_EXTERNAL";
+ case IMAGE_SYM_CLASS_BLOCK: return "BLOCK";
+ case IMAGE_SYM_CLASS_FUNCTION: return "FUNCTION";
+ case IMAGE_SYM_CLASS_END_OF_STRUCT: return "END_OF_STRUCT";
+ case IMAGE_SYM_CLASS_FILE: return "FILE";
+ case IMAGE_SYM_CLASS_SECTION: return "SECTION";
+ case IMAGE_SYM_CLASS_WEAK_EXTERNAL: return "WEAK_EXTERNAL";
+ case IMAGE_SYM_CLASS_CLR_TOKEN: return "CLR_TOKEN";
+ }
+
+ static char s_szName[32];
+ RTStrPrintf(s_szName, sizeof(s_szName), "Unknown%#04x", bStorageClass);
+ return s_szName;
+}
+#endif /* LOG_ENABLED */
+
+
+/**
+ * Adds a chunk of COFF line numbers.
+ *
+ * @param pThis The COFF/CodeView reader instance.
+ * @param pszFile The source file name.
+ * @param iSection The section number.
+ * @param paLines Pointer to the first line number table entry.
+ * @param cLines The number of line number table entries to add.
+ */
+static void rtDbgModCvAddCoffLineNumbers(PRTDBGMODCV pThis, const char *pszFile, uint32_t iSection,
+ PCIMAGE_LINENUMBER paLines, uint32_t cLines)
+{
+ RT_NOREF_PV(iSection);
+ Log4(("Adding %u line numbers in section #%u for %s\n", cLines, iSection, pszFile));
+ PCIMAGE_LINENUMBER pCur = paLines;
+ while (cLines-- > 0)
+ {
+ if (pCur->Linenumber)
+ {
+ int rc = RTDbgModLineAdd(pThis->hCnt, pszFile, pCur->Linenumber, RTDBGSEGIDX_RVA, pCur->Type.VirtualAddress, NULL);
+ Log4((" %#010x: %u [%Rrc]\n", pCur->Type.VirtualAddress, pCur->Linenumber, rc)); NOREF(rc);
+ }
+ pCur++;
+ }
+}
+
+
+/**
+ * Adds a COFF symbol.
+ *
+ * @returns IPRT status (ignored)
+ * @param pThis The COFF/CodeView reader instance.
+ * @param idxSeg IPRT RVA or ABS segment index indicator.
+ * @param uValue The symbol value.
+ * @param pszName The symbol name.
+ */
+static int rtDbgModCvAddCoffSymbol(PRTDBGMODCV pThis, uint32_t idxSeg, uint32_t uValue, const char *pszName)
+{
+ int rc = RTDbgModSymbolAdd(pThis->hCnt, pszName, idxSeg, uValue, 0, 0 /*fFlags*/, NULL);
+ Log(("Symbol: %s:%08x %s [%Rrc]\n", idxSeg == RTDBGSEGIDX_RVA ? "rva" : "abs", uValue, pszName, rc));
+ if (rc == VERR_DBG_ADDRESS_CONFLICT || rc == VERR_DBG_DUPLICATE_SYMBOL)
+ rc = VINF_SUCCESS;
+ return rc;
+}
+
+
+/**
+ * Processes the COFF symbol table.
+ *
+ * @returns IPRT status code
+ * @param pThis The COFF/CodeView reader instance.
+ * @param paSymbols Pointer to the symbol table.
+ * @param cSymbols The number of entries in the symbol table.
+ * @param paLines Pointer to the line number table.
+ * @param cLines The number of entires in the line number table.
+ * @param pszzStrTab Pointer to the string table.
+ * @param cbStrTab Size of the string table.
+ */
+static int rtDbgModCvProcessCoffSymbolTable(PRTDBGMODCV pThis,
+ PCIMAGE_SYMBOL paSymbols, uint32_t cSymbols,
+ PCIMAGE_LINENUMBER paLines, uint32_t cLines,
+ const char *pszzStrTab, uint32_t cbStrTab)
+{
+ Log3(("Processing COFF symbol table with %#x symbols\n", cSymbols));
+
+ /*
+ * Making some bold assumption that the line numbers for the section in
+ * the file are allocated sequentially, we do multiple passes until we've
+ * gathered them all.
+ */
+ int rc = VINF_SUCCESS;
+ uint32_t cSections = 1;
+ uint32_t iLineSect = 1;
+ uint32_t iLine = 0;
+ do
+ {
+ /*
+ * Process the symbols.
+ */
+ char szShort[9];
+ char szFile[RTPATH_MAX];
+ uint32_t iSymbol = 0;
+ szFile[0] = '\0';
+ szShort[8] = '\0'; /* avoid having to terminate it all the time. */
+
+ while (iSymbol < cSymbols && RT_SUCCESS(rc))
+ {
+ /* Copy the symbol in and hope it works around the misalignment
+ issues everywhere. */
+ IMAGE_SYMBOL Sym;
+ memcpy(&Sym, &paSymbols[iSymbol], sizeof(Sym));
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(Sym.NumberOfAuxSymbols < cSymbols);
+
+ /* Calc a zero terminated symbol name. */
+ const char *pszName;
+ if (Sym.N.Name.Short)
+ pszName = (const char *)memcpy(szShort, &Sym.N, 8);
+ else
+ {
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(Sym.N.Name.Long < cbStrTab);
+ pszName = pszzStrTab + Sym.N.Name.Long;
+ }
+
+ /* Only log stuff and count sections the in the first pass.*/
+ if (iLineSect == 1)
+ {
+ Log3(("%04x: s=%#06x v=%#010x t=%#06x a=%#04x c=%#04x (%s) name='%s'\n",
+ iSymbol, Sym.SectionNumber, Sym.Value, Sym.Type, Sym.NumberOfAuxSymbols,
+ Sym.StorageClass, rtDbgModCvGetCoffStorageClassName(Sym.StorageClass), pszName));
+ if ((int16_t)cSections <= Sym.SectionNumber && Sym.SectionNumber > 0)
+ cSections = Sym.SectionNumber + 1;
+ }
+
+ /*
+ * Use storage class to pick what we need (which isn't much because,
+ * MS only provides a very restricted set of symbols).
+ */
+ IMAGE_AUX_SYMBOL Aux;
+ switch (Sym.StorageClass)
+ {
+ case IMAGE_SYM_CLASS_NULL:
+ /* a NOP */
+ break;
+
+ case IMAGE_SYM_CLASS_FILE:
+ {
+ /* Change the current file name (for line numbers). Pretend
+ ANSI and ISO-8859-1 are similar enough for out purposes... */
+ RTDBGMODCV_CHECK_NOMSG_RET_BF(Sym.NumberOfAuxSymbols > 0);
+ const char *pszFile = (const char *)&paSymbols[iSymbol + 1];
+ char *pszDst = szFile;
+ rc = RTLatin1ToUtf8Ex(pszFile, Sym.NumberOfAuxSymbols * sizeof(IMAGE_SYMBOL), &pszDst, sizeof(szFile), NULL);
+ if (RT_FAILURE(rc))
+ Log(("Error converting COFF filename: %Rrc\n", rc));
+ else if (iLineSect == 1)
+ Log3((" filename='%s'\n", szFile));
+ break;
+ }
+
+ case IMAGE_SYM_CLASS_STATIC:
+ if ( Sym.NumberOfAuxSymbols == 1
+ && ( iLineSect == 1
+ || Sym.SectionNumber == (int32_t)iLineSect) )
+ {
+ memcpy(&Aux, &paSymbols[iSymbol + 1], sizeof(Aux));
+ if (iLineSect == 1)
+ Log3((" section: cb=%#010x #relocs=%#06x #lines=%#06x csum=%#x num=%#x sel=%x rvd=%u\n",
+ Aux.Section.Length, Aux.Section.NumberOfRelocations,
+ Aux.Section.NumberOfLinenumbers,
+ Aux.Section.CheckSum,
+ RT_MAKE_U32(Aux.Section.Number, Aux.Section.HighNumber),
+ Aux.Section.Selection,
+ Aux.Section.bReserved));
+ if ( Sym.SectionNumber == (int32_t)iLineSect
+ && Aux.Section.NumberOfLinenumbers > 0)
+ {
+ uint32_t cLinesToAdd = RT_MIN(Aux.Section.NumberOfLinenumbers, cLines - iLine);
+ if (iLine < cLines && szFile[0])
+ rtDbgModCvAddCoffLineNumbers(pThis, szFile, iLineSect, &paLines[iLine], cLinesToAdd);
+ iLine += cLinesToAdd;
+ }
+ }
+ /* Not so sure about the quality here, but might be useful. */
+ else if ( iLineSect == 1
+ && Sym.NumberOfAuxSymbols == 0
+ && Sym.SectionNumber != IMAGE_SYM_UNDEFINED
+ && Sym.SectionNumber != IMAGE_SYM_ABSOLUTE
+ && Sym.SectionNumber != IMAGE_SYM_DEBUG
+ && Sym.Value > 0
+ && *pszName)
+ rtDbgModCvAddCoffSymbol(pThis, RTDBGSEGIDX_RVA, Sym.Value, pszName);
+ break;
+
+ case IMAGE_SYM_CLASS_EXTERNAL:
+ /* Add functions (first pass only). */
+ if ( iLineSect == 1
+ && (ISFCN(Sym.Type) || Sym.Type == 0)
+ && Sym.NumberOfAuxSymbols == 0
+ && *pszName )
+ {
+ if (Sym.SectionNumber == IMAGE_SYM_ABSOLUTE)
+ rtDbgModCvAddCoffSymbol(pThis, RTDBGSEGIDX_ABS, Sym.Value, pszName);
+ else if ( Sym.SectionNumber != IMAGE_SYM_UNDEFINED
+ && Sym.SectionNumber != IMAGE_SYM_DEBUG)
+ rtDbgModCvAddCoffSymbol(pThis, RTDBGSEGIDX_RVA, Sym.Value, pszName);
+ }
+ break;
+
+ case IMAGE_SYM_CLASS_FUNCTION:
+ /* Not sure this is really used. */
+ break;
+
+ case IMAGE_SYM_CLASS_END_OF_FUNCTION:
+ case IMAGE_SYM_CLASS_AUTOMATIC:
+ case IMAGE_SYM_CLASS_REGISTER:
+ case IMAGE_SYM_CLASS_EXTERNAL_DEF:
+ case IMAGE_SYM_CLASS_LABEL:
+ case IMAGE_SYM_CLASS_UNDEFINED_LABEL:
+ case IMAGE_SYM_CLASS_MEMBER_OF_STRUCT:
+ case IMAGE_SYM_CLASS_ARGUMENT:
+ case IMAGE_SYM_CLASS_STRUCT_TAG:
+ case IMAGE_SYM_CLASS_MEMBER_OF_UNION:
+ case IMAGE_SYM_CLASS_UNION_TAG:
+ case IMAGE_SYM_CLASS_TYPE_DEFINITION:
+ case IMAGE_SYM_CLASS_UNDEFINED_STATIC:
+ case IMAGE_SYM_CLASS_ENUM_TAG:
+ case IMAGE_SYM_CLASS_MEMBER_OF_ENUM:
+ case IMAGE_SYM_CLASS_REGISTER_PARAM:
+ case IMAGE_SYM_CLASS_BIT_FIELD:
+ case IMAGE_SYM_CLASS_FAR_EXTERNAL:
+ case IMAGE_SYM_CLASS_BLOCK:
+ case IMAGE_SYM_CLASS_END_OF_STRUCT:
+ case IMAGE_SYM_CLASS_SECTION:
+ case IMAGE_SYM_CLASS_WEAK_EXTERNAL:
+ case IMAGE_SYM_CLASS_CLR_TOKEN:
+ /* Not used by MS, I think. */
+ break;
+
+ default:
+ Log(("RTDbgCv: Unexpected COFF storage class %#x (%u)\n", Sym.StorageClass, Sym.StorageClass));
+ break;
+ }
+
+ /* next symbol */
+ iSymbol += 1 + Sym.NumberOfAuxSymbols;
+ }
+
+ /* Next section with line numbers. */
+ iLineSect++;
+ } while (iLine < cLines && iLineSect < cSections && RT_SUCCESS(rc));
+
+ return rc;
+}
+
+
+/**
+ * Loads COFF debug information into the container.
+ *
+ * @returns IPRT status code.
+ * @param pThis The COFF/CodeView debug reader instance.
+ */
+static int rtDbgModCvLoadCoffInfo(PRTDBGMODCV pThis)
+{
+ /*
+ * Read the whole section into memory.
+ * Note! Cannot use rtDbgModCvReadAt or rtDbgModCvReadAtAlloc here.
+ */
+ int rc;
+ uint8_t *pbDbgSect = (uint8_t *)RTMemAlloc(pThis->cbCoffDbgInfo);
+ if (pbDbgSect)
+ {
+ if (pThis->hFile == NIL_RTFILE)
+ rc = pThis->pMod->pImgVt->pfnReadAt(pThis->pMod, UINT32_MAX, pThis->offCoffDbgInfo, pbDbgSect, pThis->cbCoffDbgInfo);
+ else
+ rc = RTFileReadAt(pThis->hFile, pThis->offCoffDbgInfo, pbDbgSect, pThis->cbCoffDbgInfo, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /* The string table follows after the symbol table. */
+ const char *pszzStrTab = (const char *)( pbDbgSect
+ + pThis->CoffHdr.LvaToFirstSymbol
+ + pThis->CoffHdr.NumberOfSymbols * sizeof(IMAGE_SYMBOL));
+ uint32_t cbStrTab = (uint32_t)((uintptr_t)(pbDbgSect + pThis->cbCoffDbgInfo) - (uintptr_t)pszzStrTab);
+ /** @todo The symbol table starts with a size. Read it and checking. Also verify
+ * that the symtab ends with a terminator character. */
+
+ rc = rtDbgModCvProcessCoffSymbolTable(pThis,
+ (PCIMAGE_SYMBOL)(pbDbgSect + pThis->CoffHdr.LvaToFirstSymbol),
+ pThis->CoffHdr.NumberOfSymbols,
+ (PCIMAGE_LINENUMBER)(pbDbgSect + pThis->CoffHdr.LvaToFirstLinenumber),
+ pThis->CoffHdr.NumberOfLinenumbers,
+ pszzStrTab, cbStrTab);
+ }
+ RTMemFree(pbDbgSect);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+
+
+
+
+
+/*
+ *
+ * CodeView Debug module implementation.
+ * CodeView Debug module implementation.
+ * CodeView Debug module implementation.
+ *
+ */
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnUnwindFrame} */
+static DECLCALLBACK(int) rtDbgModCv_UnwindFrame(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState)
+{
+ RT_NOREF(pMod, iSeg, off, pState);
+ return VERR_DBG_NO_UNWIND_INFO;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByAddr} */
+static DECLCALLBACK(int) rtDbgModCv_LineByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off,
+ PRTINTPTR poffDisp, PRTDBGLINE pLineInfo)
+{
+ PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
+ return RTDbgModLineByAddr(pThis->hCnt, iSeg, off, poffDisp, pLineInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByOrdinal} */
+static DECLCALLBACK(int) rtDbgModCv_LineByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo)
+{
+ PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
+ return RTDbgModLineByOrdinal(pThis->hCnt, iOrdinal, pLineInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineCount} */
+static DECLCALLBACK(uint32_t) rtDbgModCv_LineCount(PRTDBGMODINT pMod)
+{
+ PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
+ return RTDbgModLineCount(pThis->hCnt);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineAdd} */
+static DECLCALLBACK(int) rtDbgModCv_LineAdd(PRTDBGMODINT pMod, const char *pszFile, size_t cchFile, uint32_t uLineNo,
+ uint32_t iSeg, RTUINTPTR off, uint32_t *piOrdinal)
+{
+ PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
+ Assert(!pszFile[cchFile]); NOREF(cchFile);
+ return RTDbgModLineAdd(pThis->hCnt, pszFile, uLineNo, iSeg, off, piOrdinal);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByAddr} */
+static DECLCALLBACK(int) rtDbgModCv_SymbolByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags,
+ PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo)
+{
+ PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
+ return RTDbgModSymbolByAddr(pThis->hCnt, iSeg, off, fFlags, poffDisp, pSymInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByName} */
+static DECLCALLBACK(int) rtDbgModCv_SymbolByName(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol,
+ PRTDBGSYMBOL pSymInfo)
+{
+ PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
+ Assert(!pszSymbol[cchSymbol]); RT_NOREF_PV(cchSymbol);
+ return RTDbgModSymbolByName(pThis->hCnt, pszSymbol/*, cchSymbol*/, pSymInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByOrdinal} */
+static DECLCALLBACK(int) rtDbgModCv_SymbolByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo)
+{
+ PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
+ return RTDbgModSymbolByOrdinal(pThis->hCnt, iOrdinal, pSymInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolCount} */
+static DECLCALLBACK(uint32_t) rtDbgModCv_SymbolCount(PRTDBGMODINT pMod)
+{
+ PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
+ return RTDbgModSymbolCount(pThis->hCnt);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolAdd} */
+static DECLCALLBACK(int) rtDbgModCv_SymbolAdd(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol,
+ RTDBGSEGIDX iSeg, RTUINTPTR off, RTUINTPTR cb, uint32_t fFlags,
+ uint32_t *piOrdinal)
+{
+ PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
+ Assert(!pszSymbol[cchSymbol]); NOREF(cchSymbol);
+ return RTDbgModSymbolAdd(pThis->hCnt, pszSymbol, iSeg, off, cb, fFlags, piOrdinal);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentByIndex} */
+static DECLCALLBACK(int) rtDbgModCv_SegmentByIndex(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo)
+{
+ PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
+ return RTDbgModSegmentByIndex(pThis->hCnt, iSeg, pSegInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentCount} */
+static DECLCALLBACK(RTDBGSEGIDX) rtDbgModCv_SegmentCount(PRTDBGMODINT pMod)
+{
+ PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
+ return RTDbgModSegmentCount(pThis->hCnt);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentAdd} */
+static DECLCALLBACK(int) rtDbgModCv_SegmentAdd(PRTDBGMODINT pMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName, size_t cchName,
+ uint32_t fFlags, PRTDBGSEGIDX piSeg)
+{
+ PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
+ Assert(!pszName[cchName]); NOREF(cchName);
+ return RTDbgModSegmentAdd(pThis->hCnt, uRva, cb, pszName, fFlags, piSeg);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnImageSize} */
+static DECLCALLBACK(RTUINTPTR) rtDbgModCv_ImageSize(PRTDBGMODINT pMod)
+{
+ PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
+ if (pThis->cbImage)
+ return pThis->cbImage;
+ return RTDbgModImageSize(pThis->hCnt);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnRvaToSegOff} */
+static DECLCALLBACK(RTDBGSEGIDX) rtDbgModCv_RvaToSegOff(PRTDBGMODINT pMod, RTUINTPTR uRva, PRTUINTPTR poffSeg)
+{
+ PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
+ return RTDbgModRvaToSegOff(pThis->hCnt, uRva, poffSeg);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnClose} */
+static DECLCALLBACK(int) rtDbgModCv_Close(PRTDBGMODINT pMod)
+{
+ PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
+
+ RTDbgModRelease(pThis->hCnt);
+ if (pThis->hFile != NIL_RTFILE)
+ RTFileClose(pThis->hFile);
+ RTMemFree(pThis->paDirEnts);
+ RTMemFree(pThis);
+
+ pMod->pvDbgPriv = NULL; /* for internal use */
+ return VINF_SUCCESS;
+}
+
+
+/*
+ *
+ * Probing code used by rtDbgModCv_TryOpen.
+ * Probing code used by rtDbgModCv_TryOpen.
+ *
+ */
+
+
+
+/**
+ * @callback_method_impl{FNRTLDRENUMSEGS, Used to add segments from the image}
+ */
+static DECLCALLBACK(int) rtDbgModCvAddSegmentsCallback(RTLDRMOD hLdrMod, PCRTLDRSEG pSeg, void *pvUser)
+{
+ PRTDBGMODCV pThis = (PRTDBGMODCV)pvUser;
+ Log(("Segment %s: LinkAddress=%#llx RVA=%#llx cb=%#llx\n",
+ pSeg->pszName, (uint64_t)pSeg->LinkAddress, (uint64_t)pSeg->RVA, pSeg->cb));
+ NOREF(hLdrMod);
+
+ /* If the segment doesn't have a mapping, just add a dummy so the indexing
+ works out correctly (same as for the image). */
+ if (pSeg->RVA == NIL_RTLDRADDR)
+ return RTDbgModSegmentAdd(pThis->hCnt, 0, 0, pSeg->pszName, 0 /*fFlags*/, NULL);
+
+ RTLDRADDR cb = RT_MAX(pSeg->cb, pSeg->cbMapped);
+ return RTDbgModSegmentAdd(pThis->hCnt, pSeg->RVA, cb, pSeg->pszName, 0 /*fFlags*/, NULL);
+}
+
+
+/**
+ * Copies the sections over from the DBG file.
+ *
+ * Called if we don't have an associated executable image.
+ *
+ * @returns IPRT status code.
+ * @param pThis The CV module instance.
+ * @param pDbgHdr The DBG file header.
+ * @param pszFilename The filename (for logging).
+ */
+static int rtDbgModCvAddSegmentsFromDbg(PRTDBGMODCV pThis, PCIMAGE_SEPARATE_DEBUG_HEADER pDbgHdr, const char *pszFilename)
+{
+ RT_NOREF_PV(pszFilename);
+
+ /*
+ * Validate the header fields a little.
+ */
+ if ( pDbgHdr->NumberOfSections < 1
+ || pDbgHdr->NumberOfSections > 4096)
+ {
+ Log(("RTDbgModCv: Bad NumberOfSections: %d\n", pDbgHdr->NumberOfSections));
+ return VERR_CV_BAD_FORMAT;
+ }
+ if (!RT_IS_POWER_OF_TWO(pDbgHdr->SectionAlignment))
+ {
+ Log(("RTDbgModCv: Bad SectionAlignment: %#x\n", pDbgHdr->SectionAlignment));
+ return VERR_CV_BAD_FORMAT;
+ }
+
+ /*
+ * Read the section table.
+ */
+ size_t cbShs = pDbgHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
+ PIMAGE_SECTION_HEADER paShs = (PIMAGE_SECTION_HEADER)RTMemAlloc(cbShs);
+ if (!paShs)
+ return VERR_NO_MEMORY;
+ int rc = RTFileReadAt(pThis->hFile, sizeof(*pDbgHdr), paShs, cbShs, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Do some basic validation.
+ */
+ uint32_t cbHeaders = 0;
+ uint32_t uRvaPrev = 0;
+ for (uint32_t i = 0; i < pDbgHdr->NumberOfSections; i++)
+ {
+ Log3(("RTDbgModCv: Section #%02u %#010x LB %#010x %.*s\n",
+ i, paShs[i].VirtualAddress, paShs[i].Misc.VirtualSize, sizeof(paShs[i].Name), paShs[i].Name));
+
+ if (paShs[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD)
+ continue;
+
+ if (paShs[i].VirtualAddress < uRvaPrev)
+ {
+ Log(("RTDbgModCv: %s: Overlap or soring error, VirtualAddress=%#x uRvaPrev=%#x - section #%d '%.*s'!!!\n",
+ pszFilename, paShs[i].VirtualAddress, uRvaPrev, i, sizeof(paShs[i].Name), paShs[i].Name));
+ rc = VERR_CV_BAD_FORMAT;
+ }
+ else if ( paShs[i].VirtualAddress > pDbgHdr->SizeOfImage
+ || paShs[i].Misc.VirtualSize > pDbgHdr->SizeOfImage
+ || paShs[i].VirtualAddress + paShs[i].Misc.VirtualSize > pDbgHdr->SizeOfImage)
+ {
+ Log(("RTDbgModCv: %s: VirtualAddress=%#x VirtualSize=%#x (total %x) - beyond image size (%#x) - section #%d '%.*s'!!!\n",
+ pszFilename, paShs[i].VirtualAddress, paShs[i].Misc.VirtualSize,
+ paShs[i].VirtualAddress + paShs[i].Misc.VirtualSize,
+ pThis->cbImage, i, sizeof(paShs[i].Name), paShs[i].Name));
+ rc = VERR_CV_BAD_FORMAT;
+ }
+ else if (paShs[i].VirtualAddress & (pDbgHdr->SectionAlignment - 1))
+ {
+ Log(("RTDbgModCv: %s: VirtualAddress=%#x misaligned (%#x) - section #%d '%.*s'!!!\n",
+ pszFilename, paShs[i].VirtualAddress, pDbgHdr->SectionAlignment, i, sizeof(paShs[i].Name), paShs[i].Name));
+ rc = VERR_CV_BAD_FORMAT;
+ }
+ else
+ {
+ if (uRvaPrev == 0)
+ cbHeaders = paShs[i].VirtualAddress;
+ uRvaPrev = paShs[i].VirtualAddress + paShs[i].Misc.VirtualSize;
+ continue;
+ }
+ }
+ if (RT_SUCCESS(rc) && uRvaPrev == 0)
+ {
+ Log(("RTDbgModCv: %s: No loadable sections.\n", pszFilename));
+ rc = VERR_CV_BAD_FORMAT;
+ }
+ if (RT_SUCCESS(rc) && cbHeaders == 0)
+ {
+ Log(("RTDbgModCv: %s: No space for PE headers.\n", pszFilename));
+ rc = VERR_CV_BAD_FORMAT;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Add sections.
+ */
+ rc = RTDbgModSegmentAdd(pThis->hCnt, 0, cbHeaders, "NtHdrs", 0 /*fFlags*/, NULL);
+ for (uint32_t i = 0; RT_SUCCESS(rc) && i < pDbgHdr->NumberOfSections; i++)
+ {
+ char szName[sizeof(paShs[i].Name) + 1];
+ memcpy(szName, paShs[i].Name, sizeof(paShs[i].Name));
+ szName[sizeof(szName) - 1] = '\0';
+
+ if (paShs[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD)
+ rc = RTDbgModSegmentAdd(pThis->hCnt, 0, 0, szName, 0 /*fFlags*/, NULL);
+ else
+ rc = RTDbgModSegmentAdd(pThis->hCnt, paShs[i].VirtualAddress, paShs[i].Misc.VirtualSize, szName,
+ 0 /*fFlags*/, NULL);
+ }
+ if (RT_SUCCESS(rc))
+ pThis->fHaveLoadedSegments = true;
+ }
+ }
+
+ RTMemFree(paShs);
+ return rc;
+}
+
+
+/**
+ * Instantiates the CV/COFF reader.
+ *
+ * @returns IPRT status code
+ * @param pDbgMod The debug module instance.
+ * @param enmFileType The type of input file.
+ * @param hFile The file handle, NIL_RTFILE of image.
+ * @param ppThis Where to return the reader instance.
+ */
+static int rtDbgModCvCreateInstance(PRTDBGMODINT pDbgMod, RTCVFILETYPE enmFileType, RTFILE hFile, PRTDBGMODCV *ppThis)
+{
+ /*
+ * Do we already have an instance? Happens if we find multiple debug
+ * formats we support.
+ */
+ PRTDBGMODCV pThis = (PRTDBGMODCV)pDbgMod->pvDbgPriv;
+ if (pThis)
+ {
+ Assert(pThis->enmType == enmFileType);
+ Assert(pThis->hFile == hFile);
+ Assert(pThis->pMod == pDbgMod);
+ *ppThis = pThis;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Create a new instance.
+ */
+ pThis = (PRTDBGMODCV)RTMemAllocZ(sizeof(RTDBGMODCV));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+ int rc = RTDbgModCreate(&pThis->hCnt, pDbgMod->pszName, 0 /*cbSeg*/, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ pDbgMod->pvDbgPriv = pThis;
+ pThis->enmType = enmFileType;
+ pThis->hFile = hFile;
+ pThis->pMod = pDbgMod;
+ pThis->offBase = UINT32_MAX;
+ pThis->offCoffDbgInfo = UINT32_MAX;
+ *ppThis = pThis;
+ return VINF_SUCCESS;
+ }
+ RTMemFree(pThis);
+ return rc;
+}
+
+
+/**
+ * Common part of the COFF probing.
+ *
+ * @returns status code.
+ * @param pDbgMod The debug module instance. On success pvDbgPriv
+ * will point to a valid RTDBGMODCV.
+ * @param enmFileType The kind of file this is we're probing.
+ * @param hFile The file with debug info in it.
+ * @param off The offset where to expect CV debug info.
+ * @param cb The number of bytes of debug info.
+ * @param pszFilename The path to the file (for logging).
+ */
+static int rtDbgModCvProbeCoff(PRTDBGMODINT pDbgMod, RTCVFILETYPE enmFileType, RTFILE hFile,
+ uint32_t off, uint32_t cb, const char *pszFilename)
+{
+ RT_NOREF_PV(pszFilename);
+
+ /*
+ * Check that there is sufficient data for a header, then read it.
+ */
+ if (cb < sizeof(IMAGE_COFF_SYMBOLS_HEADER))
+ {
+ Log(("RTDbgModCv: Not enough room for COFF header.\n"));
+ return VERR_BAD_EXE_FORMAT;
+ }
+ if (cb >= UINT32_C(128) * _1M)
+ {
+ Log(("RTDbgModCv: COFF debug information is to large (%'u bytes), max is 128MB\n", cb));
+ return VERR_BAD_EXE_FORMAT;
+ }
+
+ int rc;
+ IMAGE_COFF_SYMBOLS_HEADER Hdr;
+ if (hFile == NIL_RTFILE)
+ rc = pDbgMod->pImgVt->pfnReadAt(pDbgMod, UINT32_MAX, off, &Hdr, sizeof(Hdr));
+ else
+ rc = RTFileReadAt(hFile, off, &Hdr, sizeof(Hdr), NULL);
+ if (RT_FAILURE(rc))
+ {
+ Log(("RTDbgModCv: Error reading COFF header: %Rrc\n", rc));
+ return rc;
+ }
+
+ Log2(("RTDbgModCv: Found COFF debug info header at %#x (LB %#x) in %s\n", off, cb, pszFilename));
+ Log2((" NumberOfSymbols = %#010x\n", Hdr.NumberOfSymbols));
+ Log2((" LvaToFirstSymbol = %#010x\n", Hdr.LvaToFirstSymbol));
+ Log2((" NumberOfLinenumbers = %#010x\n", Hdr.NumberOfLinenumbers));
+ Log2((" LvaToFirstLinenumber = %#010x\n", Hdr.LvaToFirstLinenumber));
+ Log2((" RvaToFirstByteOfCode = %#010x\n", Hdr.RvaToFirstByteOfCode));
+ Log2((" RvaToLastByteOfCode = %#010x\n", Hdr.RvaToLastByteOfCode));
+ Log2((" RvaToFirstByteOfData = %#010x\n", Hdr.RvaToFirstByteOfData));
+ Log2((" RvaToLastByteOfData = %#010x\n", Hdr.RvaToLastByteOfData));
+
+ /*
+ * Validate the COFF header.
+ */
+ if ( (uint64_t)Hdr.LvaToFirstSymbol + (uint64_t)Hdr.NumberOfSymbols * sizeof(IMAGE_SYMBOL) > cb
+ || (Hdr.LvaToFirstSymbol < sizeof(Hdr) && Hdr.NumberOfSymbols > 0))
+ {
+ Log(("RTDbgModCv: Bad COFF symbol count or/and offset: LvaToFirstSymbol=%#x, NumberOfSymbols=%#x cbCoff=%#x\n",
+ Hdr.LvaToFirstSymbol, Hdr.NumberOfSymbols, cb));
+ return VERR_BAD_EXE_FORMAT;
+ }
+ if ( (uint64_t)Hdr.LvaToFirstLinenumber + (uint64_t)Hdr.NumberOfLinenumbers * sizeof(IMAGE_LINENUMBER) > cb
+ || (Hdr.LvaToFirstLinenumber < sizeof(Hdr) && Hdr.NumberOfLinenumbers > 0))
+ {
+ Log(("RTDbgModCv: Bad COFF symbol count or/and offset: LvaToFirstSymbol=%#x, NumberOfSymbols=%#x cbCoff=%#x\n",
+ Hdr.LvaToFirstSymbol, Hdr.NumberOfSymbols, cb));
+ return VERR_BAD_EXE_FORMAT;
+ }
+ if (Hdr.NumberOfSymbols < 2)
+ {
+ Log(("RTDbgModCv: The COFF symbol table is too short to be of any worth... (%u syms)\n", Hdr.NumberOfSymbols));
+ return VERR_NO_DATA;
+ }
+
+ /*
+ * What we care about looks fine, use it.
+ */
+ PRTDBGMODCV pThis;
+ rc = rtDbgModCvCreateInstance(pDbgMod, enmFileType, hFile, &pThis);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->offCoffDbgInfo = off;
+ pThis->cbCoffDbgInfo = cb;
+ pThis->CoffHdr = Hdr;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Common part of the CodeView probing.
+ *
+ * @returns status code.
+ * @param pDbgMod The debug module instance. On success pvDbgPriv
+ * will point to a valid RTDBGMODCV.
+ * @param pCvHdr The CodeView base header.
+ * @param enmFileType The kind of file this is we're probing.
+ * @param hFile The file with debug info in it.
+ * @param off The offset where to expect CV debug info.
+ * @param cb The number of bytes of debug info.
+ * @param enmArch The desired image architecture.
+ * @param pszFilename The path to the file (for logging).
+ */
+static int rtDbgModCvProbeCommon(PRTDBGMODINT pDbgMod, PRTCVHDR pCvHdr, RTCVFILETYPE enmFileType, RTFILE hFile,
+ uint32_t off, uint32_t cb, RTLDRARCH enmArch, const char *pszFilename)
+{
+ int rc = VERR_DBG_NO_MATCHING_INTERPRETER;
+ RT_NOREF_PV(enmArch); RT_NOREF_PV(pszFilename);
+
+ /* Is a codeview format we (wish to) support? */
+ if ( pCvHdr->u32Magic == RTCVHDR_MAGIC_NB11
+ || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB09
+ || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB08
+ || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB05
+ || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB04
+ || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB02
+ || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB00
+ )
+ {
+ /* We're assuming it's a base header, so the offset must be within
+ the area defined by the debug info we got from the loader. */
+ if (pCvHdr->off < cb && pCvHdr->off >= sizeof(*pCvHdr))
+ {
+ Log(("RTDbgModCv: Found %c%c%c%c at %#x - size %#x, directory at %#x. file type %d\n",
+ RT_BYTE1(pCvHdr->u32Magic), RT_BYTE2(pCvHdr->u32Magic), RT_BYTE3(pCvHdr->u32Magic), RT_BYTE4(pCvHdr->u32Magic),
+ off, cb, pCvHdr->off, enmFileType));
+
+ /*
+ * Create a module instance, if not already done.
+ */
+ PRTDBGMODCV pThis;
+ rc = rtDbgModCvCreateInstance(pDbgMod, enmFileType, hFile, &pThis);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->u32CvMagic = pCvHdr->u32Magic;
+ pThis->offBase = off;
+ pThis->cbDbgInfo = cb;
+ pThis->offDir = pCvHdr->off;
+ return VINF_SUCCESS;
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+/** @callback_method_impl{FNRTLDRENUMDBG} */
+static DECLCALLBACK(int) rtDbgModCvEnumCallback(RTLDRMOD hLdrMod, PCRTLDRDBGINFO pDbgInfo, void *pvUser)
+{
+ PRTDBGMODINT pDbgMod = (PRTDBGMODINT)pvUser;
+ Assert(!pDbgMod->pvDbgPriv);
+ RT_NOREF_PV(hLdrMod);
+
+ /* Skip external files, RTDbgMod will deal with those
+ via RTDBGMODINT::pszDbgFile. */
+ if (pDbgInfo->pszExtFile)
+ return VINF_SUCCESS;
+
+ /* We only handle the codeview sections. */
+ if (pDbgInfo->enmType == RTLDRDBGINFOTYPE_CODEVIEW)
+ {
+ /* Read the specified header and check if we like it. */
+ RTCVHDR CvHdr;
+ int rc = pDbgMod->pImgVt->pfnReadAt(pDbgMod, pDbgInfo->iDbgInfo, pDbgInfo->offFile, &CvHdr, sizeof(CvHdr));
+ if (RT_SUCCESS(rc))
+ rc = rtDbgModCvProbeCommon(pDbgMod, &CvHdr, RTCVFILETYPE_IMAGE, NIL_RTFILE, pDbgInfo->offFile, pDbgInfo->cb,
+ pDbgMod->pImgVt->pfnGetArch(pDbgMod), pDbgMod->pszImgFile);
+ }
+ else if (pDbgInfo->enmType == RTLDRDBGINFOTYPE_COFF)
+ {
+ /* Join paths with the DBG code. */
+ rtDbgModCvProbeCoff(pDbgMod, RTCVFILETYPE_IMAGE, NIL_RTFILE, pDbgInfo->offFile, pDbgInfo->cb, pDbgMod->pszImgFile);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Part two of the external file probing.
+ *
+ * @returns status code.
+ * @param pThis The debug module instance. On success pvDbgPriv
+ * will point to a valid RTDBGMODCV.
+ * @param enmFileType The kind of file this is we're probing.
+ * @param hFile The file with debug info in it.
+ * @param off The offset where to expect CV debug info.
+ * @param cb The number of bytes of debug info.
+ * @param enmArch The desired image architecture.
+ * @param pszFilename The path to the file (for logging).
+ */
+static int rtDbgModCvProbeFile2(PRTDBGMODINT pThis, RTCVFILETYPE enmFileType, RTFILE hFile, uint32_t off, uint32_t cb,
+ RTLDRARCH enmArch, const char *pszFilename)
+{
+ RTCVHDR CvHdr;
+ int rc = RTFileReadAt(hFile, off, &CvHdr, sizeof(CvHdr), NULL);
+ if (RT_SUCCESS(rc))
+ rc = rtDbgModCvProbeCommon(pThis, &CvHdr, enmFileType, hFile, off, cb, enmArch, pszFilename);
+ return rc;
+}
+
+
+/**
+ * Probes an external file for CodeView information.
+ *
+ * @returns status code.
+ * @param pDbgMod The debug module instance. On success pvDbgPriv
+ * will point to a valid RTDBGMODCV.
+ * @param pszFilename The path to the file to probe.
+ * @param enmArch The desired image architecture.
+ */
+static int rtDbgModCvProbeFile(PRTDBGMODINT pDbgMod, const char *pszFilename, RTLDRARCH enmArch)
+{
+ RTFILE hFile;
+ int rc = RTFileOpen(&hFile, pszFilename, RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Check for .DBG file
+ */
+ IMAGE_SEPARATE_DEBUG_HEADER DbgHdr;
+ rc = RTFileReadAt(hFile, 0, &DbgHdr, sizeof(DbgHdr), NULL);
+ if ( RT_SUCCESS(rc)
+ && DbgHdr.Signature == IMAGE_SEPARATE_DEBUG_SIGNATURE)
+ {
+ Log2(("RTDbgModCv: Found separate debug header in %s:\n", pszFilename));
+ Log2((" Flags = %#x\n", DbgHdr.Flags));
+ Log2((" Machine = %#x\n", DbgHdr.Machine));
+ Log2((" Characteristics = %#x\n", DbgHdr.Characteristics));
+ Log2((" TimeDateStamp = %#x\n", DbgHdr.TimeDateStamp));
+ Log2((" CheckSum = %#x\n", DbgHdr.CheckSum));
+ Log2((" ImageBase = %#x\n", DbgHdr.ImageBase));
+ Log2((" SizeOfImage = %#x\n", DbgHdr.SizeOfImage));
+ Log2((" NumberOfSections = %#x\n", DbgHdr.NumberOfSections));
+ Log2((" ExportedNamesSize = %#x\n", DbgHdr.ExportedNamesSize));
+ Log2((" DebugDirectorySize = %#x\n", DbgHdr.DebugDirectorySize));
+ Log2((" SectionAlignment = %#x\n", DbgHdr.SectionAlignment));
+
+ /*
+ * Match up the architecture if specified.
+ */
+ switch (enmArch)
+ {
+ case RTLDRARCH_X86_32:
+ if (DbgHdr.Machine != IMAGE_FILE_MACHINE_I386)
+ rc = VERR_LDR_ARCH_MISMATCH;
+ break;
+ case RTLDRARCH_AMD64:
+ if (DbgHdr.Machine != IMAGE_FILE_MACHINE_AMD64)
+ rc = VERR_LDR_ARCH_MISMATCH;
+ break;
+
+ default:
+ case RTLDRARCH_HOST:
+ AssertFailed();
+ case RTLDRARCH_WHATEVER:
+ break;
+ }
+ if (RT_FAILURE(rc))
+ {
+ RTFileClose(hFile);
+ return rc;
+ }
+
+ /*
+ * Probe for readable debug info in the debug directory.
+ */
+ uint32_t offDbgDir = sizeof(DbgHdr)
+ + DbgHdr.NumberOfSections * sizeof(IMAGE_SECTION_HEADER)
+ + DbgHdr.ExportedNamesSize;
+
+ uint32_t cEntries = DbgHdr.DebugDirectorySize / sizeof(IMAGE_DEBUG_DIRECTORY);
+ for (uint32_t i = 0; i < cEntries; i++, offDbgDir += sizeof(IMAGE_DEBUG_DIRECTORY))
+ {
+ IMAGE_DEBUG_DIRECTORY DbgDir;
+ rc = RTFileReadAt(hFile, offDbgDir, &DbgDir, sizeof(DbgDir), NULL);
+ if (RT_FAILURE(rc))
+ break;
+ if (DbgDir.Type == IMAGE_DEBUG_TYPE_CODEVIEW)
+ rc = rtDbgModCvProbeFile2(pDbgMod, RTCVFILETYPE_DBG, hFile,
+ DbgDir.PointerToRawData, DbgDir.SizeOfData,
+ enmArch, pszFilename);
+ else if (DbgDir.Type == IMAGE_DEBUG_TYPE_COFF)
+ rc = rtDbgModCvProbeCoff(pDbgMod, RTCVFILETYPE_DBG, hFile,
+ DbgDir.PointerToRawData, DbgDir.SizeOfData, pszFilename);
+ }
+
+ /*
+ * If we get down here with an instance, it prooves that we've found
+ * something, regardless of any errors. Add the sections and such.
+ */
+ PRTDBGMODCV pThis = (PRTDBGMODCV)pDbgMod->pvDbgPriv;
+ if (pThis)
+ {
+ pThis->cbImage = DbgHdr.SizeOfImage;
+ if (pDbgMod->pImgVt)
+ rc = VINF_SUCCESS;
+ else
+ {
+ rc = rtDbgModCvAddSegmentsFromDbg(pThis, &DbgHdr, pszFilename);
+ if (RT_FAILURE(rc))
+ rtDbgModCv_Close(pDbgMod);
+ }
+ return rc;
+ }
+
+ /* Failed to find CV or smth, look at the end of the file just to be sure... */
+ }
+
+ /*
+ * Look for CV tail header.
+ */
+ uint64_t cbFile;
+ rc = RTFileSeek(hFile, -(RTFOFF)sizeof(RTCVHDR), RTFILE_SEEK_END, &cbFile);
+ if (RT_SUCCESS(rc))
+ {
+ cbFile += sizeof(RTCVHDR);
+ RTCVHDR CvHdr;
+ rc = RTFileRead(hFile, &CvHdr, sizeof(CvHdr), NULL);
+ if (RT_SUCCESS(rc))
+ rc = rtDbgModCvProbeFile2(pDbgMod, RTCVFILETYPE_OTHER_AT_END, hFile,
+ cbFile - CvHdr.off, CvHdr.off, enmArch, pszFilename);
+ }
+
+ if (RT_FAILURE(rc))
+ RTFileClose(hFile);
+ return rc;
+}
+
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnTryOpen} */
+static DECLCALLBACK(int) rtDbgModCv_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch)
+{
+ /*
+ * Look for debug info.
+ */
+ int rc = VERR_DBG_NO_MATCHING_INTERPRETER;
+ if (pMod->pszDbgFile)
+ rc = rtDbgModCvProbeFile(pMod, pMod->pszDbgFile, enmArch);
+
+ if (!pMod->pvDbgPriv && pMod->pImgVt)
+ {
+ int rc2 = pMod->pImgVt->pfnEnumDbgInfo(pMod, rtDbgModCvEnumCallback, pMod);
+ if (RT_FAILURE(rc2))
+ rc = rc2;
+
+ if (!pMod->pvDbgPriv)
+ {
+ /* Try the executable in case it has a NBxx tail header. */
+ rc2 = rtDbgModCvProbeFile(pMod, pMod->pszImgFile, enmArch);
+ if (RT_FAILURE(rc2) && (RT_SUCCESS(rc) || rc == VERR_DBG_NO_MATCHING_INTERPRETER))
+ rc = rc2;
+ }
+ }
+
+ PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
+ if (!pThis)
+ return RT_SUCCESS_NP(rc) ? VERR_DBG_NO_MATCHING_INTERPRETER : rc;
+ Assert(pThis->offBase != UINT32_MAX || pThis->offCoffDbgInfo != UINT32_MAX);
+
+ /*
+ * Load the debug info.
+ */
+ if (pMod->pImgVt)
+ {
+ rc = pMod->pImgVt->pfnEnumSegments(pMod, rtDbgModCvAddSegmentsCallback, pThis);
+ pThis->fHaveLoadedSegments = true;
+ }
+ if (RT_SUCCESS(rc) && pThis->offBase != UINT32_MAX)
+ rc = rtDbgModCvLoadCodeViewInfo(pThis);
+ if (RT_SUCCESS(rc) && pThis->offCoffDbgInfo != UINT32_MAX)
+ rc = rtDbgModCvLoadCoffInfo(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ Log(("RTDbgCv: Successfully loaded debug info\n"));
+ return VINF_SUCCESS;
+ }
+
+ Log(("RTDbgCv: Debug info load error %Rrc\n", rc));
+ rtDbgModCv_Close(pMod);
+ return rc;
+}
+
+
+
+
+
+/** Virtual function table for the CodeView debug info reader. */
+DECL_HIDDEN_CONST(RTDBGMODVTDBG) const g_rtDbgModVtDbgCodeView =
+{
+ /*.u32Magic = */ RTDBGMODVTDBG_MAGIC,
+ /*.fSupports = */ RT_DBGTYPE_CODEVIEW,
+ /*.pszName = */ "codeview",
+ /*.pfnTryOpen = */ rtDbgModCv_TryOpen,
+ /*.pfnClose = */ rtDbgModCv_Close,
+
+ /*.pfnRvaToSegOff = */ rtDbgModCv_RvaToSegOff,
+ /*.pfnImageSize = */ rtDbgModCv_ImageSize,
+
+ /*.pfnSegmentAdd = */ rtDbgModCv_SegmentAdd,
+ /*.pfnSegmentCount = */ rtDbgModCv_SegmentCount,
+ /*.pfnSegmentByIndex = */ rtDbgModCv_SegmentByIndex,
+
+ /*.pfnSymbolAdd = */ rtDbgModCv_SymbolAdd,
+ /*.pfnSymbolCount = */ rtDbgModCv_SymbolCount,
+ /*.pfnSymbolByOrdinal = */ rtDbgModCv_SymbolByOrdinal,
+ /*.pfnSymbolByName = */ rtDbgModCv_SymbolByName,
+ /*.pfnSymbolByAddr = */ rtDbgModCv_SymbolByAddr,
+
+ /*.pfnLineAdd = */ rtDbgModCv_LineAdd,
+ /*.pfnLineCount = */ rtDbgModCv_LineCount,
+ /*.pfnLineByOrdinal = */ rtDbgModCv_LineByOrdinal,
+ /*.pfnLineByAddr = */ rtDbgModCv_LineByAddr,
+
+ /*.pfnUnwindFrame = */ rtDbgModCv_UnwindFrame,
+
+ /*.u32EndMagic = */ RTDBGMODVTDBG_MAGIC
+};
+
diff --git a/src/VBox/Runtime/common/dbg/dbgmodcontainer.cpp b/src/VBox/Runtime/common/dbg/dbgmodcontainer.cpp
new file mode 100644
index 00000000..cf09ce75
--- /dev/null
+++ b/src/VBox/Runtime/common/dbg/dbgmodcontainer.cpp
@@ -0,0 +1,1050 @@
+/* $Id: dbgmodcontainer.cpp $ */
+/** @file
+ * IPRT - Debug Info Container.
+ */
+
+/*
+ * Copyright (C) 2009-2023 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 *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DBG
+#include <iprt/dbg.h>
+#include "internal/iprt.h"
+
+#include <iprt/avl.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#define RTDBGMODCNT_WITH_MEM_CACHE
+#ifdef RTDBGMODCNT_WITH_MEM_CACHE
+# include <iprt/memcache.h>
+#endif
+#include <iprt/string.h>
+#include <iprt/strcache.h>
+#include "internal/dbgmod.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Symbol entry.
+ */
+typedef struct RTDBGMODCTNSYMBOL
+{
+ /** The address core. */
+ AVLRUINTPTRNODECORE AddrCore;
+ /** The name space core. */
+ RTSTRSPACECORE NameCore;
+ /** The ordinal number core. */
+ AVLU32NODECORE OrdinalCore;
+ /** The segment index. */
+ RTDBGSEGIDX iSeg;
+ /** The symbol flags. */
+ uint32_t fFlags;
+ /** The symbol size.
+ * This may be zero while the range in AddrCore indicates 0. */
+ RTUINTPTR cb;
+} RTDBGMODCTNSYMBOL;
+/** Pointer to a symbol entry in the debug info container. */
+typedef RTDBGMODCTNSYMBOL *PRTDBGMODCTNSYMBOL;
+/** Pointer to a const symbol entry in the debug info container. */
+typedef RTDBGMODCTNSYMBOL const *PCRTDBGMODCTNSYMBOL;
+
+/**
+ * Line number entry.
+ */
+typedef struct RTDBGMODCTNLINE
+{
+ /** The address core.
+ * The Key is the address of the line number. */
+ AVLUINTPTRNODECORE AddrCore;
+ /** The ordinal number core. */
+ AVLU32NODECORE OrdinalCore;
+ /** Pointer to the file name (in string cache). */
+ const char *pszFile;
+ /** The line number. */
+ uint32_t uLineNo;
+ /** The segment index. */
+ RTDBGSEGIDX iSeg;
+} RTDBGMODCTNLINE;
+/** Pointer to a line number entry. */
+typedef RTDBGMODCTNLINE *PRTDBGMODCTNLINE;
+/** Pointer to const a line number entry. */
+typedef RTDBGMODCTNLINE const *PCRTDBGMODCTNLINE;
+
+/**
+ * Segment entry.
+ */
+typedef struct RTDBGMODCTNSEGMENT
+{
+ /** The symbol address space tree. */
+ AVLRUINTPTRTREE SymAddrTree;
+ /** The line number address space tree. */
+ AVLUINTPTRTREE LineAddrTree;
+ /** The segment offset. */
+ RTUINTPTR off;
+ /** The segment size. */
+ RTUINTPTR cb;
+ /** The segment flags. */
+ uint32_t fFlags;
+ /** The segment name. */
+ const char *pszName;
+} RTDBGMODCTNSEGMENT;
+/** Pointer to a segment entry in the debug info container. */
+typedef RTDBGMODCTNSEGMENT *PRTDBGMODCTNSEGMENT;
+/** Pointer to a const segment entry in the debug info container. */
+typedef RTDBGMODCTNSEGMENT const *PCRTDBGMODCTNSEGMENT;
+
+/**
+ * Instance data.
+ */
+typedef struct RTDBGMODCTN
+{
+ /** The name space. */
+ RTSTRSPACE Names;
+ /** Tree containing any absolute addresses. */
+ AVLRUINTPTRTREE AbsAddrTree;
+ /** Tree organizing the symbols by ordinal number. */
+ AVLU32TREE SymbolOrdinalTree;
+ /** Tree organizing the line numbers by ordinal number. */
+ AVLU32TREE LineOrdinalTree;
+ /** Segment table. */
+ PRTDBGMODCTNSEGMENT paSegs;
+ /** The number of segments in the segment table. */
+ RTDBGSEGIDX cSegs;
+ /** The image size. 0 means unlimited. */
+ RTUINTPTR cb;
+ /** The next symbol ordinal. */
+ uint32_t iNextSymbolOrdinal;
+ /** The next line number ordinal. */
+ uint32_t iNextLineOrdinal;
+#ifdef RTDBGMODCNT_WITH_MEM_CACHE
+ /** Line number allocator.
+ * Using a cache is a bit overkill since we normally won't free them, but
+ * it's a construct that exists and does the job relatively efficiently. */
+ RTMEMCACHE hLineNumAllocator;
+#endif
+} RTDBGMODCTN;
+/** Pointer to instance data for the debug info container. */
+typedef RTDBGMODCTN *PRTDBGMODCTN;
+
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnUnwindFrame} */
+static DECLCALLBACK(int)
+rtDbgModContainer_UnwindFrame(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState)
+{
+ RT_NOREF(pMod, iSeg, off, pState);
+ return VERR_DBG_NO_UNWIND_INFO;
+}
+
+
+/**
+ * Fills in a RTDBGSYMBOL structure.
+ *
+ * @returns VINF_SUCCESS.
+ * @param pMySym Our internal symbol representation.
+ * @param pExtSym The external symbol representation.
+ */
+DECLINLINE(int) rtDbgModContainerReturnSymbol(PCRTDBGMODCTNSYMBOL pMySym, PRTDBGSYMBOL pExtSym)
+{
+ pExtSym->Value = pMySym->AddrCore.Key;
+ pExtSym->offSeg = pMySym->AddrCore.Key;
+ pExtSym->iSeg = pMySym->iSeg;
+ pExtSym->fFlags = pMySym->fFlags;
+ pExtSym->cb = pMySym->cb;
+ pExtSym->iOrdinal = pMySym->OrdinalCore.Key;
+ Assert(pMySym->NameCore.cchString < sizeof(pExtSym->szName));
+ memcpy(pExtSym->szName, pMySym->NameCore.pszString, pMySym->NameCore.cchString + 1);
+ return VINF_SUCCESS;
+}
+
+
+
+/** @copydoc RTDBGMODVTDBG::pfnLineByAddr */
+static DECLCALLBACK(int) rtDbgModContainer_LineByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off,
+ PRTINTPTR poffDisp, PRTDBGLINE pLineInfo)
+{
+ PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv;
+
+ /*
+ * Validate the input address.
+ */
+ AssertMsgReturn(iSeg < pThis->cSegs,
+ ("iSeg=%#x cSegs=%#x\n", iSeg, pThis->cSegs),
+ VERR_DBG_INVALID_SEGMENT_INDEX);
+ AssertMsgReturn(off < pThis->paSegs[iSeg].cb,
+ ("off=%RTptr cbSeg=%RTptr\n", off, pThis->paSegs[iSeg].cb),
+ VERR_DBG_INVALID_SEGMENT_OFFSET);
+
+ /*
+ * Lookup the nearest line number with an address less or equal to the specified address.
+ */
+ PAVLUINTPTRNODECORE pAvlCore = RTAvlUIntPtrGetBestFit(&pThis->paSegs[iSeg].LineAddrTree, off, false /*fAbove*/);
+ if (!pAvlCore)
+ return pThis->iNextLineOrdinal
+ ? VERR_DBG_LINE_NOT_FOUND
+ : VERR_DBG_NO_LINE_NUMBERS;
+ PCRTDBGMODCTNLINE pMyLine = RT_FROM_MEMBER(pAvlCore, RTDBGMODCTNLINE const, AddrCore);
+ pLineInfo->Address = pMyLine->AddrCore.Key;
+ pLineInfo->offSeg = pMyLine->AddrCore.Key;
+ pLineInfo->iSeg = iSeg;
+ pLineInfo->uLineNo = pMyLine->uLineNo;
+ pLineInfo->iOrdinal = pMyLine->OrdinalCore.Key;
+ strcpy(pLineInfo->szFilename, pMyLine->pszFile);
+ if (poffDisp)
+ *poffDisp = off - pMyLine->AddrCore.Key;
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc RTDBGMODVTDBG::pfnLineByOrdinal */
+static DECLCALLBACK(int) rtDbgModContainer_LineByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo)
+{
+ PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv;
+
+ /*
+ * Look it up.
+ */
+ if (iOrdinal >= pThis->iNextLineOrdinal)
+ return pThis->iNextLineOrdinal
+ ? VERR_DBG_LINE_NOT_FOUND
+ : VERR_DBG_NO_LINE_NUMBERS;
+ PAVLU32NODECORE pAvlCore = RTAvlU32Get(&pThis->LineOrdinalTree, iOrdinal);
+ AssertReturn(pAvlCore, VERR_DBG_LINE_NOT_FOUND);
+ PCRTDBGMODCTNLINE pMyLine = RT_FROM_MEMBER(pAvlCore, RTDBGMODCTNLINE const, OrdinalCore);
+ pLineInfo->Address = pMyLine->AddrCore.Key;
+ pLineInfo->offSeg = pMyLine->AddrCore.Key;
+ pLineInfo->iSeg = pMyLine->iSeg;
+ pLineInfo->uLineNo = pMyLine->uLineNo;
+ pLineInfo->iOrdinal = pMyLine->OrdinalCore.Key;
+ strcpy(pLineInfo->szFilename, pMyLine->pszFile);
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc RTDBGMODVTDBG::pfnLineCount */
+static DECLCALLBACK(uint32_t) rtDbgModContainer_LineCount(PRTDBGMODINT pMod)
+{
+ PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv;
+
+ /* Note! The ordinal numbers are 0-based. */
+ return pThis->iNextLineOrdinal;
+}
+
+
+/** @copydoc RTDBGMODVTDBG::pfnLineAdd */
+static DECLCALLBACK(int) rtDbgModContainer_LineAdd(PRTDBGMODINT pMod, const char *pszFile, size_t cchFile, uint32_t uLineNo,
+ uint32_t iSeg, RTUINTPTR off, uint32_t *piOrdinal)
+{
+ PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv;
+
+ /*
+ * Validate the input address.
+ */
+ AssertMsgReturn(iSeg < pThis->cSegs, ("iSeg=%#x cSegs=%#x\n", iSeg, pThis->cSegs),
+ VERR_DBG_INVALID_SEGMENT_INDEX);
+ AssertMsgReturn(off <= pThis->paSegs[iSeg].cb, ("off=%RTptr cbSeg=%RTptr\n", off, pThis->paSegs[iSeg].cb),
+ VERR_DBG_INVALID_SEGMENT_OFFSET);
+
+ /*
+ * Create a new entry.
+ */
+#ifdef RTDBGMODCNT_WITH_MEM_CACHE
+ PRTDBGMODCTNLINE pLine = (PRTDBGMODCTNLINE)RTMemCacheAlloc(pThis->hLineNumAllocator);
+#else
+ PRTDBGMODCTNLINE pLine = (PRTDBGMODCTNLINE)RTMemAllocZ(sizeof(*pLine));
+#endif
+ if (!pLine)
+ return VERR_NO_MEMORY;
+ pLine->AddrCore.Key = off;
+ pLine->OrdinalCore.Key = pThis->iNextLineOrdinal;
+ pLine->uLineNo = uLineNo;
+ pLine->iSeg = iSeg;
+ pLine->pszFile = RTStrCacheEnterN(g_hDbgModStrCache, pszFile, cchFile);
+ int rc;
+ if (pLine->pszFile)
+ {
+ if (RTAvlUIntPtrInsert(&pThis->paSegs[iSeg].LineAddrTree, &pLine->AddrCore))
+ {
+ if (RTAvlU32Insert(&pThis->LineOrdinalTree, &pLine->OrdinalCore))
+ {
+ if (piOrdinal)
+ *piOrdinal = pThis->iNextLineOrdinal;
+ pThis->iNextLineOrdinal++;
+ return VINF_SUCCESS;
+ }
+
+ rc = VERR_INTERNAL_ERROR_5;
+ RTAvlUIntPtrRemove(&pThis->paSegs[iSeg].LineAddrTree, pLine->AddrCore.Key);
+ }
+
+ /* bail out */
+ rc = VERR_DBG_ADDRESS_CONFLICT;
+ RTStrCacheRelease(g_hDbgModStrCache, pLine->pszFile);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+#ifdef RTDBGMODCNT_WITH_MEM_CACHE
+ RTMemCacheFree(pThis->hLineNumAllocator, pLine);
+#else
+ RTMemFree(pLine);
+#endif
+ return rc;
+}
+
+
+/** @copydoc RTDBGMODVTDBG::pfnSymbolByAddr */
+static DECLCALLBACK(int) rtDbgModContainer_SymbolByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags,
+ PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo)
+{
+ PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv;
+
+ /*
+ * Validate the input address.
+ */
+ AssertMsgReturn( iSeg == RTDBGSEGIDX_ABS
+ || iSeg < pThis->cSegs,
+ ("iSeg=%#x cSegs=%#x\n", iSeg, pThis->cSegs),
+ VERR_DBG_INVALID_SEGMENT_INDEX);
+ AssertMsgReturn( iSeg >= RTDBGSEGIDX_SPECIAL_FIRST
+ || off <= pThis->paSegs[iSeg].cb,
+ ("off=%RTptr cbSeg=%RTptr\n", off, pThis->paSegs[iSeg].cb),
+ VERR_DBG_INVALID_SEGMENT_OFFSET);
+
+ /*
+ * Lookup the nearest symbol with an address less or equal to the specified address.
+ */
+ PAVLRUINTPTRNODECORE pAvlCore = RTAvlrUIntPtrGetBestFit( iSeg == RTDBGSEGIDX_ABS
+ ? &pThis->AbsAddrTree
+ : &pThis->paSegs[iSeg].SymAddrTree,
+ off,
+ fFlags == RTDBGSYMADDR_FLAGS_GREATER_OR_EQUAL /*fAbove*/);
+ if (!pAvlCore)
+ return VERR_SYMBOL_NOT_FOUND;
+ PCRTDBGMODCTNSYMBOL pMySym = RT_FROM_MEMBER(pAvlCore, RTDBGMODCTNSYMBOL const, AddrCore);
+ if (poffDisp)
+ *poffDisp = off - pMySym->AddrCore.Key;
+ return rtDbgModContainerReturnSymbol(pMySym, pSymInfo);
+}
+
+
+/** @copydoc RTDBGMODVTDBG::pfnSymbolByName */
+static DECLCALLBACK(int) rtDbgModContainer_SymbolByName(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol, PRTDBGSYMBOL pSymInfo)
+{
+ PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv;
+ NOREF(cchSymbol);
+
+ /*
+ * Look it up in the name space.
+ */
+ PRTSTRSPACECORE pStrCore = RTStrSpaceGet(&pThis->Names, pszSymbol);
+ if (!pStrCore)
+ return VERR_SYMBOL_NOT_FOUND;
+ PCRTDBGMODCTNSYMBOL pMySym = RT_FROM_MEMBER(pStrCore, RTDBGMODCTNSYMBOL const, NameCore);
+ return rtDbgModContainerReturnSymbol(pMySym, pSymInfo);
+}
+
+
+/** @copydoc RTDBGMODVTDBG::pfnSymbolByOrdinal */
+static DECLCALLBACK(int) rtDbgModContainer_SymbolByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo)
+{
+ PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv;
+
+ /*
+ * Look it up in the ordinal tree.
+ */
+ if (iOrdinal >= pThis->iNextSymbolOrdinal)
+ return pThis->iNextSymbolOrdinal
+ ? VERR_DBG_NO_SYMBOLS
+ : VERR_SYMBOL_NOT_FOUND;
+ PAVLU32NODECORE pAvlCore = RTAvlU32Get(&pThis->SymbolOrdinalTree, iOrdinal);
+ AssertReturn(pAvlCore, VERR_SYMBOL_NOT_FOUND);
+ PCRTDBGMODCTNSYMBOL pMySym = RT_FROM_MEMBER(pAvlCore, RTDBGMODCTNSYMBOL const, OrdinalCore);
+ return rtDbgModContainerReturnSymbol(pMySym, pSymInfo);
+}
+
+
+/** @copydoc RTDBGMODVTDBG::pfnSymbolCount */
+static DECLCALLBACK(uint32_t) rtDbgModContainer_SymbolCount(PRTDBGMODINT pMod)
+{
+ PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv;
+
+ /* Note! The ordinal numbers are 0-based. */
+ return pThis->iNextSymbolOrdinal;
+}
+
+
+/**
+ * Worker for rtDbgModContainer_SymbolAdd that removes a symbol to resolve
+ * address conflicts.
+ *
+ * We don't shift ordinals up as that could be very expensive, instead we move
+ * the last one up to take the place of the one we're removing. Caller must
+ * take this into account.
+ *
+ * @param pThis The container.
+ * @param pAddrTree The address tree to remove from.
+ * @param pToRemove The conflicting symbol to be removed.
+ */
+static void rtDbgModContainer_SymbolReplace(PRTDBGMODCTN pThis, PAVLRUINTPTRTREE pAddrTree, PRTDBGMODCTNSYMBOL pToRemove)
+{
+ Log(("rtDbgModContainer_SymbolReplace: pToRemove=%p ordinal=%u %04x:%08RX64 %s\n",
+ pToRemove, pToRemove->OrdinalCore.Key, pToRemove->iSeg, pToRemove->AddrCore.Key, pToRemove->NameCore.pszString));
+
+ /* Unlink it. */
+ PRTSTRSPACECORE pRemovedName = RTStrSpaceRemove(&pThis->Names, pToRemove->NameCore.pszString);
+ Assert(pRemovedName); RT_NOREF_PV(pRemovedName);
+ pToRemove->NameCore.pszString = NULL;
+
+ PAVLRUINTPTRNODECORE pRemovedAddr = RTAvlrUIntPtrRemove(pAddrTree, pToRemove->AddrCore.Key);
+ Assert(pRemovedAddr); RT_NOREF_PV(pRemovedAddr);
+ pToRemove->AddrCore.Key = 0;
+ pToRemove->AddrCore.KeyLast = 0;
+
+ uint32_t const iOrdinal = pToRemove->OrdinalCore.Key;
+ PAVLU32NODECORE pRemovedOrdinal = RTAvlU32Remove(&pThis->SymbolOrdinalTree, iOrdinal);
+ Assert(pRemovedOrdinal); RT_NOREF_PV(pRemovedOrdinal);
+
+ RTMemFree(pToRemove);
+
+ /* Jump the last symbol ordinal to take its place, unless pToRemove is the last one. */
+ if (iOrdinal >= pThis->iNextSymbolOrdinal - 1)
+ pThis->iNextSymbolOrdinal -= 1;
+ else
+ {
+ PAVLU32NODECORE pLastOrdinal = RTAvlU32Remove(&pThis->SymbolOrdinalTree, pThis->iNextSymbolOrdinal - 1);
+ AssertReturnVoid(pLastOrdinal);
+
+ pThis->iNextSymbolOrdinal -= 1;
+ pLastOrdinal->Key = iOrdinal;
+ bool fInsert = RTAvlU32Insert(&pThis->SymbolOrdinalTree, pLastOrdinal);
+ Assert(fInsert); RT_NOREF_PV(fInsert);
+ }
+}
+
+
+/** @copydoc RTDBGMODVTDBG::pfnSymbolAdd */
+static DECLCALLBACK(int) rtDbgModContainer_SymbolAdd(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol,
+ RTDBGSEGIDX iSeg, RTUINTPTR off, RTUINTPTR cb, uint32_t fFlags,
+ uint32_t *piOrdinal)
+{
+ PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv;
+
+ /*
+ * Address validation. The other arguments have already been validated.
+ */
+ AssertMsgReturn( iSeg == RTDBGSEGIDX_ABS
+ || iSeg < pThis->cSegs,
+ ("iSeg=%#x cSegs=%#x\n", iSeg, pThis->cSegs),
+ VERR_DBG_INVALID_SEGMENT_INDEX);
+ AssertMsgReturn( iSeg >= RTDBGSEGIDX_SPECIAL_FIRST
+ || off <= pThis->paSegs[iSeg].cb,
+ ("off=%RTptr cb=%RTptr cbSeg=%RTptr\n", off, cb, pThis->paSegs[iSeg].cb),
+ VERR_DBG_INVALID_SEGMENT_OFFSET);
+
+ /* Be a little relaxed wrt to the symbol size. */
+ int rc = VINF_SUCCESS;
+ if (iSeg != RTDBGSEGIDX_ABS && off + cb > pThis->paSegs[iSeg].cb)
+ {
+ cb = pThis->paSegs[iSeg].cb - off;
+ rc = VINF_DBG_ADJUSTED_SYM_SIZE;
+ }
+
+ /*
+ * Create a new entry.
+ */
+ PRTDBGMODCTNSYMBOL pSymbol = (PRTDBGMODCTNSYMBOL)RTMemAllocZ(sizeof(*pSymbol));
+ if (!pSymbol)
+ return VERR_NO_MEMORY;
+
+ pSymbol->AddrCore.Key = off;
+ pSymbol->AddrCore.KeyLast = off + (cb ? cb - 1 : 0);
+ pSymbol->OrdinalCore.Key = pThis->iNextSymbolOrdinal;
+ pSymbol->iSeg = iSeg;
+ pSymbol->cb = cb;
+ pSymbol->fFlags = fFlags;
+ pSymbol->NameCore.pszString = RTStrCacheEnterN(g_hDbgModStrCache, pszSymbol, cchSymbol);
+ if (pSymbol->NameCore.pszString)
+ {
+ if (RTStrSpaceInsert(&pThis->Names, &pSymbol->NameCore))
+ {
+ PAVLRUINTPTRTREE pAddrTree = iSeg == RTDBGSEGIDX_ABS
+ ? &pThis->AbsAddrTree
+ : &pThis->paSegs[iSeg].SymAddrTree;
+ if (RTAvlrUIntPtrInsert(pAddrTree, &pSymbol->AddrCore))
+ {
+ if (RTAvlU32Insert(&pThis->SymbolOrdinalTree, &pSymbol->OrdinalCore))
+ {
+ /*
+ * Success.
+ */
+ if (piOrdinal)
+ *piOrdinal = pThis->iNextSymbolOrdinal;
+ Log12(("rtDbgModContainer_SymbolAdd: ordinal=%u %04x:%08RX64 LB %#RX64 %s\n",
+ pThis->iNextSymbolOrdinal, iSeg, off, cb, pSymbol->NameCore.pszString));
+ pThis->iNextSymbolOrdinal++;
+ return rc;
+ }
+
+ /* bail out */
+ rc = VERR_INTERNAL_ERROR_5;
+ RTAvlrUIntPtrRemove(pAddrTree, pSymbol->AddrCore.Key);
+ }
+ /*
+ * Did the caller specify a conflict resolution method?
+ */
+ else if (fFlags & ( RTDBGSYMBOLADD_F_REPLACE_SAME_ADDR
+ | RTDBGSYMBOLADD_F_REPLACE_ANY
+ | RTDBGSYMBOLADD_F_ADJUST_SIZES_ON_CONFLICT))
+ {
+ /*
+ * Handle anything at or before the start address first:
+ */
+ AssertCompileMemberOffset(RTDBGMODCTNSYMBOL, AddrCore, 0);
+ PRTDBGMODCTNSYMBOL pConflict = (PRTDBGMODCTNSYMBOL)RTAvlrUIntPtrRangeGet(pAddrTree, pSymbol->AddrCore.Key);
+ if (pConflict)
+ {
+ if (pConflict->AddrCore.Key == pSymbol->AddrCore.Key)
+ {
+ /* Same address, only option is replacing it. */
+ if (fFlags & (RTDBGSYMBOLADD_F_REPLACE_SAME_ADDR | RTDBGSYMBOLADD_F_REPLACE_ANY))
+ rtDbgModContainer_SymbolReplace(pThis, pAddrTree, pConflict);
+ else
+ rc = VERR_DBG_ADDRESS_CONFLICT;
+ }
+ else if (fFlags & RTDBGSYMBOLADD_F_ADJUST_SIZES_ON_CONFLICT)
+ {
+ /* Reduce the size of the symbol before us, adopting the size if we've got none. */
+ Assert(pConflict->AddrCore.Key < pSymbol->AddrCore.Key);
+ if (!pSymbol->cb)
+ {
+ pSymbol->AddrCore.KeyLast = pSymbol->AddrCore.KeyLast;
+ pSymbol->cb = pSymbol->AddrCore.KeyLast - pConflict->AddrCore.Key + 1;
+ rc = VINF_DBG_ADJUSTED_SYM_SIZE;
+ }
+ pConflict->AddrCore.KeyLast = pSymbol->AddrCore.Key - 1;
+ pConflict->cb = pSymbol->AddrCore.Key - pConflict->AddrCore.Key;
+ }
+ else if (fFlags & RTDBGSYMBOLADD_F_REPLACE_ANY)
+ rtDbgModContainer_SymbolReplace(pThis, pAddrTree, pConflict);
+ else
+ rc = VERR_DBG_ADDRESS_CONFLICT;
+ }
+
+ /*
+ * Try insert again and deal with symbols in the range.
+ */
+ while (RT_SUCCESS(rc))
+ {
+ if (RTAvlrUIntPtrInsert(pAddrTree, &pSymbol->AddrCore))
+ {
+ pSymbol->OrdinalCore.Key = pThis->iNextSymbolOrdinal;
+ if (RTAvlU32Insert(&pThis->SymbolOrdinalTree, &pSymbol->OrdinalCore))
+ {
+ /*
+ * Success.
+ */
+ if (piOrdinal)
+ *piOrdinal = pThis->iNextSymbolOrdinal;
+ pThis->iNextSymbolOrdinal++;
+ Log12(("rtDbgModContainer_SymbolAdd: ordinal=%u %04x:%08RX64 LB %#RX64 %s [replace codepath]\n",
+ pThis->iNextSymbolOrdinal, iSeg, off, cb, pSymbol->NameCore.pszString));
+ return rc;
+ }
+
+ rc = VERR_INTERNAL_ERROR_5;
+ RTAvlrUIntPtrRemove(pAddrTree, pSymbol->AddrCore.Key);
+ break;
+ }
+
+ /* Get the first symbol above us and see if we can do anything about it (or ourselves). */
+ AssertCompileMemberOffset(RTDBGMODCTNSYMBOL, AddrCore, 0);
+ pConflict = (PRTDBGMODCTNSYMBOL)RTAvlrUIntPtrGetBestFit(pAddrTree, pSymbol->AddrCore.Key, true /*fAbove*/);
+ AssertBreakStmt(pConflict, rc = VERR_DBG_ADDRESS_CONFLICT);
+ Assert(pSymbol->AddrCore.Key != pConflict->AddrCore.Key);
+ Assert(pSymbol->AddrCore.KeyLast >= pConflict->AddrCore.Key);
+
+ if (fFlags & RTDBGSYMBOLADD_F_ADJUST_SIZES_ON_CONFLICT)
+ {
+ Assert(pSymbol->cb > 0);
+ pSymbol->AddrCore.Key = pConflict->AddrCore.Key - 1;
+ pSymbol->cb = pConflict->AddrCore.Key - pSymbol->AddrCore.Key;
+ rc = VINF_DBG_ADJUSTED_SYM_SIZE;
+ }
+ else if (fFlags & RTDBGSYMBOLADD_F_REPLACE_ANY)
+ rtDbgModContainer_SymbolReplace(pThis, pAddrTree, pConflict);
+ else
+ rc = VERR_DBG_ADDRESS_CONFLICT;
+ }
+ }
+ else
+ rc = VERR_DBG_ADDRESS_CONFLICT;
+ RTStrSpaceRemove(&pThis->Names, pSymbol->NameCore.pszString);
+ }
+ else
+ rc = VERR_DBG_DUPLICATE_SYMBOL;
+ RTStrCacheRelease(g_hDbgModStrCache, pSymbol->NameCore.pszString);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ RTMemFree(pSymbol);
+ return rc;
+}
+
+
+/** @copydoc RTDBGMODVTDBG::pfnSegmentByIndex */
+static DECLCALLBACK(int) rtDbgModContainer_SegmentByIndex(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo)
+{
+ PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv;
+ if (iSeg >= pThis->cSegs)
+ return VERR_DBG_INVALID_SEGMENT_INDEX;
+ pSegInfo->Address = RTUINTPTR_MAX;
+ pSegInfo->uRva = pThis->paSegs[iSeg].off;
+ pSegInfo->cb = pThis->paSegs[iSeg].cb;
+ pSegInfo->fFlags = pThis->paSegs[iSeg].fFlags;
+ pSegInfo->iSeg = iSeg;
+ strcpy(pSegInfo->szName, pThis->paSegs[iSeg].pszName);
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc RTDBGMODVTDBG::pfnSegmentCount */
+static DECLCALLBACK(RTDBGSEGIDX) rtDbgModContainer_SegmentCount(PRTDBGMODINT pMod)
+{
+ PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv;
+ return pThis->cSegs;
+}
+
+
+/** @copydoc RTDBGMODVTDBG::pfnSegmentAdd */
+static DECLCALLBACK(int) rtDbgModContainer_SegmentAdd(PRTDBGMODINT pMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName, size_t cchName,
+ uint32_t fFlags, PRTDBGSEGIDX piSeg)
+{
+ PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv;
+
+ /*
+ * Input validation (the bits the caller cannot do).
+ */
+ /* Overlapping segments are not yet supported. Will use flags to deal with it if it becomes necessary. */
+ RTUINTPTR uRvaLast = uRva + RT_MAX(cb, 1) - 1;
+ RTUINTPTR uRvaLastMax = uRvaLast;
+ RTDBGSEGIDX iSeg = pThis->cSegs;
+ while (iSeg-- > 0)
+ {
+ RTUINTPTR uCurRva = pThis->paSegs[iSeg].off;
+ RTUINTPTR uCurRvaLast = uCurRva + RT_MAX(pThis->paSegs[iSeg].cb, 1) - 1;
+ if ( uRva <= uCurRvaLast
+ && uRvaLast >= uCurRva
+ && ( /* HACK ALERT! Allow empty segments to share space (bios/watcom, elf). */
+ (cb != 0 && pThis->paSegs[iSeg].cb != 0)
+ || ( cb == 0
+ && uRva != uCurRva
+ && uRva != uCurRvaLast)
+ || ( pThis->paSegs[iSeg].cb == 0
+ && uCurRva != uRva
+ && uCurRva != uRvaLast)
+ )
+ )
+ AssertMsgFailedReturn(("uRva=%RTptr uRvaLast=%RTptr (cb=%RTptr) \"%s\";\n"
+ "uRva=%RTptr uRvaLast=%RTptr (cb=%RTptr) \"%s\" iSeg=%#x\n",
+ uRva, uRvaLast, cb, pszName,
+ uCurRva, uCurRvaLast, pThis->paSegs[iSeg].cb, pThis->paSegs[iSeg].pszName, iSeg),
+ VERR_DBG_SEGMENT_INDEX_CONFLICT);
+ if (uRvaLastMax < uCurRvaLast)
+ uRvaLastMax = uCurRvaLast;
+ }
+ /* Strict ordered segment addition at the moment. */
+ iSeg = pThis->cSegs;
+ AssertMsgReturn(!piSeg || *piSeg == NIL_RTDBGSEGIDX || *piSeg == iSeg,
+ ("iSeg=%#x *piSeg=%#x\n", iSeg, *piSeg),
+ VERR_DBG_INVALID_SEGMENT_INDEX);
+
+ /*
+ * Add an entry to the segment table, extending it if necessary.
+ */
+ if (!(iSeg % 8))
+ {
+ void *pvSegs = RTMemRealloc(pThis->paSegs, sizeof(RTDBGMODCTNSEGMENT) * (iSeg + 8));
+ if (!pvSegs)
+ return VERR_NO_MEMORY;
+ pThis->paSegs = (PRTDBGMODCTNSEGMENT)pvSegs;
+ }
+
+ pThis->paSegs[iSeg].SymAddrTree = NULL;
+ pThis->paSegs[iSeg].LineAddrTree = NULL;
+ pThis->paSegs[iSeg].off = uRva;
+ pThis->paSegs[iSeg].cb = cb;
+ pThis->paSegs[iSeg].fFlags = fFlags;
+ pThis->paSegs[iSeg].pszName = RTStrCacheEnterN(g_hDbgModStrCache, pszName, cchName);
+ if (pThis->paSegs[iSeg].pszName)
+ {
+ if (piSeg)
+ *piSeg = iSeg;
+ pThis->cSegs++;
+ pThis->cb = uRvaLastMax + 1;
+ if (!pThis->cb)
+ pThis->cb = RTUINTPTR_MAX;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+/** @copydoc RTDBGMODVTDBG::pfnImageSize */
+static DECLCALLBACK(RTUINTPTR) rtDbgModContainer_ImageSize(PRTDBGMODINT pMod)
+{
+ PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv;
+ return pThis->cb;
+}
+
+
+/** @copydoc RTDBGMODVTDBG::pfnRvaToSegOff */
+static DECLCALLBACK(RTDBGSEGIDX) rtDbgModContainer_RvaToSegOff(PRTDBGMODINT pMod, RTUINTPTR uRva, PRTUINTPTR poffSeg)
+{
+ PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv;
+ PCRTDBGMODCTNSEGMENT paSeg = pThis->paSegs;
+ uint32_t const cSegs = pThis->cSegs;
+#if 0
+ if (cSegs <= 7)
+#endif
+ {
+ /*
+ * Linear search.
+ */
+ for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
+ {
+ RTUINTPTR offSeg = uRva - paSeg[iSeg].off;
+ if (offSeg < paSeg[iSeg].cb)
+ {
+ if (poffSeg)
+ *poffSeg = offSeg;
+ return iSeg;
+ }
+ }
+ }
+#if 0 /** @todo binary search doesn't work if we've got empty segments in the list */
+ else
+ {
+ /*
+ * Binary search.
+ */
+ uint32_t iFirst = 0;
+ uint32_t iLast = cSegs - 1;
+ for (;;)
+ {
+ uint32_t iSeg = iFirst + (iLast - iFirst) / 2;
+ RTUINTPTR offSeg = uRva - paSeg[iSeg].off;
+ if (offSeg < paSeg[iSeg].cb)
+ {
+#if 0 /* Enable if we change the above test. */
+ if (offSeg == 0 && paSeg[iSeg].cb == 0)
+ while ( iSeg > 0
+ && paSeg[iSeg - 1].cb == 0
+ && paSeg[iSeg - 1].off == uRva)
+ iSeg--;
+#endif
+
+ if (poffSeg)
+ *poffSeg = offSeg;
+ return iSeg;
+ }
+
+ /* advance */
+ if (uRva < paSeg[iSeg].off)
+ {
+ /* between iFirst and iSeg. */
+ if (iSeg == iFirst)
+ break;
+ iLast = iSeg - 1;
+ }
+ else
+ {
+ /* between iSeg and iLast. paSeg[iSeg].cb == 0 ends up here too. */
+ if (iSeg == iLast)
+ break;
+ iFirst = iSeg + 1;
+ }
+ }
+ }
+#endif
+
+ /* Invalid. */
+ return NIL_RTDBGSEGIDX;
+}
+
+
+/** Destroy a line number node. */
+static DECLCALLBACK(int) rtDbgModContainer_DestroyTreeLineNode(PAVLU32NODECORE pNode, void *pvUser)
+{
+ PRTDBGMODCTN pThis = (PRTDBGMODCTN)pvUser;
+ PRTDBGMODCTNLINE pLine = RT_FROM_MEMBER(pNode, RTDBGMODCTNLINE, OrdinalCore);
+ RTStrCacheRelease(g_hDbgModStrCache, pLine->pszFile);
+ pLine->pszFile = NULL;
+#ifdef RTDBGMODCNT_WITH_MEM_CACHE
+ RTMemCacheFree(pThis->hLineNumAllocator, pLine);
+#else
+ RTMemFree(pLine); NOREF(pThis);
+#endif
+ return 0;
+}
+
+
+/** Destroy a symbol node. */
+static DECLCALLBACK(int) rtDbgModContainer_DestroyTreeNode(PAVLRUINTPTRNODECORE pNode, void *pvUser)
+{
+ PRTDBGMODCTNSYMBOL pSym = RT_FROM_MEMBER(pNode, RTDBGMODCTNSYMBOL, AddrCore);
+ RTStrCacheRelease(g_hDbgModStrCache, pSym->NameCore.pszString);
+ pSym->NameCore.pszString = NULL;
+
+#if 0
+ //PRTDBGMODCTN pThis = (PRTDBGMODCTN)pvUser;
+ //PAVLU32NODECORE pRemoved = RTAvlU32Remove(&pThis->SymbolOrdinalTree, pSym->OrdinalCore.Key);
+ //Assert(pRemoved == &pSym->OrdinalCore); RT_NOREF_PV(pRemoved);
+#else
+ RT_NOREF_PV(pvUser);
+#endif
+
+ RTMemFree(pSym);
+ return 0;
+}
+
+
+/** @copydoc RTDBGMODVTDBG::pfnClose */
+static DECLCALLBACK(int) rtDbgModContainer_Close(PRTDBGMODINT pMod)
+{
+ PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv;
+
+ /*
+ * Destroy the symbols and instance data.
+ */
+ for (uint32_t iSeg = 0; iSeg < pThis->cSegs; iSeg++)
+ {
+ RTAvlrUIntPtrDestroy(&pThis->paSegs[iSeg].SymAddrTree, rtDbgModContainer_DestroyTreeNode, pThis);
+ RTStrCacheRelease(g_hDbgModStrCache, pThis->paSegs[iSeg].pszName);
+ pThis->paSegs[iSeg].pszName = NULL;
+ }
+
+ RTAvlrUIntPtrDestroy(&pThis->AbsAddrTree, rtDbgModContainer_DestroyTreeNode, pThis);
+
+ pThis->Names = NULL;
+
+#ifdef RTDBGMODCNT_WITH_MEM_CACHE
+ RTMemCacheDestroy(pThis->hLineNumAllocator);
+ pThis->hLineNumAllocator = NIL_RTMEMCACHE;
+#else
+ RTAvlU32Destroy(&pThis->LineOrdinalTree, rtDbgModContainer_DestroyTreeLineNode, pThis);
+#endif
+
+ RTMemFree(pThis->paSegs);
+ pThis->paSegs = NULL;
+
+ RTMemFree(pThis);
+
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc RTDBGMODVTDBG::pfnTryOpen */
+static DECLCALLBACK(int) rtDbgModContainer_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch)
+{
+ NOREF(pMod); NOREF(enmArch);
+ return VERR_INTERNAL_ERROR_5;
+}
+
+
+
+/** Virtual function table for the debug info container. */
+DECL_HIDDEN_CONST(RTDBGMODVTDBG) const g_rtDbgModVtDbgContainer =
+{
+ /*.u32Magic = */ RTDBGMODVTDBG_MAGIC,
+ /*.fSupports = */ 0, /* (Don't call my TryOpen, please.) */
+ /*.pszName = */ "container",
+ /*.pfnTryOpen = */ rtDbgModContainer_TryOpen,
+ /*.pfnClose = */ rtDbgModContainer_Close,
+
+ /*.pfnRvaToSegOff = */ rtDbgModContainer_RvaToSegOff,
+ /*.pfnImageSize = */ rtDbgModContainer_ImageSize,
+
+ /*.pfnSegmentAdd = */ rtDbgModContainer_SegmentAdd,
+ /*.pfnSegmentCount = */ rtDbgModContainer_SegmentCount,
+ /*.pfnSegmentByIndex = */ rtDbgModContainer_SegmentByIndex,
+
+ /*.pfnSymbolAdd = */ rtDbgModContainer_SymbolAdd,
+ /*.pfnSymbolCount = */ rtDbgModContainer_SymbolCount,
+ /*.pfnSymbolByOrdinal = */ rtDbgModContainer_SymbolByOrdinal,
+ /*.pfnSymbolByName = */ rtDbgModContainer_SymbolByName,
+ /*.pfnSymbolByAddr = */ rtDbgModContainer_SymbolByAddr,
+
+ /*.pfnLineAdd = */ rtDbgModContainer_LineAdd,
+ /*.pfnLineCount = */ rtDbgModContainer_LineCount,
+ /*.pfnLineByOrdinal = */ rtDbgModContainer_LineByOrdinal,
+ /*.pfnLineByAddr = */ rtDbgModContainer_LineByAddr,
+
+ /*.pfnUnwindFrame = */ rtDbgModContainer_UnwindFrame,
+
+ /*.u32EndMagic = */ RTDBGMODVTDBG_MAGIC
+};
+
+
+
+/**
+ * Special container operation for removing all symbols.
+ *
+ * @returns IPRT status code.
+ * @param pMod The module instance.
+ */
+DECLHIDDEN(int) rtDbgModContainer_SymbolRemoveAll(PRTDBGMODINT pMod)
+{
+ PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv;
+
+ for (uint32_t iSeg = 0; iSeg < pThis->cSegs; iSeg++)
+ {
+ RTAvlrUIntPtrDestroy(&pThis->paSegs[iSeg].SymAddrTree, rtDbgModContainer_DestroyTreeNode, NULL);
+ Assert(pThis->paSegs[iSeg].SymAddrTree == NULL);
+ }
+
+ RTAvlrUIntPtrDestroy(&pThis->AbsAddrTree, rtDbgModContainer_DestroyTreeNode, NULL);
+ Assert(pThis->AbsAddrTree == NULL);
+
+ pThis->Names = NULL;
+ pThis->iNextSymbolOrdinal = 0;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Special container operation for removing all line numbers.
+ *
+ * @returns IPRT status code.
+ * @param pMod The module instance.
+ */
+DECLHIDDEN(int) rtDbgModContainer_LineRemoveAll(PRTDBGMODINT pMod)
+{
+ PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv;
+
+ for (uint32_t iSeg = 0; iSeg < pThis->cSegs; iSeg++)
+ pThis->paSegs[iSeg].LineAddrTree = NULL;
+
+ RTAvlU32Destroy(&pThis->LineOrdinalTree, rtDbgModContainer_DestroyTreeLineNode, pThis);
+ Assert(pThis->LineOrdinalTree == NULL);
+
+ pThis->iNextLineOrdinal = 0;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Special container operation for removing everything.
+ *
+ * @returns IPRT status code.
+ * @param pMod The module instance.
+ */
+DECLHIDDEN(int) rtDbgModContainer_RemoveAll(PRTDBGMODINT pMod)
+{
+ PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv;
+
+ rtDbgModContainer_LineRemoveAll(pMod);
+ rtDbgModContainer_SymbolRemoveAll(pMod);
+
+ for (uint32_t iSeg = 0; iSeg < pThis->cSegs; iSeg++)
+ {
+ RTStrCacheRelease(g_hDbgModStrCache, pThis->paSegs[iSeg].pszName);
+ pThis->paSegs[iSeg].pszName = NULL;
+ }
+
+ pThis->cSegs = 0;
+ pThis->cb = 0;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Creates a generic debug info container and associates it with the module.
+ *
+ * @returns IPRT status code.
+ * @param pMod The module instance.
+ * @param cbSeg The size of the initial segment. 0 if segments are to be
+ * created manually later on.
+ */
+DECLHIDDEN(int) rtDbgModContainerCreate(PRTDBGMODINT pMod, RTUINTPTR cbSeg)
+{
+ PRTDBGMODCTN pThis = (PRTDBGMODCTN)RTMemAlloc(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ pThis->Names = NULL;
+ pThis->AbsAddrTree = NULL;
+ pThis->SymbolOrdinalTree = NULL;
+ pThis->LineOrdinalTree = NULL;
+ pThis->paSegs = NULL;
+ pThis->cSegs = 0;
+ pThis->cb = 0;
+ pThis->iNextSymbolOrdinal = 0;
+ pThis->iNextLineOrdinal = 0;
+
+ pMod->pDbgVt = &g_rtDbgModVtDbgContainer;
+ pMod->pvDbgPriv = pThis;
+
+#ifdef RTDBGMODCNT_WITH_MEM_CACHE
+ int rc = RTMemCacheCreate(&pThis->hLineNumAllocator, sizeof(RTDBGMODCTNLINE), sizeof(void *), UINT32_MAX,
+ NULL /*pfnCtor*/, NULL /*pfnDtor*/, NULL /*pvUser*/, 0 /*fFlags*/);
+#else
+ int rc = VINF_SUCCESS;
+#endif
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Add the initial segment.
+ */
+ if (cbSeg)
+ rc = rtDbgModContainer_SegmentAdd(pMod, 0, cbSeg, "default", sizeof("default") - 1, 0, NULL);
+ if (RT_SUCCESS(rc))
+ return rc;
+
+#ifdef RTDBGMODCNT_WITH_MEM_CACHE
+ RTMemCacheDestroy(pThis->hLineNumAllocator);
+#endif
+ }
+
+ RTMemFree(pThis);
+ pMod->pDbgVt = NULL;
+ pMod->pvDbgPriv = NULL;
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/common/dbg/dbgmoddbghelp.cpp b/src/VBox/Runtime/common/dbg/dbgmoddbghelp.cpp
new file mode 100644
index 00000000..831bce8c
--- /dev/null
+++ b/src/VBox/Runtime/common/dbg/dbgmoddbghelp.cpp
@@ -0,0 +1,537 @@
+/* $Id: dbgmoddbghelp.cpp $ */
+/** @file
+ * IPRT - Debug Info Reader Using DbgHelp.dll if Present.
+ */
+
+/*
+ * Copyright (C) 2013-2023 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 *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DBG
+#include <iprt/dbg.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/list.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include "internal/dbgmod.h"
+
+#include <iprt/win/windows.h>
+#include <iprt/win/dbghelp.h>
+#include <iprt/win/lazy-dbghelp.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** For passing arguments to DbgHelp.dll callback. */
+typedef struct RTDBGMODBGHELPARGS
+{
+ RTDBGMOD hCnt;
+ PRTDBGMODINT pMod;
+ uint64_t uModAddr;
+ RTLDRADDR uNextRva;
+
+ /** UTF-8 version of the previous file name. */
+ char *pszPrev;
+ /** Copy of the previous file name. */
+ PRTUTF16 pwszPrev;
+ /** Number of bytes pwszPrev points to. */
+ size_t cbPrevUtf16Alloc;
+} RTDBGMODBGHELPARGS;
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnUnwindFrame} */
+static DECLCALLBACK(int) rtDbgModDbgHelp_UnwindFrame(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState)
+{
+ RT_NOREF(pMod, iSeg, off, pState);
+ return VERR_DBG_NO_UNWIND_INFO;
+}
+
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByAddr} */
+static DECLCALLBACK(int) rtDbgModDbgHelp_LineByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off,
+ PRTINTPTR poffDisp, PRTDBGLINE pLineInfo)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModLineByAddr(hCnt, iSeg, off, poffDisp, pLineInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByOrdinal} */
+static DECLCALLBACK(int) rtDbgModDbgHelp_LineByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModLineByOrdinal(hCnt, iOrdinal, pLineInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineCount} */
+static DECLCALLBACK(uint32_t) rtDbgModDbgHelp_LineCount(PRTDBGMODINT pMod)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModLineCount(hCnt);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineAdd} */
+static DECLCALLBACK(int) rtDbgModDbgHelp_LineAdd(PRTDBGMODINT pMod, const char *pszFile, size_t cchFile, uint32_t uLineNo,
+ uint32_t iSeg, RTUINTPTR off, uint32_t *piOrdinal)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ Assert(!pszFile[cchFile]); NOREF(cchFile);
+ return RTDbgModLineAdd(hCnt, pszFile, uLineNo, iSeg, off, piOrdinal);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByAddr} */
+static DECLCALLBACK(int) rtDbgModDbgHelp_SymbolByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags,
+ PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModSymbolByAddr(hCnt, iSeg, off, fFlags, poffDisp, pSymInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByName} */
+static DECLCALLBACK(int) rtDbgModDbgHelp_SymbolByName(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol,
+ PRTDBGSYMBOL pSymInfo)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ Assert(!pszSymbol[cchSymbol]); RT_NOREF_PV(cchSymbol);
+ return RTDbgModSymbolByName(hCnt, pszSymbol/*, cchSymbol*/, pSymInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByOrdinal} */
+static DECLCALLBACK(int) rtDbgModDbgHelp_SymbolByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModSymbolByOrdinal(hCnt, iOrdinal, pSymInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolCount} */
+static DECLCALLBACK(uint32_t) rtDbgModDbgHelp_SymbolCount(PRTDBGMODINT pMod)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModSymbolCount(hCnt);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolAdd} */
+static DECLCALLBACK(int) rtDbgModDbgHelp_SymbolAdd(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol,
+ RTDBGSEGIDX iSeg, RTUINTPTR off, RTUINTPTR cb, uint32_t fFlags,
+ uint32_t *piOrdinal)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ Assert(!pszSymbol[cchSymbol]); NOREF(cchSymbol);
+ return RTDbgModSymbolAdd(hCnt, pszSymbol, iSeg, off, cb, fFlags, piOrdinal);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentByIndex} */
+static DECLCALLBACK(int) rtDbgModDbgHelp_SegmentByIndex(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModSegmentByIndex(hCnt, iSeg, pSegInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentCount} */
+static DECLCALLBACK(RTDBGSEGIDX) rtDbgModDbgHelp_SegmentCount(PRTDBGMODINT pMod)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModSegmentCount(hCnt);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentAdd} */
+static DECLCALLBACK(int) rtDbgModDbgHelp_SegmentAdd(PRTDBGMODINT pMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName, size_t cchName,
+ uint32_t fFlags, PRTDBGSEGIDX piSeg)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ Assert(!pszName[cchName]); NOREF(cchName);
+ return RTDbgModSegmentAdd(hCnt, uRva, cb, pszName, fFlags, piSeg);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnImageSize} */
+static DECLCALLBACK(RTUINTPTR) rtDbgModDbgHelp_ImageSize(PRTDBGMODINT pMod)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ RTUINTPTR cb1 = RTDbgModImageSize(hCnt);
+ RTUINTPTR cb2 = pMod->pImgVt->pfnImageSize(pMod);
+ return RT_MAX(cb1, cb2);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnRvaToSegOff} */
+static DECLCALLBACK(RTDBGSEGIDX) rtDbgModDbgHelp_RvaToSegOff(PRTDBGMODINT pMod, RTUINTPTR uRva, PRTUINTPTR poffSeg)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModRvaToSegOff(hCnt, uRva, poffSeg);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnClose} */
+static DECLCALLBACK(int) rtDbgModDbgHelp_Close(PRTDBGMODINT pMod)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;;
+ RTDbgModRelease(hCnt);
+ pMod->pvDbgPriv = NULL;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * SymEnumLinesW callback that adds a line number to the container.
+ *
+ * @returns TRUE, FALSE if we're out of memory.
+ * @param pLineInfo Line number information.
+ * @param pvUser Pointer to a RTDBGMODBGHELPARGS structure.
+ */
+static BOOL CALLBACK rtDbgModDbgHelpCopyLineNumberCallback(PSRCCODEINFOW pLineInfo, PVOID pvUser)
+{
+ RTDBGMODBGHELPARGS *pArgs = (RTDBGMODBGHELPARGS *)pvUser;
+
+ if (pLineInfo->Address < pArgs->uModAddr)
+ {
+ Log((" %#018RX64 %05u %s [SKIPPED - INVALID ADDRESS!]\n", pLineInfo->Address, pLineInfo->LineNumber));
+ return TRUE;
+ }
+
+ /*
+ * To save having to call RTUtf16ToUtf8 every time, we keep a copy of the
+ * previous file name both as UTF-8 and UTF-16.
+ */
+ /** @todo we could combine RTUtf16Len and memcmp... */
+ size_t cbLen = (RTUtf16Len(pLineInfo->FileName) + 1) * sizeof(RTUTF16);
+ if ( !pArgs->pwszPrev
+ || pArgs->cbPrevUtf16Alloc < cbLen
+ || memcmp(pArgs->pwszPrev, pLineInfo->FileName, cbLen) )
+ {
+ if (pArgs->cbPrevUtf16Alloc >= cbLen)
+ memcpy(pArgs->pwszPrev, pLineInfo->FileName, cbLen);
+ else
+ {
+ RTMemFree(pArgs->pwszPrev);
+ pArgs->cbPrevUtf16Alloc = cbLen;
+ pArgs->pwszPrev = (PRTUTF16)RTMemDupEx(pLineInfo->FileName, cbLen, pArgs->cbPrevUtf16Alloc - cbLen);
+ if (!pArgs->pwszPrev)
+ pArgs->cbPrevUtf16Alloc = 0;
+ }
+
+ RTStrFree(pArgs->pszPrev);
+ pArgs->pszPrev = NULL;
+ int rc = RTUtf16ToUtf8(pLineInfo->FileName, &pArgs->pszPrev);
+ if (RT_FAILURE(rc))
+ {
+ SetLastError(ERROR_OUTOFMEMORY);
+ Log(("rtDbgModDbgHelpCopyLineNumberCallback: Out of memory\n"));
+ return FALSE;
+ }
+ }
+
+ /*
+ * Add the line number to the container.
+ */
+ int rc = RTDbgModLineAdd(pArgs->hCnt, pArgs->pszPrev, pLineInfo->LineNumber,
+ RTDBGSEGIDX_RVA, pLineInfo->Address - pArgs->uModAddr, NULL);
+ Log((" %#018RX64 %05u %s [%Rrc]\n", pLineInfo->Address, pLineInfo->LineNumber, pArgs->pszPrev, rc));
+ NOREF(rc);
+
+ return TRUE;
+}
+
+
+/**
+ * Copies the line numbers into the container.
+ *
+ * @returns IPRT status code.
+ * @param pMod The debug module.
+ * @param hCnt The container that will keep the symbols.
+ * @param hFake The fake process handle.
+ * @param uModAddr The module load address.
+ */
+static int rtDbgModDbgHelpCopyLineNumbers(PRTDBGMODINT pMod, RTDBGMOD hCnt, HANDLE hFake, uint64_t uModAddr)
+{
+ RTDBGMODBGHELPARGS Args;
+ Args.hCnt = hCnt;
+ Args.pMod = pMod;
+ Args.uModAddr = uModAddr;
+ Args.pszPrev = NULL;
+ Args.pwszPrev = NULL;
+ Args.cbPrevUtf16Alloc = 0;
+
+ int rc;
+ if (SymEnumLinesW(hFake, uModAddr, NULL /*pszObj*/, NULL /*pszFile*/, rtDbgModDbgHelpCopyLineNumberCallback, &Args))
+ rc = VINF_SUCCESS;
+ else
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ Log(("Line number enum: %Rrc (%u)\n", rc, GetLastError()));
+ if (rc == VERR_NOT_SUPPORTED)
+ rc = VINF_SUCCESS;
+ }
+
+ RTStrFree(Args.pszPrev);
+ RTMemFree(Args.pwszPrev);
+ return rc;
+}
+
+
+/**
+ * SymEnumSymbols callback that adds a symbol to the container.
+ *
+ * @returns TRUE
+ * @param pSymInfo The symbol information.
+ * @param cbSymbol The symbol size (estimated).
+ * @param pvUser Pointer to a RTDBGMODBGHELPARGS structure.
+ */
+static BOOL CALLBACK rtDbgModDbgHelpCopySymbolsCallback(PSYMBOL_INFO pSymInfo, ULONG cbSymbol, PVOID pvUser)
+{
+ RTDBGMODBGHELPARGS *pArgs = (RTDBGMODBGHELPARGS *)pvUser;
+ if (pSymInfo->Address < pArgs->uModAddr) /* NT4 SP1 ntfs.dbg */
+ {
+ Log((" %#018RX64 LB %#07x %s [SKIPPED - INVALID ADDRESS!]\n", pSymInfo->Address, cbSymbol, pSymInfo->Name));
+ return TRUE;
+ }
+ if (pSymInfo->NameLen >= RTDBG_SYMBOL_NAME_LENGTH)
+ {
+ Log((" %#018RX64 LB %#07x %s [SKIPPED - TOO LONG (%u > %u)!]\n", pSymInfo->Address, cbSymbol, pSymInfo->Name,
+ pSymInfo->NameLen, RTDBG_SYMBOL_NAME_LENGTH));
+ return TRUE;
+ }
+
+ /* ASSUMES the symbol name is ASCII. */
+ int rc = RTDbgModSymbolAdd(pArgs->hCnt, pSymInfo->Name, RTDBGSEGIDX_RVA,
+ pSymInfo->Address - pArgs->uModAddr, cbSymbol, 0, NULL);
+ Log((" %#018RX64 LB %#07x %s [%Rrc]\n", pSymInfo->Address, cbSymbol, pSymInfo->Name, rc));
+ NOREF(rc);
+
+ return TRUE;
+}
+
+
+/**
+ * Copies the symbols into the container.
+ *
+ * @returns IPRT status code.
+ * @param pMod The debug module.
+ * @param hCnt The container that will keep the symbols.
+ * @param hFake The fake process handle.
+ * @param uModAddr The module load address.
+ */
+static int rtDbgModDbgHelpCopySymbols(PRTDBGMODINT pMod, RTDBGMOD hCnt, HANDLE hFake, uint64_t uModAddr)
+{
+ RTDBGMODBGHELPARGS Args;
+ Args.hCnt = hCnt;
+ Args.pMod = pMod;
+ Args.uModAddr = uModAddr;
+ int rc;
+ if (SymEnumSymbols(hFake, uModAddr, NULL, rtDbgModDbgHelpCopySymbolsCallback, &Args))
+ rc = VINF_SUCCESS;
+ else
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ Log(("SymEnumSymbols: %Rrc (%u)\n", rc, GetLastError()));
+ }
+ return rc;
+}
+
+
+/** @callback_method_impl{FNRTLDRENUMSEGS,
+ * Copies the PE segments over into the container.} */
+static DECLCALLBACK(int) rtDbgModDbgHelpAddSegmentsCallback(RTLDRMOD hLdrMod, PCRTLDRSEG pSeg, void *pvUser)
+{
+ RTDBGMODBGHELPARGS *pArgs = (RTDBGMODBGHELPARGS *)pvUser;
+ RT_NOREF_PV(hLdrMod);
+
+ Log(("Segment %.*s: LinkAddress=%#llx RVA=%#llx cb=%#llx\n",
+ pSeg->cchName, pSeg->pszName, (uint64_t)pSeg->LinkAddress, (uint64_t)pSeg->RVA, pSeg->cb));
+
+ Assert(pSeg->cchName > 0);
+ Assert(!pSeg->pszName[pSeg->cchName]);
+
+ RTLDRADDR cb = RT_MAX(pSeg->cb, pSeg->cbMapped);
+ RTLDRADDR uRva = pSeg->RVA;
+ if (!uRva)
+ pArgs->uModAddr = pSeg->LinkAddress;
+ else if (uRva == NIL_RTLDRADDR)
+ {
+ cb = 0;
+ uRva = pArgs->uNextRva;
+ }
+ pArgs->uNextRva = uRva + cb;
+
+ return RTDbgModSegmentAdd(pArgs->hCnt, uRva, cb, pSeg->pszName, 0 /*fFlags*/, NULL);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnTryOpen} */
+static DECLCALLBACK(int) rtDbgModDbgHelp_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch)
+{
+ NOREF(enmArch);
+
+ /*
+ * Currently only support external files with a executable already present.
+ */
+ if (!pMod->pszDbgFile)
+ return VERR_DBG_NO_MATCHING_INTERPRETER;
+ if (!pMod->pImgVt)
+ return VERR_DBG_NO_MATCHING_INTERPRETER;
+
+ /*
+ * Create a container for copying the information into. We do this early
+ * so we can determine the image base address.
+ */
+ RTDBGMOD hCnt;
+ int rc = RTDbgModCreate(&hCnt, pMod->pszName, 0 /*cbSeg*/, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ RTDBGMODBGHELPARGS Args;
+ RT_ZERO(Args);
+ Args.hCnt = hCnt;
+ rc = pMod->pImgVt->pfnEnumSegments(pMod, rtDbgModDbgHelpAddSegmentsCallback, &Args);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t cbImage = pMod->pImgVt->pfnImageSize(pMod);
+ uint64_t uImageBase = Args.uModAddr ? Args.uModAddr : 0x4000000;
+
+ /*
+ * Try load the module into an empty address space.
+ */
+ static uint32_t volatile s_uFakeHandle = 0x3940000;
+ HANDLE hFake;
+ do
+ hFake = (HANDLE)(uintptr_t)ASMAtomicIncU32(&s_uFakeHandle);
+ while (hFake == NULL || hFake == INVALID_HANDLE_VALUE);
+
+ LogFlow(("rtDbgModDbgHelp_TryOpen: \n"));
+ if (SymInitialize(hFake, NULL /*SearchPath*/, FALSE /*fInvalidProcess*/))
+ {
+ SymSetOptions(SYMOPT_LOAD_LINES | SymGetOptions());
+
+ PRTUTF16 pwszDbgFile;
+ rc = RTStrToUtf16(pMod->pszDbgFile, &pwszDbgFile);
+ if (RT_SUCCESS(rc))
+ {
+ uint64_t uModAddr = SymLoadModuleExW(hFake, NULL /*hFile*/, pwszDbgFile, NULL /*pszModName*/,
+ uImageBase, cbImage, NULL /*pModData*/, 0 /*fFlags*/);
+ if (uModAddr != 0)
+ {
+ rc = rtDbgModDbgHelpCopySymbols(pMod, hCnt, hFake, uModAddr);
+ if (RT_SUCCESS(rc))
+ rc = rtDbgModDbgHelpCopyLineNumbers(pMod, hCnt, hFake, uModAddr);
+ if (RT_SUCCESS(rc))
+ {
+ pMod->pvDbgPriv = hCnt;
+ pMod->pDbgVt = &g_rtDbgModVtDbgDbgHelp;
+ hCnt = NIL_RTDBGMOD;
+ LogFlow(("rtDbgModDbgHelp_TryOpen: Successfully loaded '%s' at %#llx\n",
+ pMod->pszDbgFile, (uint64_t)uImageBase));
+ }
+
+ SymUnloadModule64(hFake, uModAddr);
+ }
+ else
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ if (RT_SUCCESS_NP(rc))
+ rc = VERR_DBG_NO_MATCHING_INTERPRETER;
+ LogFlow(("rtDbgModDbgHelp_TryOpen: Error loading the module '%s' at %#llx: %Rrc (%u)\n",
+ pMod->pszDbgFile, (uint64_t)uImageBase, rc, GetLastError()));
+ }
+ RTUtf16Free(pwszDbgFile);
+ }
+ else
+ LogFlow(("rtDbgModDbgHelp_TryOpen: Unicode version issue: %Rrc\n", rc));
+
+ BOOL fRc2 = SymCleanup(hFake); Assert(fRc2); NOREF(fRc2);
+ }
+ else
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ if (RT_SUCCESS_NP(rc))
+ rc = VERR_DBG_NO_MATCHING_INTERPRETER;
+ LogFlow(("rtDbgModDbgHelp_TryOpen: SymInitialize failed: %Rrc (%u)\n", rc, GetLastError()));
+ }
+ }
+ RTDbgModRelease(hCnt);
+ }
+ return rc;
+}
+
+
+
+/** Virtual function table for the DBGHELP debug info reader. */
+DECL_HIDDEN_CONST(RTDBGMODVTDBG) const g_rtDbgModVtDbgDbgHelp =
+{
+ /*.u32Magic = */ RTDBGMODVTDBG_MAGIC,
+ /*.fSupports = */ RT_DBGTYPE_CODEVIEW,
+ /*.pszName = */ "dbghelp",
+ /*.pfnTryOpen = */ rtDbgModDbgHelp_TryOpen,
+ /*.pfnClose = */ rtDbgModDbgHelp_Close,
+
+ /*.pfnRvaToSegOff = */ rtDbgModDbgHelp_RvaToSegOff,
+ /*.pfnImageSize = */ rtDbgModDbgHelp_ImageSize,
+
+ /*.pfnSegmentAdd = */ rtDbgModDbgHelp_SegmentAdd,
+ /*.pfnSegmentCount = */ rtDbgModDbgHelp_SegmentCount,
+ /*.pfnSegmentByIndex = */ rtDbgModDbgHelp_SegmentByIndex,
+
+ /*.pfnSymbolAdd = */ rtDbgModDbgHelp_SymbolAdd,
+ /*.pfnSymbolCount = */ rtDbgModDbgHelp_SymbolCount,
+ /*.pfnSymbolByOrdinal = */ rtDbgModDbgHelp_SymbolByOrdinal,
+ /*.pfnSymbolByName = */ rtDbgModDbgHelp_SymbolByName,
+ /*.pfnSymbolByAddr = */ rtDbgModDbgHelp_SymbolByAddr,
+
+ /*.pfnLineAdd = */ rtDbgModDbgHelp_LineAdd,
+ /*.pfnLineCount = */ rtDbgModDbgHelp_LineCount,
+ /*.pfnLineByOrdinal = */ rtDbgModDbgHelp_LineByOrdinal,
+ /*.pfnLineByAddr = */ rtDbgModDbgHelp_LineByAddr,
+
+ /*.pfnUnwindFrame = */ rtDbgModDbgHelp_UnwindFrame,
+
+ /*.u32EndMagic = */ RTDBGMODVTDBG_MAGIC
+};
+
diff --git a/src/VBox/Runtime/common/dbg/dbgmoddeferred.cpp b/src/VBox/Runtime/common/dbg/dbgmoddeferred.cpp
new file mode 100644
index 00000000..5dbdc5e2
--- /dev/null
+++ b/src/VBox/Runtime/common/dbg/dbgmoddeferred.cpp
@@ -0,0 +1,730 @@
+/* $Id: dbgmoddeferred.cpp $ */
+/** @file
+ * IPRT - Debug Module Deferred Loading Stub.
+ */
+
+/*
+ * Copyright (C) 2013-2023 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 <iprt/dbg.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include "internal/dbgmod.h"
+#include "internal/magics.h"
+
+
+
+/**
+ * Releases the instance data.
+ *
+ * @param pThis The instance data.
+ */
+static void rtDbgModDeferredReleaseInstanceData(PRTDBGMODDEFERRED pThis)
+{
+ AssertPtr(pThis);
+ Assert(pThis->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs); Assert(cRefs < 8);
+ if (!cRefs)
+ {
+ RTDbgCfgRelease(pThis->hDbgCfg);
+ pThis->hDbgCfg = NIL_RTDBGCFG;
+ pThis->u32Magic = RTDBGMODDEFERRED_MAGIC_DEAD;
+ RTMemFree(pThis);
+ }
+}
+
+
+/**
+ * Does the deferred loading of the real data (image and/or debug info).
+ *
+ * @returns VINF_SUCCESS or VERR_DBG_DEFERRED_LOAD_FAILED.
+ * @param pMod The generic module instance data.
+ * @param fForcedRetry Whether it's a forced retry by one of the
+ * pfnTryOpen methods.
+ */
+static int rtDbgModDeferredDoIt(PRTDBGMODINT pMod, bool fForcedRetry)
+{
+ RTCritSectEnter(&pMod->CritSect);
+
+ int rc;
+ if (!pMod->fDeferredFailed || fForcedRetry)
+ {
+ bool const fDbgVt = pMod->pDbgVt == &g_rtDbgModVtDbgDeferred;
+ bool const fImgVt = pMod->pImgVt == &g_rtDbgModVtImgDeferred;
+ AssertReturnStmt(fDbgVt || fImgVt, RTCritSectLeave(&pMod->CritSect), VERR_INTERNAL_ERROR_5);
+
+ PRTDBGMODDEFERRED pThis = (PRTDBGMODDEFERRED)(fDbgVt ? pMod->pvDbgPriv : pMod->pvImgPriv);
+
+ /* Reset the method tables and private data pointes so the deferred loading
+ procedure can figure out what to do and won't get confused. */
+ if (fDbgVt)
+ {
+ pMod->pvDbgPriv = NULL;
+ pMod->pDbgVt = NULL;
+ }
+
+ if (fImgVt)
+ {
+ pMod->pvImgPriv = NULL;
+ pMod->pImgVt = NULL;
+ }
+
+ /* Do the deferred loading. */
+ rc = pThis->pfnDeferred(pMod, pThis);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(!fDbgVt || pMod->pDbgVt != NULL);
+ Assert(!fImgVt || pMod->pImgVt != NULL);
+
+ pMod->fDeferred = false;
+ pMod->fDeferredFailed = false;
+
+ rtDbgModDeferredReleaseInstanceData(pThis);
+ if (fImgVt && fDbgVt)
+ rtDbgModDeferredReleaseInstanceData(pThis);
+ }
+ else
+ {
+ /* Failed, bail out and restore the deferred setup. */
+ pMod->fDeferredFailed = true;
+
+ if (fDbgVt)
+ {
+ Assert(!pMod->pDbgVt);
+ pMod->pDbgVt = &g_rtDbgModVtDbgDeferred;
+ pMod->pvDbgPriv = pThis;
+ }
+
+ if (fImgVt)
+ {
+ Assert(!pMod->pImgVt);
+ pMod->pImgVt = &g_rtDbgModVtImgDeferred;
+ pMod->pvImgPriv = pThis;
+ }
+ }
+ }
+ else
+ rc = VERR_DBG_DEFERRED_LOAD_FAILED;
+
+ RTCritSectLeave(&pMod->CritSect);
+ return rc;
+}
+
+
+
+
+/*
+ *
+ * D e b u g I n f o M e t h o d s
+ * D e b u g I n f o M e t h o d s
+ * D e b u g I n f o M e t h o d s
+ *
+ */
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnUnwindFrame} */
+static DECLCALLBACK(int)
+rtDbgModDeferredDbg_UnwindFrame(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState)
+{
+ Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/);
+ if (RT_SUCCESS(rc))
+ rc = pMod->pDbgVt->pfnUnwindFrame(pMod, iSeg, off, pState);
+ return rc;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByAddr} */
+static DECLCALLBACK(int) rtDbgModDeferredDbg_LineByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off,
+ PRTINTPTR poffDisp, PRTDBGLINE pLineInfo)
+{
+ Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/);
+ if (RT_SUCCESS(rc))
+ rc = pMod->pDbgVt->pfnLineByAddr(pMod, iSeg, off, poffDisp, pLineInfo);
+ return rc;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByOrdinal} */
+static DECLCALLBACK(int) rtDbgModDeferredDbg_LineByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo)
+{
+ Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/);
+ if (RT_SUCCESS(rc))
+ rc = pMod->pDbgVt->pfnLineByOrdinal(pMod, iOrdinal, pLineInfo);
+ return rc;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineCount} */
+static DECLCALLBACK(uint32_t) rtDbgModDeferredDbg_LineCount(PRTDBGMODINT pMod)
+{
+ Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/);
+ if (RT_SUCCESS(rc))
+ return pMod->pDbgVt->pfnLineCount(pMod);
+ return 0;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineAdd} */
+static DECLCALLBACK(int) rtDbgModDeferredDbg_LineAdd(PRTDBGMODINT pMod, const char *pszFile, size_t cchFile, uint32_t uLineNo,
+ uint32_t iSeg, RTUINTPTR off, uint32_t *piOrdinal)
+{
+ Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/);
+ if (RT_SUCCESS(rc))
+ rc = pMod->pDbgVt->pfnLineAdd(pMod, pszFile, cchFile, uLineNo, iSeg, off, piOrdinal);
+ return rc;
+}
+
+
+/**
+ * Fill in symbol info for the fake start symbol.
+ *
+ * @returns VINF_SUCCESS
+ * @param pThis The deferred load data.
+ * @param pSymInfo The output structure.
+ */
+static int rtDbgModDeferredDbgSymInfo_Start(PRTDBGMODDEFERRED pThis, PRTDBGSYMBOL pSymInfo)
+{
+ pSymInfo->Value = 0;
+ pSymInfo->cb = pThis->cbImage;
+ pSymInfo->offSeg = 0;
+ pSymInfo->iSeg = 0;
+ pSymInfo->fFlags = 0;
+ pSymInfo->iOrdinal = 0;
+ strcpy(pSymInfo->szName, "DeferredStart");
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Fill in symbol info for the fake last symbol.
+ *
+ * @returns VINF_SUCCESS
+ * @param pThis The deferred load data.
+ * @param pSymInfo The output structure.
+ */
+static int rtDbgModDeferredDbgSymInfo_Last(PRTDBGMODDEFERRED pThis, PRTDBGSYMBOL pSymInfo)
+{
+ pSymInfo->Value = pThis->cbImage - 1;
+ pSymInfo->cb = 0;
+ pSymInfo->offSeg = pThis->cbImage - 1;
+ pSymInfo->iSeg = 0;
+ pSymInfo->fFlags = 0;
+ pSymInfo->iOrdinal = 1;
+ strcpy(pSymInfo->szName, "DeferredLast");
+ return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByAddr} */
+static DECLCALLBACK(int) rtDbgModDeferredDbg_SymbolByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags,
+ PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo)
+{
+ Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ if ( (fFlags & RTDBGSYMADDR_FLAGS_SKIP_ABS_IN_DEFERRED)
+ && iSeg == RTDBGSEGIDX_ABS)
+ return VERR_SYMBOL_NOT_FOUND;
+
+ int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/);
+ if (RT_SUCCESS(rc))
+ rc = pMod->pDbgVt->pfnSymbolByAddr(pMod, iSeg, off, fFlags, poffDisp, pSymInfo);
+ else
+ {
+ PRTDBGMODDEFERRED pThis = (PRTDBGMODDEFERRED)pMod->pvDbgPriv;
+ if (off == 0)
+ rc = rtDbgModDeferredDbgSymInfo_Start(pThis, pSymInfo);
+ else if (off >= pThis->cbImage - 1 || (fFlags & RTDBGSYMADDR_FLAGS_GREATER_OR_EQUAL))
+ rc = rtDbgModDeferredDbgSymInfo_Last(pThis, pSymInfo);
+ else
+ rc = rtDbgModDeferredDbgSymInfo_Start(pThis, pSymInfo);
+ if (poffDisp)
+ *poffDisp = off - pSymInfo->offSeg;
+ }
+ return rc;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByName} */
+static DECLCALLBACK(int) rtDbgModDeferredDbg_SymbolByName(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol,
+ PRTDBGSYMBOL pSymInfo)
+{
+ Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/);
+ if (RT_SUCCESS(rc))
+ rc = pMod->pDbgVt->pfnSymbolByName(pMod, pszSymbol, cchSymbol, pSymInfo);
+ else
+ {
+ PRTDBGMODDEFERRED pThis = (PRTDBGMODDEFERRED)pMod->pvDbgPriv;
+ if ( cchSymbol == sizeof("DeferredStart") - 1
+ && !memcmp(pszSymbol, RT_STR_TUPLE("DeferredStart")))
+ rc = rtDbgModDeferredDbgSymInfo_Start(pThis, pSymInfo);
+ else if ( cchSymbol == sizeof("DeferredLast") - 1
+ && !memcmp(pszSymbol, RT_STR_TUPLE("DeferredLast")))
+ rc = rtDbgModDeferredDbgSymInfo_Last(pThis, pSymInfo);
+ else
+ rc = VERR_SYMBOL_NOT_FOUND;
+ }
+ return rc;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByOrdinal} */
+static DECLCALLBACK(int) rtDbgModDeferredDbg_SymbolByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo)
+{
+ Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/);
+ if (RT_SUCCESS(rc))
+ rc = pMod->pDbgVt->pfnSymbolByOrdinal(pMod, iOrdinal, pSymInfo);
+ else
+ {
+ PRTDBGMODDEFERRED pThis = (PRTDBGMODDEFERRED)pMod->pvDbgPriv;
+ if (iOrdinal == 0)
+ rc = rtDbgModDeferredDbgSymInfo_Start(pThis, pSymInfo);
+ else if (iOrdinal == 1)
+ rc = rtDbgModDeferredDbgSymInfo_Last(pThis, pSymInfo);
+ else
+ rc = VERR_SYMBOL_NOT_FOUND;
+ }
+ return rc;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolCount} */
+static DECLCALLBACK(uint32_t) rtDbgModDeferredDbg_SymbolCount(PRTDBGMODINT pMod)
+{
+ Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/);
+ if (RT_SUCCESS(rc))
+ return pMod->pDbgVt->pfnSymbolCount(pMod);
+ return 2;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolAdd} */
+static DECLCALLBACK(int) rtDbgModDeferredDbg_SymbolAdd(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol,
+ RTDBGSEGIDX iSeg, RTUINTPTR off, RTUINTPTR cb, uint32_t fFlags,
+ uint32_t *piOrdinal)
+{
+ Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/);
+ if (RT_SUCCESS(rc))
+ rc = pMod->pDbgVt->pfnSymbolAdd(pMod, pszSymbol, cchSymbol, iSeg, off, cb, fFlags, piOrdinal);
+ return rc;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentByIndex} */
+static DECLCALLBACK(int) rtDbgModDeferredDbg_SegmentByIndex(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo)
+{
+ Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/);
+ if (RT_SUCCESS(rc))
+ rc = pMod->pDbgVt->pfnSegmentByIndex(pMod, iSeg, pSegInfo);
+ else if (iSeg == 0)
+ {
+ PRTDBGMODDEFERRED pThis = (PRTDBGMODDEFERRED)pMod->pvDbgPriv;
+ pSegInfo->Address = 0;
+ pSegInfo->uRva = 0;
+ pSegInfo->cb = pThis->cbImage;
+ pSegInfo->fFlags = 0;
+ pSegInfo->iSeg = 0;
+ memcpy(pSegInfo->szName, RT_STR_TUPLE("LATER"));
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_DBG_INVALID_SEGMENT_INDEX;
+ return rc;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentCount} */
+static DECLCALLBACK(RTDBGSEGIDX) rtDbgModDeferredDbg_SegmentCount(PRTDBGMODINT pMod)
+{
+ Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/);
+ if (RT_SUCCESS(rc))
+ return pMod->pDbgVt->pfnSegmentCount(pMod);
+ return 1;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentAdd} */
+static DECLCALLBACK(int) rtDbgModDeferredDbg_SegmentAdd(PRTDBGMODINT pMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName,
+ size_t cchName, uint32_t fFlags, PRTDBGSEGIDX piSeg)
+{
+ Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/);
+ if (RT_SUCCESS(rc))
+ rc = pMod->pDbgVt->pfnSegmentAdd(pMod, uRva, cb, pszName, cchName, fFlags, piSeg);
+ return rc;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnImageSize} */
+static DECLCALLBACK(RTUINTPTR) rtDbgModDeferredDbg_ImageSize(PRTDBGMODINT pMod)
+{
+ PRTDBGMODDEFERRED pThis = (PRTDBGMODDEFERRED)pMod->pvDbgPriv;
+ Assert(pThis->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ return pThis->cbImage;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnRvaToSegOff} */
+static DECLCALLBACK(RTDBGSEGIDX) rtDbgModDeferredDbg_RvaToSegOff(PRTDBGMODINT pMod, RTUINTPTR uRva, PRTUINTPTR poffSeg)
+{
+ Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/);
+ if (RT_SUCCESS(rc))
+ return pMod->pDbgVt->pfnRvaToSegOff(pMod, uRva, poffSeg);
+ return 0;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnClose} */
+static DECLCALLBACK(int) rtDbgModDeferredDbg_Close(PRTDBGMODINT pMod)
+{
+ rtDbgModDeferredReleaseInstanceData((PRTDBGMODDEFERRED)pMod->pvDbgPriv);
+ return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnTryOpen} */
+static DECLCALLBACK(int) rtDbgModDeferredDbg_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch)
+{
+ NOREF(enmArch);
+ return rtDbgModDeferredDoIt(pMod, true /*fForceRetry*/);
+}
+
+
+
+/** Virtual function table for the deferred debug info reader. */
+DECL_HIDDEN_CONST(RTDBGMODVTDBG) const g_rtDbgModVtDbgDeferred =
+{
+ /*.u32Magic = */ RTDBGMODVTDBG_MAGIC,
+ /*.fSupports = */ RT_DBGTYPE_MAP,
+ /*.pszName = */ "deferred",
+ /*.pfnTryOpen = */ rtDbgModDeferredDbg_TryOpen,
+ /*.pfnClose = */ rtDbgModDeferredDbg_Close,
+
+ /*.pfnRvaToSegOff = */ rtDbgModDeferredDbg_RvaToSegOff,
+ /*.pfnImageSize = */ rtDbgModDeferredDbg_ImageSize,
+
+ /*.pfnSegmentAdd = */ rtDbgModDeferredDbg_SegmentAdd,
+ /*.pfnSegmentCount = */ rtDbgModDeferredDbg_SegmentCount,
+ /*.pfnSegmentByIndex = */ rtDbgModDeferredDbg_SegmentByIndex,
+
+ /*.pfnSymbolAdd = */ rtDbgModDeferredDbg_SymbolAdd,
+ /*.pfnSymbolCount = */ rtDbgModDeferredDbg_SymbolCount,
+ /*.pfnSymbolByOrdinal = */ rtDbgModDeferredDbg_SymbolByOrdinal,
+ /*.pfnSymbolByName = */ rtDbgModDeferredDbg_SymbolByName,
+ /*.pfnSymbolByAddr = */ rtDbgModDeferredDbg_SymbolByAddr,
+
+ /*.pfnLineAdd = */ rtDbgModDeferredDbg_LineAdd,
+ /*.pfnLineCount = */ rtDbgModDeferredDbg_LineCount,
+ /*.pfnLineByOrdinal = */ rtDbgModDeferredDbg_LineByOrdinal,
+ /*.pfnLineByAddr = */ rtDbgModDeferredDbg_LineByAddr,
+
+ /*.pfnUnwindFrame = */ rtDbgModDeferredDbg_UnwindFrame,
+
+ /*.u32EndMagic = */ RTDBGMODVTDBG_MAGIC
+};
+
+
+
+
+/*
+ *
+ * I m a g e M e t h o d s
+ * I m a g e M e t h o d s
+ * I m a g e M e t h o d s
+ *
+ */
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnUnwindFrame} */
+static DECLCALLBACK(int)
+rtDbgModDeferredImg_UnwindFrame(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState)
+{
+ Assert(((PRTDBGMODDEFERRED)pMod->pvImgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/);
+ if (RT_SUCCESS(rc))
+ rc = pMod->pImgVt->pfnUnwindFrame(pMod, iSeg, off, pState);
+ return rc;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnQueryProp} */
+static DECLCALLBACK(int)
+rtDbgModDeferredImg_QueryProp(PRTDBGMODINT pMod, RTLDRPROP enmProp, void *pvBuf, size_t cbBuf, size_t *pcbRet)
+{
+ Assert(((PRTDBGMODDEFERRED)pMod->pvImgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/);
+ if (RT_SUCCESS(rc))
+ rc = pMod->pImgVt->pfnQueryProp(pMod, enmProp, pvBuf, cbBuf, pcbRet);
+ return rc;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnGetArch} */
+static DECLCALLBACK(RTLDRARCH) rtDbgModDeferredImg_GetArch(PRTDBGMODINT pMod)
+{
+ Assert(((PRTDBGMODDEFERRED)pMod->pvImgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC);
+
+ RTLDRARCH enmArch;
+ int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/);
+ if (RT_SUCCESS(rc))
+ enmArch = pMod->pImgVt->pfnGetArch(pMod);
+ else
+ enmArch = RTLDRARCH_WHATEVER;
+ return enmArch;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnGetFormat} */
+static DECLCALLBACK(RTLDRFMT) rtDbgModDeferredImg_GetFormat(PRTDBGMODINT pMod)
+{
+ Assert(((PRTDBGMODDEFERRED)pMod->pvImgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC);
+
+ RTLDRFMT enmFmt;
+ int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/);
+ if (RT_SUCCESS(rc))
+ enmFmt = pMod->pImgVt->pfnGetFormat(pMod);
+ else
+ enmFmt = RTLDRFMT_INVALID;
+ return enmFmt;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnReadAt} */
+static DECLCALLBACK(int) rtDbgModDeferredImg_ReadAt(PRTDBGMODINT pMod, uint32_t iDbgInfoHint, RTFOFF off, void *pvBuf, size_t cb)
+{
+ Assert(((PRTDBGMODDEFERRED)pMod->pvImgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/);
+ if (RT_SUCCESS(rc))
+ rc = pMod->pImgVt->pfnReadAt(pMod, iDbgInfoHint, off, pvBuf, cb);
+ return rc;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnUnmapPart} */
+static DECLCALLBACK(int) rtDbgModDeferredImg_UnmapPart(PRTDBGMODINT pMod, size_t cb, void const **ppvMap)
+{
+ Assert(((PRTDBGMODDEFERRED)pMod->pvImgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/);
+ if (RT_SUCCESS(rc))
+ rc = pMod->pImgVt->pfnUnmapPart(pMod, cb, ppvMap);
+ return rc;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnMapPart} */
+static DECLCALLBACK(int) rtDbgModDeferredImg_MapPart(PRTDBGMODINT pMod, uint32_t iDbgInfo, RTFOFF off, size_t cb, void const **ppvMap)
+{
+ Assert(((PRTDBGMODDEFERRED)pMod->pvImgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/);
+ if (RT_SUCCESS(rc))
+ rc = pMod->pImgVt->pfnMapPart(pMod, iDbgInfo, off, cb, ppvMap);
+ return rc;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnImageSize} */
+static DECLCALLBACK(RTUINTPTR) rtDbgModDeferredImg_ImageSize(PRTDBGMODINT pMod)
+{
+ PRTDBGMODDEFERRED pThis = (PRTDBGMODDEFERRED)pMod->pvImgPriv;
+ Assert(pThis->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ return pThis->cbImage;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnRvaToSegOffset} */
+static DECLCALLBACK(int) rtDbgModDeferredImg_RvaToSegOffset(PRTDBGMODINT pMod, RTLDRADDR Rva,
+ PRTDBGSEGIDX piSeg, PRTLDRADDR poffSeg)
+{
+ Assert(((PRTDBGMODDEFERRED)pMod->pvImgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/);
+ if (RT_SUCCESS(rc))
+ rc = pMod->pImgVt->pfnRvaToSegOffset(pMod, Rva, piSeg, poffSeg);
+ return rc;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnLinkAddressToSegOffset} */
+static DECLCALLBACK(int) rtDbgModDeferredImg_LinkAddressToSegOffset(PRTDBGMODINT pMod, RTLDRADDR LinkAddress,
+ PRTDBGSEGIDX piSeg, PRTLDRADDR poffSeg)
+{
+ Assert(((PRTDBGMODDEFERRED)pMod->pvImgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/);
+ if (RT_SUCCESS(rc))
+ rc = pMod->pImgVt->pfnLinkAddressToSegOffset(pMod, LinkAddress, piSeg, poffSeg);
+ return rc;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnEnumSymbols} */
+static DECLCALLBACK(int) rtDbgModDeferredImg_EnumSymbols(PRTDBGMODINT pMod, uint32_t fFlags, RTLDRADDR BaseAddress,
+ PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
+{
+ Assert(((PRTDBGMODDEFERRED)pMod->pvImgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/);
+ if (RT_SUCCESS(rc))
+ rc = pMod->pImgVt->pfnEnumSymbols(pMod, fFlags, BaseAddress, pfnCallback, pvUser);
+ return rc;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnEnumSegments} */
+static DECLCALLBACK(int) rtDbgModDeferredImg_EnumSegments(PRTDBGMODINT pMod, PFNRTLDRENUMSEGS pfnCallback, void *pvUser)
+{
+ Assert(((PRTDBGMODDEFERRED)pMod->pvImgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/);
+ if (RT_SUCCESS(rc))
+ rc = pMod->pImgVt->pfnEnumSegments(pMod, pfnCallback, pvUser);
+ return rc;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnEnumDbgInfo} */
+static DECLCALLBACK(int) rtDbgModDeferredImg_EnumDbgInfo(PRTDBGMODINT pMod, PFNRTLDRENUMDBG pfnCallback, void *pvUser)
+{
+ Assert(((PRTDBGMODDEFERRED)pMod->pvImgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC);
+ int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/);
+ if (RT_SUCCESS(rc))
+ rc = pMod->pImgVt->pfnEnumDbgInfo(pMod, pfnCallback, pvUser);
+ return rc;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnClose} */
+static DECLCALLBACK(int) rtDbgModDeferredImg_Close(PRTDBGMODINT pMod)
+{
+ rtDbgModDeferredReleaseInstanceData((PRTDBGMODDEFERRED)pMod->pvImgPriv);
+ return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnTryOpen} */
+static DECLCALLBACK(int) rtDbgModDeferredImg_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch, uint32_t fLdrFlags)
+{
+ RT_NOREF(enmArch, fLdrFlags);
+ return rtDbgModDeferredDoIt(pMod, true /*fForceRetry*/);
+}
+
+
+/** Virtual function table for the RTLdr based image reader. */
+DECL_HIDDEN_CONST(RTDBGMODVTIMG) const g_rtDbgModVtImgDeferred =
+{
+ /*.u32Magic = */ RTDBGMODVTIMG_MAGIC,
+ /*.fReserved = */ 0,
+ /*.pszName = */ "deferred",
+ /*.pfnTryOpen = */ rtDbgModDeferredImg_TryOpen,
+ /*.pfnClose = */ rtDbgModDeferredImg_Close,
+ /*.pfnEnumDbgInfo = */ rtDbgModDeferredImg_EnumDbgInfo,
+ /*.pfnEnumSegments = */ rtDbgModDeferredImg_EnumSegments,
+ /*.pfnEnumSymbols = */ rtDbgModDeferredImg_EnumSymbols,
+ /*.pfnGetLoadedSize = */ rtDbgModDeferredImg_ImageSize,
+ /*.pfnLinkAddressToSegOffset = */ rtDbgModDeferredImg_LinkAddressToSegOffset,
+ /*.pfnRvaToSegOffset = */ rtDbgModDeferredImg_RvaToSegOffset,
+ /*.pfnMapPart = */ rtDbgModDeferredImg_MapPart,
+ /*.pfnUnmapPart = */ rtDbgModDeferredImg_UnmapPart,
+ /*.pfnReadAt = */ rtDbgModDeferredImg_ReadAt,
+ /*.pfnGetFormat = */ rtDbgModDeferredImg_GetFormat,
+ /*.pfnGetArch = */ rtDbgModDeferredImg_GetArch,
+ /*.pfnQueryProp = */ rtDbgModDeferredImg_QueryProp,
+ /*.pfnUnwindFrame = */ rtDbgModDeferredImg_UnwindFrame,
+
+ /*.u32EndMagic = */ RTDBGMODVTIMG_MAGIC
+};
+
+
+/**
+ * Creates a deferred loading stub for both image and debug info.
+ *
+ * @returns IPRT status code.
+ * @param pDbgMod The debug module.
+ * @param pfnDeferred The callback that will try load the image and
+ * debug info.
+ * @param cbImage The size of the image.
+ * @param hDbgCfg The debug config handle. Can be NIL. A
+ * reference will be retained.
+ * @param cbDeferred The size of the deferred instance data, 0 if the
+ * default structure is good enough.
+ * @param fFlags RTDBGMOD_F_XXX.
+ * @param ppDeferred Where to return the instance data. Can be NULL.
+ */
+DECLHIDDEN(int) rtDbgModDeferredCreate(PRTDBGMODINT pDbgMod, PFNRTDBGMODDEFERRED pfnDeferred, RTUINTPTR cbImage,
+ RTDBGCFG hDbgCfg, size_t cbDeferred, uint32_t fFlags, PRTDBGMODDEFERRED *ppDeferred)
+{
+ AssertReturn(!pDbgMod->pDbgVt, VERR_DBG_MOD_IPE);
+
+ if (cbDeferred < sizeof(RTDBGMODDEFERRED))
+ cbDeferred = sizeof(RTDBGMODDEFERRED);
+ PRTDBGMODDEFERRED pDeferred = (PRTDBGMODDEFERRED)RTMemAllocZ(cbDeferred);
+ if (!pDeferred)
+ return VERR_NO_MEMORY;
+
+ pDeferred->u32Magic = RTDBGMODDEFERRED_MAGIC;
+ pDeferred->cRefs = 1 + (pDbgMod->pImgVt == NULL);
+ pDeferred->cbImage = cbImage;
+ if (hDbgCfg != NIL_RTDBGCFG)
+ RTDbgCfgRetain(hDbgCfg);
+ pDeferred->hDbgCfg = hDbgCfg;
+ pDeferred->pfnDeferred = pfnDeferred;
+ pDeferred->fFlags = fFlags;
+
+ pDbgMod->pDbgVt = &g_rtDbgModVtDbgDeferred;
+ pDbgMod->pvDbgPriv = pDeferred;
+ if (!pDbgMod->pImgVt)
+ {
+ pDbgMod->pImgVt = &g_rtDbgModVtImgDeferred;
+ pDbgMod->pvImgPriv = pDeferred;
+ }
+ pDbgMod->fDeferred = true;
+ pDbgMod->fDeferredFailed = false;
+
+ if (ppDeferred)
+ *ppDeferred = pDeferred;
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/common/dbg/dbgmoddwarf.cpp b/src/VBox/Runtime/common/dbg/dbgmoddwarf.cpp
new file mode 100644
index 00000000..48645bab
--- /dev/null
+++ b/src/VBox/Runtime/common/dbg/dbgmoddwarf.cpp
@@ -0,0 +1,6287 @@
+/* $Id: dbgmoddwarf.cpp $ */
+/** @file
+ * IPRT - Debug Info Reader For DWARF.
+ */
+
+/*
+ * Copyright (C) 2011-2023 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 *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DBG_DWARF
+#include <iprt/dbg.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/list.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#define RTDBGMODDWARF_WITH_MEM_CACHE
+#ifdef RTDBGMODDWARF_WITH_MEM_CACHE
+# include <iprt/memcache.h>
+#endif
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/strcache.h>
+#include <iprt/x86.h>
+#include <iprt/formats/dwarf.h>
+#include "internal/dbgmod.h"
+
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Pointer to a DWARF section reader. */
+typedef struct RTDWARFCURSOR *PRTDWARFCURSOR;
+/** Pointer to an attribute descriptor. */
+typedef struct RTDWARFATTRDESC const *PCRTDWARFATTRDESC;
+/** Pointer to a DIE. */
+typedef struct RTDWARFDIE *PRTDWARFDIE;
+/** Pointer to a const DIE. */
+typedef struct RTDWARFDIE const *PCRTDWARFDIE;
+
+/**
+ * DWARF sections.
+ */
+typedef enum krtDbgModDwarfSect
+{
+ krtDbgModDwarfSect_abbrev = 0,
+ krtDbgModDwarfSect_aranges,
+ krtDbgModDwarfSect_frame,
+ krtDbgModDwarfSect_info,
+ krtDbgModDwarfSect_inlined,
+ krtDbgModDwarfSect_line,
+ krtDbgModDwarfSect_loc,
+ krtDbgModDwarfSect_macinfo,
+ krtDbgModDwarfSect_pubnames,
+ krtDbgModDwarfSect_pubtypes,
+ krtDbgModDwarfSect_ranges,
+ krtDbgModDwarfSect_str,
+ krtDbgModDwarfSect_types,
+ /** End of valid parts (exclusive). */
+ krtDbgModDwarfSect_End
+} krtDbgModDwarfSect;
+
+/**
+ * Abbreviation cache entry.
+ */
+typedef struct RTDWARFABBREV
+{
+ /** Whether there are children or not. */
+ bool fChildren;
+#ifdef LOG_ENABLED
+ uint8_t cbHdr; /**< For calcing ABGOFF matching dwarfdump. */
+#endif
+ /** The tag. */
+ uint16_t uTag;
+ /** Offset into the abbrev section of the specification pairs. */
+ uint32_t offSpec;
+ /** The abbreviation table offset this is entry is valid for.
+ * UINT32_MAX if not valid. */
+ uint32_t offAbbrev;
+} RTDWARFABBREV;
+/** Pointer to an abbreviation cache entry. */
+typedef RTDWARFABBREV *PRTDWARFABBREV;
+/** Pointer to a const abbreviation cache entry. */
+typedef RTDWARFABBREV const *PCRTDWARFABBREV;
+
+/**
+ * Structure for gathering segment info.
+ */
+typedef struct RTDBGDWARFSEG
+{
+ /** The highest offset in the segment. */
+ uint64_t offHighest;
+ /** Calculated base address. */
+ uint64_t uBaseAddr;
+ /** Estimated The segment size. */
+ uint64_t cbSegment;
+ /** Segment number (RTLDRSEG::Sel16bit). */
+ RTSEL uSegment;
+} RTDBGDWARFSEG;
+/** Pointer to segment info. */
+typedef RTDBGDWARFSEG *PRTDBGDWARFSEG;
+
+
+/**
+ * The instance data of the DWARF reader.
+ */
+typedef struct RTDBGMODDWARF
+{
+ /** The debug container containing doing the real work. */
+ RTDBGMOD hCnt;
+ /** The image module (no reference). */
+ PRTDBGMODINT pImgMod;
+ /** The debug info module (no reference). */
+ PRTDBGMODINT pDbgInfoMod;
+ /** Nested image module (with reference ofc). */
+ PRTDBGMODINT pNestedMod;
+
+ /** DWARF debug info sections. */
+ struct
+ {
+ /** The file offset of the part. */
+ RTFOFF offFile;
+ /** The size of the part. */
+ size_t cb;
+ /** The memory mapping of the part. */
+ void const *pv;
+ /** Set if present. */
+ bool fPresent;
+ /** The debug info ordinal number in the image file. */
+ uint32_t iDbgInfo;
+ } aSections[krtDbgModDwarfSect_End];
+
+ /** The offset into the abbreviation section of the current cache. */
+ uint32_t offCachedAbbrev;
+ /** The number of cached abbreviations we've allocated space for. */
+ uint32_t cCachedAbbrevsAlloced;
+ /** Array of cached abbreviations, indexed by code. */
+ PRTDWARFABBREV paCachedAbbrevs;
+ /** Used by rtDwarfAbbrev_Lookup when the result is uncachable. */
+ RTDWARFABBREV LookupAbbrev;
+
+ /** The list of compilation units (RTDWARFDIE). */
+ RTLISTANCHOR CompileUnitList;
+
+ /** Set if we have to use link addresses because the module does not have
+ * fixups (mach_kernel). */
+ bool fUseLinkAddress;
+ /** This is set to -1 if we're doing everything in one pass.
+ * Otherwise it's 1 or 2:
+ * - In pass 1, we collect segment info.
+ * - In pass 2, we add debug info to the container.
+ * The two pass parsing is necessary for watcom generated symbol files as
+ * these contains no information about the code and data segments in the
+ * image. So we have to figure out some approximate stuff based on the
+ * segments and offsets we encounter in the debug info. */
+ int8_t iWatcomPass;
+ /** Segment index hint. */
+ uint16_t iSegHint;
+ /** The number of segments in paSegs.
+ * (During segment copying, this is abused to count useful segments.) */
+ uint32_t cSegs;
+ /** Pointer to segments if iWatcomPass isn't -1. */
+ PRTDBGDWARFSEG paSegs;
+#ifdef RTDBGMODDWARF_WITH_MEM_CACHE
+ /** DIE allocators. */
+ struct
+ {
+ RTMEMCACHE hMemCache;
+ uint32_t cbMax;
+ } aDieAllocators[2];
+#endif
+} RTDBGMODDWARF;
+/** Pointer to instance data of the DWARF reader. */
+typedef RTDBGMODDWARF *PRTDBGMODDWARF;
+
+/**
+ * DWARF cursor for reading byte data.
+ */
+typedef struct RTDWARFCURSOR
+{
+ /** The current position. */
+ uint8_t const *pb;
+ /** The number of bytes left to read. */
+ size_t cbLeft;
+ /** The number of bytes left to read in the current unit. */
+ size_t cbUnitLeft;
+ /** The DWARF debug info reader instance. (Can be NULL for eh_frame.) */
+ PRTDBGMODDWARF pDwarfMod;
+ /** Set if this is 64-bit DWARF, clear if 32-bit. */
+ bool f64bitDwarf;
+ /** Set if the format endian is native, clear if endian needs to be
+ * inverted. */
+ bool fNativEndian;
+ /** The size of a native address. */
+ uint8_t cbNativeAddr;
+ /** The cursor status code. This is VINF_SUCCESS until some error
+ * occurs. */
+ int rc;
+ /** The start of the area covered by the cursor.
+ * Used for repositioning the cursor relative to the start of a section. */
+ uint8_t const *pbStart;
+ /** The section. */
+ krtDbgModDwarfSect enmSect;
+} RTDWARFCURSOR;
+
+
+/**
+ * DWARF line number program state.
+ */
+typedef struct RTDWARFLINESTATE
+{
+ /** @name Virtual Line Number Machine Registers.
+ * @{ */
+ struct
+ {
+ uint64_t uAddress;
+ uint64_t idxOp;
+ uint32_t iFile;
+ uint32_t uLine;
+ uint32_t uColumn;
+ bool fIsStatement;
+ bool fBasicBlock;
+ bool fEndSequence;
+ bool fPrologueEnd;
+ bool fEpilogueBegin;
+ uint32_t uIsa;
+ uint32_t uDiscriminator;
+ RTSEL uSegment;
+ } Regs;
+ /** @} */
+
+ /** Header. */
+ struct
+ {
+ uint32_t uVer;
+ uint64_t offFirstOpcode;
+ uint8_t cbMinInstr;
+ uint8_t cMaxOpsPerInstr;
+ uint8_t u8DefIsStmt;
+ int8_t s8LineBase;
+ uint8_t u8LineRange;
+ uint8_t u8OpcodeBase;
+ uint8_t const *pacStdOperands;
+ } Hdr;
+
+ /** @name Include Path Table (0-based)
+ * @{ */
+ const char **papszIncPaths;
+ uint32_t cIncPaths;
+ /** @} */
+
+ /** @name File Name Table (0-based, dummy zero entry)
+ * @{ */
+ char **papszFileNames;
+ uint32_t cFileNames;
+ /** @} */
+
+ /** The DWARF debug info reader instance. */
+ PRTDBGMODDWARF pDwarfMod;
+} RTDWARFLINESTATE;
+/** Pointer to a DWARF line number program state. */
+typedef RTDWARFLINESTATE *PRTDWARFLINESTATE;
+
+
+/**
+ * Decodes an attribute and stores it in the specified DIE member field.
+ *
+ * @returns IPRT status code.
+ * @param pDie Pointer to the DIE structure.
+ * @param pbMember Pointer to the first byte in the member.
+ * @param pDesc The attribute descriptor.
+ * @param uForm The data form.
+ * @param pCursor The cursor to read data from.
+ */
+typedef DECLCALLBACKTYPE(int, FNRTDWARFATTRDECODER,(PRTDWARFDIE pDie, uint8_t *pbMember, PCRTDWARFATTRDESC pDesc,
+ uint32_t uForm, PRTDWARFCURSOR pCursor));
+/** Pointer to an attribute decoder callback. */
+typedef FNRTDWARFATTRDECODER *PFNRTDWARFATTRDECODER;
+
+/**
+ * Attribute descriptor.
+ */
+typedef struct RTDWARFATTRDESC
+{
+ /** The attribute. */
+ uint16_t uAttr;
+ /** The data member offset. */
+ uint16_t off;
+ /** The data member size and initialization method. */
+ uint8_t cbInit;
+ uint8_t bPadding[3]; /**< Alignment padding. */
+ /** The decoder function. */
+ PFNRTDWARFATTRDECODER pfnDecoder;
+} RTDWARFATTRDESC;
+
+/** Define a attribute entry. */
+#define ATTR_ENTRY(a_uAttr, a_Struct, a_Member, a_Init, a_pfnDecoder) \
+ { \
+ a_uAttr, \
+ (uint16_t)RT_OFFSETOF(a_Struct, a_Member), \
+ a_Init | ((uint8_t)RT_SIZEOFMEMB(a_Struct, a_Member) & ATTR_SIZE_MASK), \
+ { 0, 0, 0 }, \
+ a_pfnDecoder\
+ }
+
+/** @name Attribute size and init methods.
+ * @{ */
+#define ATTR_INIT_ZERO UINT8_C(0x00)
+#define ATTR_INIT_FFFS UINT8_C(0x80)
+#define ATTR_INIT_MASK UINT8_C(0x80)
+#define ATTR_SIZE_MASK UINT8_C(0x3f)
+#define ATTR_GET_SIZE(a_pAttrDesc) ((a_pAttrDesc)->cbInit & ATTR_SIZE_MASK)
+/** @} */
+
+
+/**
+ * DIE descriptor.
+ */
+typedef struct RTDWARFDIEDESC
+{
+ /** The size of the DIE. */
+ size_t cbDie;
+ /** The number of attributes. */
+ size_t cAttributes;
+ /** Pointer to the array of attributes. */
+ PCRTDWARFATTRDESC paAttributes;
+} RTDWARFDIEDESC;
+typedef struct RTDWARFDIEDESC const *PCRTDWARFDIEDESC;
+/** DIE descriptor initializer. */
+#define DIE_DESC_INIT(a_Type, a_aAttrs) { sizeof(a_Type), RT_ELEMENTS(a_aAttrs), &a_aAttrs[0] }
+
+
+/**
+ * DIE core structure, all inherits (starts with) this.
+ */
+typedef struct RTDWARFDIE
+{
+ /** Pointer to the parent node. NULL if root unit. */
+ struct RTDWARFDIE *pParent;
+ /** Our node in the sibling list. */
+ RTLISTNODE SiblingNode;
+ /** List of children. */
+ RTLISTNODE ChildList;
+ /** The number of attributes successfully decoded. */
+ uint8_t cDecodedAttrs;
+ /** The number of unknown or otherwise unhandled attributes. */
+ uint8_t cUnhandledAttrs;
+#ifdef RTDBGMODDWARF_WITH_MEM_CACHE
+ /** The allocator index. */
+ uint8_t iAllocator;
+#endif
+ /** The die tag, indicating which union structure to use. */
+ uint16_t uTag;
+ /** Offset of the abbreviation specification (within debug_abbrev). */
+ uint32_t offSpec;
+} RTDWARFDIE;
+
+
+/**
+ * DWARF address structure.
+ */
+typedef struct RTDWARFADDR
+{
+ /** The address. */
+ uint64_t uAddress;
+} RTDWARFADDR;
+typedef RTDWARFADDR *PRTDWARFADDR;
+typedef RTDWARFADDR const *PCRTDWARFADDR;
+
+
+/**
+ * DWARF address range.
+ */
+typedef struct RTDWARFADDRRANGE
+{
+ uint64_t uLowAddress;
+ uint64_t uHighAddress;
+ uint8_t const *pbRanges; /* ?? */
+ uint8_t cAttrs : 2;
+ uint8_t fHaveLowAddress : 1;
+ uint8_t fHaveHighAddress : 1;
+ uint8_t fHaveHighIsAddress : 1;
+ uint8_t fHaveRanges : 1;
+} RTDWARFADDRRANGE;
+typedef RTDWARFADDRRANGE *PRTDWARFADDRRANGE;
+typedef RTDWARFADDRRANGE const *PCRTDWARFADDRRANGE;
+
+/** What a RTDWARFREF is relative to. */
+typedef enum krtDwarfRef
+{
+ krtDwarfRef_NotSet,
+ krtDwarfRef_LineSection,
+ krtDwarfRef_LocSection,
+ krtDwarfRef_RangesSection,
+ krtDwarfRef_InfoSection,
+ krtDwarfRef_SameUnit,
+ krtDwarfRef_TypeId64
+} krtDwarfRef;
+
+/**
+ * DWARF reference.
+ */
+typedef struct RTDWARFREF
+{
+ /** The offset. */
+ uint64_t off;
+ /** What the offset is relative to. */
+ krtDwarfRef enmWrt;
+} RTDWARFREF;
+typedef RTDWARFREF *PRTDWARFREF;
+typedef RTDWARFREF const *PCRTDWARFREF;
+
+
+/**
+ * DWARF Location state.
+ */
+typedef struct RTDWARFLOCST
+{
+ /** The input cursor. */
+ RTDWARFCURSOR Cursor;
+ /** Points to the current top of the stack. Initial value -1. */
+ int32_t iTop;
+ /** The value stack. */
+ uint64_t auStack[64];
+} RTDWARFLOCST;
+/** Pointer to location state. */
+typedef RTDWARFLOCST *PRTDWARFLOCST;
+
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static FNRTDWARFATTRDECODER rtDwarfDecode_Address;
+static FNRTDWARFATTRDECODER rtDwarfDecode_Bool;
+static FNRTDWARFATTRDECODER rtDwarfDecode_LowHighPc;
+static FNRTDWARFATTRDECODER rtDwarfDecode_Ranges;
+static FNRTDWARFATTRDECODER rtDwarfDecode_Reference;
+static FNRTDWARFATTRDECODER rtDwarfDecode_SectOff;
+static FNRTDWARFATTRDECODER rtDwarfDecode_String;
+static FNRTDWARFATTRDECODER rtDwarfDecode_UnsignedInt;
+static FNRTDWARFATTRDECODER rtDwarfDecode_SegmentLoc;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** RTDWARFDIE description. */
+static const RTDWARFDIEDESC g_CoreDieDesc = { sizeof(RTDWARFDIE), 0, NULL };
+
+
+/**
+ * DW_TAG_compile_unit & DW_TAG_partial_unit.
+ */
+typedef struct RTDWARFDIECOMPILEUNIT
+{
+ /** The DIE core structure. */
+ RTDWARFDIE Core;
+ /** The unit name. */
+ const char *pszName;
+ /** The address range of the code belonging to this unit. */
+ RTDWARFADDRRANGE PcRange;
+ /** The language name. */
+ uint16_t uLanguage;
+ /** The identifier case. */
+ uint8_t uIdentifierCase;
+ /** String are UTF-8 encoded. If not set, the encoding is
+ * unknown. */
+ bool fUseUtf8;
+ /** The unit contains main() or equivalent. */
+ bool fMainFunction;
+ /** The line numbers for this unit. */
+ RTDWARFREF StmtListRef;
+ /** The macro information for this unit. */
+ RTDWARFREF MacroInfoRef;
+ /** Reference to the base types. */
+ RTDWARFREF BaseTypesRef;
+ /** Working directory for the unit. */
+ const char *pszCurDir;
+ /** The name of the compiler or whatever that produced this unit. */
+ const char *pszProducer;
+
+ /** @name From the unit header.
+ * @{ */
+ /** The offset into debug_info of this unit (for references). */
+ uint64_t offUnit;
+ /** The length of this unit. */
+ uint64_t cbUnit;
+ /** The offset into debug_abbrev of the abbreviation for this unit. */
+ uint64_t offAbbrev;
+ /** The native address size. */
+ uint8_t cbNativeAddr;
+ /** The DWARF version. */
+ uint8_t uDwarfVer;
+ /** @} */
+} RTDWARFDIECOMPILEUNIT;
+typedef RTDWARFDIECOMPILEUNIT *PRTDWARFDIECOMPILEUNIT;
+
+
+/** RTDWARFDIECOMPILEUNIT attributes. */
+static const RTDWARFATTRDESC g_aCompileUnitAttrs[] =
+{
+ ATTR_ENTRY(DW_AT_name, RTDWARFDIECOMPILEUNIT, pszName, ATTR_INIT_ZERO, rtDwarfDecode_String),
+ ATTR_ENTRY(DW_AT_low_pc, RTDWARFDIECOMPILEUNIT, PcRange, ATTR_INIT_ZERO, rtDwarfDecode_LowHighPc),
+ ATTR_ENTRY(DW_AT_high_pc, RTDWARFDIECOMPILEUNIT, PcRange, ATTR_INIT_ZERO, rtDwarfDecode_LowHighPc),
+ ATTR_ENTRY(DW_AT_ranges, RTDWARFDIECOMPILEUNIT, PcRange, ATTR_INIT_ZERO, rtDwarfDecode_Ranges),
+ ATTR_ENTRY(DW_AT_language, RTDWARFDIECOMPILEUNIT, uLanguage, ATTR_INIT_ZERO, rtDwarfDecode_UnsignedInt),
+ ATTR_ENTRY(DW_AT_macro_info, RTDWARFDIECOMPILEUNIT, MacroInfoRef, ATTR_INIT_ZERO, rtDwarfDecode_SectOff),
+ ATTR_ENTRY(DW_AT_stmt_list, RTDWARFDIECOMPILEUNIT, StmtListRef, ATTR_INIT_ZERO, rtDwarfDecode_SectOff),
+ ATTR_ENTRY(DW_AT_comp_dir, RTDWARFDIECOMPILEUNIT, pszCurDir, ATTR_INIT_ZERO, rtDwarfDecode_String),
+ ATTR_ENTRY(DW_AT_producer, RTDWARFDIECOMPILEUNIT, pszProducer, ATTR_INIT_ZERO, rtDwarfDecode_String),
+ ATTR_ENTRY(DW_AT_identifier_case, RTDWARFDIECOMPILEUNIT, uIdentifierCase,ATTR_INIT_ZERO, rtDwarfDecode_UnsignedInt),
+ ATTR_ENTRY(DW_AT_base_types, RTDWARFDIECOMPILEUNIT, BaseTypesRef, ATTR_INIT_ZERO, rtDwarfDecode_Reference),
+ ATTR_ENTRY(DW_AT_use_UTF8, RTDWARFDIECOMPILEUNIT, fUseUtf8, ATTR_INIT_ZERO, rtDwarfDecode_Bool),
+ ATTR_ENTRY(DW_AT_main_subprogram, RTDWARFDIECOMPILEUNIT, fMainFunction, ATTR_INIT_ZERO, rtDwarfDecode_Bool)
+};
+
+/** RTDWARFDIECOMPILEUNIT description. */
+static const RTDWARFDIEDESC g_CompileUnitDesc = DIE_DESC_INIT(RTDWARFDIECOMPILEUNIT, g_aCompileUnitAttrs);
+
+
+/**
+ * DW_TAG_subprogram.
+ */
+typedef struct RTDWARFDIESUBPROGRAM
+{
+ /** The DIE core structure. */
+ RTDWARFDIE Core;
+ /** The name. */
+ const char *pszName;
+ /** The linkage name. */
+ const char *pszLinkageName;
+ /** The address range of the code belonging to this unit. */
+ RTDWARFADDRRANGE PcRange;
+ /** The first instruction in the function. */
+ RTDWARFADDR EntryPc;
+ /** Segment number (watcom). */
+ RTSEL uSegment;
+ /** Reference to the specification. */
+ RTDWARFREF SpecRef;
+} RTDWARFDIESUBPROGRAM;
+/** Pointer to a DW_TAG_subprogram DIE. */
+typedef RTDWARFDIESUBPROGRAM *PRTDWARFDIESUBPROGRAM;
+/** Pointer to a const DW_TAG_subprogram DIE. */
+typedef RTDWARFDIESUBPROGRAM const *PCRTDWARFDIESUBPROGRAM;
+
+
+/** RTDWARFDIESUBPROGRAM attributes. */
+static const RTDWARFATTRDESC g_aSubProgramAttrs[] =
+{
+ ATTR_ENTRY(DW_AT_name, RTDWARFDIESUBPROGRAM, pszName, ATTR_INIT_ZERO, rtDwarfDecode_String),
+ ATTR_ENTRY(DW_AT_linkage_name, RTDWARFDIESUBPROGRAM, pszLinkageName, ATTR_INIT_ZERO, rtDwarfDecode_String),
+ ATTR_ENTRY(DW_AT_MIPS_linkage_name, RTDWARFDIESUBPROGRAM, pszLinkageName, ATTR_INIT_ZERO, rtDwarfDecode_String),
+ ATTR_ENTRY(DW_AT_low_pc, RTDWARFDIESUBPROGRAM, PcRange, ATTR_INIT_ZERO, rtDwarfDecode_LowHighPc),
+ ATTR_ENTRY(DW_AT_high_pc, RTDWARFDIESUBPROGRAM, PcRange, ATTR_INIT_ZERO, rtDwarfDecode_LowHighPc),
+ ATTR_ENTRY(DW_AT_ranges, RTDWARFDIESUBPROGRAM, PcRange, ATTR_INIT_ZERO, rtDwarfDecode_Ranges),
+ ATTR_ENTRY(DW_AT_entry_pc, RTDWARFDIESUBPROGRAM, EntryPc, ATTR_INIT_ZERO, rtDwarfDecode_Address),
+ ATTR_ENTRY(DW_AT_segment, RTDWARFDIESUBPROGRAM, uSegment, ATTR_INIT_ZERO, rtDwarfDecode_SegmentLoc),
+ ATTR_ENTRY(DW_AT_specification, RTDWARFDIESUBPROGRAM, SpecRef, ATTR_INIT_ZERO, rtDwarfDecode_Reference)
+};
+
+/** RTDWARFDIESUBPROGRAM description. */
+static const RTDWARFDIEDESC g_SubProgramDesc = DIE_DESC_INIT(RTDWARFDIESUBPROGRAM, g_aSubProgramAttrs);
+
+
+/** RTDWARFDIESUBPROGRAM attributes for the specification hack. */
+static const RTDWARFATTRDESC g_aSubProgramSpecHackAttrs[] =
+{
+ ATTR_ENTRY(DW_AT_name, RTDWARFDIESUBPROGRAM, pszName, ATTR_INIT_ZERO, rtDwarfDecode_String),
+ ATTR_ENTRY(DW_AT_linkage_name, RTDWARFDIESUBPROGRAM, pszLinkageName, ATTR_INIT_ZERO, rtDwarfDecode_String),
+ ATTR_ENTRY(DW_AT_MIPS_linkage_name, RTDWARFDIESUBPROGRAM, pszLinkageName, ATTR_INIT_ZERO, rtDwarfDecode_String),
+};
+
+/** RTDWARFDIESUBPROGRAM description for the specification hack. */
+static const RTDWARFDIEDESC g_SubProgramSpecHackDesc = DIE_DESC_INIT(RTDWARFDIESUBPROGRAM, g_aSubProgramSpecHackAttrs);
+
+
+/**
+ * DW_TAG_label.
+ */
+typedef struct RTDWARFDIELABEL
+{
+ /** The DIE core structure. */
+ RTDWARFDIE Core;
+ /** The name. */
+ const char *pszName;
+ /** The address of the first instruction. */
+ RTDWARFADDR Address;
+ /** Segment number (watcom). */
+ RTSEL uSegment;
+ /** Externally visible? */
+ bool fExternal;
+} RTDWARFDIELABEL;
+/** Pointer to a DW_TAG_label DIE. */
+typedef RTDWARFDIELABEL *PRTDWARFDIELABEL;
+/** Pointer to a const DW_TAG_label DIE. */
+typedef RTDWARFDIELABEL const *PCRTDWARFDIELABEL;
+
+
+/** RTDWARFDIESUBPROGRAM attributes. */
+static const RTDWARFATTRDESC g_aLabelAttrs[] =
+{
+ ATTR_ENTRY(DW_AT_name, RTDWARFDIELABEL, pszName, ATTR_INIT_ZERO, rtDwarfDecode_String),
+ ATTR_ENTRY(DW_AT_low_pc, RTDWARFDIELABEL, Address, ATTR_INIT_ZERO, rtDwarfDecode_Address),
+ ATTR_ENTRY(DW_AT_segment, RTDWARFDIELABEL, uSegment, ATTR_INIT_ZERO, rtDwarfDecode_SegmentLoc),
+ ATTR_ENTRY(DW_AT_external, RTDWARFDIELABEL, fExternal, ATTR_INIT_ZERO, rtDwarfDecode_Bool)
+};
+
+/** RTDWARFDIESUBPROGRAM description. */
+static const RTDWARFDIEDESC g_LabelDesc = DIE_DESC_INIT(RTDWARFDIELABEL, g_aLabelAttrs);
+
+
+/**
+ * Tag names and descriptors.
+ */
+static const struct RTDWARFTAGDESC
+{
+ /** The tag value. */
+ uint16_t uTag;
+ /** The tag name as string. */
+ const char *pszName;
+ /** The DIE descriptor to use. */
+ PCRTDWARFDIEDESC pDesc;
+} g_aTagDescs[] =
+{
+#define TAGDESC(a_Name, a_pDesc) { DW_ ## a_Name, #a_Name, a_pDesc }
+#define TAGDESC_EMPTY() { 0, NULL, &g_CoreDieDesc }
+#define TAGDESC_CORE(a_Name) TAGDESC(a_Name, &g_CoreDieDesc)
+ TAGDESC_EMPTY(), /* 0x00 */
+ TAGDESC_CORE(TAG_array_type),
+ TAGDESC_CORE(TAG_class_type),
+ TAGDESC_CORE(TAG_entry_point),
+ TAGDESC_CORE(TAG_enumeration_type), /* 0x04 */
+ TAGDESC_CORE(TAG_formal_parameter),
+ TAGDESC_EMPTY(),
+ TAGDESC_EMPTY(),
+ TAGDESC_CORE(TAG_imported_declaration), /* 0x08 */
+ TAGDESC_EMPTY(),
+ TAGDESC(TAG_label, &g_LabelDesc),
+ TAGDESC_CORE(TAG_lexical_block),
+ TAGDESC_EMPTY(), /* 0x0c */
+ TAGDESC_CORE(TAG_member),
+ TAGDESC_EMPTY(),
+ TAGDESC_CORE(TAG_pointer_type),
+ TAGDESC_CORE(TAG_reference_type), /* 0x10 */
+ TAGDESC_CORE(TAG_compile_unit),
+ TAGDESC_CORE(TAG_string_type),
+ TAGDESC_CORE(TAG_structure_type),
+ TAGDESC_EMPTY(), /* 0x14 */
+ TAGDESC_CORE(TAG_subroutine_type),
+ TAGDESC_CORE(TAG_typedef),
+ TAGDESC_CORE(TAG_union_type),
+ TAGDESC_CORE(TAG_unspecified_parameters), /* 0x18 */
+ TAGDESC_CORE(TAG_variant),
+ TAGDESC_CORE(TAG_common_block),
+ TAGDESC_CORE(TAG_common_inclusion),
+ TAGDESC_CORE(TAG_inheritance), /* 0x1c */
+ TAGDESC_CORE(TAG_inlined_subroutine),
+ TAGDESC_CORE(TAG_module),
+ TAGDESC_CORE(TAG_ptr_to_member_type),
+ TAGDESC_CORE(TAG_set_type), /* 0x20 */
+ TAGDESC_CORE(TAG_subrange_type),
+ TAGDESC_CORE(TAG_with_stmt),
+ TAGDESC_CORE(TAG_access_declaration),
+ TAGDESC_CORE(TAG_base_type), /* 0x24 */
+ TAGDESC_CORE(TAG_catch_block),
+ TAGDESC_CORE(TAG_const_type),
+ TAGDESC_CORE(TAG_constant),
+ TAGDESC_CORE(TAG_enumerator), /* 0x28 */
+ TAGDESC_CORE(TAG_file_type),
+ TAGDESC_CORE(TAG_friend),
+ TAGDESC_CORE(TAG_namelist),
+ TAGDESC_CORE(TAG_namelist_item), /* 0x2c */
+ TAGDESC_CORE(TAG_packed_type),
+ TAGDESC(TAG_subprogram, &g_SubProgramDesc),
+ TAGDESC_CORE(TAG_template_type_parameter),
+ TAGDESC_CORE(TAG_template_value_parameter), /* 0x30 */
+ TAGDESC_CORE(TAG_thrown_type),
+ TAGDESC_CORE(TAG_try_block),
+ TAGDESC_CORE(TAG_variant_part),
+ TAGDESC_CORE(TAG_variable), /* 0x34 */
+ TAGDESC_CORE(TAG_volatile_type),
+ TAGDESC_CORE(TAG_dwarf_procedure),
+ TAGDESC_CORE(TAG_restrict_type),
+ TAGDESC_CORE(TAG_interface_type), /* 0x38 */
+ TAGDESC_CORE(TAG_namespace),
+ TAGDESC_CORE(TAG_imported_module),
+ TAGDESC_CORE(TAG_unspecified_type),
+ TAGDESC_CORE(TAG_partial_unit), /* 0x3c */
+ TAGDESC_CORE(TAG_imported_unit),
+ TAGDESC_EMPTY(),
+ TAGDESC_CORE(TAG_condition),
+ TAGDESC_CORE(TAG_shared_type), /* 0x40 */
+ TAGDESC_CORE(TAG_type_unit),
+ TAGDESC_CORE(TAG_rvalue_reference_type),
+ TAGDESC_CORE(TAG_template_alias)
+#undef TAGDESC
+#undef TAGDESC_EMPTY
+#undef TAGDESC_CORE
+};
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int rtDwarfInfo_ParseDie(PRTDBGMODDWARF pThis, PRTDWARFDIE pDie, PCRTDWARFDIEDESC pDieDesc,
+ PRTDWARFCURSOR pCursor, PCRTDWARFABBREV pAbbrev, bool fInitDie);
+
+
+
+#if defined(LOG_ENABLED) || defined(RT_STRICT)
+
+# if 0 /* unused */
+/**
+ * Turns a tag value into a string for logging purposes.
+ *
+ * @returns String name.
+ * @param uTag The tag.
+ */
+static const char *rtDwarfLog_GetTagName(uint32_t uTag)
+{
+ if (uTag < RT_ELEMENTS(g_aTagDescs))
+ {
+ const char *pszTag = g_aTagDescs[uTag].pszName;
+ if (pszTag)
+ return pszTag;
+ }
+
+ static char s_szStatic[32];
+ RTStrPrintf(s_szStatic, sizeof(s_szStatic),"DW_TAG_%#x", uTag);
+ return s_szStatic;
+}
+# endif
+
+
+/**
+ * Turns an attributevalue into a string for logging purposes.
+ *
+ * @returns String name.
+ * @param uAttr The attribute.
+ */
+static const char *rtDwarfLog_AttrName(uint32_t uAttr)
+{
+ switch (uAttr)
+ {
+ RT_CASE_RET_STR(DW_AT_sibling);
+ RT_CASE_RET_STR(DW_AT_location);
+ RT_CASE_RET_STR(DW_AT_name);
+ RT_CASE_RET_STR(DW_AT_ordering);
+ RT_CASE_RET_STR(DW_AT_byte_size);
+ RT_CASE_RET_STR(DW_AT_bit_offset);
+ RT_CASE_RET_STR(DW_AT_bit_size);
+ RT_CASE_RET_STR(DW_AT_stmt_list);
+ RT_CASE_RET_STR(DW_AT_low_pc);
+ RT_CASE_RET_STR(DW_AT_high_pc);
+ RT_CASE_RET_STR(DW_AT_language);
+ RT_CASE_RET_STR(DW_AT_discr);
+ RT_CASE_RET_STR(DW_AT_discr_value);
+ RT_CASE_RET_STR(DW_AT_visibility);
+ RT_CASE_RET_STR(DW_AT_import);
+ RT_CASE_RET_STR(DW_AT_string_length);
+ RT_CASE_RET_STR(DW_AT_common_reference);
+ RT_CASE_RET_STR(DW_AT_comp_dir);
+ RT_CASE_RET_STR(DW_AT_const_value);
+ RT_CASE_RET_STR(DW_AT_containing_type);
+ RT_CASE_RET_STR(DW_AT_default_value);
+ RT_CASE_RET_STR(DW_AT_inline);
+ RT_CASE_RET_STR(DW_AT_is_optional);
+ RT_CASE_RET_STR(DW_AT_lower_bound);
+ RT_CASE_RET_STR(DW_AT_producer);
+ RT_CASE_RET_STR(DW_AT_prototyped);
+ RT_CASE_RET_STR(DW_AT_return_addr);
+ RT_CASE_RET_STR(DW_AT_start_scope);
+ RT_CASE_RET_STR(DW_AT_bit_stride);
+ RT_CASE_RET_STR(DW_AT_upper_bound);
+ RT_CASE_RET_STR(DW_AT_abstract_origin);
+ RT_CASE_RET_STR(DW_AT_accessibility);
+ RT_CASE_RET_STR(DW_AT_address_class);
+ RT_CASE_RET_STR(DW_AT_artificial);
+ RT_CASE_RET_STR(DW_AT_base_types);
+ RT_CASE_RET_STR(DW_AT_calling_convention);
+ RT_CASE_RET_STR(DW_AT_count);
+ RT_CASE_RET_STR(DW_AT_data_member_location);
+ RT_CASE_RET_STR(DW_AT_decl_column);
+ RT_CASE_RET_STR(DW_AT_decl_file);
+ RT_CASE_RET_STR(DW_AT_decl_line);
+ RT_CASE_RET_STR(DW_AT_declaration);
+ RT_CASE_RET_STR(DW_AT_discr_list);
+ RT_CASE_RET_STR(DW_AT_encoding);
+ RT_CASE_RET_STR(DW_AT_external);
+ RT_CASE_RET_STR(DW_AT_frame_base);
+ RT_CASE_RET_STR(DW_AT_friend);
+ RT_CASE_RET_STR(DW_AT_identifier_case);
+ RT_CASE_RET_STR(DW_AT_macro_info);
+ RT_CASE_RET_STR(DW_AT_namelist_item);
+ RT_CASE_RET_STR(DW_AT_priority);
+ RT_CASE_RET_STR(DW_AT_segment);
+ RT_CASE_RET_STR(DW_AT_specification);
+ RT_CASE_RET_STR(DW_AT_static_link);
+ RT_CASE_RET_STR(DW_AT_type);
+ RT_CASE_RET_STR(DW_AT_use_location);
+ RT_CASE_RET_STR(DW_AT_variable_parameter);
+ RT_CASE_RET_STR(DW_AT_virtuality);
+ RT_CASE_RET_STR(DW_AT_vtable_elem_location);
+ RT_CASE_RET_STR(DW_AT_allocated);
+ RT_CASE_RET_STR(DW_AT_associated);
+ RT_CASE_RET_STR(DW_AT_data_location);
+ RT_CASE_RET_STR(DW_AT_byte_stride);
+ RT_CASE_RET_STR(DW_AT_entry_pc);
+ RT_CASE_RET_STR(DW_AT_use_UTF8);
+ RT_CASE_RET_STR(DW_AT_extension);
+ RT_CASE_RET_STR(DW_AT_ranges);
+ RT_CASE_RET_STR(DW_AT_trampoline);
+ RT_CASE_RET_STR(DW_AT_call_column);
+ RT_CASE_RET_STR(DW_AT_call_file);
+ RT_CASE_RET_STR(DW_AT_call_line);
+ RT_CASE_RET_STR(DW_AT_description);
+ RT_CASE_RET_STR(DW_AT_binary_scale);
+ RT_CASE_RET_STR(DW_AT_decimal_scale);
+ RT_CASE_RET_STR(DW_AT_small);
+ RT_CASE_RET_STR(DW_AT_decimal_sign);
+ RT_CASE_RET_STR(DW_AT_digit_count);
+ RT_CASE_RET_STR(DW_AT_picture_string);
+ RT_CASE_RET_STR(DW_AT_mutable);
+ RT_CASE_RET_STR(DW_AT_threads_scaled);
+ RT_CASE_RET_STR(DW_AT_explicit);
+ RT_CASE_RET_STR(DW_AT_object_pointer);
+ RT_CASE_RET_STR(DW_AT_endianity);
+ RT_CASE_RET_STR(DW_AT_elemental);
+ RT_CASE_RET_STR(DW_AT_pure);
+ RT_CASE_RET_STR(DW_AT_recursive);
+ RT_CASE_RET_STR(DW_AT_signature);
+ RT_CASE_RET_STR(DW_AT_main_subprogram);
+ RT_CASE_RET_STR(DW_AT_data_bit_offset);
+ RT_CASE_RET_STR(DW_AT_const_expr);
+ RT_CASE_RET_STR(DW_AT_enum_class);
+ RT_CASE_RET_STR(DW_AT_linkage_name);
+ RT_CASE_RET_STR(DW_AT_MIPS_linkage_name);
+ RT_CASE_RET_STR(DW_AT_WATCOM_memory_model);
+ RT_CASE_RET_STR(DW_AT_WATCOM_references_start);
+ RT_CASE_RET_STR(DW_AT_WATCOM_parm_entry);
+ }
+ static char s_szStatic[32];
+ RTStrPrintf(s_szStatic, sizeof(s_szStatic),"DW_AT_%#x", uAttr);
+ return s_szStatic;
+}
+
+
+/**
+ * Turns a form value into a string for logging purposes.
+ *
+ * @returns String name.
+ * @param uForm The form.
+ */
+static const char *rtDwarfLog_FormName(uint32_t uForm)
+{
+ switch (uForm)
+ {
+ RT_CASE_RET_STR(DW_FORM_addr);
+ RT_CASE_RET_STR(DW_FORM_block2);
+ RT_CASE_RET_STR(DW_FORM_block4);
+ RT_CASE_RET_STR(DW_FORM_data2);
+ RT_CASE_RET_STR(DW_FORM_data4);
+ RT_CASE_RET_STR(DW_FORM_data8);
+ RT_CASE_RET_STR(DW_FORM_string);
+ RT_CASE_RET_STR(DW_FORM_block);
+ RT_CASE_RET_STR(DW_FORM_block1);
+ RT_CASE_RET_STR(DW_FORM_data1);
+ RT_CASE_RET_STR(DW_FORM_flag);
+ RT_CASE_RET_STR(DW_FORM_sdata);
+ RT_CASE_RET_STR(DW_FORM_strp);
+ RT_CASE_RET_STR(DW_FORM_udata);
+ RT_CASE_RET_STR(DW_FORM_ref_addr);
+ RT_CASE_RET_STR(DW_FORM_ref1);
+ RT_CASE_RET_STR(DW_FORM_ref2);
+ RT_CASE_RET_STR(DW_FORM_ref4);
+ RT_CASE_RET_STR(DW_FORM_ref8);
+ RT_CASE_RET_STR(DW_FORM_ref_udata);
+ RT_CASE_RET_STR(DW_FORM_indirect);
+ RT_CASE_RET_STR(DW_FORM_sec_offset);
+ RT_CASE_RET_STR(DW_FORM_exprloc);
+ RT_CASE_RET_STR(DW_FORM_flag_present);
+ RT_CASE_RET_STR(DW_FORM_ref_sig8);
+ }
+ static char s_szStatic[32];
+ RTStrPrintf(s_szStatic, sizeof(s_szStatic),"DW_FORM_%#x", uForm);
+ return s_szStatic;
+}
+
+#endif /* LOG_ENABLED || RT_STRICT */
+
+
+
+/** @callback_method_impl{FNRTLDRENUMSEGS} */
+static DECLCALLBACK(int) rtDbgModDwarfScanSegmentsCallback(RTLDRMOD hLdrMod, PCRTLDRSEG pSeg, void *pvUser)
+{
+ PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pvUser;
+ Log(("Segment %.*s: LinkAddress=%#llx RVA=%#llx cb=%#llx\n",
+ pSeg->cchName, pSeg->pszName, (uint64_t)pSeg->LinkAddress, (uint64_t)pSeg->RVA, pSeg->cb));
+ NOREF(hLdrMod);
+
+ /* Count relevant segments. */
+ if (pSeg->RVA != NIL_RTLDRADDR)
+ pThis->cSegs++;
+
+ return VINF_SUCCESS;
+}
+
+
+/** @callback_method_impl{FNRTLDRENUMSEGS} */
+static DECLCALLBACK(int) rtDbgModDwarfAddSegmentsCallback(RTLDRMOD hLdrMod, PCRTLDRSEG pSeg, void *pvUser)
+{
+ PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pvUser;
+ Log(("Segment %.*s: LinkAddress=%#llx RVA=%#llx cb=%#llx cbMapped=%#llx\n",
+ pSeg->cchName, pSeg->pszName, (uint64_t)pSeg->LinkAddress, (uint64_t)pSeg->RVA, pSeg->cb, pSeg->cbMapped));
+ NOREF(hLdrMod);
+ Assert(pSeg->cchName > 0);
+ Assert(!pSeg->pszName[pSeg->cchName]);
+
+ /* If the segment doesn't have a mapping, just add a dummy so the indexing
+ works out correctly (same as for the image). */
+ if (pSeg->RVA == NIL_RTLDRADDR)
+ return RTDbgModSegmentAdd(pThis->hCnt, 0, 0, pSeg->pszName, 0 /*fFlags*/, NULL);
+
+ /* The link address is 0 for all segments in a relocatable ELF image. */
+ RTLDRADDR cb = pSeg->cb;
+ if ( cb < pSeg->cbMapped
+ && RTLdrGetFormat(hLdrMod) != RTLDRFMT_LX /* for debugging our drivers; 64KB section align by linker, 4KB by loader. */
+ )
+ cb = pSeg->cbMapped;
+ return RTDbgModSegmentAdd(pThis->hCnt, pSeg->RVA, cb, pSeg->pszName, 0 /*fFlags*/, NULL);
+}
+
+
+/**
+ * Calls rtDbgModDwarfAddSegmentsCallback for each segment in the executable
+ * image.
+ *
+ * @returns IPRT status code.
+ * @param pThis The DWARF instance.
+ */
+static int rtDbgModDwarfAddSegmentsFromImage(PRTDBGMODDWARF pThis)
+{
+ AssertReturn(pThis->pImgMod && pThis->pImgMod->pImgVt, VERR_INTERNAL_ERROR_2);
+ Assert(!pThis->cSegs);
+ int rc = pThis->pImgMod->pImgVt->pfnEnumSegments(pThis->pImgMod, rtDbgModDwarfScanSegmentsCallback, pThis);
+ if (RT_SUCCESS(rc))
+ {
+ if (pThis->cSegs == 0)
+ pThis->iWatcomPass = 1;
+ else
+ {
+ pThis->cSegs = 0;
+ pThis->iWatcomPass = -1;
+ rc = pThis->pImgMod->pImgVt->pfnEnumSegments(pThis->pImgMod, rtDbgModDwarfAddSegmentsCallback, pThis);
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Looks up a segment.
+ *
+ * @returns Pointer to the segment on success, NULL if not found.
+ * @param pThis The DWARF instance.
+ * @param uSeg The segment number / selector.
+ */
+static PRTDBGDWARFSEG rtDbgModDwarfFindSegment(PRTDBGMODDWARF pThis, RTSEL uSeg)
+{
+ uint32_t cSegs = pThis->cSegs;
+ uint32_t iSeg = pThis->iSegHint;
+ PRTDBGDWARFSEG paSegs = pThis->paSegs;
+ if ( iSeg < cSegs
+ && paSegs[iSeg].uSegment == uSeg)
+ return &paSegs[iSeg];
+
+ for (iSeg = 0; iSeg < cSegs; iSeg++)
+ if (uSeg == paSegs[iSeg].uSegment)
+ {
+ pThis->iSegHint = iSeg;
+ return &paSegs[iSeg];
+ }
+
+ AssertFailed();
+ return NULL;
+}
+
+
+/**
+ * Record a segment:offset during pass 1.
+ *
+ * @returns IPRT status code.
+ * @param pThis The DWARF instance.
+ * @param uSeg The segment number / selector.
+ * @param offSeg The segment offset.
+ */
+static int rtDbgModDwarfRecordSegOffset(PRTDBGMODDWARF pThis, RTSEL uSeg, uint64_t offSeg)
+{
+ /* Look up the segment. */
+ uint32_t cSegs = pThis->cSegs;
+ uint32_t iSeg = pThis->iSegHint;
+ PRTDBGDWARFSEG paSegs = pThis->paSegs;
+ if ( iSeg >= cSegs
+ || paSegs[iSeg].uSegment != uSeg)
+ {
+ for (iSeg = 0; iSeg < cSegs; iSeg++)
+ if (uSeg <= paSegs[iSeg].uSegment)
+ break;
+ if ( iSeg >= cSegs
+ || paSegs[iSeg].uSegment != uSeg)
+ {
+ /* Add */
+ void *pvNew = RTMemRealloc(paSegs, (pThis->cSegs + 1) * sizeof(paSegs[0]));
+ if (!pvNew)
+ return VERR_NO_MEMORY;
+ pThis->paSegs = paSegs = (PRTDBGDWARFSEG)pvNew;
+ if (iSeg != cSegs)
+ memmove(&paSegs[iSeg + 1], &paSegs[iSeg], (cSegs - iSeg) * sizeof(paSegs[0]));
+ paSegs[iSeg].offHighest = offSeg;
+ paSegs[iSeg].uBaseAddr = 0;
+ paSegs[iSeg].cbSegment = 0;
+ paSegs[iSeg].uSegment = uSeg;
+ pThis->cSegs++;
+ }
+
+ pThis->iSegHint = iSeg;
+ }
+
+ /* Increase it's range? */
+ if (paSegs[iSeg].offHighest < offSeg)
+ {
+ Log3(("rtDbgModDwarfRecordSegOffset: iSeg=%d uSeg=%#06x offSeg=%#llx\n", iSeg, uSeg, offSeg));
+ paSegs[iSeg].offHighest = offSeg;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Calls pfnSegmentAdd for each segment in the executable image.
+ *
+ * @returns IPRT status code.
+ * @param pThis The DWARF instance.
+ */
+static int rtDbgModDwarfAddSegmentsFromPass1(PRTDBGMODDWARF pThis)
+{
+ AssertReturn(pThis->cSegs, VERR_DWARF_BAD_INFO);
+ uint32_t const cSegs = pThis->cSegs;
+ PRTDBGDWARFSEG paSegs = pThis->paSegs;
+
+ /*
+ * Are the segments assigned more or less in numerical order?
+ */
+ if ( paSegs[0].uSegment < 16U
+ && paSegs[cSegs - 1].uSegment - paSegs[0].uSegment + 1U <= cSegs + 16U)
+ {
+ /** @todo heuristics, plase. */
+ AssertFailedReturn(VERR_DWARF_TODO);
+
+ }
+ /*
+ * Assume DOS segmentation.
+ */
+ else
+ {
+ for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
+ paSegs[iSeg].uBaseAddr = (uint32_t)paSegs[iSeg].uSegment << 16;
+ for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
+ paSegs[iSeg].cbSegment = paSegs[iSeg].offHighest;
+ }
+
+ /*
+ * Add them.
+ */
+ for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
+ {
+ Log3(("rtDbgModDwarfAddSegmentsFromPass1: Seg#%u: %#010llx LB %#llx uSegment=%#x\n",
+ iSeg, paSegs[iSeg].uBaseAddr, paSegs[iSeg].cbSegment, paSegs[iSeg].uSegment));
+ char szName[32];
+ RTStrPrintf(szName, sizeof(szName), "seg-%#04xh", paSegs[iSeg].uSegment);
+ int rc = RTDbgModSegmentAdd(pThis->hCnt, paSegs[iSeg].uBaseAddr, paSegs[iSeg].cbSegment,
+ szName, 0 /*fFlags*/, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Loads a DWARF section from the image file.
+ *
+ * @returns IPRT status code.
+ * @param pThis The DWARF instance.
+ * @param enmSect The section to load.
+ */
+static int rtDbgModDwarfLoadSection(PRTDBGMODDWARF pThis, krtDbgModDwarfSect enmSect)
+{
+ /*
+ * Don't load stuff twice.
+ */
+ if (pThis->aSections[enmSect].pv)
+ return VINF_SUCCESS;
+
+ /*
+ * Sections that are not present cannot be loaded, treat them like they
+ * are empty
+ */
+ if (!pThis->aSections[enmSect].fPresent)
+ {
+ Assert(pThis->aSections[enmSect].cb);
+ return VINF_SUCCESS;
+ }
+ if (!pThis->aSections[enmSect].cb)
+ return VINF_SUCCESS;
+
+ /*
+ * Sections must be readable with the current image interface.
+ */
+ if (pThis->aSections[enmSect].offFile < 0)
+ return VERR_OUT_OF_RANGE;
+
+ /*
+ * Do the job.
+ */
+ return pThis->pDbgInfoMod->pImgVt->pfnMapPart(pThis->pDbgInfoMod,
+ pThis->aSections[enmSect].iDbgInfo,
+ pThis->aSections[enmSect].offFile,
+ pThis->aSections[enmSect].cb,
+ &pThis->aSections[enmSect].pv);
+}
+
+
+#ifdef SOME_UNUSED_FUNCTION
+/**
+ * Unloads a DWARF section previously mapped by rtDbgModDwarfLoadSection.
+ *
+ * @returns IPRT status code.
+ * @param pThis The DWARF instance.
+ * @param enmSect The section to unload.
+ */
+static int rtDbgModDwarfUnloadSection(PRTDBGMODDWARF pThis, krtDbgModDwarfSect enmSect)
+{
+ if (!pThis->aSections[enmSect].pv)
+ return VINF_SUCCESS;
+
+ int rc = pThis->pDbgInfoMod->pImgVt->pfnUnmapPart(pThis->pDbgInfoMod, pThis->aSections[enmSect].cb, &pThis->aSections[enmSect].pv);
+ AssertRC(rc);
+ return rc;
+}
+#endif
+
+
+/**
+ * Converts to UTF-8 or otherwise makes sure it's valid UTF-8.
+ *
+ * @returns IPRT status code.
+ * @param pThis The DWARF instance.
+ * @param ppsz Pointer to the string pointer. May be
+ * reallocated (RTStr*).
+ */
+static int rtDbgModDwarfStringToUtf8(PRTDBGMODDWARF pThis, char **ppsz)
+{
+ /** @todo DWARF & UTF-8. */
+ NOREF(pThis);
+ RTStrPurgeEncoding(*ppsz);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Convers a link address into a segment+offset or RVA.
+ *
+ * @returns IPRT status code.
+ * @param pThis The DWARF instance.
+ * @param uSegment The segment, 0 if not applicable.
+ * @param LinkAddress The address to convert..
+ * @param piSeg The segment index.
+ * @param poffSeg Where to return the segment offset.
+ */
+static int rtDbgModDwarfLinkAddressToSegOffset(PRTDBGMODDWARF pThis, RTSEL uSegment, uint64_t LinkAddress,
+ PRTDBGSEGIDX piSeg, PRTLDRADDR poffSeg)
+{
+ if (pThis->paSegs)
+ {
+ PRTDBGDWARFSEG pSeg = rtDbgModDwarfFindSegment(pThis, uSegment);
+ if (pSeg)
+ {
+ *piSeg = pSeg - pThis->paSegs;
+ *poffSeg = LinkAddress;
+ return VINF_SUCCESS;
+ }
+ }
+
+ if (pThis->fUseLinkAddress)
+ return pThis->pImgMod->pImgVt->pfnLinkAddressToSegOffset(pThis->pImgMod, LinkAddress, piSeg, poffSeg);
+
+ /* If we have a non-zero segment number, assume it's correct for now.
+ This helps loading watcom linked LX drivers. */
+ if (uSegment > 0)
+ {
+ *piSeg = uSegment - 1;
+ *poffSeg = LinkAddress;
+ return VINF_SUCCESS;
+ }
+
+ return pThis->pImgMod->pImgVt->pfnRvaToSegOffset(pThis->pImgMod, LinkAddress, piSeg, poffSeg);
+}
+
+
+/**
+ * Converts a segment+offset address into an RVA.
+ *
+ * @returns IPRT status code.
+ * @param pThis The DWARF instance.
+ * @param idxSegment The segment index.
+ * @param offSegment The segment offset.
+ * @param puRva Where to return the calculated RVA.
+ */
+static int rtDbgModDwarfSegOffsetToRva(PRTDBGMODDWARF pThis, RTDBGSEGIDX idxSegment, uint64_t offSegment, PRTUINTPTR puRva)
+{
+ if (pThis->paSegs)
+ {
+ PRTDBGDWARFSEG pSeg = rtDbgModDwarfFindSegment(pThis, idxSegment);
+ if (pSeg)
+ {
+ *puRva = pSeg->uBaseAddr + offSegment;
+ return VINF_SUCCESS;
+ }
+ }
+
+ RTUINTPTR uRva = RTDbgModSegmentRva(pThis->pImgMod, idxSegment);
+ if (uRva != RTUINTPTR_MAX)
+ {
+ *puRva = uRva + offSegment;
+ return VINF_SUCCESS;
+ }
+ return VERR_INVALID_POINTER;
+}
+
+/**
+ * Converts a segment+offset address into an RVA.
+ *
+ * @returns IPRT status code.
+ * @param pThis The DWARF instance.
+ * @param uRva The RVA to convert.
+ * @param pidxSegment Where to return the segment index.
+ * @param poffSegment Where to return the segment offset.
+ */
+static int rtDbgModDwarfRvaToSegOffset(PRTDBGMODDWARF pThis, RTUINTPTR uRva, RTDBGSEGIDX *pidxSegment, uint64_t *poffSegment)
+{
+ RTUINTPTR offSeg = 0;
+ RTDBGSEGIDX idxSeg = RTDbgModRvaToSegOff(pThis->pImgMod, uRva, &offSeg);
+ if (idxSeg != NIL_RTDBGSEGIDX)
+ {
+ *pidxSegment = idxSeg;
+ *poffSegment = offSeg;
+ return VINF_SUCCESS;
+ }
+ return VERR_INVALID_POINTER;
+}
+
+
+
+/*
+ *
+ * DWARF Cursor.
+ * DWARF Cursor.
+ * DWARF Cursor.
+ *
+ */
+
+
+/**
+ * Reads a 8-bit unsigned integer and advances the cursor.
+ *
+ * @returns 8-bit unsigned integer. On error RTDWARFCURSOR::rc is set and @a
+ * uErrValue is returned.
+ * @param pCursor The cursor.
+ * @param uErrValue What to return on read error.
+ */
+static uint8_t rtDwarfCursor_GetU8(PRTDWARFCURSOR pCursor, uint8_t uErrValue)
+{
+ if (pCursor->cbUnitLeft < 1)
+ {
+ pCursor->rc = VERR_DWARF_UNEXPECTED_END;
+ return uErrValue;
+ }
+
+ uint8_t u8 = pCursor->pb[0];
+ pCursor->pb += 1;
+ pCursor->cbUnitLeft -= 1;
+ pCursor->cbLeft -= 1;
+ return u8;
+}
+
+
+/**
+ * Reads a 16-bit unsigned integer and advances the cursor.
+ *
+ * @returns 16-bit unsigned integer. On error RTDWARFCURSOR::rc is set and @a
+ * uErrValue is returned.
+ * @param pCursor The cursor.
+ * @param uErrValue What to return on read error.
+ */
+static uint16_t rtDwarfCursor_GetU16(PRTDWARFCURSOR pCursor, uint16_t uErrValue)
+{
+ if (pCursor->cbUnitLeft < 2)
+ {
+ pCursor->pb += pCursor->cbUnitLeft;
+ pCursor->cbLeft -= pCursor->cbUnitLeft;
+ pCursor->cbUnitLeft = 0;
+ pCursor->rc = VERR_DWARF_UNEXPECTED_END;
+ return uErrValue;
+ }
+
+ uint16_t u16 = RT_MAKE_U16(pCursor->pb[0], pCursor->pb[1]);
+ pCursor->pb += 2;
+ pCursor->cbUnitLeft -= 2;
+ pCursor->cbLeft -= 2;
+ if (!pCursor->fNativEndian)
+ u16 = RT_BSWAP_U16(u16);
+ return u16;
+}
+
+
+/**
+ * Reads a 32-bit unsigned integer and advances the cursor.
+ *
+ * @returns 32-bit unsigned integer. On error RTDWARFCURSOR::rc is set and @a
+ * uErrValue is returned.
+ * @param pCursor The cursor.
+ * @param uErrValue What to return on read error.
+ */
+static uint32_t rtDwarfCursor_GetU32(PRTDWARFCURSOR pCursor, uint32_t uErrValue)
+{
+ if (pCursor->cbUnitLeft < 4)
+ {
+ pCursor->pb += pCursor->cbUnitLeft;
+ pCursor->cbLeft -= pCursor->cbUnitLeft;
+ pCursor->cbUnitLeft = 0;
+ pCursor->rc = VERR_DWARF_UNEXPECTED_END;
+ return uErrValue;
+ }
+
+ uint32_t u32 = RT_MAKE_U32_FROM_U8(pCursor->pb[0], pCursor->pb[1], pCursor->pb[2], pCursor->pb[3]);
+ pCursor->pb += 4;
+ pCursor->cbUnitLeft -= 4;
+ pCursor->cbLeft -= 4;
+ if (!pCursor->fNativEndian)
+ u32 = RT_BSWAP_U32(u32);
+ return u32;
+}
+
+
+/**
+ * Reads a 64-bit unsigned integer and advances the cursor.
+ *
+ * @returns 64-bit unsigned integer. On error RTDWARFCURSOR::rc is set and @a
+ * uErrValue is returned.
+ * @param pCursor The cursor.
+ * @param uErrValue What to return on read error.
+ */
+static uint64_t rtDwarfCursor_GetU64(PRTDWARFCURSOR pCursor, uint64_t uErrValue)
+{
+ if (pCursor->cbUnitLeft < 8)
+ {
+ pCursor->pb += pCursor->cbUnitLeft;
+ pCursor->cbLeft -= pCursor->cbUnitLeft;
+ pCursor->cbUnitLeft = 0;
+ pCursor->rc = VERR_DWARF_UNEXPECTED_END;
+ return uErrValue;
+ }
+
+ uint64_t u64 = RT_MAKE_U64_FROM_U8(pCursor->pb[0], pCursor->pb[1], pCursor->pb[2], pCursor->pb[3],
+ pCursor->pb[4], pCursor->pb[5], pCursor->pb[6], pCursor->pb[7]);
+ pCursor->pb += 8;
+ pCursor->cbUnitLeft -= 8;
+ pCursor->cbLeft -= 8;
+ if (!pCursor->fNativEndian)
+ u64 = RT_BSWAP_U64(u64);
+ return u64;
+}
+
+
+/**
+ * Reads an unsigned LEB128 encoded number.
+ *
+ * @returns unsigned 64-bit number. On error RTDWARFCURSOR::rc is set and @a
+ * uErrValue is returned.
+ * @param pCursor The cursor.
+ * @param uErrValue The value to return on error.
+ */
+static uint64_t rtDwarfCursor_GetULeb128(PRTDWARFCURSOR pCursor, uint64_t uErrValue)
+{
+ if (pCursor->cbUnitLeft < 1)
+ {
+ pCursor->rc = VERR_DWARF_UNEXPECTED_END;
+ return uErrValue;
+ }
+
+ /*
+ * Special case - single byte.
+ */
+ uint8_t b = pCursor->pb[0];
+ if (!(b & 0x80))
+ {
+ pCursor->pb += 1;
+ pCursor->cbUnitLeft -= 1;
+ pCursor->cbLeft -= 1;
+ return b;
+ }
+
+ /*
+ * Generic case.
+ */
+ /* Decode. */
+ uint32_t off = 1;
+ uint64_t u64Ret = b & 0x7f;
+ do
+ {
+ if (off == pCursor->cbUnitLeft)
+ {
+ pCursor->rc = VERR_DWARF_UNEXPECTED_END;
+ u64Ret = uErrValue;
+ break;
+ }
+ b = pCursor->pb[off];
+ u64Ret |= (b & 0x7f) << off * 7;
+ off++;
+ } while (b & 0x80);
+
+ /* Update the cursor. */
+ pCursor->pb += off;
+ pCursor->cbUnitLeft -= off;
+ pCursor->cbLeft -= off;
+
+ /* Check the range. */
+ uint32_t cBits = off * 7;
+ if (cBits > 64)
+ {
+ pCursor->rc = VERR_DWARF_LEB_OVERFLOW;
+ u64Ret = uErrValue;
+ }
+
+ return u64Ret;
+}
+
+
+/**
+ * Reads a signed LEB128 encoded number.
+ *
+ * @returns signed 64-bit number. On error RTDWARFCURSOR::rc is set and @a
+ * uErrValue is returned.
+ * @param pCursor The cursor.
+ * @param sErrValue The value to return on error.
+ */
+static int64_t rtDwarfCursor_GetSLeb128(PRTDWARFCURSOR pCursor, int64_t sErrValue)
+{
+ if (pCursor->cbUnitLeft < 1)
+ {
+ pCursor->rc = VERR_DWARF_UNEXPECTED_END;
+ return sErrValue;
+ }
+
+ /*
+ * Special case - single byte.
+ */
+ uint8_t b = pCursor->pb[0];
+ if (!(b & 0x80))
+ {
+ pCursor->pb += 1;
+ pCursor->cbUnitLeft -= 1;
+ pCursor->cbLeft -= 1;
+ if (b & 0x40)
+ b |= 0x80;
+ return (int8_t)b;
+ }
+
+ /*
+ * Generic case.
+ */
+ /* Decode it. */
+ uint32_t off = 1;
+ uint64_t u64Ret = b & 0x7f;
+ do
+ {
+ if (off == pCursor->cbUnitLeft)
+ {
+ pCursor->rc = VERR_DWARF_UNEXPECTED_END;
+ u64Ret = (uint64_t)sErrValue;
+ break;
+ }
+ b = pCursor->pb[off];
+ u64Ret |= (b & 0x7f) << off * 7;
+ off++;
+ } while (b & 0x80);
+
+ /* Update cursor. */
+ pCursor->pb += off;
+ pCursor->cbUnitLeft -= off;
+ pCursor->cbLeft -= off;
+
+ /* Check the range. */
+ uint32_t cBits = off * 7;
+ if (cBits > 64)
+ {
+ pCursor->rc = VERR_DWARF_LEB_OVERFLOW;
+ u64Ret = (uint64_t)sErrValue;
+ }
+ /* Sign extend the value. */
+ else if (u64Ret & RT_BIT_64(cBits - 1))
+ u64Ret |= ~(RT_BIT_64(cBits - 1) - 1);
+
+ return (int64_t)u64Ret;
+}
+
+
+/**
+ * Reads an unsigned LEB128 encoded number, max 32-bit width.
+ *
+ * @returns unsigned 32-bit number. On error RTDWARFCURSOR::rc is set and @a
+ * uErrValue is returned.
+ * @param pCursor The cursor.
+ * @param uErrValue The value to return on error.
+ */
+static uint32_t rtDwarfCursor_GetULeb128AsU32(PRTDWARFCURSOR pCursor, uint32_t uErrValue)
+{
+ uint64_t u64 = rtDwarfCursor_GetULeb128(pCursor, uErrValue);
+ if (u64 > UINT32_MAX)
+ {
+ pCursor->rc = VERR_DWARF_LEB_OVERFLOW;
+ return uErrValue;
+ }
+ return (uint32_t)u64;
+}
+
+
+/**
+ * Reads a signed LEB128 encoded number, max 32-bit width.
+ *
+ * @returns signed 32-bit number. On error RTDWARFCURSOR::rc is set and @a
+ * uErrValue is returned.
+ * @param pCursor The cursor.
+ * @param sErrValue The value to return on error.
+ */
+static int32_t rtDwarfCursor_GetSLeb128AsS32(PRTDWARFCURSOR pCursor, int32_t sErrValue)
+{
+ int64_t s64 = rtDwarfCursor_GetSLeb128(pCursor, sErrValue);
+ if (s64 > INT32_MAX || s64 < INT32_MIN)
+ {
+ pCursor->rc = VERR_DWARF_LEB_OVERFLOW;
+ return sErrValue;
+ }
+ return (int32_t)s64;
+}
+
+
+/**
+ * Skips a LEB128 encoded number.
+ *
+ * @returns IPRT status code.
+ * @param pCursor The cursor.
+ */
+static int rtDwarfCursor_SkipLeb128(PRTDWARFCURSOR pCursor)
+{
+ if (RT_FAILURE(pCursor->rc))
+ return pCursor->rc;
+
+ if (pCursor->cbUnitLeft < 1)
+ return pCursor->rc = VERR_DWARF_UNEXPECTED_END;
+
+ uint32_t offSkip = 1;
+ if (pCursor->pb[0] & 0x80)
+ do
+ {
+ if (offSkip == pCursor->cbUnitLeft)
+ {
+ pCursor->rc = VERR_DWARF_UNEXPECTED_END;
+ break;
+ }
+ } while (pCursor->pb[offSkip++] & 0x80);
+
+ pCursor->pb += offSkip;
+ pCursor->cbUnitLeft -= offSkip;
+ pCursor->cbLeft -= offSkip;
+ return pCursor->rc;
+}
+
+
+/**
+ * Advances the cursor a given number of bytes.
+ *
+ * @returns IPRT status code.
+ * @param pCursor The cursor.
+ * @param offSkip The number of bytes to advance.
+ */
+static int rtDwarfCursor_SkipBytes(PRTDWARFCURSOR pCursor, uint64_t offSkip)
+{
+ if (RT_FAILURE(pCursor->rc))
+ return pCursor->rc;
+ if (pCursor->cbUnitLeft < offSkip)
+ return pCursor->rc = VERR_DWARF_UNEXPECTED_END;
+
+ size_t const offSkipSizeT = (size_t)offSkip;
+ pCursor->cbUnitLeft -= offSkipSizeT;
+ pCursor->cbLeft -= offSkipSizeT;
+ pCursor->pb += offSkipSizeT;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Reads a zero terminated string, advancing the cursor beyond the terminator.
+ *
+ * @returns Pointer to the string.
+ * @param pCursor The cursor.
+ * @param pszErrValue What to return if the string isn't terminated
+ * before the end of the unit.
+ */
+static const char *rtDwarfCursor_GetSZ(PRTDWARFCURSOR pCursor, const char *pszErrValue)
+{
+ const char *pszRet = (const char *)pCursor->pb;
+ for (;;)
+ {
+ if (!pCursor->cbUnitLeft)
+ {
+ pCursor->rc = VERR_DWARF_BAD_STRING;
+ return pszErrValue;
+ }
+ pCursor->cbUnitLeft--;
+ pCursor->cbLeft--;
+ if (!*pCursor->pb++)
+ break;
+ }
+ return pszRet;
+}
+
+
+/**
+ * Reads a 1, 2, 4 or 8 byte unsigned value.
+ *
+ * @returns 64-bit unsigned value.
+ * @param pCursor The cursor.
+ * @param cbValue The value size.
+ * @param uErrValue The error value.
+ */
+static uint64_t rtDwarfCursor_GetVarSizedU(PRTDWARFCURSOR pCursor, size_t cbValue, uint64_t uErrValue)
+{
+ uint64_t u64Ret;
+ switch (cbValue)
+ {
+ case 1: u64Ret = rtDwarfCursor_GetU8( pCursor, UINT8_MAX); break;
+ case 2: u64Ret = rtDwarfCursor_GetU16(pCursor, UINT16_MAX); break;
+ case 4: u64Ret = rtDwarfCursor_GetU32(pCursor, UINT32_MAX); break;
+ case 8: u64Ret = rtDwarfCursor_GetU64(pCursor, UINT64_MAX); break;
+ default:
+ pCursor->rc = VERR_DWARF_BAD_INFO;
+ return uErrValue;
+ }
+ if (RT_FAILURE(pCursor->rc))
+ return uErrValue;
+ return u64Ret;
+}
+
+
+#if 0 /* unused */
+/**
+ * Gets the pointer to a variable size block and advances the cursor.
+ *
+ * @returns Pointer to the block at the current cursor location. On error
+ * RTDWARFCURSOR::rc is set and NULL returned.
+ * @param pCursor The cursor.
+ * @param cbBlock The block size.
+ */
+static const uint8_t *rtDwarfCursor_GetBlock(PRTDWARFCURSOR pCursor, uint32_t cbBlock)
+{
+ if (cbBlock > pCursor->cbUnitLeft)
+ {
+ pCursor->rc = VERR_DWARF_UNEXPECTED_END;
+ return NULL;
+ }
+
+ uint8_t const *pb = &pCursor->pb[0];
+ pCursor->pb += cbBlock;
+ pCursor->cbUnitLeft -= cbBlock;
+ pCursor->cbLeft -= cbBlock;
+ return pb;
+}
+#endif
+
+
+/**
+ * Reads an unsigned DWARF half number.
+ *
+ * @returns The number. On error RTDWARFCURSOR::rc is set and @a
+ * uErrValue is returned.
+ * @param pCursor The cursor.
+ * @param uErrValue What to return on error.
+ */
+static uint16_t rtDwarfCursor_GetUHalf(PRTDWARFCURSOR pCursor, uint16_t uErrValue)
+{
+ return rtDwarfCursor_GetU16(pCursor, uErrValue);
+}
+
+
+/**
+ * Reads an unsigned DWARF byte number.
+ *
+ * @returns The number. On error RTDWARFCURSOR::rc is set and @a
+ * uErrValue is returned.
+ * @param pCursor The cursor.
+ * @param uErrValue What to return on error.
+ */
+static uint8_t rtDwarfCursor_GetUByte(PRTDWARFCURSOR pCursor, uint8_t uErrValue)
+{
+ return rtDwarfCursor_GetU8(pCursor, uErrValue);
+}
+
+
+/**
+ * Reads a signed DWARF byte number.
+ *
+ * @returns The number. On error RTDWARFCURSOR::rc is set and @a
+ * uErrValue is returned.
+ * @param pCursor The cursor.
+ * @param iErrValue What to return on error.
+ */
+static int8_t rtDwarfCursor_GetSByte(PRTDWARFCURSOR pCursor, int8_t iErrValue)
+{
+ return (int8_t)rtDwarfCursor_GetU8(pCursor, (uint8_t)iErrValue);
+}
+
+
+/**
+ * Reads a unsigned DWARF offset value.
+ *
+ * @returns The value. On error RTDWARFCURSOR::rc is set and @a
+ * uErrValue is returned.
+ * @param pCursor The cursor.
+ * @param uErrValue What to return on error.
+ */
+static uint64_t rtDwarfCursor_GetUOff(PRTDWARFCURSOR pCursor, uint64_t uErrValue)
+{
+ if (pCursor->f64bitDwarf)
+ return rtDwarfCursor_GetU64(pCursor, uErrValue);
+ return rtDwarfCursor_GetU32(pCursor, (uint32_t)uErrValue);
+}
+
+
+/**
+ * Reads a unsigned DWARF native offset value.
+ *
+ * @returns The value. On error RTDWARFCURSOR::rc is set and @a
+ * uErrValue is returned.
+ * @param pCursor The cursor.
+ * @param uErrValue What to return on error.
+ */
+static uint64_t rtDwarfCursor_GetNativeUOff(PRTDWARFCURSOR pCursor, uint64_t uErrValue)
+{
+ switch (pCursor->cbNativeAddr)
+ {
+ case 1: return rtDwarfCursor_GetU8(pCursor, (uint8_t )uErrValue);
+ case 2: return rtDwarfCursor_GetU16(pCursor, (uint16_t)uErrValue);
+ case 4: return rtDwarfCursor_GetU32(pCursor, (uint32_t)uErrValue);
+ case 8: return rtDwarfCursor_GetU64(pCursor, uErrValue);
+ default:
+ pCursor->rc = VERR_INTERNAL_ERROR_2;
+ return uErrValue;
+ }
+}
+
+
+/**
+ * Reads a 1, 2, 4 or 8 byte unsigned value.
+ *
+ * @returns 64-bit unsigned value.
+ * @param pCursor The cursor.
+ * @param bPtrEnc The pointer encoding.
+ * @param uErrValue The error value.
+ */
+static uint64_t rtDwarfCursor_GetPtrEnc(PRTDWARFCURSOR pCursor, uint8_t bPtrEnc, uint64_t uErrValue)
+{
+ uint64_t u64Ret;
+ switch (bPtrEnc & DW_EH_PE_FORMAT_MASK)
+ {
+ case DW_EH_PE_ptr:
+ u64Ret = rtDwarfCursor_GetNativeUOff(pCursor, uErrValue);
+ break;
+ case DW_EH_PE_uleb128:
+ u64Ret = rtDwarfCursor_GetULeb128(pCursor, uErrValue);
+ break;
+ case DW_EH_PE_udata2:
+ u64Ret = rtDwarfCursor_GetU16(pCursor, UINT16_MAX);
+ break;
+ case DW_EH_PE_udata4:
+ u64Ret = rtDwarfCursor_GetU32(pCursor, UINT32_MAX);
+ break;
+ case DW_EH_PE_udata8:
+ u64Ret = rtDwarfCursor_GetU64(pCursor, UINT64_MAX);
+ break;
+ case DW_EH_PE_sleb128:
+ u64Ret = rtDwarfCursor_GetSLeb128(pCursor, uErrValue);
+ break;
+ case DW_EH_PE_sdata2:
+ u64Ret = (int64_t)(int16_t)rtDwarfCursor_GetU16(pCursor, UINT16_MAX);
+ break;
+ case DW_EH_PE_sdata4:
+ u64Ret = (int64_t)(int32_t)rtDwarfCursor_GetU32(pCursor, UINT32_MAX);
+ break;
+ case DW_EH_PE_sdata8:
+ u64Ret = rtDwarfCursor_GetU64(pCursor, UINT64_MAX);
+ break;
+ default:
+ pCursor->rc = VERR_DWARF_BAD_INFO;
+ return uErrValue;
+ }
+ if (RT_FAILURE(pCursor->rc))
+ return uErrValue;
+ return u64Ret;
+}
+
+
+/**
+ * Gets the unit length, updating the unit length member and DWARF bitness
+ * members of the cursor.
+ *
+ * @returns The unit length.
+ * @param pCursor The cursor.
+ */
+static uint64_t rtDwarfCursor_GetInitialLength(PRTDWARFCURSOR pCursor)
+{
+ /*
+ * Read the initial length.
+ */
+ pCursor->cbUnitLeft = pCursor->cbLeft;
+ uint64_t cbUnit = rtDwarfCursor_GetU32(pCursor, 0);
+ if (cbUnit != UINT32_C(0xffffffff))
+ pCursor->f64bitDwarf = false;
+ else
+ {
+ pCursor->f64bitDwarf = true;
+ cbUnit = rtDwarfCursor_GetU64(pCursor, 0);
+ }
+
+
+ /*
+ * Set the unit length, quitely fixing bad lengths.
+ */
+ pCursor->cbUnitLeft = (size_t)cbUnit;
+ if ( pCursor->cbUnitLeft > pCursor->cbLeft
+ || pCursor->cbUnitLeft != cbUnit)
+ pCursor->cbUnitLeft = pCursor->cbLeft;
+
+ return cbUnit;
+}
+
+
+/**
+ * Calculates the section offset corresponding to the current cursor position.
+ *
+ * @returns 32-bit section offset. If out of range, RTDWARFCURSOR::rc will be
+ * set and UINT32_MAX returned.
+ * @param pCursor The cursor.
+ */
+static uint32_t rtDwarfCursor_CalcSectOffsetU32(PRTDWARFCURSOR pCursor)
+{
+ size_t off = pCursor->pb - pCursor->pbStart;
+ uint32_t offRet = (uint32_t)off;
+ if (offRet != off)
+ {
+ AssertFailed();
+ pCursor->rc = VERR_OUT_OF_RANGE;
+ offRet = UINT32_MAX;
+ }
+ return offRet;
+}
+
+
+/**
+ * Calculates an absolute cursor position from one relative to the current
+ * cursor position.
+ *
+ * @returns The absolute cursor position.
+ * @param pCursor The cursor.
+ * @param offRelative The relative position. Must be a positive
+ * offset.
+ */
+static uint8_t const *rtDwarfCursor_CalcPos(PRTDWARFCURSOR pCursor, size_t offRelative)
+{
+ if (offRelative > pCursor->cbUnitLeft)
+ {
+ Log(("rtDwarfCursor_CalcPos: bad position %#zx, cbUnitLeft=%#zu\n", offRelative, pCursor->cbUnitLeft));
+ pCursor->rc = VERR_DWARF_BAD_POS;
+ return NULL;
+ }
+ return pCursor->pb + offRelative;
+}
+
+
+/**
+ * Advances the cursor to the given position.
+ *
+ * @returns IPRT status code.
+ * @param pCursor The cursor.
+ * @param pbNewPos The new position - returned by
+ * rtDwarfCursor_CalcPos().
+ */
+static int rtDwarfCursor_AdvanceToPos(PRTDWARFCURSOR pCursor, uint8_t const *pbNewPos)
+{
+ if (RT_FAILURE(pCursor->rc))
+ return pCursor->rc;
+ AssertPtr(pbNewPos);
+ if ((uintptr_t)pbNewPos < (uintptr_t)pCursor->pb)
+ {
+ Log(("rtDwarfCursor_AdvanceToPos: bad position %p, current %p\n", pbNewPos, pCursor->pb));
+ return pCursor->rc = VERR_DWARF_BAD_POS;
+ }
+
+ uintptr_t cbAdj = (uintptr_t)pbNewPos - (uintptr_t)pCursor->pb;
+ if (RT_UNLIKELY(cbAdj > pCursor->cbUnitLeft))
+ {
+ AssertFailed();
+ pCursor->rc = VERR_DWARF_BAD_POS;
+ cbAdj = pCursor->cbUnitLeft;
+ }
+
+ pCursor->cbUnitLeft -= cbAdj;
+ pCursor->cbLeft -= cbAdj;
+ pCursor->pb += cbAdj;
+ return pCursor->rc;
+}
+
+
+/**
+ * Check if the cursor is at the end of the current DWARF unit.
+ *
+ * @retval true if at the end or a cursor error is pending.
+ * @retval false if not.
+ * @param pCursor The cursor.
+ */
+static bool rtDwarfCursor_IsAtEndOfUnit(PRTDWARFCURSOR pCursor)
+{
+ return !pCursor->cbUnitLeft || RT_FAILURE(pCursor->rc);
+}
+
+
+/**
+ * Skips to the end of the current unit.
+ *
+ * @returns IPRT status code.
+ * @param pCursor The cursor.
+ */
+static int rtDwarfCursor_SkipUnit(PRTDWARFCURSOR pCursor)
+{
+ pCursor->pb += pCursor->cbUnitLeft;
+ pCursor->cbLeft -= pCursor->cbUnitLeft;
+ pCursor->cbUnitLeft = 0;
+ return pCursor->rc;
+}
+
+
+/**
+ * Check if the cursor is at the end of the section (or whatever the cursor is
+ * processing).
+ *
+ * @retval true if at the end or a cursor error is pending.
+ * @retval false if not.
+ * @param pCursor The cursor.
+ */
+static bool rtDwarfCursor_IsAtEnd(PRTDWARFCURSOR pCursor)
+{
+ return !pCursor->cbLeft || RT_FAILURE(pCursor->rc);
+}
+
+
+/**
+ * Initialize a section reader cursor.
+ *
+ * @returns IPRT status code.
+ * @param pCursor The cursor.
+ * @param pThis The dwarf module.
+ * @param enmSect The name of the section to read.
+ */
+static int rtDwarfCursor_Init(PRTDWARFCURSOR pCursor, PRTDBGMODDWARF pThis, krtDbgModDwarfSect enmSect)
+{
+ int rc = rtDbgModDwarfLoadSection(pThis, enmSect);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ pCursor->enmSect = enmSect;
+ pCursor->pbStart = (uint8_t const *)pThis->aSections[enmSect].pv;
+ pCursor->pb = pCursor->pbStart;
+ pCursor->cbLeft = pThis->aSections[enmSect].cb;
+ pCursor->cbUnitLeft = pCursor->cbLeft;
+ pCursor->pDwarfMod = pThis;
+ pCursor->f64bitDwarf = false;
+ /** @todo ask the image about the endian used as well as the address
+ * width. */
+ pCursor->fNativEndian = true;
+ pCursor->cbNativeAddr = 4;
+ pCursor->rc = VINF_SUCCESS;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Initialize a section reader cursor with a skip offset.
+ *
+ * @returns IPRT status code.
+ * @param pCursor The cursor.
+ * @param pThis The dwarf module.
+ * @param enmSect The name of the section to read.
+ * @param offSect The offset to skip into the section.
+ */
+static int rtDwarfCursor_InitWithOffset(PRTDWARFCURSOR pCursor, PRTDBGMODDWARF pThis,
+ krtDbgModDwarfSect enmSect, uint32_t offSect)
+{
+ if (offSect > pThis->aSections[enmSect].cb)
+ {
+ Log(("rtDwarfCursor_InitWithOffset: offSect=%#x cb=%#x enmSect=%d\n", offSect, pThis->aSections[enmSect].cb, enmSect));
+ return VERR_DWARF_BAD_POS;
+ }
+
+ int rc = rtDwarfCursor_Init(pCursor, pThis, enmSect);
+ if (RT_SUCCESS(rc))
+ {
+ /* pCursor->pbStart += offSect; - we're skipping, offsets are relative to start of section... */
+ pCursor->pb += offSect;
+ pCursor->cbLeft -= offSect;
+ pCursor->cbUnitLeft -= offSect;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Initialize a cursor for a block (subsection) retrieved from the given cursor.
+ *
+ * The parent cursor will be advanced past the block.
+ *
+ * @returns IPRT status code.
+ * @param pCursor The cursor.
+ * @param pParent The parent cursor. Will be moved by @a cbBlock.
+ * @param cbBlock The size of the block the new cursor should
+ * cover.
+ */
+static int rtDwarfCursor_InitForBlock(PRTDWARFCURSOR pCursor, PRTDWARFCURSOR pParent, uint32_t cbBlock)
+{
+ if (RT_FAILURE(pParent->rc))
+ return pParent->rc;
+ if (pParent->cbUnitLeft < cbBlock)
+ {
+ Log(("rtDwarfCursor_InitForBlock: cbUnitLeft=%#x < cbBlock=%#x \n", pParent->cbUnitLeft, cbBlock));
+ return VERR_DWARF_BAD_POS;
+ }
+
+ *pCursor = *pParent;
+ pCursor->cbLeft = cbBlock;
+ pCursor->cbUnitLeft = cbBlock;
+
+ pParent->pb += cbBlock;
+ pParent->cbLeft -= cbBlock;
+ pParent->cbUnitLeft -= cbBlock;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Initialize a reader cursor for a memory block (eh_frame).
+ *
+ * @returns IPRT status code.
+ * @param pCursor The cursor.
+ * @param pvMem The memory block.
+ * @param cbMem The size of the memory block.
+ */
+static int rtDwarfCursor_InitForMem(PRTDWARFCURSOR pCursor, void const *pvMem, size_t cbMem)
+{
+ pCursor->enmSect = krtDbgModDwarfSect_End;
+ pCursor->pbStart = (uint8_t const *)pvMem;
+ pCursor->pb = (uint8_t const *)pvMem;
+ pCursor->cbLeft = cbMem;
+ pCursor->cbUnitLeft = cbMem;
+ pCursor->pDwarfMod = NULL;
+ pCursor->f64bitDwarf = false;
+ /** @todo ask the image about the endian used as well as the address
+ * width. */
+ pCursor->fNativEndian = true;
+ pCursor->cbNativeAddr = 4;
+ pCursor->rc = VINF_SUCCESS;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Deletes a section reader initialized by rtDwarfCursor_Init.
+ *
+ * @returns @a rcOther or RTDWARCURSOR::rc.
+ * @param pCursor The section reader.
+ * @param rcOther Other error code to be returned if it indicates
+ * error or if the cursor status is OK.
+ */
+static int rtDwarfCursor_Delete(PRTDWARFCURSOR pCursor, int rcOther)
+{
+ /* ... and a drop of poison. */
+ pCursor->pb = NULL;
+ pCursor->cbLeft = ~(size_t)0;
+ pCursor->cbUnitLeft = ~(size_t)0;
+ pCursor->pDwarfMod = NULL;
+ if (RT_FAILURE(pCursor->rc) && RT_SUCCESS(rcOther))
+ rcOther = pCursor->rc;
+ pCursor->rc = VERR_INTERNAL_ERROR_4;
+ return rcOther;
+}
+
+
+/*
+ *
+ * DWARF Frame Unwind Information.
+ * DWARF Frame Unwind Information.
+ * DWARF Frame Unwind Information.
+ *
+ */
+
+/**
+ * Common information entry (CIE) information.
+ */
+typedef struct RTDWARFCIEINFO
+{
+ /** The segment location of the CIE. */
+ uint64_t offCie;
+ /** The DWARF version. */
+ uint8_t uDwarfVer;
+ /** The address pointer encoding. */
+ uint8_t bAddressPtrEnc;
+ /** The segment size (v4). */
+ uint8_t cbSegment;
+ /** The return register column. UINT8_MAX if default register. */
+ uint8_t bRetReg;
+ /** The LSDA pointer encoding. */
+ uint8_t bLsdaPtrEnc;
+
+ /** Set if the EH data field is present ('eh'). */
+ bool fHasEhData : 1;
+ /** Set if there is an augmentation data size ('z'). */
+ bool fHasAugmentationSize : 1;
+ /** Set if the augmentation data contains a LSDA (pointer size byte in CIE,
+ * pointer in FDA) ('L'). */
+ bool fHasLanguageSpecificDataArea : 1;
+ /** Set if the augmentation data contains a personality routine
+ * (pointer size + pointer) ('P'). */
+ bool fHasPersonalityRoutine : 1;
+ /** Set if the augmentation data contains the address encoding . */
+ bool fHasAddressEnc : 1;
+ /** Set if signal frame. */
+ bool fIsSignalFrame : 1;
+ /** Set if we've encountered unknown augmentation data. This
+ * means the CIE is incomplete and cannot be used. */
+ bool fHasUnknowAugmentation : 1;
+
+ /** Copy of the augmentation string. */
+ const char *pszAugmentation;
+
+ /** Code alignment factor for the instruction. */
+ uint64_t uCodeAlignFactor;
+ /** Data alignment factor for the instructions. */
+ int64_t iDataAlignFactor;
+
+ /** Pointer to the instruction sequence. */
+ uint8_t const *pbInstructions;
+ /** The length of the instruction sequence. */
+ size_t cbInstructions;
+} RTDWARFCIEINFO;
+/** Pointer to CIE info. */
+typedef RTDWARFCIEINFO *PRTDWARFCIEINFO;
+/** Pointer to const CIE info. */
+typedef RTDWARFCIEINFO const *PCRTDWARFCIEINFO;
+
+
+/** Number of registers we care about.
+ * @note We're currently not expecting to be decoding ppc, arm, ia64 or such,
+ * only x86 and x86_64. We can easily increase the column count. */
+#define RTDWARFCF_MAX_REGISTERS 96
+
+
+/**
+ * Call frame state row.
+ */
+typedef struct RTDWARFCFROW
+{
+ /** Stack worked by DW_CFA_remember_state and DW_CFA_restore_state. */
+ struct RTDWARFCFROW *pNextOnStack;
+
+ /** @name CFA - Canonical frame address expression.
+ * Since there are partial CFA instructions, we cannot be lazy like with the
+ * register but keep register+offset around. For DW_CFA_def_cfa_expression
+ * we just take down the program location, though.
+ * @{ */
+ /** Pointer to DW_CFA_def_cfa_expression instruction, NULL if reg+offset. */
+ uint8_t const *pbCfaExprInstr;
+ /** The CFA register offset. */
+ int64_t offCfaReg;
+ /** The CFA base register number. */
+ uint16_t uCfaBaseReg;
+ /** Set if we've got a valid CFA definition. */
+ bool fCfaDefined : 1;
+ /** @} */
+
+ /** Set if on the heap and needs freeing. */
+ bool fOnHeap : 1;
+ /** Pointer to the instructions bytes defining registers.
+ * NULL means */
+ uint8_t const *apbRegInstrs[RTDWARFCF_MAX_REGISTERS];
+} RTDWARFCFROW;
+typedef RTDWARFCFROW *PRTDWARFCFROW;
+typedef RTDWARFCFROW const *PCRTDWARFCFROW;
+
+/** Row program execution state. */
+typedef struct RTDWARFCFEXEC
+{
+ PRTDWARFCFROW pRow;
+ /** Number of PC bytes left to advance before we get a hit. */
+ uint64_t cbLeftToAdvance;
+ /** Number of pushed rows. */
+ uint32_t cPushes;
+ /** Set if little endian, clear if big endian. */
+ bool fLittleEndian;
+ /** The CIE. */
+ PCRTDWARFCIEINFO pCie;
+ /** The program counter value for the FDE. Subjected to segment.
+ * Needed for DW_CFA_set_loc. */
+ uint64_t uPcBegin;
+ /** The offset relative to uPcBegin for which we're searching for a row.
+ * Needed for DW_CFA_set_loc. */
+ uint64_t offInRange;
+} RTDWARFCFEXEC;
+typedef RTDWARFCFEXEC *PRTDWARFCFEXEC;
+
+
+/* Set of macros for getting and skipping operands. */
+#define SKIP_ULEB128_OR_LEB128() \
+ do \
+ { \
+ AssertReturn(offInstr < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO); \
+ } while (pbInstr[offInstr++] & 0x80)
+
+#define GET_ULEB128_AS_U14(a_uDst) \
+ do \
+ { \
+ AssertReturn(offInstr < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO); \
+ uint8_t b = pbInstr[offInstr++]; \
+ (a_uDst) = b & 0x7f; \
+ if (b & 0x80) \
+ { \
+ AssertReturn(offInstr < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO); \
+ b = pbInstr[offInstr++]; \
+ AssertReturn(!(b & 0x80), VERR_DBG_MALFORMED_UNWIND_INFO); \
+ (a_uDst) |= (uint16_t)b << 7; \
+ } \
+ } while (0)
+#define GET_ULEB128_AS_U63(a_uDst) \
+ do \
+ { \
+ AssertReturn(offInstr < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO); \
+ uint8_t b = pbInstr[offInstr++]; \
+ (a_uDst) = b & 0x7f; \
+ if (b & 0x80) \
+ { \
+ unsigned cShift = 7; \
+ do \
+ { \
+ AssertReturn(offInstr < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO); \
+ AssertReturn(cShift < 63, VERR_DWARF_LEB_OVERFLOW); \
+ b = pbInstr[offInstr++]; \
+ (a_uDst) |= (uint16_t)(b & 0x7f) << cShift; \
+ cShift += 7; \
+ } while (b & 0x80); \
+ } \
+ } while (0)
+#define GET_LEB128_AS_I63(a_uDst) \
+ do \
+ { \
+ AssertReturn(offInstr < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO); \
+ uint8_t b = pbInstr[offInstr++]; \
+ if (!(b & 0x80)) \
+ (a_uDst) = !(b & 0x40) ? b : (int64_t)(int8_t)(b | 0x80); \
+ else \
+ { \
+ /* Read value into unsigned variable: */ \
+ unsigned cShift = 7; \
+ uint64_t uTmp = b & 0x7f; \
+ do \
+ { \
+ AssertReturn(offInstr < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO); \
+ AssertReturn(cShift < 63, VERR_DWARF_LEB_OVERFLOW); \
+ b = pbInstr[offInstr++]; \
+ uTmp |= (uint16_t)(b & 0x7f) << cShift; \
+ cShift += 7; \
+ } while (b & 0x80); \
+ /* Sign extend before setting the destination value: */ \
+ cShift -= 7 + 1; \
+ if (uTmp & RT_BIT_64(cShift)) \
+ uTmp |= ~(RT_BIT_64(cShift) - 1); \
+ (a_uDst) = (int64_t)uTmp; \
+ } \
+ } while (0)
+
+#define SKIP_BLOCK() \
+ do \
+ { \
+ uint16_t cbBlock; \
+ GET_ULEB128_AS_U14(cbBlock); \
+ AssertReturn(offInstr + cbBlock <= cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO); \
+ offInstr += cbBlock; \
+ } while (0)
+
+
+static int rtDwarfUnwind_Execute(PRTDWARFCFEXEC pExecState, uint8_t const *pbInstr, uint32_t cbInstr)
+{
+ PRTDWARFCFROW pRow = pExecState->pRow;
+ for (uint32_t offInstr = 0; offInstr < cbInstr;)
+ {
+ /*
+ * Instruction switches.
+ */
+ uint8_t const bInstr = pbInstr[offInstr++];
+ switch (bInstr & DW_CFA_high_bit_mask)
+ {
+ case DW_CFA_advance_loc:
+ {
+ uint8_t const cbAdvance = bInstr & ~DW_CFA_high_bit_mask;
+ if (cbAdvance > pExecState->cbLeftToAdvance)
+ return VINF_SUCCESS;
+ pExecState->cbLeftToAdvance -= cbAdvance;
+ break;
+ }
+
+ case DW_CFA_offset:
+ {
+ uint8_t iReg = bInstr & ~DW_CFA_high_bit_mask;
+ if (iReg < RT_ELEMENTS(pRow->apbRegInstrs))
+ pRow->apbRegInstrs[iReg] = &pbInstr[offInstr - 1];
+ SKIP_ULEB128_OR_LEB128();
+ break;
+ }
+
+ case 0:
+ switch (bInstr)
+ {
+ case DW_CFA_nop:
+ break;
+
+ /*
+ * Register instructions.
+ */
+ case DW_CFA_register:
+ case DW_CFA_offset_extended:
+ case DW_CFA_offset_extended_sf:
+ case DW_CFA_val_offset:
+ case DW_CFA_val_offset_sf:
+ {
+ uint8_t const * const pbCurInstr = &pbInstr[offInstr - 1];
+ uint16_t iReg;
+ GET_ULEB128_AS_U14(iReg);
+ if (iReg < RT_ELEMENTS(pRow->apbRegInstrs))
+ pRow->apbRegInstrs[iReg] = pbCurInstr;
+ SKIP_ULEB128_OR_LEB128();
+ break;
+ }
+
+ case DW_CFA_expression:
+ case DW_CFA_val_expression:
+ {
+ uint8_t const * const pbCurInstr = &pbInstr[offInstr - 1];
+ uint16_t iReg;
+ GET_ULEB128_AS_U14(iReg);
+ if (iReg < RT_ELEMENTS(pRow->apbRegInstrs))
+ pRow->apbRegInstrs[iReg] = pbCurInstr;
+ SKIP_BLOCK();
+ break;
+ }
+
+ case DW_CFA_restore_extended:
+ {
+ uint8_t const * const pbCurInstr = &pbInstr[offInstr - 1];
+ uint16_t iReg;
+ GET_ULEB128_AS_U14(iReg);
+ if (iReg < RT_ELEMENTS(pRow->apbRegInstrs))
+ pRow->apbRegInstrs[iReg] = pbCurInstr;
+ break;
+ }
+
+ case DW_CFA_undefined:
+ {
+ uint16_t iReg;
+ GET_ULEB128_AS_U14(iReg);
+ if (iReg < RT_ELEMENTS(pRow->apbRegInstrs))
+ pRow->apbRegInstrs[iReg] = NULL;
+ break;
+ }
+
+ case DW_CFA_same_value:
+ {
+ uint8_t const * const pbCurInstr = &pbInstr[offInstr - 1];
+ uint16_t iReg;
+ GET_ULEB128_AS_U14(iReg);
+ if (iReg < RT_ELEMENTS(pRow->apbRegInstrs))
+ pRow->apbRegInstrs[iReg] = pbCurInstr;
+ break;
+ }
+
+
+ /*
+ * CFA instructions.
+ */
+ case DW_CFA_def_cfa:
+ {
+ GET_ULEB128_AS_U14(pRow->uCfaBaseReg);
+ uint64_t offCfaReg;
+ GET_ULEB128_AS_U63(offCfaReg);
+ pRow->offCfaReg = offCfaReg;
+ pRow->pbCfaExprInstr = NULL;
+ pRow->fCfaDefined = true;
+ break;
+ }
+
+ case DW_CFA_def_cfa_register:
+ {
+ GET_ULEB128_AS_U14(pRow->uCfaBaseReg);
+ pRow->pbCfaExprInstr = NULL;
+ pRow->fCfaDefined = true;
+ /* Leaves offCfaReg as is. */
+ break;
+ }
+
+ case DW_CFA_def_cfa_offset:
+ {
+ uint64_t offCfaReg;
+ GET_ULEB128_AS_U63(offCfaReg);
+ pRow->offCfaReg = offCfaReg;
+ pRow->pbCfaExprInstr = NULL;
+ pRow->fCfaDefined = true;
+ /* Leaves uCfaBaseReg as is. */
+ break;
+ }
+
+ case DW_CFA_def_cfa_sf:
+ GET_ULEB128_AS_U14(pRow->uCfaBaseReg);
+ GET_LEB128_AS_I63(pRow->offCfaReg);
+ pRow->pbCfaExprInstr = NULL;
+ pRow->fCfaDefined = true;
+ break;
+
+ case DW_CFA_def_cfa_offset_sf:
+ GET_LEB128_AS_I63(pRow->offCfaReg);
+ pRow->pbCfaExprInstr = NULL;
+ pRow->fCfaDefined = true;
+ /* Leaves uCfaBaseReg as is. */
+ break;
+
+ case DW_CFA_def_cfa_expression:
+ pRow->pbCfaExprInstr = &pbInstr[offInstr - 1];
+ pRow->fCfaDefined = true;
+ SKIP_BLOCK();
+ break;
+
+ /*
+ * Less likely instructions:
+ */
+ case DW_CFA_advance_loc1:
+ {
+ AssertReturn(offInstr < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO);
+ uint8_t const cbAdvance = pbInstr[offInstr++];
+ if (cbAdvance > pExecState->cbLeftToAdvance)
+ return VINF_SUCCESS;
+ pExecState->cbLeftToAdvance -= cbAdvance;
+ break;
+ }
+
+ case DW_CFA_advance_loc2:
+ {
+ AssertReturn(offInstr + 1 < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO);
+ uint16_t const cbAdvance = pExecState->fLittleEndian
+ ? RT_MAKE_U16(pbInstr[offInstr], pbInstr[offInstr + 1])
+ : RT_MAKE_U16(pbInstr[offInstr + 1], pbInstr[offInstr]);
+ if (cbAdvance > pExecState->cbLeftToAdvance)
+ return VINF_SUCCESS;
+ pExecState->cbLeftToAdvance -= cbAdvance;
+ offInstr += 2;
+ break;
+ }
+
+ case DW_CFA_advance_loc4:
+ {
+ AssertReturn(offInstr + 3 < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO);
+ uint32_t const cbAdvance = pExecState->fLittleEndian
+ ? RT_MAKE_U32_FROM_U8(pbInstr[offInstr + 0], pbInstr[offInstr + 1],
+ pbInstr[offInstr + 2], pbInstr[offInstr + 3])
+ : RT_MAKE_U32_FROM_U8(pbInstr[offInstr + 3], pbInstr[offInstr + 2],
+ pbInstr[offInstr + 1], pbInstr[offInstr + 0]);
+ if (cbAdvance > pExecState->cbLeftToAdvance)
+ return VINF_SUCCESS;
+ pExecState->cbLeftToAdvance -= cbAdvance;
+ offInstr += 4;
+ break;
+ }
+
+ /*
+ * This bugger is really annoying and probably never used.
+ */
+ case DW_CFA_set_loc:
+ {
+ /* Ignore the segment number. */
+ if (pExecState->pCie->cbSegment)
+ {
+ offInstr += pExecState->pCie->cbSegment;
+ AssertReturn(offInstr < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO);
+ }
+
+ /* Retrieve the address. sigh. */
+ uint64_t uAddress;
+ switch (pExecState->pCie->bAddressPtrEnc & (DW_EH_PE_FORMAT_MASK | DW_EH_PE_indirect))
+ {
+ case DW_EH_PE_udata2:
+ AssertReturn(offInstr + 1 < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO);
+ if (pExecState->fLittleEndian)
+ uAddress = RT_MAKE_U16(pbInstr[offInstr], pbInstr[offInstr + 1]);
+ else
+ uAddress = RT_MAKE_U16(pbInstr[offInstr + 1], pbInstr[offInstr]);
+ offInstr += 2;
+ break;
+ case DW_EH_PE_sdata2:
+ AssertReturn(offInstr + 1 < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO);
+ if (pExecState->fLittleEndian)
+ uAddress = (int64_t)(int16_t)RT_MAKE_U16(pbInstr[offInstr], pbInstr[offInstr + 1]);
+ else
+ uAddress = (int64_t)(int16_t)RT_MAKE_U16(pbInstr[offInstr + 1], pbInstr[offInstr]);
+ offInstr += 2;
+ break;
+ case DW_EH_PE_udata4:
+ AssertReturn(offInstr + 3 < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO);
+ if (pExecState->fLittleEndian)
+ uAddress = RT_MAKE_U32_FROM_U8(pbInstr[offInstr + 0], pbInstr[offInstr + 1],
+ pbInstr[offInstr + 2], pbInstr[offInstr + 3]);
+ else
+ uAddress = RT_MAKE_U32_FROM_U8(pbInstr[offInstr + 3], pbInstr[offInstr + 2],
+ pbInstr[offInstr + 1], pbInstr[offInstr + 0]);
+
+ offInstr += 4;
+ break;
+ case DW_EH_PE_sdata4:
+ AssertReturn(offInstr + 3 < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO);
+ if (pExecState->fLittleEndian)
+ uAddress = (int64_t)(int32_t)RT_MAKE_U32_FROM_U8(pbInstr[offInstr + 0], pbInstr[offInstr + 1],
+ pbInstr[offInstr + 2], pbInstr[offInstr + 3]);
+ else
+ uAddress = (int64_t)(int32_t)RT_MAKE_U32_FROM_U8(pbInstr[offInstr + 3], pbInstr[offInstr + 2],
+ pbInstr[offInstr + 1], pbInstr[offInstr + 0]);
+ offInstr += 4;
+ break;
+ case DW_EH_PE_udata8:
+ case DW_EH_PE_sdata8:
+ AssertReturn(offInstr + 7 < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO);
+ if (pExecState->fLittleEndian)
+ uAddress = RT_MAKE_U64_FROM_U8(pbInstr[offInstr + 0], pbInstr[offInstr + 1],
+ pbInstr[offInstr + 2], pbInstr[offInstr + 3],
+ pbInstr[offInstr + 4], pbInstr[offInstr + 5],
+ pbInstr[offInstr + 6], pbInstr[offInstr + 7]);
+ else
+ uAddress = RT_MAKE_U64_FROM_U8(pbInstr[offInstr + 7], pbInstr[offInstr + 6],
+ pbInstr[offInstr + 5], pbInstr[offInstr + 4],
+ pbInstr[offInstr + 3], pbInstr[offInstr + 2],
+ pbInstr[offInstr + 1], pbInstr[offInstr + 0]);
+ offInstr += 8;
+ break;
+ case DW_EH_PE_sleb128:
+ case DW_EH_PE_uleb128:
+ default:
+ AssertMsgFailedReturn(("%#x\n", pExecState->pCie->bAddressPtrEnc), VERR_DWARF_TODO);
+ }
+ AssertReturn(uAddress >= pExecState->uPcBegin, VERR_DBG_MALFORMED_UNWIND_INFO);
+
+ /* Did we advance past the desire address already? */
+ if (uAddress > pExecState->uPcBegin + pExecState->offInRange)
+ return VINF_SUCCESS;
+ pExecState->cbLeftToAdvance = pExecState->uPcBegin + pExecState->offInRange - uAddress;
+ break;
+
+
+ /*
+ * Row state push/pop instructions.
+ */
+
+ case DW_CFA_remember_state:
+ {
+ AssertReturn(pExecState->cPushes < 10, VERR_DBG_MALFORMED_UNWIND_INFO);
+ PRTDWARFCFROW pNewRow = (PRTDWARFCFROW)RTMemTmpAlloc(sizeof(*pNewRow));
+ AssertReturn(pNewRow, VERR_NO_TMP_MEMORY);
+ memcpy(pNewRow, pRow, sizeof(*pNewRow));
+ pNewRow->pNextOnStack = pRow;
+ pNewRow->fOnHeap = true;
+ pExecState->pRow = pNewRow;
+ pExecState->cPushes += 1;
+ pRow = pNewRow;
+ break;
+ }
+
+ case DW_CFA_restore_state:
+ AssertReturn(pRow->pNextOnStack, VERR_DBG_MALFORMED_UNWIND_INFO);
+ Assert(pRow->fOnHeap);
+ Assert(pExecState->cPushes > 0);
+ pExecState->cPushes -= 1;
+ pExecState->pRow = pRow->pNextOnStack;
+ RTMemTmpFree(pRow);
+ pRow = pExecState->pRow;
+ break;
+ }
+ }
+ break;
+
+ case DW_CFA_restore:
+ {
+ uint8_t const * const pbCurInstr = &pbInstr[offInstr - 1];
+ uint8_t const iReg = bInstr & ~DW_CFA_high_bit_mask;
+ if (iReg < RT_ELEMENTS(pRow->apbRegInstrs))
+ pRow->apbRegInstrs[iReg] = pbCurInstr;
+ break;
+ }
+ }
+ }
+ return VINF_TRY_AGAIN;
+}
+
+
+/**
+ * Register getter for AMD64.
+ *
+ * @returns true if found, false if not.
+ * @param pState The unwind state to get the register from.
+ * @param iReg The dwarf register number.
+ * @param puValue Where to store the register value.
+ */
+static bool rtDwarfUnwind_Amd64GetRegFromState(PCRTDBGUNWINDSTATE pState, uint16_t iReg, uint64_t *puValue)
+{
+ switch (iReg)
+ {
+ case DWREG_AMD64_RAX: *puValue = pState->u.x86.auRegs[X86_GREG_xAX]; return true;
+ case DWREG_AMD64_RDX: *puValue = pState->u.x86.auRegs[X86_GREG_xDX]; return true;
+ case DWREG_AMD64_RCX: *puValue = pState->u.x86.auRegs[X86_GREG_xCX]; return true;
+ case DWREG_AMD64_RBX: *puValue = pState->u.x86.auRegs[X86_GREG_xBX]; return true;
+ case DWREG_AMD64_RSI: *puValue = pState->u.x86.auRegs[X86_GREG_xSI]; return true;
+ case DWREG_AMD64_RDI: *puValue = pState->u.x86.auRegs[X86_GREG_xDI]; return true;
+ case DWREG_AMD64_RBP: *puValue = pState->u.x86.auRegs[X86_GREG_xBP]; return true;
+ case DWREG_AMD64_RSP: *puValue = pState->u.x86.auRegs[X86_GREG_xSP]; return true;
+ case DWREG_AMD64_R8: *puValue = pState->u.x86.auRegs[X86_GREG_x8]; return true;
+ case DWREG_AMD64_R9: *puValue = pState->u.x86.auRegs[X86_GREG_x9]; return true;
+ case DWREG_AMD64_R10: *puValue = pState->u.x86.auRegs[X86_GREG_x10]; return true;
+ case DWREG_AMD64_R11: *puValue = pState->u.x86.auRegs[X86_GREG_x11]; return true;
+ case DWREG_AMD64_R12: *puValue = pState->u.x86.auRegs[X86_GREG_x12]; return true;
+ case DWREG_AMD64_R13: *puValue = pState->u.x86.auRegs[X86_GREG_x13]; return true;
+ case DWREG_AMD64_R14: *puValue = pState->u.x86.auRegs[X86_GREG_x14]; return true;
+ case DWREG_AMD64_R15: *puValue = pState->u.x86.auRegs[X86_GREG_x15]; return true;
+ case DWREG_AMD64_RFLAGS: *puValue = pState->u.x86.uRFlags; return true;
+ case DWREG_AMD64_ES: *puValue = pState->u.x86.auSegs[X86_SREG_ES]; return true;
+ case DWREG_AMD64_CS: *puValue = pState->u.x86.auSegs[X86_SREG_CS]; return true;
+ case DWREG_AMD64_SS: *puValue = pState->u.x86.auSegs[X86_SREG_SS]; return true;
+ case DWREG_AMD64_DS: *puValue = pState->u.x86.auSegs[X86_SREG_DS]; return true;
+ case DWREG_AMD64_FS: *puValue = pState->u.x86.auSegs[X86_SREG_FS]; return true;
+ case DWREG_AMD64_GS: *puValue = pState->u.x86.auSegs[X86_SREG_GS]; return true;
+ }
+ return false;
+}
+
+
+/**
+ * Register getter for 386+.
+ *
+ * @returns true if found, false if not.
+ * @param pState The unwind state to get the register from.
+ * @param iReg The dwarf register number.
+ * @param puValue Where to store the register value.
+ */
+static bool rtDwarfUnwind_X86GetRegFromState(PCRTDBGUNWINDSTATE pState, uint16_t iReg, uint64_t *puValue)
+{
+ switch (iReg)
+ {
+ case DWREG_X86_EAX: *puValue = pState->u.x86.auRegs[X86_GREG_xAX]; return true;
+ case DWREG_X86_ECX: *puValue = pState->u.x86.auRegs[X86_GREG_xCX]; return true;
+ case DWREG_X86_EDX: *puValue = pState->u.x86.auRegs[X86_GREG_xDX]; return true;
+ case DWREG_X86_EBX: *puValue = pState->u.x86.auRegs[X86_GREG_xBX]; return true;
+ case DWREG_X86_ESP: *puValue = pState->u.x86.auRegs[X86_GREG_xSP]; return true;
+ case DWREG_X86_EBP: *puValue = pState->u.x86.auRegs[X86_GREG_xBP]; return true;
+ case DWREG_X86_ESI: *puValue = pState->u.x86.auRegs[X86_GREG_xSI]; return true;
+ case DWREG_X86_EDI: *puValue = pState->u.x86.auRegs[X86_GREG_xDI]; return true;
+ case DWREG_X86_EFLAGS: *puValue = pState->u.x86.uRFlags; return true;
+ case DWREG_X86_ES: *puValue = pState->u.x86.auSegs[X86_SREG_ES]; return true;
+ case DWREG_X86_CS: *puValue = pState->u.x86.auSegs[X86_SREG_CS]; return true;
+ case DWREG_X86_SS: *puValue = pState->u.x86.auSegs[X86_SREG_SS]; return true;
+ case DWREG_X86_DS: *puValue = pState->u.x86.auSegs[X86_SREG_DS]; return true;
+ case DWREG_X86_FS: *puValue = pState->u.x86.auSegs[X86_SREG_FS]; return true;
+ case DWREG_X86_GS: *puValue = pState->u.x86.auSegs[X86_SREG_GS]; return true;
+ }
+ return false;
+}
+
+/** Register getter. */
+typedef bool FNDWARFUNWINDGEREGFROMSTATE(PCRTDBGUNWINDSTATE pState, uint16_t iReg, uint64_t *puValue);
+/** Pointer to a register getter. */
+typedef FNDWARFUNWINDGEREGFROMSTATE *PFNDWARFUNWINDGEREGFROMSTATE;
+
+
+
+/**
+ * Does the heavy work for figuring out the return value of a register.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_NOT_FOUND if register is undefined.
+ *
+ * @param pRow The DWARF unwind table "row" to use.
+ * @param uReg The DWARF register number.
+ * @param pCie The corresponding CIE.
+ * @param uCfa The canonical frame address to use.
+ * @param pState The unwind to use when reading stack.
+ * @param pOldState The unwind state to get register values from.
+ * @param pfnGetReg The register value getter.
+ * @param puValue Where to store the return value.
+ * @param cbValue The size this register would have on the stack.
+ */
+static int rtDwarfUnwind_CalcRegisterValue(PRTDWARFCFROW pRow, unsigned uReg, PCRTDWARFCIEINFO pCie, uint64_t uCfa,
+ PRTDBGUNWINDSTATE pState, PCRTDBGUNWINDSTATE pOldState,
+ PFNDWARFUNWINDGEREGFROMSTATE pfnGetReg, uint64_t *puValue, uint8_t cbValue)
+{
+ Assert(uReg < RT_ELEMENTS(pRow->apbRegInstrs));
+ uint8_t const *pbInstr = pRow->apbRegInstrs[uReg];
+ if (!pbInstr)
+ return VERR_NOT_FOUND;
+
+ uint32_t cbInstr = UINT32_MAX / 2;
+ uint32_t offInstr = 1;
+ uint8_t const bInstr = *pbInstr;
+ switch (bInstr)
+ {
+ default:
+ if ((bInstr & DW_CFA_high_bit_mask) == DW_CFA_offset)
+ {
+ uint64_t offCfa;
+ GET_ULEB128_AS_U63(offCfa);
+ int rc = pState->pfnReadStack(pState, uCfa + (int64_t)offCfa * pCie->iDataAlignFactor, cbValue, puValue);
+ Log8(("rtDwarfUnwind_CalcRegisterValue(%#x): DW_CFA_offset %#RX64: %Rrc, %#RX64\n", uReg, uCfa + (int64_t)offCfa * pCie->iDataAlignFactor, rc, *puValue));
+ return rc;
+ }
+ AssertReturn((bInstr & DW_CFA_high_bit_mask) == DW_CFA_restore, VERR_INTERNAL_ERROR);
+ RT_FALL_THRU();
+ case DW_CFA_restore_extended:
+ /* Need to search the CIE for the rule. */
+ Log8(("rtDwarfUnwind_CalcRegisterValue(%#x): DW_CFA_restore/extended:\n", uReg));
+ AssertFailedReturn(VERR_DWARF_TODO);
+
+ case DW_CFA_offset_extended:
+ {
+ SKIP_ULEB128_OR_LEB128();
+ uint64_t offCfa;
+ GET_ULEB128_AS_U63(offCfa);
+ int rc = pState->pfnReadStack(pState, uCfa + (int64_t)offCfa * pCie->iDataAlignFactor, cbValue, puValue);
+ Log8(("rtDwarfUnwind_CalcRegisterValue(%#x): DW_CFA_offset_extended %#RX64: %Rrc, %#RX64\n", uReg, uCfa + (int64_t)offCfa * pCie->iDataAlignFactor, rc, *puValue));
+ return rc;
+ }
+
+ case DW_CFA_offset_extended_sf:
+ {
+ SKIP_ULEB128_OR_LEB128();
+ int64_t offCfa;
+ GET_LEB128_AS_I63(offCfa);
+ int rc = pState->pfnReadStack(pState, uCfa + offCfa * pCie->iDataAlignFactor, cbValue, puValue);
+ Log8(("rtDwarfUnwind_CalcRegisterValue(%#x): DW_CFA_offset_extended_sf %#RX64: %Rrc, %#RX64\n", uReg, uCfa + offCfa * pCie->iDataAlignFactor, rc, *puValue));
+ return rc;
+ }
+
+ case DW_CFA_val_offset:
+ {
+ SKIP_ULEB128_OR_LEB128();
+ uint64_t offCfa;
+ GET_ULEB128_AS_U63(offCfa);
+ *puValue = uCfa + (int64_t)offCfa * pCie->iDataAlignFactor;
+ Log8(("rtDwarfUnwind_CalcRegisterValue(%#x): DW_CFA_val_offset: %#RX64\n", uReg, *puValue));
+ return VINF_SUCCESS;
+ }
+
+ case DW_CFA_val_offset_sf:
+ {
+ SKIP_ULEB128_OR_LEB128();
+ int64_t offCfa;
+ GET_LEB128_AS_I63(offCfa);
+ *puValue = uCfa + offCfa * pCie->iDataAlignFactor;
+ Log8(("rtDwarfUnwind_CalcRegisterValue(%#x): DW_CFA_val_offset_sf: %#RX64\n", uReg, *puValue));
+ return VINF_SUCCESS;
+ }
+
+ case DW_CFA_register:
+ {
+ SKIP_ULEB128_OR_LEB128();
+ uint16_t iSrcReg;
+ GET_ULEB128_AS_U14(iSrcReg);
+ if (pfnGetReg(pOldState, uReg, puValue))
+ {
+ Log8(("rtDwarfUnwind_CalcRegisterValue(%#x): DW_CFA_register: %#RX64\n", uReg, *puValue));
+ return VINF_SUCCESS;
+ }
+ Log8(("rtDwarfUnwind_CalcRegisterValue(%#x): DW_CFA_register: VERR_NOT_FOUND\n", uReg));
+ return VERR_NOT_FOUND;
+ }
+
+ case DW_CFA_expression:
+ Log8(("rtDwarfUnwind_CalcRegisterValue(%#x): DW_CFA_expression: TODO\n", uReg));
+ AssertFailedReturn(VERR_DWARF_TODO);
+
+ case DW_CFA_val_expression:
+ Log8(("rtDwarfUnwind_CalcRegisterValue(%#x): DW_CFA_val_expression: TODO\n", uReg));
+ AssertFailedReturn(VERR_DWARF_TODO);
+
+ case DW_CFA_undefined:
+ Log8(("rtDwarfUnwind_CalcRegisterValue(%#x): DW_CFA_undefined\n", uReg));
+ return VERR_NOT_FOUND;
+
+ case DW_CFA_same_value:
+ if (pfnGetReg(pOldState, uReg, puValue))
+ {
+ Log8(("rtDwarfUnwind_CalcRegisterValue(%#x): DW_CFA_same_value: %#RX64\n", uReg, *puValue));
+ return VINF_SUCCESS;
+ }
+ Log8(("rtDwarfUnwind_CalcRegisterValue(%#x): DW_CFA_same_value: VERR_NOT_FOUND\n", uReg));
+ return VERR_NOT_FOUND;
+ }
+}
+
+
+DECLINLINE(void) rtDwarfUnwind_UpdateX86GRegFromRow(PRTDBGUNWINDSTATE pState, PCRTDBGUNWINDSTATE pOldState, unsigned idxGReg,
+ PRTDWARFCFROW pRow, unsigned idxDwReg, PCRTDWARFCIEINFO pCie,
+ uint64_t uCfa, PFNDWARFUNWINDGEREGFROMSTATE pfnGetReg, uint8_t cbGReg)
+{
+ int rc = rtDwarfUnwind_CalcRegisterValue(pRow, idxDwReg, pCie, uCfa, pState, pOldState, pfnGetReg,
+ &pState->u.x86.auRegs[idxGReg], cbGReg);
+ if (RT_SUCCESS(rc))
+ pState->u.x86.Loaded.s.fRegs |= RT_BIT_32(idxGReg);
+}
+
+
+DECLINLINE(void) rtDwarfUnwind_UpdateX86SRegFromRow(PRTDBGUNWINDSTATE pState, PCRTDBGUNWINDSTATE pOldState, unsigned idxSReg,
+ PRTDWARFCFROW pRow, unsigned idxDwReg, PCRTDWARFCIEINFO pCie,
+ uint64_t uCfa, PFNDWARFUNWINDGEREGFROMSTATE pfnGetReg)
+{
+ uint64_t uValue = pState->u.x86.auSegs[idxSReg];
+ int rc = rtDwarfUnwind_CalcRegisterValue(pRow, idxDwReg, pCie, uCfa, pState, pOldState, pfnGetReg, &uValue, sizeof(uint16_t));
+ if (RT_SUCCESS(rc))
+ {
+ pState->u.x86.auSegs[idxSReg] = (uint16_t)uValue;
+ pState->u.x86.Loaded.s.fSegs |= RT_BIT_32(idxSReg);
+ }
+}
+
+
+DECLINLINE(void) rtDwarfUnwind_UpdateX86RFlagsFromRow(PRTDBGUNWINDSTATE pState, PCRTDBGUNWINDSTATE pOldState,
+ PRTDWARFCFROW pRow, unsigned idxDwReg, PCRTDWARFCIEINFO pCie,
+ uint64_t uCfa, PFNDWARFUNWINDGEREGFROMSTATE pfnGetReg)
+{
+ int rc = rtDwarfUnwind_CalcRegisterValue(pRow, idxDwReg, pCie, uCfa, pState, pOldState, pfnGetReg,
+ &pState->u.x86.uRFlags, sizeof(uint32_t));
+ if (RT_SUCCESS(rc))
+ pState->u.x86.Loaded.s.fRFlags = 1;
+}
+
+
+DECLINLINE(void) rtDwarfUnwind_UpdatePCFromRow(PRTDBGUNWINDSTATE pState, PCRTDBGUNWINDSTATE pOldState,
+ PRTDWARFCFROW pRow, unsigned idxDwReg, PCRTDWARFCIEINFO pCie,
+ uint64_t uCfa, PFNDWARFUNWINDGEREGFROMSTATE pfnGetReg, uint8_t cbPc)
+{
+ if (pCie->bRetReg != UINT8_MAX)
+ idxDwReg = pCie->bRetReg;
+ int rc = rtDwarfUnwind_CalcRegisterValue(pRow, idxDwReg, pCie, uCfa, pState, pOldState, pfnGetReg, &pState->uPc, cbPc);
+ if (RT_SUCCESS(rc))
+ pState->u.x86.Loaded.s.fPc = 1;
+ else
+ {
+ rc = pState->pfnReadStack(pState, uCfa - cbPc, cbPc, &pState->uPc);
+ if (RT_SUCCESS(rc))
+ pState->u.x86.Loaded.s.fPc = 1;
+ }
+}
+
+
+
+/**
+ * Updates @a pState with the rules found in @a pRow.
+ *
+ * @returns IPRT status code.
+ * @param pState The unwind state to update.
+ * @param pRow The "row" in the dwarf unwind table.
+ * @param pCie The CIE structure for the row.
+ * @param enmImageArch The image architecture.
+ */
+static int rtDwarfUnwind_UpdateStateFromRow(PRTDBGUNWINDSTATE pState, PRTDWARFCFROW pRow,
+ PCRTDWARFCIEINFO pCie, RTLDRARCH enmImageArch)
+{
+ /*
+ * We need to make a copy of the current state so we can get at the
+ * current register values while calculating the ones of the next frame.
+ */
+ RTDBGUNWINDSTATE const Old = *pState;
+
+ /*
+ * Get the register state getter.
+ */
+ PFNDWARFUNWINDGEREGFROMSTATE pfnGetReg;
+ switch (enmImageArch)
+ {
+ case RTLDRARCH_AMD64:
+ pfnGetReg = rtDwarfUnwind_Amd64GetRegFromState;
+ break;
+ case RTLDRARCH_X86_32:
+ case RTLDRARCH_X86_16:
+ pfnGetReg = rtDwarfUnwind_X86GetRegFromState;
+ break;
+ default:
+ return VERR_NOT_SUPPORTED;
+ }
+
+ /*
+ * Calc the canonical frame address for the current row.
+ */
+ AssertReturn(pRow->fCfaDefined, VERR_DBG_MALFORMED_UNWIND_INFO);
+ uint64_t uCfa = 0;
+ if (!pRow->pbCfaExprInstr)
+ {
+ pfnGetReg(&Old, pRow->uCfaBaseReg, &uCfa);
+ uCfa += pRow->offCfaReg;
+ }
+ else
+ {
+ AssertFailed();
+ return VERR_DWARF_TODO;
+ }
+ Log8(("rtDwarfUnwind_UpdateStateFromRow: uCfa=%RX64\n", uCfa));
+
+ /*
+ * Do the architecture specific register updating.
+ */
+ switch (enmImageArch)
+ {
+ case RTLDRARCH_AMD64:
+ pState->enmRetType = RTDBGRETURNTYPE_NEAR64;
+ pState->u.x86.FrameAddr.off = uCfa - 8*2;
+ pState->u.x86.Loaded.fAll = 0;
+ pState->u.x86.Loaded.s.fFrameAddr = 1;
+ rtDwarfUnwind_UpdatePCFromRow(pState, &Old, pRow, DWREG_AMD64_RA, pCie, uCfa, pfnGetReg, sizeof(uint64_t));
+ rtDwarfUnwind_UpdateX86RFlagsFromRow(pState, &Old, pRow, DWREG_AMD64_RFLAGS, pCie, uCfa, pfnGetReg);
+ rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xAX, pRow, DWREG_AMD64_RAX, pCie, uCfa, pfnGetReg, sizeof(uint64_t));
+ rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xCX, pRow, DWREG_AMD64_RCX, pCie, uCfa, pfnGetReg, sizeof(uint64_t));
+ rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xDX, pRow, DWREG_AMD64_RDX, pCie, uCfa, pfnGetReg, sizeof(uint64_t));
+ rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xBX, pRow, DWREG_AMD64_RBX, pCie, uCfa, pfnGetReg, sizeof(uint64_t));
+ rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xSP, pRow, DWREG_AMD64_RSP, pCie, uCfa, pfnGetReg, sizeof(uint64_t));
+ rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xBP, pRow, DWREG_AMD64_RBP, pCie, uCfa, pfnGetReg, sizeof(uint64_t));
+ rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xSI, pRow, DWREG_AMD64_RSI, pCie, uCfa, pfnGetReg, sizeof(uint64_t));
+ rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xDI, pRow, DWREG_AMD64_RDI, pCie, uCfa, pfnGetReg, sizeof(uint64_t));
+ rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_x8, pRow, DWREG_AMD64_R8, pCie, uCfa, pfnGetReg, sizeof(uint64_t));
+ rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_x9, pRow, DWREG_AMD64_R9, pCie, uCfa, pfnGetReg, sizeof(uint64_t));
+ rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_x10, pRow, DWREG_AMD64_R10, pCie, uCfa, pfnGetReg, sizeof(uint64_t));
+ rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_x11, pRow, DWREG_AMD64_R11, pCie, uCfa, pfnGetReg, sizeof(uint64_t));
+ rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_x12, pRow, DWREG_AMD64_R12, pCie, uCfa, pfnGetReg, sizeof(uint64_t));
+ rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_x13, pRow, DWREG_AMD64_R13, pCie, uCfa, pfnGetReg, sizeof(uint64_t));
+ rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_x14, pRow, DWREG_AMD64_R14, pCie, uCfa, pfnGetReg, sizeof(uint64_t));
+ rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_x15, pRow, DWREG_AMD64_R15, pCie, uCfa, pfnGetReg, sizeof(uint64_t));
+ rtDwarfUnwind_UpdateX86SRegFromRow(pState, &Old, X86_SREG_ES, pRow, DWREG_AMD64_ES, pCie, uCfa, pfnGetReg);
+ rtDwarfUnwind_UpdateX86SRegFromRow(pState, &Old, X86_SREG_CS, pRow, DWREG_AMD64_CS, pCie, uCfa, pfnGetReg);
+ rtDwarfUnwind_UpdateX86SRegFromRow(pState, &Old, X86_SREG_SS, pRow, DWREG_AMD64_SS, pCie, uCfa, pfnGetReg);
+ rtDwarfUnwind_UpdateX86SRegFromRow(pState, &Old, X86_SREG_DS, pRow, DWREG_AMD64_DS, pCie, uCfa, pfnGetReg);
+ rtDwarfUnwind_UpdateX86SRegFromRow(pState, &Old, X86_SREG_FS, pRow, DWREG_AMD64_FS, pCie, uCfa, pfnGetReg);
+ rtDwarfUnwind_UpdateX86SRegFromRow(pState, &Old, X86_SREG_GS, pRow, DWREG_AMD64_GS, pCie, uCfa, pfnGetReg);
+ break;
+
+ case RTLDRARCH_X86_32:
+ case RTLDRARCH_X86_16:
+ pState->enmRetType = RTDBGRETURNTYPE_NEAR32;
+ pState->u.x86.FrameAddr.off = uCfa - 4*2;
+ pState->u.x86.Loaded.fAll = 0;
+ pState->u.x86.Loaded.s.fFrameAddr = 1;
+ rtDwarfUnwind_UpdatePCFromRow(pState, &Old, pRow, DWREG_X86_RA, pCie, uCfa, pfnGetReg, sizeof(uint32_t));
+ rtDwarfUnwind_UpdateX86RFlagsFromRow(pState, &Old, pRow, DWREG_X86_EFLAGS, pCie, uCfa, pfnGetReg);
+ rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xAX, pRow, DWREG_X86_EAX, pCie, uCfa, pfnGetReg, sizeof(uint32_t));
+ rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xCX, pRow, DWREG_X86_ECX, pCie, uCfa, pfnGetReg, sizeof(uint32_t));
+ rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xDX, pRow, DWREG_X86_EDX, pCie, uCfa, pfnGetReg, sizeof(uint32_t));
+ rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xBX, pRow, DWREG_X86_EBX, pCie, uCfa, pfnGetReg, sizeof(uint32_t));
+ rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xSP, pRow, DWREG_X86_ESP, pCie, uCfa, pfnGetReg, sizeof(uint32_t));
+ rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xBP, pRow, DWREG_X86_EBP, pCie, uCfa, pfnGetReg, sizeof(uint32_t));
+ rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xSI, pRow, DWREG_X86_ESI, pCie, uCfa, pfnGetReg, sizeof(uint32_t));
+ rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xDI, pRow, DWREG_X86_EDI, pCie, uCfa, pfnGetReg, sizeof(uint32_t));
+ rtDwarfUnwind_UpdateX86SRegFromRow(pState, &Old, X86_SREG_ES, pRow, DWREG_X86_ES, pCie, uCfa, pfnGetReg);
+ rtDwarfUnwind_UpdateX86SRegFromRow(pState, &Old, X86_SREG_CS, pRow, DWREG_X86_CS, pCie, uCfa, pfnGetReg);
+ rtDwarfUnwind_UpdateX86SRegFromRow(pState, &Old, X86_SREG_SS, pRow, DWREG_X86_SS, pCie, uCfa, pfnGetReg);
+ rtDwarfUnwind_UpdateX86SRegFromRow(pState, &Old, X86_SREG_DS, pRow, DWREG_X86_DS, pCie, uCfa, pfnGetReg);
+ rtDwarfUnwind_UpdateX86SRegFromRow(pState, &Old, X86_SREG_FS, pRow, DWREG_X86_FS, pCie, uCfa, pfnGetReg);
+ rtDwarfUnwind_UpdateX86SRegFromRow(pState, &Old, X86_SREG_GS, pRow, DWREG_X86_GS, pCie, uCfa, pfnGetReg);
+ if (pState->u.x86.Loaded.s.fRegs & RT_BIT_32(X86_GREG_xSP))
+ pState->u.x86.FrameAddr.off = pState->u.x86.auRegs[X86_GREG_xSP] - 8;
+ else
+ pState->u.x86.FrameAddr.off = uCfa - 8;
+ pState->u.x86.FrameAddr.sel = pState->u.x86.auSegs[X86_SREG_SS];
+ if (pState->u.x86.Loaded.s.fSegs & RT_BIT_32(X86_SREG_CS))
+ {
+ if ((pState->uPc >> 16) == pState->u.x86.auSegs[X86_SREG_CS])
+ {
+ pState->enmRetType = RTDBGRETURNTYPE_FAR16;
+ pState->uPc &= UINT16_MAX;
+ Log8(("rtDwarfUnwind_UpdateStateFromRow: Detected FAR16 return to %04x:%04RX64\n", pState->u.x86.auSegs[X86_SREG_CS], pState->uPc));
+ }
+ else
+ {
+ pState->enmRetType = RTDBGRETURNTYPE_FAR32;
+ Log8(("rtDwarfUnwind_UpdateStateFromRow: CS loaded, assume far return.\n"));
+ }
+ }
+ break;
+
+ default:
+ AssertFailedReturn(VERR_NOT_SUPPORTED);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Processes a FDE, taking over after the PC range field.
+ *
+ * @returns IPRT status code.
+ * @param pCursor The cursor.
+ * @param pCie Information about the corresponding CIE.
+ * @param uPcBegin The PC begin field value (sans segment).
+ * @param cbPcRange The PC range from @a uPcBegin.
+ * @param offInRange The offset into the range corresponding to
+ * pState->uPc.
+ * @param enmImageArch The image architecture.
+ * @param pState The unwind state to work.
+ */
+static int rtDwarfUnwind_ProcessFde(PRTDWARFCURSOR pCursor, PCRTDWARFCIEINFO pCie, uint64_t uPcBegin,
+ uint64_t cbPcRange, uint64_t offInRange, RTLDRARCH enmImageArch, PRTDBGUNWINDSTATE pState)
+{
+ /*
+ * Deal with augmented data fields.
+ */
+ /* The size. */
+ size_t cbInstr = ~(size_t)0;
+ if (pCie->fHasAugmentationSize)
+ {
+ uint64_t cbAugData = rtDwarfCursor_GetULeb128(pCursor, UINT64_MAX);
+ if (RT_FAILURE(pCursor->rc))
+ return pCursor->rc;
+ if (cbAugData > pCursor->cbUnitLeft)
+ return VERR_DBG_MALFORMED_UNWIND_INFO;
+ cbInstr = pCursor->cbUnitLeft - cbAugData;
+ }
+ else if (pCie->fHasUnknowAugmentation)
+ return VERR_DBG_MALFORMED_UNWIND_INFO;
+
+ /* Parse the string and fetch FDE fields. */
+ if (!pCie->fHasEhData)
+ for (const char *pszAug = pCie->pszAugmentation; *pszAug != '\0'; pszAug++)
+ switch (*pszAug)
+ {
+ case 'L':
+ if (pCie->bLsdaPtrEnc != DW_EH_PE_omit)
+ rtDwarfCursor_GetPtrEnc(pCursor, pCie->bLsdaPtrEnc, 0);
+ break;
+ }
+
+ /* Skip unconsumed bytes. */
+ if ( cbInstr != ~(size_t)0
+ && pCursor->cbUnitLeft > cbInstr)
+ rtDwarfCursor_SkipBytes(pCursor, pCursor->cbUnitLeft - cbInstr);
+ if (RT_FAILURE(pCursor->rc))
+ return pCursor->rc;
+
+ /*
+ * Now "execute" the programs till we've constructed the desired row.
+ */
+ RTDWARFCFROW Row;
+ RTDWARFCFEXEC ExecState = { &Row, offInRange, 0, true /** @todo byte-order*/, pCie, uPcBegin, offInRange };
+ RT_ZERO(Row);
+
+ int rc = rtDwarfUnwind_Execute(&ExecState, pCie->pbInstructions, (uint32_t)pCie->cbInstructions);
+ if (rc == VINF_TRY_AGAIN)
+ rc = rtDwarfUnwind_Execute(&ExecState, pCursor->pb, (uint32_t)pCursor->cbUnitLeft);
+
+ /* On success, extract whatever state we've got. */
+ if (RT_SUCCESS(rc))
+ rc = rtDwarfUnwind_UpdateStateFromRow(pState, &Row, pCie, enmImageArch);
+
+ /*
+ * Clean up allocations in case of pushes.
+ */
+ if (ExecState.pRow == &Row)
+ Assert(!ExecState.pRow->fOnHeap);
+ else
+ do
+ {
+ PRTDWARFCFROW pPopped = ExecState.pRow;
+ ExecState.pRow = ExecState.pRow->pNextOnStack;
+ Assert(pPopped->fOnHeap);
+ RTMemTmpFree(pPopped);
+ } while (ExecState.pRow && ExecState.pRow != &Row);
+
+ RT_NOREF(pState, uPcBegin, cbPcRange, offInRange);
+ return rc;
+}
+
+
+/**
+ * Load the information we need from a CIE.
+ *
+ * This starts after the initial length and CIE_pointer fields has
+ * been processed.
+ *
+ * @returns IPRT status code.
+ * @param pCursor The cursor.
+ * @param pNewCie The structure to populate with parsed CIE info.
+ * @param offUnit The unit offset.
+ * @param bDefaultPtrEnc The default pointer encoding.
+ */
+static int rtDwarfUnwind_LoadCie(PRTDWARFCURSOR pCursor, PRTDWARFCIEINFO pNewCie, uint64_t offUnit, uint8_t bDefaultPtrEnc)
+{
+ /*
+ * Initialize the CIE record and get the version.
+ */
+ RT_ZERO(*pNewCie);
+ pNewCie->offCie = offUnit;
+ pNewCie->bLsdaPtrEnc = DW_EH_PE_omit;
+ pNewCie->bAddressPtrEnc = DW_EH_PE_omit; /* set later */
+ pNewCie->uDwarfVer = rtDwarfCursor_GetUByte(pCursor, 0);
+ if ( pNewCie->uDwarfVer >= 1 /* Note! Some GCC versions may emit v1 here. */
+ && pNewCie->uDwarfVer <= 5)
+ { /* likely */ }
+ else
+ {
+ Log(("rtDwarfUnwind_LoadCie(%RX64): uDwarfVer=%u: VERR_VERSION_MISMATCH\n", offUnit, pNewCie->uDwarfVer));
+ return VERR_VERSION_MISMATCH;
+ }
+
+ /*
+ * The augmentation string.
+ *
+ * First deal with special "eh" string from oldish GCC (dwarf2out.c about 1997), specified in LSB:
+ * https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html
+ */
+ pNewCie->pszAugmentation = rtDwarfCursor_GetSZ(pCursor, "");
+ if ( pNewCie->pszAugmentation[0] == 'e'
+ && pNewCie->pszAugmentation[1] == 'h'
+ && pNewCie->pszAugmentation[2] == '\0')
+ {
+ pNewCie->fHasEhData = true;
+ rtDwarfCursor_GetPtrEnc(pCursor, bDefaultPtrEnc, 0);
+ }
+ else
+ {
+ /* Regular augmentation string. */
+ for (const char *pszAug = pNewCie->pszAugmentation; *pszAug != '\0'; pszAug++)
+ switch (*pszAug)
+ {
+ case 'z':
+ pNewCie->fHasAugmentationSize = true;
+ break;
+ case 'L':
+ pNewCie->fHasLanguageSpecificDataArea = true;
+ break;
+ case 'P':
+ pNewCie->fHasPersonalityRoutine = true;
+ break;
+ case 'R':
+ pNewCie->fHasAddressEnc = true;
+ break;
+ case 'S':
+ pNewCie->fIsSignalFrame = true;
+ break;
+ default:
+ pNewCie->fHasUnknowAugmentation = true;
+ break;
+ }
+ }
+
+ /*
+ * More standard fields
+ */
+ uint8_t cbAddress = 0;
+ if (pNewCie->uDwarfVer >= 4)
+ {
+ cbAddress = rtDwarfCursor_GetU8(pCursor, bDefaultPtrEnc == DW_EH_PE_udata8 ? 8 : 4);
+ pNewCie->cbSegment = rtDwarfCursor_GetU8(pCursor, 0);
+ }
+ pNewCie->uCodeAlignFactor = rtDwarfCursor_GetULeb128(pCursor, 1);
+ pNewCie->iDataAlignFactor = rtDwarfCursor_GetSLeb128(pCursor, 1);
+ pNewCie->bRetReg = rtDwarfCursor_GetU8(pCursor, UINT8_MAX);
+
+ /*
+ * Augmentation data.
+ */
+ if (!pNewCie->fHasEhData)
+ {
+ /* The size. */
+ size_t cbInstr = ~(size_t)0;
+ if (pNewCie->fHasAugmentationSize)
+ {
+ uint64_t cbAugData = rtDwarfCursor_GetULeb128(pCursor, UINT64_MAX);
+ if (RT_FAILURE(pCursor->rc))
+ {
+ Log(("rtDwarfUnwind_LoadCie(%#RX64): rtDwarfCursor_GetULeb128 -> %Rrc!\n", offUnit, pCursor->rc));
+ return pCursor->rc;
+ }
+ if (cbAugData > pCursor->cbUnitLeft)
+ {
+ Log(("rtDwarfUnwind_LoadCie(%#RX64): cbAugData=%#x pCursor->cbUnitLeft=%#x -> VERR_DBG_MALFORMED_UNWIND_INFO!\n", offUnit, cbAugData, pCursor->cbUnitLeft));
+ return VERR_DBG_MALFORMED_UNWIND_INFO;
+ }
+ cbInstr = pCursor->cbUnitLeft - cbAugData;
+ }
+ else if (pNewCie->fHasUnknowAugmentation)
+ {
+ Log(("rtDwarfUnwind_LoadCie(%#RX64): fHasUnknowAugmentation=1 -> VERR_DBG_MALFORMED_UNWIND_INFO!\n", offUnit));
+ return VERR_DBG_MALFORMED_UNWIND_INFO;
+ }
+
+ /* Parse the string. */
+ for (const char *pszAug = pNewCie->pszAugmentation; *pszAug != '\0'; pszAug++)
+ switch (*pszAug)
+ {
+ case 'L':
+ pNewCie->bLsdaPtrEnc = rtDwarfCursor_GetU8(pCursor, DW_EH_PE_omit);
+ break;
+ case 'P':
+ rtDwarfCursor_GetPtrEnc(pCursor, rtDwarfCursor_GetU8(pCursor, DW_EH_PE_omit), 0);
+ break;
+ case 'R':
+ pNewCie->bAddressPtrEnc = rtDwarfCursor_GetU8(pCursor, DW_EH_PE_omit);
+ break;
+ }
+
+ /* Skip unconsumed bytes. */
+ if ( cbInstr != ~(size_t)0
+ && pCursor->cbUnitLeft > cbInstr)
+ rtDwarfCursor_SkipBytes(pCursor, pCursor->cbUnitLeft - cbInstr);
+ }
+
+ /*
+ * Note down where the instructions are.
+ */
+ pNewCie->pbInstructions = pCursor->pb;
+ pNewCie->cbInstructions = pCursor->cbUnitLeft;
+
+ /*
+ * Determine the target address encoding. Make sure we resolve DW_EH_PE_ptr.
+ */
+ if (pNewCie->bAddressPtrEnc == DW_EH_PE_omit)
+ switch (cbAddress)
+ {
+ case 2: pNewCie->bAddressPtrEnc = DW_EH_PE_udata2; break;
+ case 4: pNewCie->bAddressPtrEnc = DW_EH_PE_udata4; break;
+ case 8: pNewCie->bAddressPtrEnc = DW_EH_PE_udata8; break;
+ default: pNewCie->bAddressPtrEnc = bDefaultPtrEnc; break;
+ }
+ else if ((pNewCie->bAddressPtrEnc & DW_EH_PE_FORMAT_MASK) == DW_EH_PE_ptr)
+ pNewCie->bAddressPtrEnc = bDefaultPtrEnc;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Does a slow unwind of a '.debug_frame' or '.eh_frame' section.
+ *
+ * @returns IPRT status code.
+ * @param pCursor The cursor.
+ * @param uRvaCursor The RVA corrsponding to the cursor start location.
+ * @param idxSeg The segment of the PC location.
+ * @param offSeg The segment offset of the PC location.
+ * @param uRva The RVA of the PC location.
+ * @param pState The unwind state to work.
+ * @param bDefaultPtrEnc The default pointer encoding.
+ * @param fIsEhFrame Set if this is a '.eh_frame'. GCC generate these
+ * with different CIE_pointer values.
+ * @param enmImageArch The image architecture.
+ */
+DECLHIDDEN(int) rtDwarfUnwind_Slow(PRTDWARFCURSOR pCursor, RTUINTPTR uRvaCursor,
+ RTDBGSEGIDX idxSeg, RTUINTPTR offSeg, RTUINTPTR uRva,
+ PRTDBGUNWINDSTATE pState, uint8_t bDefaultPtrEnc, bool fIsEhFrame, RTLDRARCH enmImageArch)
+{
+ Log8(("rtDwarfUnwind_Slow: idxSeg=%#x offSeg=%RTptr uRva=%RTptr enmArch=%d PC=%#RX64\n", idxSeg, offSeg, uRva, pState->enmArch, pState->uPc));
+
+ /*
+ * CIE info we collect.
+ */
+ PRTDWARFCIEINFO paCies = NULL;
+ uint32_t cCies = 0;
+ PRTDWARFCIEINFO pCieHint = NULL;
+
+ /*
+ * Do the scanning.
+ */
+ uint64_t const offCieOffset = pCursor->f64bitDwarf ? UINT64_MAX : UINT32_MAX;
+ int rc = VERR_DBG_UNWIND_INFO_NOT_FOUND;
+ while (!rtDwarfCursor_IsAtEnd(pCursor))
+ {
+ uint64_t const offUnit = rtDwarfCursor_CalcSectOffsetU32(pCursor);
+ if (rtDwarfCursor_GetInitialLength(pCursor) == 0)
+ break;
+
+ uint64_t const offRelCie = rtDwarfCursor_GetUOff(pCursor, offCieOffset);
+ if (offRelCie != offCieOffset)
+ {
+ /*
+ * Frame descriptor entry (FDE).
+ */
+ /* Locate the corresponding CIE. The CIE pointer is self relative
+ in .eh_frame and section relative in .debug_frame. */
+ PRTDWARFCIEINFO pCieForFde;
+ uint64_t offCie = fIsEhFrame ? offUnit + 4 - offRelCie : offRelCie;
+ if (pCieHint && pCieHint->offCie == offCie)
+ pCieForFde = pCieHint;
+ else
+ {
+ pCieForFde = NULL;
+ uint32_t i = cCies;
+ while (i-- > 0)
+ if (paCies[i].offCie == offCie)
+ {
+ pCieHint = pCieForFde = &paCies[i];
+ break;
+ }
+ }
+ if (pCieForFde)
+ {
+ /* Read the PC range covered by this FDE (the fields are also known as initial_location). */
+ RTDBGSEGIDX idxFdeSeg = RTDBGSEGIDX_RVA;
+ if (pCieForFde->cbSegment)
+ idxFdeSeg = rtDwarfCursor_GetVarSizedU(pCursor, pCieForFde->cbSegment, RTDBGSEGIDX_RVA);
+ uint64_t uPcBegin;
+ switch (pCieForFde->bAddressPtrEnc & DW_EH_PE_APPL_MASK)
+ {
+ default: AssertFailed();
+ RT_FALL_THRU();
+ case DW_EH_PE_absptr:
+ uPcBegin = rtDwarfCursor_GetPtrEnc(pCursor, pCieForFde->bAddressPtrEnc, 0);
+ break;
+ case DW_EH_PE_pcrel:
+ {
+ uPcBegin = rtDwarfCursor_CalcSectOffsetU32(pCursor) + uRvaCursor;
+ uPcBegin += rtDwarfCursor_GetPtrEnc(pCursor, pCieForFde->bAddressPtrEnc, 0);
+ break;
+ }
+ }
+ uint64_t cbPcRange = rtDwarfCursor_GetPtrEnc(pCursor, pCieForFde->bAddressPtrEnc, 0);
+
+ /* Match it with what we're looking for. */
+ bool fMatch = idxFdeSeg == RTDBGSEGIDX_RVA
+ ? uRva - uPcBegin < cbPcRange
+ : idxSeg == idxFdeSeg && offSeg - uPcBegin < cbPcRange;
+ Log8(("%#08RX64: FDE pCie=%p idxFdeSeg=%#x uPcBegin=%#RX64 cbPcRange=%#x fMatch=%d\n",
+ offUnit, pCieForFde, idxFdeSeg, uPcBegin, cbPcRange, fMatch));
+ if (fMatch)
+ {
+ rc = rtDwarfUnwind_ProcessFde(pCursor, pCieForFde, uPcBegin, cbPcRange,
+ idxFdeSeg == RTDBGSEGIDX_RVA ? uRva - uPcBegin : offSeg - uPcBegin,
+ enmImageArch, pState);
+ break;
+ }
+ }
+ else
+ Log8(("%#08RX64: FDE - pCie=NULL!! offCie=%#RX64 offRelCie=%#RX64 fIsEhFrame=%d\n", offUnit, offCie, offRelCie, fIsEhFrame));
+ }
+ else
+ {
+ /*
+ * Common information entry (CIE). Record the info we need about it.
+ */
+ if ((cCies % 8) == 0)
+ {
+ void *pvNew = RTMemRealloc(paCies, sizeof(paCies[0]) * (cCies + 8));
+ if (pvNew)
+ {
+ paCies = (PRTDWARFCIEINFO)pvNew;
+ pCieHint = NULL;
+ }
+ else
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+ }
+ Log8(("%#08RX64: CIE\n", offUnit));
+ int rc2 = rtDwarfUnwind_LoadCie(pCursor, &paCies[cCies], offUnit, bDefaultPtrEnc);
+ if (RT_SUCCESS(rc2))
+ {
+ Log8(("%#08RX64: CIE #%u: offCie=%#RX64\n", offUnit, cCies, paCies[cCies].offCie));
+ cCies++;
+ }
+ }
+ rtDwarfCursor_SkipUnit(pCursor);
+ }
+
+ /*
+ * Cleanup.
+ */
+ if (paCies)
+ RTMemFree(paCies);
+ Log8(("rtDwarfUnwind_Slow: returns %Rrc PC=%#RX64\n", rc, pState->uPc));
+ return rc;
+}
+
+
+/**
+ * Helper for translating a loader architecture value to a pointe encoding.
+ *
+ * @returns Pointer encoding.
+ * @param enmLdrArch The loader architecture value to convert.
+ */
+static uint8_t rtDwarfUnwind_ArchToPtrEnc(RTLDRARCH enmLdrArch)
+{
+ switch (enmLdrArch)
+ {
+ case RTLDRARCH_AMD64:
+ case RTLDRARCH_ARM64:
+ return DW_EH_PE_udata8;
+ case RTLDRARCH_X86_16:
+ case RTLDRARCH_X86_32:
+ case RTLDRARCH_ARM32:
+ return DW_EH_PE_udata4;
+ case RTLDRARCH_HOST:
+ case RTLDRARCH_WHATEVER:
+ case RTLDRARCH_INVALID:
+ case RTLDRARCH_END:
+ case RTLDRARCH_32BIT_HACK:
+ break;
+ }
+ AssertFailed();
+ return DW_EH_PE_udata4;
+}
+
+
+/**
+ * Interface for the loader code.
+ *
+ * @returns IPRT status.
+ * @param pvSection The '.eh_frame' section data.
+ * @param cbSection The size of the '.eh_frame' section data.
+ * @param uRvaSection The RVA of the '.eh_frame' section.
+ * @param idxSeg The segment of the PC location.
+ * @param offSeg The segment offset of the PC location.
+ * @param uRva The RVA of the PC location.
+ * @param pState The unwind state to work.
+ * @param enmArch The image architecture.
+ */
+DECLHIDDEN(int) rtDwarfUnwind_EhData(void const *pvSection, size_t cbSection, RTUINTPTR uRvaSection,
+ RTDBGSEGIDX idxSeg, RTUINTPTR offSeg, RTUINTPTR uRva,
+ PRTDBGUNWINDSTATE pState, RTLDRARCH enmArch)
+{
+ RTDWARFCURSOR Cursor;
+ rtDwarfCursor_InitForMem(&Cursor, pvSection, cbSection);
+ int rc = rtDwarfUnwind_Slow(&Cursor, uRvaSection, idxSeg, offSeg, uRva, pState,
+ rtDwarfUnwind_ArchToPtrEnc(enmArch), true /*fIsEhFrame*/, enmArch);
+ LogFlow(("rtDwarfUnwind_EhData: rtDwarfUnwind_Slow -> %Rrc\n", rc));
+ rc = rtDwarfCursor_Delete(&Cursor, rc);
+ LogFlow(("rtDwarfUnwind_EhData: returns %Rrc\n", rc));
+ return rc;
+}
+
+
+/*
+ *
+ * DWARF Line Numbers.
+ * DWARF Line Numbers.
+ * DWARF Line Numbers.
+ *
+ */
+
+
+/**
+ * Defines a file name.
+ *
+ * @returns IPRT status code.
+ * @param pLnState The line number program state.
+ * @param pszFilename The name of the file.
+ * @param idxInc The include path index.
+ */
+static int rtDwarfLine_DefineFileName(PRTDWARFLINESTATE pLnState, const char *pszFilename, uint64_t idxInc)
+{
+ /*
+ * Resize the array if necessary.
+ */
+ uint32_t iFileName = pLnState->cFileNames;
+ if ((iFileName % 2) == 0)
+ {
+ void *pv = RTMemRealloc(pLnState->papszFileNames, sizeof(pLnState->papszFileNames[0]) * (iFileName + 2));
+ if (!pv)
+ return VERR_NO_MEMORY;
+ pLnState->papszFileNames = (char **)pv;
+ }
+
+ /*
+ * Add the file name.
+ */
+ if ( pszFilename[0] == '/'
+ || pszFilename[0] == '\\'
+ || (RT_C_IS_ALPHA(pszFilename[0]) && pszFilename[1] == ':') )
+ pLnState->papszFileNames[iFileName] = RTStrDup(pszFilename);
+ else if (idxInc < pLnState->cIncPaths)
+ pLnState->papszFileNames[iFileName] = RTPathJoinA(pLnState->papszIncPaths[idxInc], pszFilename);
+ else
+ return VERR_DWARF_BAD_LINE_NUMBER_HEADER;
+ if (!pLnState->papszFileNames[iFileName])
+ return VERR_NO_STR_MEMORY;
+ pLnState->cFileNames = iFileName + 1;
+
+ /*
+ * Sanitize the name.
+ */
+ int rc = rtDbgModDwarfStringToUtf8(pLnState->pDwarfMod, &pLnState->papszFileNames[iFileName]);
+ Log((" File #%02u = '%s'\n", iFileName, pLnState->papszFileNames[iFileName]));
+ return rc;
+}
+
+
+/**
+ * Adds a line to the table and resets parts of the state (DW_LNS_copy).
+ *
+ * @returns IPRT status code
+ * @param pLnState The line number program state.
+ * @param offOpCode The opcode offset (for logging
+ * purposes).
+ */
+static int rtDwarfLine_AddLine(PRTDWARFLINESTATE pLnState, uint32_t offOpCode)
+{
+ PRTDBGMODDWARF pThis = pLnState->pDwarfMod;
+ int rc;
+ if (pThis->iWatcomPass == 1)
+ rc = rtDbgModDwarfRecordSegOffset(pThis, pLnState->Regs.uSegment, pLnState->Regs.uAddress + 1);
+ else
+ {
+ const char *pszFile = pLnState->Regs.iFile < pLnState->cFileNames
+ ? pLnState->papszFileNames[pLnState->Regs.iFile]
+ : "<bad file name index>";
+ NOREF(offOpCode);
+
+ RTDBGSEGIDX iSeg;
+ RTUINTPTR offSeg;
+ rc = rtDbgModDwarfLinkAddressToSegOffset(pLnState->pDwarfMod, pLnState->Regs.uSegment, pLnState->Regs.uAddress,
+ &iSeg, &offSeg); /*AssertRC(rc);*/
+ if (RT_SUCCESS(rc))
+ {
+ Log2(("rtDwarfLine_AddLine: %x:%08llx (%#llx) %s(%d) [offOpCode=%08x]\n", iSeg, offSeg, pLnState->Regs.uAddress, pszFile, pLnState->Regs.uLine, offOpCode));
+ rc = RTDbgModLineAdd(pLnState->pDwarfMod->hCnt, pszFile, pLnState->Regs.uLine, iSeg, offSeg, NULL);
+
+ /* Ignore address conflicts for now. */
+ if (rc == VERR_DBG_ADDRESS_CONFLICT)
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VINF_SUCCESS; /* ignore failure */
+ }
+
+ pLnState->Regs.fBasicBlock = false;
+ pLnState->Regs.fPrologueEnd = false;
+ pLnState->Regs.fEpilogueBegin = false;
+ pLnState->Regs.uDiscriminator = 0;
+ return rc;
+}
+
+
+/**
+ * Reset the program to the start-of-sequence state.
+ *
+ * @param pLnState The line number program state.
+ */
+static void rtDwarfLine_ResetState(PRTDWARFLINESTATE pLnState)
+{
+ pLnState->Regs.uAddress = 0;
+ pLnState->Regs.idxOp = 0;
+ pLnState->Regs.iFile = 1;
+ pLnState->Regs.uLine = 1;
+ pLnState->Regs.uColumn = 0;
+ pLnState->Regs.fIsStatement = RT_BOOL(pLnState->Hdr.u8DefIsStmt);
+ pLnState->Regs.fBasicBlock = false;
+ pLnState->Regs.fEndSequence = false;
+ pLnState->Regs.fPrologueEnd = false;
+ pLnState->Regs.fEpilogueBegin = false;
+ pLnState->Regs.uIsa = 0;
+ pLnState->Regs.uDiscriminator = 0;
+ pLnState->Regs.uSegment = 0;
+}
+
+
+/**
+ * Runs the line number program.
+ *
+ * @returns IPRT status code.
+ * @param pLnState The line number program state.
+ * @param pCursor The cursor.
+ */
+static int rtDwarfLine_RunProgram(PRTDWARFLINESTATE pLnState, PRTDWARFCURSOR pCursor)
+{
+ LogFlow(("rtDwarfLine_RunProgram: cbUnitLeft=%zu\n", pCursor->cbUnitLeft));
+
+ int rc = VINF_SUCCESS;
+ rtDwarfLine_ResetState(pLnState);
+
+ while (!rtDwarfCursor_IsAtEndOfUnit(pCursor))
+ {
+#ifdef LOG_ENABLED
+ uint32_t const offOpCode = rtDwarfCursor_CalcSectOffsetU32(pCursor);
+#else
+ uint32_t const offOpCode = 0;
+#endif
+ uint8_t bOpCode = rtDwarfCursor_GetUByte(pCursor, DW_LNS_extended);
+ if (bOpCode >= pLnState->Hdr.u8OpcodeBase)
+ {
+ /*
+ * Special opcode.
+ */
+ uint8_t const bLogOpCode = bOpCode; NOREF(bLogOpCode);
+ bOpCode -= pLnState->Hdr.u8OpcodeBase;
+
+ int32_t const cLineDelta = bOpCode % pLnState->Hdr.u8LineRange + (int32_t)pLnState->Hdr.s8LineBase;
+ bOpCode /= pLnState->Hdr.u8LineRange;
+
+ uint64_t uTmp = bOpCode + pLnState->Regs.idxOp;
+ uint64_t const cAddressDelta = uTmp / pLnState->Hdr.cMaxOpsPerInstr * pLnState->Hdr.cbMinInstr;
+ uint64_t const cOpIndexDelta = uTmp % pLnState->Hdr.cMaxOpsPerInstr;
+
+ pLnState->Regs.uLine += cLineDelta;
+ pLnState->Regs.uAddress += cAddressDelta;
+ pLnState->Regs.idxOp += cOpIndexDelta;
+ Log2(("%08x: DW Special Opcode %#04x: uLine + %d => %u; uAddress + %#llx => %#llx; idxOp + %#llx => %#llx\n",
+ offOpCode, bLogOpCode, cLineDelta, pLnState->Regs.uLine, cAddressDelta, pLnState->Regs.uAddress,
+ cOpIndexDelta, pLnState->Regs.idxOp));
+
+ /*
+ * LLVM emits debug info for global constructors (_GLOBAL__I_a) which are not part of source
+ * code but are inserted by the compiler: The resulting line number will be 0
+ * because they are not part of the source file obviously (see https://reviews.llvm.org/rL205999),
+ * so skip adding them when they are encountered.
+ */
+ if (pLnState->Regs.uLine)
+ rc = rtDwarfLine_AddLine(pLnState, offOpCode);
+ }
+ else
+ {
+ switch (bOpCode)
+ {
+ /*
+ * Standard opcode.
+ */
+ case DW_LNS_copy:
+ Log2(("%08x: DW_LNS_copy\n", offOpCode));
+ /* See the comment about LLVM above. */
+ if (pLnState->Regs.uLine)
+ rc = rtDwarfLine_AddLine(pLnState, offOpCode);
+ break;
+
+ case DW_LNS_advance_pc:
+ {
+ uint64_t u64Adv = rtDwarfCursor_GetULeb128(pCursor, 0);
+ pLnState->Regs.uAddress += (pLnState->Regs.idxOp + u64Adv) / pLnState->Hdr.cMaxOpsPerInstr
+ * pLnState->Hdr.cbMinInstr;
+ pLnState->Regs.idxOp += (pLnState->Regs.idxOp + u64Adv) % pLnState->Hdr.cMaxOpsPerInstr;
+ Log2(("%08x: DW_LNS_advance_pc: u64Adv=%#llx (%lld) )\n", offOpCode, u64Adv, u64Adv));
+ break;
+ }
+
+ case DW_LNS_advance_line:
+ {
+ int32_t cLineDelta = rtDwarfCursor_GetSLeb128AsS32(pCursor, 0);
+ pLnState->Regs.uLine += cLineDelta;
+ Log2(("%08x: DW_LNS_advance_line: uLine + %d => %u\n", offOpCode, cLineDelta, pLnState->Regs.uLine));
+ break;
+ }
+
+ case DW_LNS_set_file:
+ pLnState->Regs.iFile = rtDwarfCursor_GetULeb128AsU32(pCursor, 0);
+ Log2(("%08x: DW_LNS_set_file: iFile=%u\n", offOpCode, pLnState->Regs.iFile));
+ break;
+
+ case DW_LNS_set_column:
+ pLnState->Regs.uColumn = rtDwarfCursor_GetULeb128AsU32(pCursor, 0);
+ Log2(("%08x: DW_LNS_set_column\n", offOpCode));
+ break;
+
+ case DW_LNS_negate_stmt:
+ pLnState->Regs.fIsStatement = !pLnState->Regs.fIsStatement;
+ Log2(("%08x: DW_LNS_negate_stmt\n", offOpCode));
+ break;
+
+ case DW_LNS_set_basic_block:
+ pLnState->Regs.fBasicBlock = true;
+ Log2(("%08x: DW_LNS_set_basic_block\n", offOpCode));
+ break;
+
+ case DW_LNS_const_add_pc:
+ {
+ uint8_t u8Adv = (255 - pLnState->Hdr.u8OpcodeBase) / pLnState->Hdr.u8LineRange;
+ if (pLnState->Hdr.cMaxOpsPerInstr <= 1)
+ pLnState->Regs.uAddress += (uint32_t)pLnState->Hdr.cbMinInstr * u8Adv;
+ else
+ {
+ pLnState->Regs.uAddress += (pLnState->Regs.idxOp + u8Adv) / pLnState->Hdr.cMaxOpsPerInstr
+ * pLnState->Hdr.cbMinInstr;
+ pLnState->Regs.idxOp = (pLnState->Regs.idxOp + u8Adv) % pLnState->Hdr.cMaxOpsPerInstr;
+ }
+ Log2(("%08x: DW_LNS_const_add_pc\n", offOpCode));
+ break;
+ }
+ case DW_LNS_fixed_advance_pc:
+ pLnState->Regs.uAddress += rtDwarfCursor_GetUHalf(pCursor, 0);
+ pLnState->Regs.idxOp = 0;
+ Log2(("%08x: DW_LNS_fixed_advance_pc\n", offOpCode));
+ break;
+
+ case DW_LNS_set_prologue_end:
+ pLnState->Regs.fPrologueEnd = true;
+ Log2(("%08x: DW_LNS_set_prologue_end\n", offOpCode));
+ break;
+
+ case DW_LNS_set_epilogue_begin:
+ pLnState->Regs.fEpilogueBegin = true;
+ Log2(("%08x: DW_LNS_set_epilogue_begin\n", offOpCode));
+ break;
+
+ case DW_LNS_set_isa:
+ pLnState->Regs.uIsa = rtDwarfCursor_GetULeb128AsU32(pCursor, 0);
+ Log2(("%08x: DW_LNS_set_isa %#x\n", offOpCode, pLnState->Regs.uIsa));
+ break;
+
+ default:
+ {
+ unsigned cOpsToSkip = pLnState->Hdr.pacStdOperands[bOpCode - 1];
+ Log(("rtDwarfLine_RunProgram: Unknown standard opcode %#x, %#x operands, at %08x.\n", bOpCode, cOpsToSkip, offOpCode));
+ while (cOpsToSkip-- > 0)
+ rc = rtDwarfCursor_SkipLeb128(pCursor);
+ break;
+ }
+
+ /*
+ * Extended opcode.
+ */
+ case DW_LNS_extended:
+ {
+ /* The instruction has a length prefix. */
+ uint64_t cbInstr = rtDwarfCursor_GetULeb128(pCursor, UINT64_MAX);
+ if (RT_FAILURE(pCursor->rc))
+ return pCursor->rc;
+ if (cbInstr > pCursor->cbUnitLeft)
+ return VERR_DWARF_BAD_LNE;
+ uint8_t const * const pbEndOfInstr = rtDwarfCursor_CalcPos(pCursor, cbInstr);
+
+ /* Get the opcode and deal with it if we know it. */
+ bOpCode = rtDwarfCursor_GetUByte(pCursor, 0);
+ switch (bOpCode)
+ {
+ case DW_LNE_end_sequence:
+#if 0 /* No need for this, I think. */
+ pLnState->Regs.fEndSequence = true;
+ rc = rtDwarfLine_AddLine(pLnState, offOpCode);
+#endif
+ rtDwarfLine_ResetState(pLnState);
+ Log2(("%08x: DW_LNE_end_sequence\n", offOpCode));
+ break;
+
+ case DW_LNE_set_address:
+ pLnState->Regs.uAddress = rtDwarfCursor_GetVarSizedU(pCursor, cbInstr - 1, UINT64_MAX);
+ pLnState->Regs.idxOp = 0;
+ Log2(("%08x: DW_LNE_set_address: %#llx\n", offOpCode, pLnState->Regs.uAddress));
+ break;
+
+ case DW_LNE_define_file:
+ {
+ const char *pszFilename = rtDwarfCursor_GetSZ(pCursor, NULL);
+ uint32_t idxInc = rtDwarfCursor_GetULeb128AsU32(pCursor, UINT32_MAX);
+ rtDwarfCursor_SkipLeb128(pCursor); /* st_mtime */
+ rtDwarfCursor_SkipLeb128(pCursor); /* st_size */
+ Log2(("%08x: DW_LNE_define_file: {%d}/%s\n", offOpCode, idxInc, pszFilename));
+
+ rc = rtDwarfCursor_AdvanceToPos(pCursor, pbEndOfInstr);
+ if (RT_SUCCESS(rc))
+ rc = rtDwarfLine_DefineFileName(pLnState, pszFilename, idxInc);
+ break;
+ }
+
+ /*
+ * Note! Was defined in DWARF 4. But... Watcom used it for setting the
+ * segment in DWARF 2, creating an incompatibility with the newer
+ * standard. And gcc 10 uses v3 for these.
+ */
+ case DW_LNE_set_descriminator:
+ if (pLnState->Hdr.uVer != 2)
+ {
+ Assert(pLnState->Hdr.uVer >= 3);
+ pLnState->Regs.uDiscriminator = rtDwarfCursor_GetULeb128AsU32(pCursor, UINT32_MAX);
+ Log2(("%08x: DW_LNE_set_descriminator: %u\n", offOpCode, pLnState->Regs.uDiscriminator));
+ }
+ else
+ {
+ uint64_t uSeg = rtDwarfCursor_GetVarSizedU(pCursor, cbInstr - 1, UINT64_MAX);
+ Log2(("%08x: DW_LNE_set_segment: %#llx, cbInstr=%#x - Watcom Extension\n", offOpCode, uSeg, cbInstr));
+ pLnState->Regs.uSegment = (RTSEL)uSeg;
+ AssertStmt(pLnState->Regs.uSegment == uSeg, rc = VERR_DWARF_BAD_INFO);
+ }
+ break;
+
+ default:
+ Log(("rtDwarfLine_RunProgram: Unknown extended opcode %#x, length %#x at %08x\n", bOpCode, cbInstr, offOpCode));
+ break;
+ }
+
+ /* Advance the cursor to the end of the instruction . */
+ rtDwarfCursor_AdvanceToPos(pCursor, pbEndOfInstr);
+ break;
+ }
+ }
+ }
+
+ /*
+ * Check the status before looping.
+ */
+ if (RT_FAILURE(rc))
+ return rc;
+ if (RT_FAILURE(pCursor->rc))
+ return pCursor->rc;
+ }
+ return rc;
+}
+
+
+/**
+ * Reads the include directories for a line number unit.
+ *
+ * @returns IPRT status code
+ * @param pLnState The line number program state.
+ * @param pCursor The cursor.
+ */
+static int rtDwarfLine_ReadFileNames(PRTDWARFLINESTATE pLnState, PRTDWARFCURSOR pCursor)
+{
+ int rc = rtDwarfLine_DefineFileName(pLnState, "/<bad-zero-file-name-entry>", 0);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ for (;;)
+ {
+ const char *psz = rtDwarfCursor_GetSZ(pCursor, NULL);
+ if (!*psz)
+ break;
+
+ uint64_t idxInc = rtDwarfCursor_GetULeb128(pCursor, UINT64_MAX);
+ rtDwarfCursor_SkipLeb128(pCursor); /* st_mtime */
+ rtDwarfCursor_SkipLeb128(pCursor); /* st_size */
+
+ rc = rtDwarfLine_DefineFileName(pLnState, psz, idxInc);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ return pCursor->rc;
+}
+
+
+/**
+ * Reads the include directories for a line number unit.
+ *
+ * @returns IPRT status code
+ * @param pLnState The line number program state.
+ * @param pCursor The cursor.
+ */
+static int rtDwarfLine_ReadIncludePaths(PRTDWARFLINESTATE pLnState, PRTDWARFCURSOR pCursor)
+{
+ const char *psz = ""; /* The zeroth is the unit dir. */
+ for (;;)
+ {
+ if ((pLnState->cIncPaths % 2) == 0)
+ {
+ void *pv = RTMemRealloc(pLnState->papszIncPaths, sizeof(pLnState->papszIncPaths[0]) * (pLnState->cIncPaths + 2));
+ if (!pv)
+ return VERR_NO_MEMORY;
+ pLnState->papszIncPaths = (const char **)pv;
+ }
+ Log((" Path #%02u = '%s'\n", pLnState->cIncPaths, psz));
+ pLnState->papszIncPaths[pLnState->cIncPaths] = psz;
+ pLnState->cIncPaths++;
+
+ psz = rtDwarfCursor_GetSZ(pCursor, NULL);
+ if (!*psz)
+ break;
+ }
+
+ return pCursor->rc;
+}
+
+
+/**
+ * Explodes the line number table for a compilation unit.
+ *
+ * @returns IPRT status code
+ * @param pThis The DWARF instance.
+ * @param pCursor The cursor to read the line number information
+ * via.
+ */
+static int rtDwarfLine_ExplodeUnit(PRTDBGMODDWARF pThis, PRTDWARFCURSOR pCursor)
+{
+ RTDWARFLINESTATE LnState;
+ RT_ZERO(LnState);
+ LnState.pDwarfMod = pThis;
+
+ /*
+ * Parse the header.
+ */
+ rtDwarfCursor_GetInitialLength(pCursor);
+ LnState.Hdr.uVer = rtDwarfCursor_GetUHalf(pCursor, 0);
+ if ( LnState.Hdr.uVer < 2
+ || LnState.Hdr.uVer > 4)
+ return rtDwarfCursor_SkipUnit(pCursor);
+
+ LnState.Hdr.offFirstOpcode = rtDwarfCursor_GetUOff(pCursor, 0);
+ uint8_t const * const pbFirstOpcode = rtDwarfCursor_CalcPos(pCursor, LnState.Hdr.offFirstOpcode);
+
+ LnState.Hdr.cbMinInstr = rtDwarfCursor_GetUByte(pCursor, 0);
+ if (LnState.Hdr.uVer >= 4)
+ LnState.Hdr.cMaxOpsPerInstr = rtDwarfCursor_GetUByte(pCursor, 0);
+ else
+ LnState.Hdr.cMaxOpsPerInstr = 1;
+ LnState.Hdr.u8DefIsStmt = rtDwarfCursor_GetUByte(pCursor, 0);
+ LnState.Hdr.s8LineBase = rtDwarfCursor_GetSByte(pCursor, 0);
+ LnState.Hdr.u8LineRange = rtDwarfCursor_GetUByte(pCursor, 0);
+ LnState.Hdr.u8OpcodeBase = rtDwarfCursor_GetUByte(pCursor, 0);
+
+ if ( !LnState.Hdr.u8OpcodeBase
+ || !LnState.Hdr.cMaxOpsPerInstr
+ || !LnState.Hdr.u8LineRange
+ || LnState.Hdr.u8DefIsStmt > 1)
+ return VERR_DWARF_BAD_LINE_NUMBER_HEADER;
+ Log2(("DWARF Line number header:\n"
+ " uVer %d\n"
+ " offFirstOpcode %#llx\n"
+ " cbMinInstr %u\n"
+ " cMaxOpsPerInstr %u\n"
+ " u8DefIsStmt %u\n"
+ " s8LineBase %d\n"
+ " u8LineRange %u\n"
+ " u8OpcodeBase %u\n",
+ LnState.Hdr.uVer, LnState.Hdr.offFirstOpcode, LnState.Hdr.cbMinInstr, LnState.Hdr.cMaxOpsPerInstr,
+ LnState.Hdr.u8DefIsStmt, LnState.Hdr.s8LineBase, LnState.Hdr.u8LineRange, LnState.Hdr.u8OpcodeBase));
+
+ LnState.Hdr.pacStdOperands = pCursor->pb;
+ for (uint8_t iStdOpcode = 1; iStdOpcode < LnState.Hdr.u8OpcodeBase; iStdOpcode++)
+ rtDwarfCursor_GetUByte(pCursor, 0);
+
+ int rc = pCursor->rc;
+ if (RT_SUCCESS(rc))
+ rc = rtDwarfLine_ReadIncludePaths(&LnState, pCursor);
+ if (RT_SUCCESS(rc))
+ rc = rtDwarfLine_ReadFileNames(&LnState, pCursor);
+
+ /*
+ * Run the program....
+ */
+ if (RT_SUCCESS(rc))
+ rc = rtDwarfCursor_AdvanceToPos(pCursor, pbFirstOpcode);
+ if (RT_SUCCESS(rc))
+ rc = rtDwarfLine_RunProgram(&LnState, pCursor);
+
+ /*
+ * Clean up.
+ */
+ size_t i = LnState.cFileNames;
+ while (i-- > 0)
+ RTStrFree(LnState.papszFileNames[i]);
+ RTMemFree(LnState.papszFileNames);
+ RTMemFree(LnState.papszIncPaths);
+
+ Assert(rtDwarfCursor_IsAtEndOfUnit(pCursor) || RT_FAILURE(rc));
+ return rc;
+}
+
+
+/**
+ * Explodes the line number table.
+ *
+ * The line numbers are insered into the debug info container.
+ *
+ * @returns IPRT status code
+ * @param pThis The DWARF instance.
+ */
+static int rtDwarfLine_ExplodeAll(PRTDBGMODDWARF pThis)
+{
+ if (!pThis->aSections[krtDbgModDwarfSect_line].fPresent)
+ return VINF_SUCCESS;
+
+ RTDWARFCURSOR Cursor;
+ int rc = rtDwarfCursor_Init(&Cursor, pThis, krtDbgModDwarfSect_line);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ while ( !rtDwarfCursor_IsAtEnd(&Cursor)
+ && RT_SUCCESS(rc))
+ rc = rtDwarfLine_ExplodeUnit(pThis, &Cursor);
+
+ return rtDwarfCursor_Delete(&Cursor, rc);
+}
+
+
+/*
+ *
+ * DWARF Abbreviations.
+ * DWARF Abbreviations.
+ * DWARF Abbreviations.
+ *
+ */
+
+/**
+ * Deals with a cache miss in rtDwarfAbbrev_Lookup.
+ *
+ * @returns Pointer to abbreviation cache entry (read only). May be rendered
+ * invalid by subsequent calls to this function.
+ * @param pThis The DWARF instance.
+ * @param uCode The abbreviation code to lookup.
+ */
+static PCRTDWARFABBREV rtDwarfAbbrev_LookupMiss(PRTDBGMODDWARF pThis, uint32_t uCode)
+{
+ /*
+ * There is no entry with code zero.
+ */
+ if (!uCode)
+ return NULL;
+
+ /*
+ * Resize the cache array if the code is considered cachable.
+ */
+ bool fFillCache = true;
+ if (pThis->cCachedAbbrevsAlloced < uCode)
+ {
+ if (uCode >= _64K)
+ fFillCache = false;
+ else
+ {
+ uint32_t cNew = RT_ALIGN(uCode, 64);
+ void *pv = RTMemRealloc(pThis->paCachedAbbrevs, sizeof(pThis->paCachedAbbrevs[0]) * cNew);
+ if (!pv)
+ fFillCache = false;
+ else
+ {
+ Log(("rtDwarfAbbrev_LookupMiss: Growing from %u to %u...\n", pThis->cCachedAbbrevsAlloced, cNew));
+ pThis->paCachedAbbrevs = (PRTDWARFABBREV)pv;
+ for (uint32_t i = pThis->cCachedAbbrevsAlloced; i < cNew; i++)
+ pThis->paCachedAbbrevs[i].offAbbrev = UINT32_MAX;
+ pThis->cCachedAbbrevsAlloced = cNew;
+ }
+ }
+ }
+
+ /*
+ * Walk the abbreviations till we find the desired code.
+ */
+ RTDWARFCURSOR Cursor;
+ int rc = rtDwarfCursor_InitWithOffset(&Cursor, pThis, krtDbgModDwarfSect_abbrev, pThis->offCachedAbbrev);
+ if (RT_FAILURE(rc))
+ return NULL;
+
+ PRTDWARFABBREV pRet = NULL;
+ if (fFillCache)
+ {
+ /*
+ * Search for the entry and fill the cache while doing so.
+ * We assume that abbreviation codes for a unit will stop when we see
+ * zero code or when the code value drops.
+ */
+ uint32_t uPrevCode = 0;
+ for (;;)
+ {
+ /* Read the 'header'. Skipping zero code bytes. */
+#ifdef LOG_ENABLED
+ uint32_t const offStart = rtDwarfCursor_CalcSectOffsetU32(&Cursor);
+#endif
+ uint32_t const uCurCode = rtDwarfCursor_GetULeb128AsU32(&Cursor, 0);
+ if (pRet && (uCurCode == 0 || uCurCode < uPrevCode))
+ break; /* probably end of unit. */
+ if (uCurCode != 0)
+ {
+ uint32_t const uCurTag = rtDwarfCursor_GetULeb128AsU32(&Cursor, 0);
+ uint8_t const uChildren = rtDwarfCursor_GetU8(&Cursor, 0);
+ if (RT_FAILURE(Cursor.rc))
+ break;
+ if ( uCurTag > 0xffff
+ || uChildren > 1)
+ {
+ Cursor.rc = VERR_DWARF_BAD_ABBREV;
+ break;
+ }
+
+ /* Cache it? */
+ if (uCurCode <= pThis->cCachedAbbrevsAlloced)
+ {
+ PRTDWARFABBREV pEntry = &pThis->paCachedAbbrevs[uCurCode - 1];
+ if (pEntry->offAbbrev != pThis->offCachedAbbrev)
+ {
+ pEntry->offAbbrev = pThis->offCachedAbbrev;
+ pEntry->fChildren = RT_BOOL(uChildren);
+ pEntry->uTag = uCurTag;
+ pEntry->offSpec = rtDwarfCursor_CalcSectOffsetU32(&Cursor);
+#ifdef LOG_ENABLED
+ pEntry->cbHdr = (uint8_t)(pEntry->offSpec - offStart);
+ Log7(("rtDwarfAbbrev_LookupMiss(%#x): fill: %#x: uTag=%#x offAbbrev=%#x%s\n",
+ uCode, offStart, pEntry->uTag, pEntry->offAbbrev, pEntry->fChildren ? " has-children" : ""));
+#endif
+ if (uCurCode == uCode)
+ {
+ Assert(!pRet);
+ pRet = pEntry;
+ if (uCurCode == pThis->cCachedAbbrevsAlloced)
+ break;
+ }
+ }
+ else if (pRet)
+ break; /* Next unit, don't cache more. */
+ /* else: We're growing the cache and re-reading old data. */
+ }
+
+ /* Skip the specification. */
+ uint32_t uAttr, uForm;
+ do
+ {
+ uAttr = rtDwarfCursor_GetULeb128AsU32(&Cursor, 0);
+ uForm = rtDwarfCursor_GetULeb128AsU32(&Cursor, 0);
+ } while (uAttr != 0);
+ }
+ if (RT_FAILURE(Cursor.rc))
+ break;
+
+ /* Done? (Maximize cache filling.) */
+ if ( pRet != NULL
+ && uCurCode >= pThis->cCachedAbbrevsAlloced)
+ break;
+ uPrevCode = uCurCode;
+ }
+ if (pRet)
+ Log6(("rtDwarfAbbrev_LookupMiss(%#x): uTag=%#x offSpec=%#x offAbbrev=%#x [fill]\n",
+ uCode, pRet->uTag, pRet->offSpec, pRet->offAbbrev));
+ else
+ Log6(("rtDwarfAbbrev_LookupMiss(%#x): failed [fill]\n", uCode));
+ }
+ else
+ {
+ /*
+ * Search for the entry with the desired code, no cache filling.
+ */
+ for (;;)
+ {
+ /* Read the 'header'. */
+#ifdef LOG_ENABLED
+ uint32_t const offStart = rtDwarfCursor_CalcSectOffsetU32(&Cursor);
+#endif
+ uint32_t const uCurCode = rtDwarfCursor_GetULeb128AsU32(&Cursor, 0);
+ uint32_t const uCurTag = rtDwarfCursor_GetULeb128AsU32(&Cursor, 0);
+ uint8_t const uChildren = rtDwarfCursor_GetU8(&Cursor, 0);
+ if (RT_FAILURE(Cursor.rc))
+ break;
+ if ( uCurTag > 0xffff
+ || uChildren > 1)
+ {
+ Cursor.rc = VERR_DWARF_BAD_ABBREV;
+ break;
+ }
+
+ /* Do we have a match? */
+ if (uCurCode == uCode)
+ {
+ pRet = &pThis->LookupAbbrev;
+ pRet->fChildren = RT_BOOL(uChildren);
+ pRet->uTag = uCurTag;
+ pRet->offSpec = rtDwarfCursor_CalcSectOffsetU32(&Cursor);
+ pRet->offAbbrev = pThis->offCachedAbbrev;
+#ifdef LOG_ENABLED
+ pRet->cbHdr = (uint8_t)(pRet->offSpec - offStart);
+#endif
+ break;
+ }
+
+ /* Skip the specification. */
+ uint32_t uAttr, uForm;
+ do
+ {
+ uAttr = rtDwarfCursor_GetULeb128AsU32(&Cursor, 0);
+ uForm = rtDwarfCursor_GetULeb128AsU32(&Cursor, 0);
+ } while (uAttr != 0);
+ if (RT_FAILURE(Cursor.rc))
+ break;
+ }
+ if (pRet)
+ Log6(("rtDwarfAbbrev_LookupMiss(%#x): uTag=%#x offSpec=%#x offAbbrev=%#x [no-fill]\n",
+ uCode, pRet->uTag, pRet->offSpec, pRet->offAbbrev));
+ else
+ Log6(("rtDwarfAbbrev_LookupMiss(%#x): failed [no-fill]\n", uCode));
+ }
+
+ rtDwarfCursor_Delete(&Cursor, VINF_SUCCESS);
+ return pRet;
+}
+
+
+/**
+ * Looks up an abbreviation.
+ *
+ * @returns Pointer to abbreviation cache entry (read only). May be rendered
+ * invalid by subsequent calls to this function.
+ * @param pThis The DWARF instance.
+ * @param uCode The abbreviation code to lookup.
+ */
+static PCRTDWARFABBREV rtDwarfAbbrev_Lookup(PRTDBGMODDWARF pThis, uint32_t uCode)
+{
+ uCode -= 1;
+ if (uCode < pThis->cCachedAbbrevsAlloced)
+ {
+ if (pThis->paCachedAbbrevs[uCode].offAbbrev == pThis->offCachedAbbrev)
+ return &pThis->paCachedAbbrevs[uCode];
+ }
+ return rtDwarfAbbrev_LookupMiss(pThis, uCode + 1);
+}
+
+
+/**
+ * Sets the abbreviation offset of the current unit.
+ *
+ * @param pThis The DWARF instance.
+ * @param offAbbrev The offset into the abbreviation section.
+ */
+static void rtDwarfAbbrev_SetUnitOffset(PRTDBGMODDWARF pThis, uint32_t offAbbrev)
+{
+ pThis->offCachedAbbrev = offAbbrev;
+}
+
+
+
+/*
+ *
+ * DIE Attribute Parsers.
+ * DIE Attribute Parsers.
+ * DIE Attribute Parsers.
+ *
+ */
+
+/**
+ * Gets the compilation unit a DIE belongs to.
+ *
+ * @returns The compilation unit DIE.
+ * @param pDie Some DIE in the unit.
+ */
+static PRTDWARFDIECOMPILEUNIT rtDwarfDie_GetCompileUnit(PRTDWARFDIE pDie)
+{
+ while (pDie->pParent)
+ pDie = pDie->pParent;
+ AssertReturn( pDie->uTag == DW_TAG_compile_unit
+ || pDie->uTag == DW_TAG_partial_unit,
+ NULL);
+ return (PRTDWARFDIECOMPILEUNIT)pDie;
+}
+
+
+/**
+ * Resolves a string section (debug_str) reference.
+ *
+ * @returns Pointer to the string (inside the string section).
+ * @param pThis The DWARF instance.
+ * @param pCursor The cursor.
+ * @param pszErrValue What to return on failure (@a
+ * pCursor->rc is set).
+ */
+static const char *rtDwarfDecodeHlp_GetStrp(PRTDBGMODDWARF pThis, PRTDWARFCURSOR pCursor, const char *pszErrValue)
+{
+ uint64_t offDebugStr = rtDwarfCursor_GetUOff(pCursor, UINT64_MAX);
+ if (RT_FAILURE(pCursor->rc))
+ return pszErrValue;
+
+ if (offDebugStr >= pThis->aSections[krtDbgModDwarfSect_str].cb)
+ {
+ /* Ugly: Exploit the cursor status field for reporting errors. */
+ pCursor->rc = VERR_DWARF_BAD_INFO;
+ return pszErrValue;
+ }
+
+ if (!pThis->aSections[krtDbgModDwarfSect_str].pv)
+ {
+ int rc = rtDbgModDwarfLoadSection(pThis, krtDbgModDwarfSect_str);
+ if (RT_FAILURE(rc))
+ {
+ /* Ugly: Exploit the cursor status field for reporting errors. */
+ pCursor->rc = rc;
+ return pszErrValue;
+ }
+ }
+
+ return (const char *)pThis->aSections[krtDbgModDwarfSect_str].pv + (size_t)offDebugStr;
+}
+
+
+/** @callback_method_impl{FNRTDWARFATTRDECODER} */
+static DECLCALLBACK(int) rtDwarfDecode_Address(PRTDWARFDIE pDie, uint8_t *pbMember, PCRTDWARFATTRDESC pDesc,
+ uint32_t uForm, PRTDWARFCURSOR pCursor)
+{
+ AssertReturn(ATTR_GET_SIZE(pDesc) == sizeof(RTDWARFADDR), VERR_INTERNAL_ERROR_3);
+ NOREF(pDie);
+
+ uint64_t uAddr;
+ switch (uForm)
+ {
+ case DW_FORM_addr: uAddr = rtDwarfCursor_GetNativeUOff(pCursor, 0); break;
+ case DW_FORM_data1: uAddr = rtDwarfCursor_GetU8(pCursor, 0); break;
+ case DW_FORM_data2: uAddr = rtDwarfCursor_GetU16(pCursor, 0); break;
+ case DW_FORM_data4: uAddr = rtDwarfCursor_GetU32(pCursor, 0); break;
+ case DW_FORM_data8: uAddr = rtDwarfCursor_GetU64(pCursor, 0); break;
+ case DW_FORM_udata: uAddr = rtDwarfCursor_GetULeb128(pCursor, 0); break;
+ default:
+ AssertMsgFailedReturn(("%#x (%s)\n", uForm, rtDwarfLog_FormName(uForm)), VERR_DWARF_UNEXPECTED_FORM);
+ }
+ if (RT_FAILURE(pCursor->rc))
+ return pCursor->rc;
+
+ PRTDWARFADDR pAddr = (PRTDWARFADDR)pbMember;
+ pAddr->uAddress = uAddr;
+
+ Log4((" %-20s %#010llx [%s]\n", rtDwarfLog_AttrName(pDesc->uAttr), uAddr, rtDwarfLog_FormName(uForm)));
+ return VINF_SUCCESS;
+}
+
+
+/** @callback_method_impl{FNRTDWARFATTRDECODER} */
+static DECLCALLBACK(int) rtDwarfDecode_Bool(PRTDWARFDIE pDie, uint8_t *pbMember, PCRTDWARFATTRDESC pDesc,
+ uint32_t uForm, PRTDWARFCURSOR pCursor)
+{
+ AssertReturn(ATTR_GET_SIZE(pDesc) == sizeof(bool), VERR_INTERNAL_ERROR_3);
+ NOREF(pDie);
+
+ bool *pfMember = (bool *)pbMember;
+ switch (uForm)
+ {
+ case DW_FORM_flag:
+ {
+ uint8_t b = rtDwarfCursor_GetU8(pCursor, UINT8_MAX);
+ if (b > 1)
+ {
+ Log(("Unexpected boolean value %#x\n", b));
+ return RT_FAILURE(pCursor->rc) ? pCursor->rc : pCursor->rc = VERR_DWARF_BAD_INFO;
+ }
+ *pfMember = RT_BOOL(b);
+ break;
+ }
+
+ case DW_FORM_flag_present:
+ *pfMember = true;
+ break;
+
+ default:
+ AssertMsgFailedReturn(("%#x\n", uForm), VERR_DWARF_UNEXPECTED_FORM);
+ }
+
+ Log4((" %-20s %RTbool [%s]\n", rtDwarfLog_AttrName(pDesc->uAttr), *pfMember, rtDwarfLog_FormName(uForm)));
+ return VINF_SUCCESS;
+}
+
+
+/** @callback_method_impl{FNRTDWARFATTRDECODER} */
+static DECLCALLBACK(int) rtDwarfDecode_LowHighPc(PRTDWARFDIE pDie, uint8_t *pbMember, PCRTDWARFATTRDESC pDesc,
+ uint32_t uForm, PRTDWARFCURSOR pCursor)
+{
+ AssertReturn(ATTR_GET_SIZE(pDesc) == sizeof(RTDWARFADDRRANGE), VERR_INTERNAL_ERROR_3);
+ AssertReturn(pDesc->uAttr == DW_AT_low_pc || pDesc->uAttr == DW_AT_high_pc, VERR_INTERNAL_ERROR_3);
+ NOREF(pDie);
+
+ uint64_t uAddr;
+ switch (uForm)
+ {
+ case DW_FORM_addr: uAddr = rtDwarfCursor_GetNativeUOff(pCursor, 0); break;
+ case DW_FORM_data1: uAddr = rtDwarfCursor_GetU8(pCursor, 0); break;
+ case DW_FORM_data2: uAddr = rtDwarfCursor_GetU16(pCursor, 0); break;
+ case DW_FORM_data4: uAddr = rtDwarfCursor_GetU32(pCursor, 0); break;
+ case DW_FORM_data8: uAddr = rtDwarfCursor_GetU64(pCursor, 0); break;
+ case DW_FORM_udata: uAddr = rtDwarfCursor_GetULeb128(pCursor, 0); break;
+ default:
+ AssertMsgFailedReturn(("%#x\n", uForm), VERR_DWARF_UNEXPECTED_FORM);
+ }
+ if (RT_FAILURE(pCursor->rc))
+ return pCursor->rc;
+
+ PRTDWARFADDRRANGE pRange = (PRTDWARFADDRRANGE)pbMember;
+ if (pDesc->uAttr == DW_AT_low_pc)
+ {
+ if (pRange->fHaveLowAddress)
+ {
+ Log(("rtDwarfDecode_LowHighPc: Duplicate DW_AT_low_pc\n"));
+ return pCursor->rc = VERR_DWARF_BAD_INFO;
+ }
+ pRange->fHaveLowAddress = true;
+ pRange->uLowAddress = uAddr;
+ }
+ else
+ {
+ if (pRange->fHaveHighAddress)
+ {
+ Log(("rtDwarfDecode_LowHighPc: Duplicate DW_AT_high_pc\n"));
+ return pCursor->rc = VERR_DWARF_BAD_INFO;
+ }
+ pRange->fHaveHighAddress = true;
+ pRange->fHaveHighIsAddress = uForm == DW_FORM_addr;
+ if (!pRange->fHaveHighIsAddress && pRange->fHaveLowAddress)
+ {
+ pRange->fHaveHighIsAddress = true;
+ pRange->uHighAddress = uAddr + pRange->uLowAddress;
+ }
+ else
+ pRange->uHighAddress = uAddr;
+
+ }
+ pRange->cAttrs++;
+
+ Log4((" %-20s %#010llx [%s]\n", rtDwarfLog_AttrName(pDesc->uAttr), uAddr, rtDwarfLog_FormName(uForm)));
+ return VINF_SUCCESS;
+}
+
+
+/** @callback_method_impl{FNRTDWARFATTRDECODER} */
+static DECLCALLBACK(int) rtDwarfDecode_Ranges(PRTDWARFDIE pDie, uint8_t *pbMember, PCRTDWARFATTRDESC pDesc,
+ uint32_t uForm, PRTDWARFCURSOR pCursor)
+{
+ AssertReturn(ATTR_GET_SIZE(pDesc) == sizeof(RTDWARFADDRRANGE), VERR_INTERNAL_ERROR_3);
+ AssertReturn(pDesc->uAttr == DW_AT_ranges, VERR_INTERNAL_ERROR_3);
+ NOREF(pDie);
+
+ /* Decode it. */
+ uint64_t off;
+ switch (uForm)
+ {
+ case DW_FORM_addr: off = rtDwarfCursor_GetNativeUOff(pCursor, 0); break;
+ case DW_FORM_data4: off = rtDwarfCursor_GetU32(pCursor, 0); break;
+ case DW_FORM_data8: off = rtDwarfCursor_GetU64(pCursor, 0); break;
+ case DW_FORM_sec_offset: off = rtDwarfCursor_GetUOff(pCursor, 0); break;
+ default:
+ AssertMsgFailedReturn(("%#x\n", uForm), VERR_DWARF_UNEXPECTED_FORM);
+ }
+ if (RT_FAILURE(pCursor->rc))
+ return pCursor->rc;
+
+ /* Validate the offset and load the ranges. */
+ PRTDBGMODDWARF pThis = pCursor->pDwarfMod;
+ if (off >= pThis->aSections[krtDbgModDwarfSect_ranges].cb)
+ {
+ Log(("rtDwarfDecode_Ranges: bad ranges off=%#llx\n", off));
+ return pCursor->rc = VERR_DWARF_BAD_POS;
+ }
+
+ if (!pThis->aSections[krtDbgModDwarfSect_ranges].pv)
+ {
+ int rc = rtDbgModDwarfLoadSection(pThis, krtDbgModDwarfSect_ranges);
+ if (RT_FAILURE(rc))
+ return pCursor->rc = rc;
+ }
+
+ /* Store the result. */
+ PRTDWARFADDRRANGE pRange = (PRTDWARFADDRRANGE)pbMember;
+ if (pRange->fHaveRanges)
+ {
+ Log(("rtDwarfDecode_Ranges: Duplicate DW_AT_ranges\n"));
+ return pCursor->rc = VERR_DWARF_BAD_INFO;
+ }
+ pRange->fHaveRanges = true;
+ pRange->cAttrs++;
+ pRange->pbRanges = (uint8_t const *)pThis->aSections[krtDbgModDwarfSect_ranges].pv + (size_t)off;
+
+ Log4((" %-20s TODO [%s]\n", rtDwarfLog_AttrName(pDesc->uAttr), rtDwarfLog_FormName(uForm)));
+ return VINF_SUCCESS;
+}
+
+
+/** @callback_method_impl{FNRTDWARFATTRDECODER} */
+static DECLCALLBACK(int) rtDwarfDecode_Reference(PRTDWARFDIE pDie, uint8_t *pbMember, PCRTDWARFATTRDESC pDesc,
+ uint32_t uForm, PRTDWARFCURSOR pCursor)
+{
+ AssertReturn(ATTR_GET_SIZE(pDesc) == sizeof(RTDWARFREF), VERR_INTERNAL_ERROR_3);
+
+ /* Decode it. */
+ uint64_t off;
+ krtDwarfRef enmWrt = krtDwarfRef_SameUnit;
+ switch (uForm)
+ {
+ case DW_FORM_ref1: off = rtDwarfCursor_GetU8(pCursor, 0); break;
+ case DW_FORM_ref2: off = rtDwarfCursor_GetU16(pCursor, 0); break;
+ case DW_FORM_ref4: off = rtDwarfCursor_GetU32(pCursor, 0); break;
+ case DW_FORM_ref8: off = rtDwarfCursor_GetU64(pCursor, 0); break;
+ case DW_FORM_ref_udata: off = rtDwarfCursor_GetULeb128(pCursor, 0); break;
+
+ case DW_FORM_ref_addr:
+ enmWrt = krtDwarfRef_InfoSection;
+ off = rtDwarfCursor_GetUOff(pCursor, 0);
+ break;
+
+ case DW_FORM_ref_sig8:
+ enmWrt = krtDwarfRef_TypeId64;
+ off = rtDwarfCursor_GetU64(pCursor, 0);
+ break;
+
+ default:
+ AssertMsgFailedReturn(("%#x\n", uForm), VERR_DWARF_UNEXPECTED_FORM);
+ }
+ if (RT_FAILURE(pCursor->rc))
+ return pCursor->rc;
+
+ /* Validate the offset and convert to debug_info relative offsets. */
+ if (enmWrt == krtDwarfRef_InfoSection)
+ {
+ if (off >= pCursor->pDwarfMod->aSections[krtDbgModDwarfSect_info].cb)
+ {
+ Log(("rtDwarfDecode_Reference: bad info off=%#llx\n", off));
+ return pCursor->rc = VERR_DWARF_BAD_POS;
+ }
+ }
+ else if (enmWrt == krtDwarfRef_SameUnit)
+ {
+ PRTDWARFDIECOMPILEUNIT pUnit = rtDwarfDie_GetCompileUnit(pDie);
+ if (off >= pUnit->cbUnit)
+ {
+ Log(("rtDwarfDecode_Reference: bad unit off=%#llx\n", off));
+ return pCursor->rc = VERR_DWARF_BAD_POS;
+ }
+ off += pUnit->offUnit;
+ enmWrt = krtDwarfRef_InfoSection;
+ }
+ /* else: not bother verifying/resolving the indirect type reference yet. */
+
+ /* Store it */
+ PRTDWARFREF pRef = (PRTDWARFREF)pbMember;
+ pRef->enmWrt = enmWrt;
+ pRef->off = off;
+
+ Log4((" %-20s %d:%#010llx [%s]\n", rtDwarfLog_AttrName(pDesc->uAttr), enmWrt, off, rtDwarfLog_FormName(uForm)));
+ return VINF_SUCCESS;
+}
+
+
+/** @callback_method_impl{FNRTDWARFATTRDECODER} */
+static DECLCALLBACK(int) rtDwarfDecode_SectOff(PRTDWARFDIE pDie, uint8_t *pbMember, PCRTDWARFATTRDESC pDesc,
+ uint32_t uForm, PRTDWARFCURSOR pCursor)
+{
+ AssertReturn(ATTR_GET_SIZE(pDesc) == sizeof(RTDWARFREF), VERR_INTERNAL_ERROR_3);
+ NOREF(pDie);
+
+ uint64_t off;
+ switch (uForm)
+ {
+ case DW_FORM_data4: off = rtDwarfCursor_GetU32(pCursor, 0); break;
+ case DW_FORM_data8: off = rtDwarfCursor_GetU64(pCursor, 0); break;
+ case DW_FORM_sec_offset: off = rtDwarfCursor_GetUOff(pCursor, 0); break;
+ default:
+ AssertMsgFailedReturn(("%#x (%s)\n", uForm, rtDwarfLog_FormName(uForm)), VERR_DWARF_UNEXPECTED_FORM);
+ }
+ if (RT_FAILURE(pCursor->rc))
+ return pCursor->rc;
+
+ krtDbgModDwarfSect enmSect;
+ krtDwarfRef enmWrt;
+ switch (pDesc->uAttr)
+ {
+ case DW_AT_stmt_list: enmSect = krtDbgModDwarfSect_line; enmWrt = krtDwarfRef_LineSection; break;
+ case DW_AT_macro_info: enmSect = krtDbgModDwarfSect_loc; enmWrt = krtDwarfRef_LocSection; break;
+ case DW_AT_ranges: enmSect = krtDbgModDwarfSect_ranges; enmWrt = krtDwarfRef_RangesSection; break;
+ default:
+ AssertMsgFailedReturn(("%u (%s)\n", pDesc->uAttr, rtDwarfLog_AttrName(pDesc->uAttr)), VERR_INTERNAL_ERROR_4);
+ }
+ size_t cbSect = pCursor->pDwarfMod->aSections[enmSect].cb;
+ if (off >= cbSect)
+ {
+ /* Watcom generates offset past the end of the section, increasing the
+ offset by one for each compile unit. So, just fudge it. */
+ Log(("rtDwarfDecode_SectOff: bad off=%#llx, attr %#x (%s), enmSect=%d cb=%#llx; Assuming watcom/gcc.\n", off,
+ pDesc->uAttr, rtDwarfLog_AttrName(pDesc->uAttr), enmSect, cbSect));
+ off = cbSect;
+ }
+
+ PRTDWARFREF pRef = (PRTDWARFREF)pbMember;
+ pRef->enmWrt = enmWrt;
+ pRef->off = off;
+
+ Log4((" %-20s %d:%#010llx [%s]\n", rtDwarfLog_AttrName(pDesc->uAttr), enmWrt, off, rtDwarfLog_FormName(uForm)));
+ return VINF_SUCCESS;
+}
+
+
+/** @callback_method_impl{FNRTDWARFATTRDECODER} */
+static DECLCALLBACK(int) rtDwarfDecode_String(PRTDWARFDIE pDie, uint8_t *pbMember, PCRTDWARFATTRDESC pDesc,
+ uint32_t uForm, PRTDWARFCURSOR pCursor)
+{
+ AssertReturn(ATTR_GET_SIZE(pDesc) == sizeof(const char *), VERR_INTERNAL_ERROR_3);
+ NOREF(pDie);
+
+ const char *psz;
+ switch (uForm)
+ {
+ case DW_FORM_string:
+ psz = rtDwarfCursor_GetSZ(pCursor, NULL);
+ break;
+
+ case DW_FORM_strp:
+ psz = rtDwarfDecodeHlp_GetStrp(pCursor->pDwarfMod, pCursor, NULL);
+ break;
+
+ default:
+ AssertMsgFailedReturn(("%#x\n", uForm), VERR_DWARF_UNEXPECTED_FORM);
+ }
+
+ *(const char **)pbMember = psz;
+ Log4((" %-20s '%s' [%s]\n", rtDwarfLog_AttrName(pDesc->uAttr), psz, rtDwarfLog_FormName(uForm)));
+ return pCursor->rc;
+}
+
+
+/** @callback_method_impl{FNRTDWARFATTRDECODER} */
+static DECLCALLBACK(int) rtDwarfDecode_UnsignedInt(PRTDWARFDIE pDie, uint8_t *pbMember, PCRTDWARFATTRDESC pDesc,
+ uint32_t uForm, PRTDWARFCURSOR pCursor)
+{
+ NOREF(pDie);
+ uint64_t u64Val;
+ switch (uForm)
+ {
+ case DW_FORM_udata: u64Val = rtDwarfCursor_GetULeb128(pCursor, 0); break;
+ case DW_FORM_data1: u64Val = rtDwarfCursor_GetU8(pCursor, 0); break;
+ case DW_FORM_data2: u64Val = rtDwarfCursor_GetU16(pCursor, 0); break;
+ case DW_FORM_data4: u64Val = rtDwarfCursor_GetU32(pCursor, 0); break;
+ case DW_FORM_data8: u64Val = rtDwarfCursor_GetU64(pCursor, 0); break;
+ default:
+ AssertMsgFailedReturn(("%#x\n", uForm), VERR_DWARF_UNEXPECTED_FORM);
+ }
+ if (RT_FAILURE(pCursor->rc))
+ return pCursor->rc;
+
+ switch (ATTR_GET_SIZE(pDesc))
+ {
+ case 1:
+ *pbMember = (uint8_t)u64Val;
+ if (*pbMember != u64Val)
+ {
+ AssertFailed();
+ return VERR_OUT_OF_RANGE;
+ }
+ break;
+
+ case 2:
+ *(uint16_t *)pbMember = (uint16_t)u64Val;
+ if (*(uint16_t *)pbMember != u64Val)
+ {
+ AssertFailed();
+ return VERR_OUT_OF_RANGE;
+ }
+ break;
+
+ case 4:
+ *(uint32_t *)pbMember = (uint32_t)u64Val;
+ if (*(uint32_t *)pbMember != u64Val)
+ {
+ AssertFailed();
+ return VERR_OUT_OF_RANGE;
+ }
+ break;
+
+ case 8:
+ *(uint64_t *)pbMember = (uint64_t)u64Val;
+ if (*(uint64_t *)pbMember != u64Val)
+ {
+ AssertFailed();
+ return VERR_OUT_OF_RANGE;
+ }
+ break;
+
+ default:
+ AssertMsgFailedReturn(("%#x\n", ATTR_GET_SIZE(pDesc)), VERR_INTERNAL_ERROR_2);
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Initialize location interpreter state from cursor & form.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_NOT_FOUND if no location information (i.e. there is source but
+ * it resulted in no byte code).
+ * @param pLoc The location state structure to initialize.
+ * @param pCursor The cursor to read from.
+ * @param uForm The attribute form.
+ */
+static int rtDwarfLoc_Init(PRTDWARFLOCST pLoc, PRTDWARFCURSOR pCursor, uint32_t uForm)
+{
+ uint32_t cbBlock;
+ switch (uForm)
+ {
+ case DW_FORM_block1:
+ cbBlock = rtDwarfCursor_GetU8(pCursor, 0);
+ break;
+
+ case DW_FORM_block2:
+ cbBlock = rtDwarfCursor_GetU16(pCursor, 0);
+ break;
+
+ case DW_FORM_block4:
+ cbBlock = rtDwarfCursor_GetU32(pCursor, 0);
+ break;
+
+ case DW_FORM_block:
+ cbBlock = rtDwarfCursor_GetULeb128(pCursor, 0);
+ break;
+
+ default:
+ AssertMsgFailedReturn(("uForm=%#x\n", uForm), VERR_DWARF_UNEXPECTED_FORM);
+ }
+ if (!cbBlock)
+ return VERR_NOT_FOUND;
+
+ int rc = rtDwarfCursor_InitForBlock(&pLoc->Cursor, pCursor, cbBlock);
+ if (RT_FAILURE(rc))
+ return rc;
+ pLoc->iTop = -1;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Pushes a value onto the stack.
+ *
+ * @returns VINF_SUCCESS or VERR_DWARF_STACK_OVERFLOW.
+ * @param pLoc The state.
+ * @param uValue The value to push.
+ */
+static int rtDwarfLoc_Push(PRTDWARFLOCST pLoc, uint64_t uValue)
+{
+ int iTop = pLoc->iTop + 1;
+ AssertReturn((unsigned)iTop < RT_ELEMENTS(pLoc->auStack), VERR_DWARF_STACK_OVERFLOW);
+ pLoc->auStack[iTop] = uValue;
+ pLoc->iTop = iTop;
+ return VINF_SUCCESS;
+}
+
+
+static int rtDwarfLoc_Evaluate(PRTDWARFLOCST pLoc, void *pvLater, void *pvUser)
+{
+ RT_NOREF_PV(pvLater); RT_NOREF_PV(pvUser);
+
+ while (!rtDwarfCursor_IsAtEndOfUnit(&pLoc->Cursor))
+ {
+ /* Read the next opcode.*/
+ uint8_t const bOpcode = rtDwarfCursor_GetU8(&pLoc->Cursor, 0);
+
+ /* Get its operands. */
+ uint64_t uOperand1 = 0;
+ uint64_t uOperand2 = 0;
+ switch (bOpcode)
+ {
+ case DW_OP_addr:
+ uOperand1 = rtDwarfCursor_GetNativeUOff(&pLoc->Cursor, 0);
+ break;
+ case DW_OP_pick:
+ case DW_OP_const1u:
+ case DW_OP_deref_size:
+ case DW_OP_xderef_size:
+ uOperand1 = rtDwarfCursor_GetU8(&pLoc->Cursor, 0);
+ break;
+ case DW_OP_const1s:
+ uOperand1 = (int8_t)rtDwarfCursor_GetU8(&pLoc->Cursor, 0);
+ break;
+ case DW_OP_const2u:
+ uOperand1 = rtDwarfCursor_GetU16(&pLoc->Cursor, 0);
+ break;
+ case DW_OP_skip:
+ case DW_OP_bra:
+ case DW_OP_const2s:
+ uOperand1 = (int16_t)rtDwarfCursor_GetU16(&pLoc->Cursor, 0);
+ break;
+ case DW_OP_const4u:
+ uOperand1 = rtDwarfCursor_GetU32(&pLoc->Cursor, 0);
+ break;
+ case DW_OP_const4s:
+ uOperand1 = (int32_t)rtDwarfCursor_GetU32(&pLoc->Cursor, 0);
+ break;
+ case DW_OP_const8u:
+ uOperand1 = rtDwarfCursor_GetU64(&pLoc->Cursor, 0);
+ break;
+ case DW_OP_const8s:
+ uOperand1 = rtDwarfCursor_GetU64(&pLoc->Cursor, 0);
+ break;
+ case DW_OP_regx:
+ case DW_OP_piece:
+ case DW_OP_plus_uconst:
+ case DW_OP_constu:
+ uOperand1 = rtDwarfCursor_GetULeb128(&pLoc->Cursor, 0);
+ break;
+ case DW_OP_consts:
+ case DW_OP_fbreg:
+ case DW_OP_breg0+0: case DW_OP_breg0+1: case DW_OP_breg0+2: case DW_OP_breg0+3:
+ case DW_OP_breg0+4: case DW_OP_breg0+5: case DW_OP_breg0+6: case DW_OP_breg0+7:
+ case DW_OP_breg0+8: case DW_OP_breg0+9: case DW_OP_breg0+10: case DW_OP_breg0+11:
+ case DW_OP_breg0+12: case DW_OP_breg0+13: case DW_OP_breg0+14: case DW_OP_breg0+15:
+ case DW_OP_breg0+16: case DW_OP_breg0+17: case DW_OP_breg0+18: case DW_OP_breg0+19:
+ case DW_OP_breg0+20: case DW_OP_breg0+21: case DW_OP_breg0+22: case DW_OP_breg0+23:
+ case DW_OP_breg0+24: case DW_OP_breg0+25: case DW_OP_breg0+26: case DW_OP_breg0+27:
+ case DW_OP_breg0+28: case DW_OP_breg0+29: case DW_OP_breg0+30: case DW_OP_breg0+31:
+ uOperand1 = rtDwarfCursor_GetSLeb128(&pLoc->Cursor, 0);
+ break;
+ case DW_OP_bregx:
+ uOperand1 = rtDwarfCursor_GetULeb128(&pLoc->Cursor, 0);
+ uOperand2 = rtDwarfCursor_GetSLeb128(&pLoc->Cursor, 0);
+ break;
+ }
+ if (RT_FAILURE(pLoc->Cursor.rc))
+ break;
+
+ /* Interpret the opcode. */
+ int rc;
+ switch (bOpcode)
+ {
+ case DW_OP_const1u:
+ case DW_OP_const1s:
+ case DW_OP_const2u:
+ case DW_OP_const2s:
+ case DW_OP_const4u:
+ case DW_OP_const4s:
+ case DW_OP_const8u:
+ case DW_OP_const8s:
+ case DW_OP_constu:
+ case DW_OP_consts:
+ case DW_OP_addr:
+ rc = rtDwarfLoc_Push(pLoc, uOperand1);
+ break;
+ case DW_OP_lit0 + 0: case DW_OP_lit0 + 1: case DW_OP_lit0 + 2: case DW_OP_lit0 + 3:
+ case DW_OP_lit0 + 4: case DW_OP_lit0 + 5: case DW_OP_lit0 + 6: case DW_OP_lit0 + 7:
+ case DW_OP_lit0 + 8: case DW_OP_lit0 + 9: case DW_OP_lit0 + 10: case DW_OP_lit0 + 11:
+ case DW_OP_lit0 + 12: case DW_OP_lit0 + 13: case DW_OP_lit0 + 14: case DW_OP_lit0 + 15:
+ case DW_OP_lit0 + 16: case DW_OP_lit0 + 17: case DW_OP_lit0 + 18: case DW_OP_lit0 + 19:
+ case DW_OP_lit0 + 20: case DW_OP_lit0 + 21: case DW_OP_lit0 + 22: case DW_OP_lit0 + 23:
+ case DW_OP_lit0 + 24: case DW_OP_lit0 + 25: case DW_OP_lit0 + 26: case DW_OP_lit0 + 27:
+ case DW_OP_lit0 + 28: case DW_OP_lit0 + 29: case DW_OP_lit0 + 30: case DW_OP_lit0 + 31:
+ rc = rtDwarfLoc_Push(pLoc, bOpcode - DW_OP_lit0);
+ break;
+ case DW_OP_nop:
+ break;
+ case DW_OP_dup: /** @todo 0 operands. */
+ case DW_OP_drop: /** @todo 0 operands. */
+ case DW_OP_over: /** @todo 0 operands. */
+ case DW_OP_pick: /** @todo 1 operands, a 1-byte stack index. */
+ case DW_OP_swap: /** @todo 0 operands. */
+ case DW_OP_rot: /** @todo 0 operands. */
+ case DW_OP_abs: /** @todo 0 operands. */
+ case DW_OP_and: /** @todo 0 operands. */
+ case DW_OP_div: /** @todo 0 operands. */
+ case DW_OP_minus: /** @todo 0 operands. */
+ case DW_OP_mod: /** @todo 0 operands. */
+ case DW_OP_mul: /** @todo 0 operands. */
+ case DW_OP_neg: /** @todo 0 operands. */
+ case DW_OP_not: /** @todo 0 operands. */
+ case DW_OP_or: /** @todo 0 operands. */
+ case DW_OP_plus: /** @todo 0 operands. */
+ case DW_OP_plus_uconst: /** @todo 1 operands, a ULEB128 addend. */
+ case DW_OP_shl: /** @todo 0 operands. */
+ case DW_OP_shr: /** @todo 0 operands. */
+ case DW_OP_shra: /** @todo 0 operands. */
+ case DW_OP_xor: /** @todo 0 operands. */
+ case DW_OP_skip: /** @todo 1 signed 2-byte constant. */
+ case DW_OP_bra: /** @todo 1 signed 2-byte constant. */
+ case DW_OP_eq: /** @todo 0 operands. */
+ case DW_OP_ge: /** @todo 0 operands. */
+ case DW_OP_gt: /** @todo 0 operands. */
+ case DW_OP_le: /** @todo 0 operands. */
+ case DW_OP_lt: /** @todo 0 operands. */
+ case DW_OP_ne: /** @todo 0 operands. */
+ case DW_OP_reg0 + 0: case DW_OP_reg0 + 1: case DW_OP_reg0 + 2: case DW_OP_reg0 + 3: /** @todo 0 operands - reg 0..31. */
+ case DW_OP_reg0 + 4: case DW_OP_reg0 + 5: case DW_OP_reg0 + 6: case DW_OP_reg0 + 7:
+ case DW_OP_reg0 + 8: case DW_OP_reg0 + 9: case DW_OP_reg0 + 10: case DW_OP_reg0 + 11:
+ case DW_OP_reg0 + 12: case DW_OP_reg0 + 13: case DW_OP_reg0 + 14: case DW_OP_reg0 + 15:
+ case DW_OP_reg0 + 16: case DW_OP_reg0 + 17: case DW_OP_reg0 + 18: case DW_OP_reg0 + 19:
+ case DW_OP_reg0 + 20: case DW_OP_reg0 + 21: case DW_OP_reg0 + 22: case DW_OP_reg0 + 23:
+ case DW_OP_reg0 + 24: case DW_OP_reg0 + 25: case DW_OP_reg0 + 26: case DW_OP_reg0 + 27:
+ case DW_OP_reg0 + 28: case DW_OP_reg0 + 29: case DW_OP_reg0 + 30: case DW_OP_reg0 + 31:
+ case DW_OP_breg0+ 0: case DW_OP_breg0+ 1: case DW_OP_breg0+ 2: case DW_OP_breg0+ 3: /** @todo 1 operand, a SLEB128 offset. */
+ case DW_OP_breg0+ 4: case DW_OP_breg0+ 5: case DW_OP_breg0+ 6: case DW_OP_breg0+ 7:
+ case DW_OP_breg0+ 8: case DW_OP_breg0+ 9: case DW_OP_breg0+ 10: case DW_OP_breg0+ 11:
+ case DW_OP_breg0+ 12: case DW_OP_breg0+ 13: case DW_OP_breg0+ 14: case DW_OP_breg0+ 15:
+ case DW_OP_breg0+ 16: case DW_OP_breg0+ 17: case DW_OP_breg0+ 18: case DW_OP_breg0+ 19:
+ case DW_OP_breg0+ 20: case DW_OP_breg0+ 21: case DW_OP_breg0+ 22: case DW_OP_breg0+ 23:
+ case DW_OP_breg0+ 24: case DW_OP_breg0+ 25: case DW_OP_breg0+ 26: case DW_OP_breg0+ 27:
+ case DW_OP_breg0+ 28: case DW_OP_breg0+ 29: case DW_OP_breg0+ 30: case DW_OP_breg0+ 31:
+ case DW_OP_piece: /** @todo 1 operand, a ULEB128 size of piece addressed. */
+ case DW_OP_regx: /** @todo 1 operand, a ULEB128 register. */
+ case DW_OP_fbreg: /** @todo 1 operand, a SLEB128 offset. */
+ case DW_OP_bregx: /** @todo 2 operands, a ULEB128 register followed by a SLEB128 offset. */
+ case DW_OP_deref: /** @todo 0 operands. */
+ case DW_OP_deref_size: /** @todo 1 operand, a 1-byte size of data retrieved. */
+ case DW_OP_xderef: /** @todo 0 operands. */
+ case DW_OP_xderef_size: /** @todo 1 operand, a 1-byte size of data retrieved. */
+ AssertMsgFailedReturn(("bOpcode=%#x\n", bOpcode), VERR_DWARF_TODO);
+ default:
+ AssertMsgFailedReturn(("bOpcode=%#x\n", bOpcode), VERR_DWARF_UNKNOWN_LOC_OPCODE);
+ }
+ }
+
+ return pLoc->Cursor.rc;
+}
+
+
+/** @callback_method_impl{FNRTDWARFATTRDECODER} */
+static DECLCALLBACK(int) rtDwarfDecode_SegmentLoc(PRTDWARFDIE pDie, uint8_t *pbMember, PCRTDWARFATTRDESC pDesc,
+ uint32_t uForm, PRTDWARFCURSOR pCursor)
+{
+ NOREF(pDie);
+ AssertReturn(ATTR_GET_SIZE(pDesc) == 2, VERR_DWARF_IPE);
+
+ int rc;
+ if ( uForm == DW_FORM_block
+ || uForm == DW_FORM_block1
+ || uForm == DW_FORM_block2
+ || uForm == DW_FORM_block4)
+ {
+ RTDWARFLOCST LocSt;
+ rc = rtDwarfLoc_Init(&LocSt, pCursor, uForm);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtDwarfLoc_Evaluate(&LocSt, NULL, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if (LocSt.iTop >= 0)
+ {
+ *(uint16_t *)pbMember = LocSt.auStack[LocSt.iTop];
+ Log4((" %-20s %#06llx [%s]\n", rtDwarfLog_AttrName(pDesc->uAttr),
+ LocSt.auStack[LocSt.iTop], rtDwarfLog_FormName(uForm)));
+ return VINF_SUCCESS;
+ }
+ rc = VERR_DWARF_STACK_UNDERFLOW;
+ }
+ }
+ }
+ else
+ rc = rtDwarfDecode_UnsignedInt(pDie, pbMember, pDesc, uForm, pCursor);
+ return rc;
+}
+
+/*
+ *
+ * DWARF debug_info parser
+ * DWARF debug_info parser
+ * DWARF debug_info parser
+ *
+ */
+
+
+/**
+ * Special hack to get the name and/or linkage name for a subprogram via a
+ * specification reference.
+ *
+ * Since this is a hack, we ignore failure.
+ *
+ * If we want to really make use of DWARF info, we'll have to create some kind
+ * of lookup tree for handling this. But currently we don't, so a hack will
+ * suffice.
+ *
+ * @param pThis The DWARF instance.
+ * @param pSubProgram The subprogram which is short on names.
+ */
+static void rtDwarfInfo_TryGetSubProgramNameFromSpecRef(PRTDBGMODDWARF pThis, PRTDWARFDIESUBPROGRAM pSubProgram)
+{
+ /*
+ * Must have a spec ref, and it must be in the info section.
+ */
+ if (pSubProgram->SpecRef.enmWrt != krtDwarfRef_InfoSection)
+ return;
+
+ /*
+ * Create a cursor for reading the info and then the abbrivation code
+ * starting the off the DIE.
+ */
+ RTDWARFCURSOR InfoCursor;
+ int rc = rtDwarfCursor_InitWithOffset(&InfoCursor, pThis, krtDbgModDwarfSect_info, pSubProgram->SpecRef.off);
+ if (RT_FAILURE(rc))
+ return;
+
+ uint32_t uAbbrCode = rtDwarfCursor_GetULeb128AsU32(&InfoCursor, UINT32_MAX);
+ if (uAbbrCode)
+ {
+ /* Only references to subprogram tags are interesting here. */
+ PCRTDWARFABBREV pAbbrev = rtDwarfAbbrev_Lookup(pThis, uAbbrCode);
+ if ( pAbbrev
+ && pAbbrev->uTag == DW_TAG_subprogram)
+ {
+ /*
+ * Use rtDwarfInfo_ParseDie to do the parsing, but with a different
+ * attribute spec than usual.
+ */
+ rtDwarfInfo_ParseDie(pThis, &pSubProgram->Core, &g_SubProgramSpecHackDesc, &InfoCursor,
+ pAbbrev, false /*fInitDie*/);
+ }
+ }
+
+ rtDwarfCursor_Delete(&InfoCursor, VINF_SUCCESS);
+}
+
+
+/**
+ * Select which name to use.
+ *
+ * @returns One of the names.
+ * @param pszName The DWARF name, may exclude namespace and class.
+ * Can also be NULL.
+ * @param pszLinkageName The linkage name. Can be NULL.
+ */
+static const char *rtDwarfInfo_SelectName(const char *pszName, const char *pszLinkageName)
+{
+ if (!pszName || !pszLinkageName)
+ return pszName ? pszName : pszLinkageName;
+
+ /*
+ * Some heuristics for selecting the link name if the normal name is missing
+ * namespace or class prefixes.
+ */
+ size_t cchName = strlen(pszName);
+ size_t cchLinkageName = strlen(pszLinkageName);
+ if (cchLinkageName <= cchName + 1)
+ return pszName;
+
+ const char *psz = strstr(pszLinkageName, pszName);
+ if (!psz || psz - pszLinkageName < 4)
+ return pszName;
+
+ return pszLinkageName;
+}
+
+
+/**
+ * Parse the attributes of a DIE.
+ *
+ * @returns IPRT status code.
+ * @param pThis The DWARF instance.
+ * @param pDie The internal DIE structure to fill.
+ */
+static int rtDwarfInfo_SnoopSymbols(PRTDBGMODDWARF pThis, PRTDWARFDIE pDie)
+{
+ int rc = VINF_SUCCESS;
+ switch (pDie->uTag)
+ {
+ case DW_TAG_subprogram:
+ {
+ PRTDWARFDIESUBPROGRAM pSubProgram = (PRTDWARFDIESUBPROGRAM)pDie;
+
+ /* Obtain referenced specification there is only partial info. */
+ if ( pSubProgram->PcRange.cAttrs
+ && !pSubProgram->pszName)
+ rtDwarfInfo_TryGetSubProgramNameFromSpecRef(pThis, pSubProgram);
+
+ if (pSubProgram->PcRange.cAttrs)
+ {
+ if (pSubProgram->PcRange.fHaveRanges)
+ Log5(("subprogram %s (%s) <implement ranges>\n", pSubProgram->pszName, pSubProgram->pszLinkageName));
+ else
+ {
+ Log5(("subprogram %s (%s) %#llx-%#llx%s\n", pSubProgram->pszName, pSubProgram->pszLinkageName,
+ pSubProgram->PcRange.uLowAddress, pSubProgram->PcRange.uHighAddress,
+ pSubProgram->PcRange.cAttrs == 2 ? "" : " !bad!"));
+ if ( ( pSubProgram->pszName || pSubProgram->pszLinkageName)
+ && pSubProgram->PcRange.cAttrs == 2)
+ {
+ if (pThis->iWatcomPass == 1)
+ rc = rtDbgModDwarfRecordSegOffset(pThis, pSubProgram->uSegment, pSubProgram->PcRange.uHighAddress);
+ else
+ {
+ RTDBGSEGIDX iSeg;
+ RTUINTPTR offSeg;
+ rc = rtDbgModDwarfLinkAddressToSegOffset(pThis, pSubProgram->uSegment,
+ pSubProgram->PcRange.uLowAddress,
+ &iSeg, &offSeg);
+ if (RT_SUCCESS(rc))
+ {
+ uint64_t cb;
+ if (pSubProgram->PcRange.uHighAddress >= pSubProgram->PcRange.uLowAddress)
+ cb = pSubProgram->PcRange.uHighAddress - pSubProgram->PcRange.uLowAddress;
+ else
+ cb = 1;
+ rc = RTDbgModSymbolAdd(pThis->hCnt,
+ rtDwarfInfo_SelectName(pSubProgram->pszName, pSubProgram->pszLinkageName),
+ iSeg, offSeg, cb, 0 /*fFlags*/, NULL /*piOrdinal*/);
+ if (RT_FAILURE(rc))
+ {
+ if ( rc == VERR_DBG_DUPLICATE_SYMBOL
+ || rc == VERR_DBG_ADDRESS_CONFLICT /** @todo figure why this happens with 10.6.8 mach_kernel, 32-bit. */
+ )
+ rc = VINF_SUCCESS;
+ else
+ AssertMsgFailed(("%Rrc\n", rc));
+ }
+ }
+ else if ( pSubProgram->PcRange.uLowAddress == 0 /* see with vmlinux */
+ && pSubProgram->PcRange.uHighAddress == 0)
+ {
+ Log5(("rtDbgModDwarfLinkAddressToSegOffset: Ignoring empty range.\n"));
+ rc = VINF_SUCCESS; /* ignore */
+ }
+ else
+ {
+ AssertRC(rc);
+ Log5(("rtDbgModDwarfLinkAddressToSegOffset failed: %Rrc\n", rc));
+ }
+ }
+ }
+ }
+ }
+ else
+ Log5(("subprogram %s (%s) external\n", pSubProgram->pszName, pSubProgram->pszLinkageName));
+ break;
+ }
+
+ case DW_TAG_label:
+ {
+ PCRTDWARFDIELABEL pLabel = (PCRTDWARFDIELABEL)pDie;
+ //if (pLabel->fExternal)
+ {
+ Log5(("label %s %#x:%#llx\n", pLabel->pszName, pLabel->uSegment, pLabel->Address.uAddress));
+ if (pThis->iWatcomPass == 1)
+ rc = rtDbgModDwarfRecordSegOffset(pThis, pLabel->uSegment, pLabel->Address.uAddress);
+ else if (pLabel->pszName && *pLabel->pszName != '\0') /* Seen empty labels with isolinux. */
+ {
+ RTDBGSEGIDX iSeg;
+ RTUINTPTR offSeg;
+ rc = rtDbgModDwarfLinkAddressToSegOffset(pThis, pLabel->uSegment, pLabel->Address.uAddress,
+ &iSeg, &offSeg);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTDbgModSymbolAdd(pThis->hCnt, pLabel->pszName, iSeg, offSeg, 0 /*cb*/,
+ 0 /*fFlags*/, NULL /*piOrdinal*/);
+ AssertMsg(RT_SUCCESS(rc) || rc == VERR_DBG_ADDRESS_CONFLICT,
+ ("%Rrc %s %x:%x\n", rc, pLabel->pszName, iSeg, offSeg));
+ }
+ else
+ Log5(("rtDbgModDwarfLinkAddressToSegOffset failed: %Rrc\n", rc));
+
+ /* Ignore errors regarding local labels. */
+ if (RT_FAILURE(rc) && !pLabel->fExternal)
+ rc = -rc;
+ }
+
+ }
+ break;
+ }
+
+ }
+ return rc;
+}
+
+
+/**
+ * Initializes the non-core fields of an internal DIE structure.
+ *
+ * @param pDie The DIE structure.
+ * @param pDieDesc The DIE descriptor.
+ */
+static void rtDwarfInfo_InitDie(PRTDWARFDIE pDie, PCRTDWARFDIEDESC pDieDesc)
+{
+ size_t i = pDieDesc->cAttributes;
+ while (i-- > 0)
+ {
+ switch (pDieDesc->paAttributes[i].cbInit & ATTR_INIT_MASK)
+ {
+ case ATTR_INIT_ZERO:
+ /* Nothing to do (RTMemAllocZ). */
+ break;
+
+ case ATTR_INIT_FFFS:
+ switch (pDieDesc->paAttributes[i].cbInit & ATTR_SIZE_MASK)
+ {
+ case 1:
+ *(uint8_t *)((uintptr_t)pDie + pDieDesc->paAttributes[i].off) = UINT8_MAX;
+ break;
+ case 2:
+ *(uint16_t *)((uintptr_t)pDie + pDieDesc->paAttributes[i].off) = UINT16_MAX;
+ break;
+ case 4:
+ *(uint32_t *)((uintptr_t)pDie + pDieDesc->paAttributes[i].off) = UINT32_MAX;
+ break;
+ case 8:
+ *(uint64_t *)((uintptr_t)pDie + pDieDesc->paAttributes[i].off) = UINT64_MAX;
+ break;
+ default:
+ AssertFailed();
+ memset((uint8_t *)pDie + pDieDesc->paAttributes[i].off, 0xff,
+ pDieDesc->paAttributes[i].cbInit & ATTR_SIZE_MASK);
+ break;
+ }
+ break;
+
+ default:
+ AssertFailed();
+ }
+ }
+}
+
+
+/**
+ * Creates a new internal DIE structure and links it up.
+ *
+ * @returns Pointer to the new DIE structure.
+ * @param pThis The DWARF instance.
+ * @param pDieDesc The DIE descriptor (for size and init).
+ * @param pAbbrev The abbreviation cache entry.
+ * @param pParent The parent DIE (NULL if unit).
+ */
+static PRTDWARFDIE rtDwarfInfo_NewDie(PRTDBGMODDWARF pThis, PCRTDWARFDIEDESC pDieDesc,
+ PCRTDWARFABBREV pAbbrev, PRTDWARFDIE pParent)
+{
+ NOREF(pThis);
+ Assert(pDieDesc->cbDie >= sizeof(RTDWARFDIE));
+#ifdef RTDBGMODDWARF_WITH_MEM_CACHE
+ uint32_t iAllocator = pDieDesc->cbDie > pThis->aDieAllocators[0].cbMax;
+ Assert(pDieDesc->cbDie <= pThis->aDieAllocators[iAllocator].cbMax);
+ PRTDWARFDIE pDie = (PRTDWARFDIE)RTMemCacheAlloc(pThis->aDieAllocators[iAllocator].hMemCache);
+#else
+ PRTDWARFDIE pDie = (PRTDWARFDIE)RTMemAllocZ(pDieDesc->cbDie);
+#endif
+ if (pDie)
+ {
+#ifdef RTDBGMODDWARF_WITH_MEM_CACHE
+ RT_BZERO(pDie, pDieDesc->cbDie);
+ pDie->iAllocator = iAllocator;
+#endif
+ rtDwarfInfo_InitDie(pDie, pDieDesc);
+
+ pDie->uTag = pAbbrev->uTag;
+ pDie->offSpec = pAbbrev->offSpec;
+ pDie->pParent = pParent;
+ if (pParent)
+ RTListAppend(&pParent->ChildList, &pDie->SiblingNode);
+ else
+ RTListInit(&pDie->SiblingNode);
+ RTListInit(&pDie->ChildList);
+
+ }
+ return pDie;
+}
+
+
+/**
+ * Free all children of a DIE.
+ *
+ * @param pThis The DWARF instance.
+ * @param pParentDie The parent DIE.
+ */
+static void rtDwarfInfo_FreeChildren(PRTDBGMODDWARF pThis, PRTDWARFDIE pParentDie)
+{
+ PRTDWARFDIE pChild, pNextChild;
+ RTListForEachSafe(&pParentDie->ChildList, pChild, pNextChild, RTDWARFDIE, SiblingNode)
+ {
+ if (!RTListIsEmpty(&pChild->ChildList))
+ rtDwarfInfo_FreeChildren(pThis, pChild);
+ RTListNodeRemove(&pChild->SiblingNode);
+#ifdef RTDBGMODDWARF_WITH_MEM_CACHE
+ RTMemCacheFree(pThis->aDieAllocators[pChild->iAllocator].hMemCache, pChild);
+#else
+ RTMemFree(pChild);
+#endif
+ }
+}
+
+
+/**
+ * Free a DIE an all its children.
+ *
+ * @param pThis The DWARF instance.
+ * @param pDie The DIE to free.
+ */
+static void rtDwarfInfo_FreeDie(PRTDBGMODDWARF pThis, PRTDWARFDIE pDie)
+{
+ rtDwarfInfo_FreeChildren(pThis, pDie);
+ RTListNodeRemove(&pDie->SiblingNode);
+#ifdef RTDBGMODDWARF_WITH_MEM_CACHE
+ RTMemCacheFree(pThis->aDieAllocators[pDie->iAllocator].hMemCache, pDie);
+#else
+ RTMemFree(pChild);
+#endif
+}
+
+
+/**
+ * Skips a form.
+ * @returns IPRT status code
+ * @param pCursor The cursor.
+ * @param uForm The form to skip.
+ */
+static int rtDwarfInfo_SkipForm(PRTDWARFCURSOR pCursor, uint32_t uForm)
+{
+ switch (uForm)
+ {
+ case DW_FORM_addr:
+ return rtDwarfCursor_SkipBytes(pCursor, pCursor->cbNativeAddr);
+
+ case DW_FORM_block:
+ case DW_FORM_exprloc:
+ return rtDwarfCursor_SkipBytes(pCursor, rtDwarfCursor_GetULeb128(pCursor, 0));
+
+ case DW_FORM_block1:
+ return rtDwarfCursor_SkipBytes(pCursor, rtDwarfCursor_GetU8(pCursor, 0));
+
+ case DW_FORM_block2:
+ return rtDwarfCursor_SkipBytes(pCursor, rtDwarfCursor_GetU16(pCursor, 0));
+
+ case DW_FORM_block4:
+ return rtDwarfCursor_SkipBytes(pCursor, rtDwarfCursor_GetU32(pCursor, 0));
+
+ case DW_FORM_data1:
+ case DW_FORM_ref1:
+ case DW_FORM_flag:
+ return rtDwarfCursor_SkipBytes(pCursor, 1);
+
+ case DW_FORM_data2:
+ case DW_FORM_ref2:
+ return rtDwarfCursor_SkipBytes(pCursor, 2);
+
+ case DW_FORM_data4:
+ case DW_FORM_ref4:
+ return rtDwarfCursor_SkipBytes(pCursor, 4);
+
+ case DW_FORM_data8:
+ case DW_FORM_ref8:
+ case DW_FORM_ref_sig8:
+ return rtDwarfCursor_SkipBytes(pCursor, 8);
+
+ case DW_FORM_udata:
+ case DW_FORM_sdata:
+ case DW_FORM_ref_udata:
+ return rtDwarfCursor_SkipLeb128(pCursor);
+
+ case DW_FORM_string:
+ rtDwarfCursor_GetSZ(pCursor, NULL);
+ return pCursor->rc;
+
+ case DW_FORM_indirect:
+ return rtDwarfInfo_SkipForm(pCursor, rtDwarfCursor_GetULeb128AsU32(pCursor, UINT32_MAX));
+
+ case DW_FORM_strp:
+ case DW_FORM_ref_addr:
+ case DW_FORM_sec_offset:
+ return rtDwarfCursor_SkipBytes(pCursor, pCursor->f64bitDwarf ? 8 : 4);
+
+ case DW_FORM_flag_present:
+ return pCursor->rc; /* no data */
+
+ default:
+ Log(("rtDwarfInfo_SkipForm: Unknown form %#x\n", uForm));
+ return VERR_DWARF_UNKNOWN_FORM;
+ }
+}
+
+
+
+#ifdef SOME_UNUSED_FUNCTION
+/**
+ * Skips a DIE.
+ *
+ * @returns IPRT status code.
+ * @param pCursor The cursor.
+ * @param pAbbrevCursor The abbreviation cursor.
+ */
+static int rtDwarfInfo_SkipDie(PRTDWARFCURSOR pCursor, PRTDWARFCURSOR pAbbrevCursor)
+{
+ for (;;)
+ {
+ uint32_t uAttr = rtDwarfCursor_GetULeb128AsU32(pAbbrevCursor, 0);
+ uint32_t uForm = rtDwarfCursor_GetULeb128AsU32(pAbbrevCursor, 0);
+ if (uAttr == 0 && uForm == 0)
+ break;
+
+ int rc = rtDwarfInfo_SkipForm(pCursor, uForm);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ return RT_FAILURE(pCursor->rc) ? pCursor->rc : pAbbrevCursor->rc;
+}
+#endif
+
+
+/**
+ * Parse the attributes of a DIE.
+ *
+ * @returns IPRT status code.
+ * @param pThis The DWARF instance.
+ * @param pDie The internal DIE structure to fill.
+ * @param pDieDesc The DIE descriptor.
+ * @param pCursor The debug_info cursor.
+ * @param pAbbrev The abbreviation cache entry.
+ * @param fInitDie Whether to initialize the DIE first. If not (@c
+ * false) it's safe to assume we're following a
+ * DW_AT_specification or DW_AT_abstract_origin,
+ * and that we shouldn't be snooping any symbols.
+ */
+static int rtDwarfInfo_ParseDie(PRTDBGMODDWARF pThis, PRTDWARFDIE pDie, PCRTDWARFDIEDESC pDieDesc,
+ PRTDWARFCURSOR pCursor, PCRTDWARFABBREV pAbbrev, bool fInitDie)
+{
+ RTDWARFCURSOR AbbrevCursor;
+ int rc = rtDwarfCursor_InitWithOffset(&AbbrevCursor, pThis, krtDbgModDwarfSect_abbrev, pAbbrev->offSpec);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if (fInitDie)
+ rtDwarfInfo_InitDie(pDie, pDieDesc);
+ for (;;)
+ {
+#ifdef LOG_ENABLED
+ uint32_t const off = (uint32_t)(AbbrevCursor.pb - AbbrevCursor.pbStart);
+#endif
+ uint32_t uAttr = rtDwarfCursor_GetULeb128AsU32(&AbbrevCursor, 0);
+ uint32_t uForm = rtDwarfCursor_GetULeb128AsU32(&AbbrevCursor, 0);
+ Log4((" %04x: %-23s [%s]\n", off, rtDwarfLog_AttrName(uAttr), rtDwarfLog_FormName(uForm)));
+ if (uAttr == 0)
+ break;
+ if (uForm == DW_FORM_indirect)
+ uForm = rtDwarfCursor_GetULeb128AsU32(pCursor, 0);
+
+ /* Look up the attribute in the descriptor and invoke the decoder. */
+ PCRTDWARFATTRDESC pAttr = NULL;
+ size_t i = pDieDesc->cAttributes;
+ while (i-- > 0)
+ if (pDieDesc->paAttributes[i].uAttr == uAttr)
+ {
+ pAttr = &pDieDesc->paAttributes[i];
+ rc = pAttr->pfnDecoder(pDie, (uint8_t *)pDie + pAttr->off, pAttr, uForm, pCursor);
+ break;
+ }
+
+ /* Some house keeping. */
+ if (pAttr)
+ pDie->cDecodedAttrs++;
+ else
+ {
+ pDie->cUnhandledAttrs++;
+ rc = rtDwarfInfo_SkipForm(pCursor, uForm);
+ }
+ if (RT_FAILURE(rc))
+ break;
+ }
+
+ rc = rtDwarfCursor_Delete(&AbbrevCursor, rc);
+ if (RT_SUCCESS(rc))
+ rc = pCursor->rc;
+
+ /*
+ * Snoop up symbols on the way out.
+ */
+ if (RT_SUCCESS(rc) && fInitDie)
+ {
+ rc = rtDwarfInfo_SnoopSymbols(pThis, pDie);
+ /* Ignore duplicates, get work done instead. */
+ /** @todo clean up global/static symbol mess. */
+ if (rc == VERR_DBG_DUPLICATE_SYMBOL || rc == VERR_DBG_ADDRESS_CONFLICT)
+ rc = VINF_SUCCESS;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Load the debug information of a unit.
+ *
+ * @returns IPRT status code.
+ * @param pThis The DWARF instance.
+ * @param pCursor The debug_info cursor.
+ * @param fKeepDies Whether to keep the DIEs or discard them as soon
+ * as possible.
+ */
+static int rtDwarfInfo_LoadUnit(PRTDBGMODDWARF pThis, PRTDWARFCURSOR pCursor, bool fKeepDies)
+{
+ Log(("rtDwarfInfo_LoadUnit: %#x\n", rtDwarfCursor_CalcSectOffsetU32(pCursor)));
+
+ /*
+ * Read the compilation unit header.
+ */
+ uint64_t offUnit = rtDwarfCursor_CalcSectOffsetU32(pCursor);
+ uint64_t cbUnit = rtDwarfCursor_GetInitialLength(pCursor);
+ cbUnit += rtDwarfCursor_CalcSectOffsetU32(pCursor) - offUnit;
+ uint16_t const uVer = rtDwarfCursor_GetUHalf(pCursor, 0);
+ if ( uVer < 2
+ || uVer > 4)
+ return rtDwarfCursor_SkipUnit(pCursor);
+ uint64_t const offAbbrev = rtDwarfCursor_GetUOff(pCursor, UINT64_MAX);
+ uint8_t const cbNativeAddr = rtDwarfCursor_GetU8(pCursor, UINT8_MAX);
+ if (RT_FAILURE(pCursor->rc))
+ return pCursor->rc;
+ Log((" uVer=%d offAbbrev=%#llx cbNativeAddr=%d\n", uVer, offAbbrev, cbNativeAddr));
+
+ /*
+ * Set up the abbreviation cache and store the native address size in the cursor.
+ */
+ if (offAbbrev > UINT32_MAX)
+ {
+ Log(("Unexpected abbrviation code offset of %#llx\n", offAbbrev));
+ return VERR_DWARF_BAD_INFO;
+ }
+ rtDwarfAbbrev_SetUnitOffset(pThis, (uint32_t)offAbbrev);
+ pCursor->cbNativeAddr = cbNativeAddr;
+
+ /*
+ * The first DIE is a compile or partial unit, parse it here.
+ */
+ uint32_t uAbbrCode = rtDwarfCursor_GetULeb128AsU32(pCursor, UINT32_MAX);
+ if (!uAbbrCode)
+ {
+ Log(("Unexpected abbrviation code of zero\n"));
+ return VERR_DWARF_BAD_INFO;
+ }
+ PCRTDWARFABBREV pAbbrev = rtDwarfAbbrev_Lookup(pThis, uAbbrCode);
+ if (!pAbbrev)
+ return VERR_DWARF_ABBREV_NOT_FOUND;
+ if ( pAbbrev->uTag != DW_TAG_compile_unit
+ && pAbbrev->uTag != DW_TAG_partial_unit)
+ {
+ Log(("Unexpected compile/partial unit tag %#x\n", pAbbrev->uTag));
+ return VERR_DWARF_BAD_INFO;
+ }
+
+ PRTDWARFDIECOMPILEUNIT pUnit;
+ pUnit = (PRTDWARFDIECOMPILEUNIT)rtDwarfInfo_NewDie(pThis, &g_CompileUnitDesc, pAbbrev, NULL /*pParent*/);
+ if (!pUnit)
+ return VERR_NO_MEMORY;
+ pUnit->offUnit = offUnit;
+ pUnit->cbUnit = cbUnit;
+ pUnit->offAbbrev = offAbbrev;
+ pUnit->cbNativeAddr = cbNativeAddr;
+ pUnit->uDwarfVer = (uint8_t)uVer;
+ RTListAppend(&pThis->CompileUnitList, &pUnit->Core.SiblingNode);
+
+ int rc = rtDwarfInfo_ParseDie(pThis, &pUnit->Core, &g_CompileUnitDesc, pCursor, pAbbrev, true /*fInitDie*/);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Parse DIEs.
+ */
+ uint32_t cDepth = 0;
+ PRTDWARFDIE pParentDie = &pUnit->Core;
+ while (!rtDwarfCursor_IsAtEndOfUnit(pCursor))
+ {
+#ifdef LOG_ENABLED
+ uint32_t offLog = rtDwarfCursor_CalcSectOffsetU32(pCursor);
+#endif
+ uAbbrCode = rtDwarfCursor_GetULeb128AsU32(pCursor, UINT32_MAX);
+ if (!uAbbrCode)
+ {
+ /* End of siblings, up one level. (Is this correct?) */
+ if (pParentDie->pParent)
+ {
+ pParentDie = pParentDie->pParent;
+ cDepth--;
+ if (!fKeepDies && pParentDie->pParent)
+ rtDwarfInfo_FreeChildren(pThis, pParentDie);
+ }
+ }
+ else
+ {
+ /*
+ * Look up the abbreviation and match the tag up with a descriptor.
+ */
+ pAbbrev = rtDwarfAbbrev_Lookup(pThis, uAbbrCode);
+ if (!pAbbrev)
+ return VERR_DWARF_ABBREV_NOT_FOUND;
+
+ PCRTDWARFDIEDESC pDieDesc;
+ const char *pszName;
+ if (pAbbrev->uTag < RT_ELEMENTS(g_aTagDescs))
+ {
+ Assert(g_aTagDescs[pAbbrev->uTag].uTag == pAbbrev->uTag || g_aTagDescs[pAbbrev->uTag].uTag == 0);
+ pszName = g_aTagDescs[pAbbrev->uTag].pszName;
+ pDieDesc = g_aTagDescs[pAbbrev->uTag].pDesc;
+ }
+ else
+ {
+ pszName = "<unknown>";
+ pDieDesc = &g_CoreDieDesc;
+ }
+ Log4(("%08x: %*stag=%s (%#x, abbrev %u @ %#x)%s\n", offLog, cDepth * 2, "", pszName,
+ pAbbrev->uTag, uAbbrCode, pAbbrev->offSpec - pAbbrev->cbHdr, pAbbrev->fChildren ? " has children" : ""));
+
+ /*
+ * Create a new internal DIE structure and parse the
+ * attributes.
+ */
+ PRTDWARFDIE pNewDie = rtDwarfInfo_NewDie(pThis, pDieDesc, pAbbrev, pParentDie);
+ if (!pNewDie)
+ return VERR_NO_MEMORY;
+
+ if (pAbbrev->fChildren)
+ {
+ pParentDie = pNewDie;
+ cDepth++;
+ }
+
+ rc = rtDwarfInfo_ParseDie(pThis, pNewDie, pDieDesc, pCursor, pAbbrev, true /*fInitDie*/);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if (!fKeepDies && !pAbbrev->fChildren)
+ rtDwarfInfo_FreeDie(pThis, pNewDie);
+ }
+ } /* while more DIEs */
+
+
+ /* Unlink and free child DIEs if told to do so. */
+ if (!fKeepDies)
+ rtDwarfInfo_FreeChildren(pThis, &pUnit->Core);
+
+ return RT_SUCCESS(rc) ? pCursor->rc : rc;
+}
+
+
+/**
+ * Extracts the symbols.
+ *
+ * The symbols are insered into the debug info container.
+ *
+ * @returns IPRT status code
+ * @param pThis The DWARF instance.
+ */
+static int rtDwarfInfo_LoadAll(PRTDBGMODDWARF pThis)
+{
+ RTDWARFCURSOR Cursor;
+ int rc = rtDwarfCursor_Init(&Cursor, pThis, krtDbgModDwarfSect_info);
+ if (RT_SUCCESS(rc))
+ {
+ while ( !rtDwarfCursor_IsAtEnd(&Cursor)
+ && RT_SUCCESS(rc))
+ rc = rtDwarfInfo_LoadUnit(pThis, &Cursor, false /* fKeepDies */);
+
+ rc = rtDwarfCursor_Delete(&Cursor, rc);
+ }
+ return rc;
+}
+
+
+
+/*
+ *
+ * Public and image level symbol handling.
+ * Public and image level symbol handling.
+ * Public and image level symbol handling.
+ * Public and image level symbol handling.
+ *
+ *
+ */
+
+#define RTDBGDWARF_SYM_ENUM_BASE_ADDRESS UINT32_C(0x200000)
+
+/** @callback_method_impl{FNRTLDRENUMSYMS,
+ * Adds missing symbols from the image symbol table.} */
+static DECLCALLBACK(int) rtDwarfSyms_EnumSymbolsCallback(RTLDRMOD hLdrMod, const char *pszSymbol, unsigned uSymbol,
+ RTLDRADDR Value, void *pvUser)
+{
+ PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pvUser;
+ RT_NOREF_PV(hLdrMod); RT_NOREF_PV(uSymbol);
+ Assert(pThis->iWatcomPass != 1);
+
+ RTLDRADDR uRva = Value - RTDBGDWARF_SYM_ENUM_BASE_ADDRESS;
+ if ( Value >= RTDBGDWARF_SYM_ENUM_BASE_ADDRESS
+ && uRva < _1G)
+ {
+ RTDBGSYMBOL SymInfo;
+ RTINTPTR offDisp;
+ int rc = RTDbgModSymbolByAddr(pThis->hCnt, RTDBGSEGIDX_RVA, uRva, RTDBGSYMADDR_FLAGS_LESS_OR_EQUAL, &offDisp, &SymInfo);
+ if ( RT_FAILURE(rc)
+ || offDisp != 0)
+ {
+ rc = RTDbgModSymbolAdd(pThis->hCnt, pszSymbol, RTDBGSEGIDX_RVA, uRva, 1, 0 /*fFlags*/, NULL /*piOrdinal*/);
+ Log(("Dwarf: Symbol #%05u %#018RTptr %s [%Rrc]\n", uSymbol, Value, pszSymbol, rc)); NOREF(rc);
+ }
+ }
+ else
+ Log(("Dwarf: Symbol #%05u %#018RTptr '%s' [SKIPPED - INVALID ADDRESS]\n", uSymbol, Value, pszSymbol));
+ return VINF_SUCCESS;
+}
+
+
+
+/**
+ * Loads additional symbols from the pubnames section and the executable image.
+ *
+ * The symbols are insered into the debug info container.
+ *
+ * @returns IPRT status code
+ * @param pThis The DWARF instance.
+ */
+static int rtDwarfSyms_LoadAll(PRTDBGMODDWARF pThis)
+{
+ /*
+ * pubnames.
+ */
+ int rc = VINF_SUCCESS;
+ if (pThis->aSections[krtDbgModDwarfSect_pubnames].fPresent)
+ {
+// RTDWARFCURSOR Cursor;
+// int rc = rtDwarfCursor_Init(&Cursor, pThis, krtDbgModDwarfSect_info);
+// if (RT_SUCCESS(rc))
+// {
+// while ( !rtDwarfCursor_IsAtEnd(&Cursor)
+// && RT_SUCCESS(rc))
+// rc = rtDwarfInfo_LoadUnit(pThis, &Cursor, false /* fKeepDies */);
+//
+// rc = rtDwarfCursor_Delete(&Cursor, rc);
+// }
+// return rc;
+ }
+
+ /*
+ * The executable image.
+ */
+ if ( pThis->pImgMod
+ && pThis->pImgMod->pImgVt->pfnEnumSymbols
+ && pThis->iWatcomPass != 1
+ && RT_SUCCESS(rc))
+ {
+ rc = pThis->pImgMod->pImgVt->pfnEnumSymbols(pThis->pImgMod,
+ RTLDR_ENUM_SYMBOL_FLAGS_ALL | RTLDR_ENUM_SYMBOL_FLAGS_NO_FWD,
+ RTDBGDWARF_SYM_ENUM_BASE_ADDRESS,
+ rtDwarfSyms_EnumSymbolsCallback,
+ pThis);
+ }
+
+ return rc;
+}
+
+
+
+
+/*
+ *
+ * DWARF Debug module implementation.
+ * DWARF Debug module implementation.
+ * DWARF Debug module implementation.
+ *
+ */
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnUnwindFrame} */
+static DECLCALLBACK(int) rtDbgModDwarf_UnwindFrame(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState)
+{
+ PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv;
+
+ /*
+ * Unwinding info is stored in the '.debug_frame' section, or altertively
+ * in the '.eh_frame' one in the image. In the latter case the dbgmodldr.cpp
+ * part of the operation will take care of it. Since the sections contain the
+ * same data, we just create a cursor and call a common function to do the job.
+ */
+ if (pThis->aSections[krtDbgModDwarfSect_frame].fPresent)
+ {
+ RTDWARFCURSOR Cursor;
+ int rc = rtDwarfCursor_Init(&Cursor, pThis, krtDbgModDwarfSect_frame);
+ if (RT_SUCCESS(rc))
+ {
+ /* Figure default pointer encoding from image arch. */
+ uint8_t bPtrEnc = rtDwarfUnwind_ArchToPtrEnc(pMod->pImgVt->pfnGetArch(pMod));
+
+ /* Make sure we've got both seg:off and rva for the input address. */
+ RTUINTPTR uRva = off;
+ if (iSeg == RTDBGSEGIDX_RVA)
+ rtDbgModDwarfRvaToSegOffset(pThis, uRva, &iSeg, &off);
+ else
+ rtDbgModDwarfSegOffsetToRva(pThis, iSeg, off, &uRva);
+
+ /* Do the work */
+ rc = rtDwarfUnwind_Slow(&Cursor, 0 /** @todo .debug_frame RVA*/, iSeg, off, uRva,
+ pState, bPtrEnc, false /*fIsEhFrame*/, pMod->pImgVt->pfnGetArch(pMod));
+
+ rc = rtDwarfCursor_Delete(&Cursor, rc);
+ }
+ return rc;
+ }
+ return VERR_DBG_NO_UNWIND_INFO;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByAddr} */
+static DECLCALLBACK(int) rtDbgModDwarf_LineByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off,
+ PRTINTPTR poffDisp, PRTDBGLINE pLineInfo)
+{
+ PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv;
+ return RTDbgModLineByAddr(pThis->hCnt, iSeg, off, poffDisp, pLineInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByOrdinal} */
+static DECLCALLBACK(int) rtDbgModDwarf_LineByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo)
+{
+ PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv;
+ return RTDbgModLineByOrdinal(pThis->hCnt, iOrdinal, pLineInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineCount} */
+static DECLCALLBACK(uint32_t) rtDbgModDwarf_LineCount(PRTDBGMODINT pMod)
+{
+ PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv;
+ return RTDbgModLineCount(pThis->hCnt);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineAdd} */
+static DECLCALLBACK(int) rtDbgModDwarf_LineAdd(PRTDBGMODINT pMod, const char *pszFile, size_t cchFile, uint32_t uLineNo,
+ uint32_t iSeg, RTUINTPTR off, uint32_t *piOrdinal)
+{
+ PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv;
+ Assert(!pszFile[cchFile]); NOREF(cchFile);
+ return RTDbgModLineAdd(pThis->hCnt, pszFile, uLineNo, iSeg, off, piOrdinal);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByAddr} */
+static DECLCALLBACK(int) rtDbgModDwarf_SymbolByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags,
+ PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo)
+{
+ PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv;
+ return RTDbgModSymbolByAddr(pThis->hCnt, iSeg, off, fFlags, poffDisp, pSymInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByName} */
+static DECLCALLBACK(int) rtDbgModDwarf_SymbolByName(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol,
+ PRTDBGSYMBOL pSymInfo)
+{
+ PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv;
+ Assert(!pszSymbol[cchSymbol]); RT_NOREF_PV(cchSymbol);
+ return RTDbgModSymbolByName(pThis->hCnt, pszSymbol/*, cchSymbol*/, pSymInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByOrdinal} */
+static DECLCALLBACK(int) rtDbgModDwarf_SymbolByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo)
+{
+ PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv;
+ return RTDbgModSymbolByOrdinal(pThis->hCnt, iOrdinal, pSymInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolCount} */
+static DECLCALLBACK(uint32_t) rtDbgModDwarf_SymbolCount(PRTDBGMODINT pMod)
+{
+ PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv;
+ return RTDbgModSymbolCount(pThis->hCnt);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolAdd} */
+static DECLCALLBACK(int) rtDbgModDwarf_SymbolAdd(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol,
+ RTDBGSEGIDX iSeg, RTUINTPTR off, RTUINTPTR cb, uint32_t fFlags,
+ uint32_t *piOrdinal)
+{
+ PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv;
+ Assert(!pszSymbol[cchSymbol]); NOREF(cchSymbol);
+ return RTDbgModSymbolAdd(pThis->hCnt, pszSymbol, iSeg, off, cb, fFlags, piOrdinal);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentByIndex} */
+static DECLCALLBACK(int) rtDbgModDwarf_SegmentByIndex(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo)
+{
+ PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv;
+ return RTDbgModSegmentByIndex(pThis->hCnt, iSeg, pSegInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentCount} */
+static DECLCALLBACK(RTDBGSEGIDX) rtDbgModDwarf_SegmentCount(PRTDBGMODINT pMod)
+{
+ PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv;
+ return RTDbgModSegmentCount(pThis->hCnt);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentAdd} */
+static DECLCALLBACK(int) rtDbgModDwarf_SegmentAdd(PRTDBGMODINT pMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName, size_t cchName,
+ uint32_t fFlags, PRTDBGSEGIDX piSeg)
+{
+ PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv;
+ Assert(!pszName[cchName]); NOREF(cchName);
+ return RTDbgModSegmentAdd(pThis->hCnt, uRva, cb, pszName, fFlags, piSeg);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnImageSize} */
+static DECLCALLBACK(RTUINTPTR) rtDbgModDwarf_ImageSize(PRTDBGMODINT pMod)
+{
+ PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv;
+ RTUINTPTR cb1 = RTDbgModImageSize(pThis->hCnt);
+ RTUINTPTR cb2 = pThis->pImgMod->pImgVt->pfnImageSize(pMod);
+ return RT_MAX(cb1, cb2);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnRvaToSegOff} */
+static DECLCALLBACK(RTDBGSEGIDX) rtDbgModDwarf_RvaToSegOff(PRTDBGMODINT pMod, RTUINTPTR uRva, PRTUINTPTR poffSeg)
+{
+ PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv;
+ return RTDbgModRvaToSegOff(pThis->hCnt, uRva, poffSeg);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnClose} */
+static DECLCALLBACK(int) rtDbgModDwarf_Close(PRTDBGMODINT pMod)
+{
+ PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv;
+
+ for (unsigned iSect = 0; iSect < RT_ELEMENTS(pThis->aSections); iSect++)
+ if (pThis->aSections[iSect].pv)
+ pThis->pDbgInfoMod->pImgVt->pfnUnmapPart(pThis->pDbgInfoMod, pThis->aSections[iSect].cb, &pThis->aSections[iSect].pv);
+
+ RTDbgModRelease(pThis->hCnt);
+ RTMemFree(pThis->paCachedAbbrevs);
+ if (pThis->pNestedMod)
+ {
+ pThis->pNestedMod->pImgVt->pfnClose(pThis->pNestedMod);
+ RTStrCacheRelease(g_hDbgModStrCache, pThis->pNestedMod->pszName);
+ RTStrCacheRelease(g_hDbgModStrCache, pThis->pNestedMod->pszDbgFile);
+ RTMemFree(pThis->pNestedMod);
+ pThis->pNestedMod = NULL;
+ }
+
+#ifdef RTDBGMODDWARF_WITH_MEM_CACHE
+ uint32_t i = RT_ELEMENTS(pThis->aDieAllocators);
+ while (i-- > 0)
+ {
+ RTMemCacheDestroy(pThis->aDieAllocators[i].hMemCache);
+ pThis->aDieAllocators[i].hMemCache = NIL_RTMEMCACHE;
+ }
+#endif
+
+ RTMemFree(pThis);
+
+ return VINF_SUCCESS;
+}
+
+
+/** @callback_method_impl{FNRTLDRENUMDBG} */
+static DECLCALLBACK(int) rtDbgModDwarfEnumCallback(RTLDRMOD hLdrMod, PCRTLDRDBGINFO pDbgInfo, void *pvUser)
+{
+ RT_NOREF_PV(hLdrMod);
+
+ /*
+ * Skip stuff we can't handle.
+ */
+ if (pDbgInfo->enmType != RTLDRDBGINFOTYPE_DWARF)
+ return VINF_SUCCESS;
+ const char *pszSection = pDbgInfo->u.Dwarf.pszSection;
+ if (!pszSection || !*pszSection)
+ return VINF_SUCCESS;
+ Assert(!pDbgInfo->pszExtFile);
+
+ /*
+ * Must have a part name starting with debug_ and possibly prefixed by dots
+ * or underscores.
+ */
+ if (!strncmp(pszSection, RT_STR_TUPLE(".debug_"))) /* ELF */
+ pszSection += sizeof(".debug_") - 1;
+ else if (!strncmp(pszSection, RT_STR_TUPLE("__debug_"))) /* Mach-O */
+ pszSection += sizeof("__debug_") - 1;
+ else if (!strcmp(pszSection, ".WATCOM_references"))
+ return VINF_SUCCESS; /* Ignore special watcom section for now.*/
+ else if ( !strcmp(pszSection, "__apple_types")
+ || !strcmp(pszSection, "__apple_namespac")
+ || !strcmp(pszSection, "__apple_objc")
+ || !strcmp(pszSection, "__apple_names"))
+ return VINF_SUCCESS; /* Ignore special apple sections for now. */
+ else
+ AssertMsgFailedReturn(("%s\n", pszSection), VINF_SUCCESS /*ignore*/);
+
+ /*
+ * Figure out which part we're talking about.
+ */
+ krtDbgModDwarfSect enmSect;
+ if (0) { /* dummy */ }
+#define ELSE_IF_STRCMP_SET(a_Name) else if (!strcmp(pszSection, #a_Name)) enmSect = krtDbgModDwarfSect_ ## a_Name
+ ELSE_IF_STRCMP_SET(abbrev);
+ ELSE_IF_STRCMP_SET(aranges);
+ ELSE_IF_STRCMP_SET(frame);
+ ELSE_IF_STRCMP_SET(info);
+ ELSE_IF_STRCMP_SET(inlined);
+ ELSE_IF_STRCMP_SET(line);
+ ELSE_IF_STRCMP_SET(loc);
+ ELSE_IF_STRCMP_SET(macinfo);
+ ELSE_IF_STRCMP_SET(pubnames);
+ ELSE_IF_STRCMP_SET(pubtypes);
+ ELSE_IF_STRCMP_SET(ranges);
+ ELSE_IF_STRCMP_SET(str);
+ ELSE_IF_STRCMP_SET(types);
+#undef ELSE_IF_STRCMP_SET
+ else
+ {
+ AssertMsgFailed(("%s\n", pszSection));
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Record the section.
+ */
+ PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pvUser;
+ AssertMsgReturn(!pThis->aSections[enmSect].fPresent, ("duplicate %s\n", pszSection), VINF_SUCCESS /*ignore*/);
+
+ pThis->aSections[enmSect].fPresent = true;
+ pThis->aSections[enmSect].offFile = pDbgInfo->offFile;
+ pThis->aSections[enmSect].pv = NULL;
+ pThis->aSections[enmSect].cb = (size_t)pDbgInfo->cb;
+ pThis->aSections[enmSect].iDbgInfo = pDbgInfo->iDbgInfo;
+ if (pThis->aSections[enmSect].cb != pDbgInfo->cb)
+ pThis->aSections[enmSect].cb = ~(size_t)0;
+
+ return VINF_SUCCESS;
+}
+
+
+static int rtDbgModDwarfTryOpenDbgFile(PRTDBGMODINT pDbgMod, PRTDBGMODDWARF pThis, RTLDRARCH enmArch)
+{
+ if ( !pDbgMod->pszDbgFile
+ || RTPathIsSame(pDbgMod->pszDbgFile, pDbgMod->pszImgFile) == (int)true /* returns VERR too */)
+ return VERR_DBG_NO_MATCHING_INTERPRETER;
+
+ /*
+ * Only open the image.
+ */
+ PRTDBGMODINT pDbgInfoMod = (PRTDBGMODINT)RTMemAllocZ(sizeof(*pDbgInfoMod));
+ if (!pDbgInfoMod)
+ return VERR_NO_MEMORY;
+
+ int rc;
+ pDbgInfoMod->u32Magic = RTDBGMOD_MAGIC;
+ pDbgInfoMod->cRefs = 1;
+ if (RTStrCacheRetain(pDbgMod->pszDbgFile) != UINT32_MAX)
+ {
+ pDbgInfoMod->pszImgFile = pDbgMod->pszDbgFile;
+ if (RTStrCacheRetain(pDbgMod->pszName) != UINT32_MAX)
+ {
+ pDbgInfoMod->pszName = pDbgMod->pszName;
+ pDbgInfoMod->pImgVt = &g_rtDbgModVtImgLdr;
+ rc = pDbgInfoMod->pImgVt->pfnTryOpen(pDbgInfoMod, enmArch, 0 /*fLdrFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->pDbgInfoMod = pDbgInfoMod;
+ pThis->pNestedMod = pDbgInfoMod;
+ return VINF_SUCCESS;
+ }
+
+ RTStrCacheRelease(g_hDbgModStrCache, pDbgInfoMod->pszName);
+ }
+ else
+ rc = VERR_NO_STR_MEMORY;
+ RTStrCacheRelease(g_hDbgModStrCache, pDbgInfoMod->pszImgFile);
+ }
+ else
+ rc = VERR_NO_STR_MEMORY;
+ RTMemFree(pDbgInfoMod);
+ return rc;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnTryOpen} */
+static DECLCALLBACK(int) rtDbgModDwarf_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch)
+{
+ /*
+ * DWARF is only supported when part of an image.
+ */
+ if (!pMod->pImgVt)
+ return VERR_DBG_NO_MATCHING_INTERPRETER;
+
+ /*
+ * Create the module instance data.
+ */
+ PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)RTMemAllocZ(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+ pThis->pDbgInfoMod = pMod;
+ pThis->pImgMod = pMod;
+ RTListInit(&pThis->CompileUnitList);
+
+ /** @todo better fUseLinkAddress heuristics! */
+ /* mach_kernel: */
+ if ( (pMod->pszDbgFile && strstr(pMod->pszDbgFile, "mach_kernel"))
+ || (pMod->pszImgFile && strstr(pMod->pszImgFile, "mach_kernel"))
+ || (pMod->pszImgFileSpecified && strstr(pMod->pszImgFileSpecified, "mach_kernel")) )
+ pThis->fUseLinkAddress = true;
+
+#ifdef RTDBGMODDWARF_WITH_MEM_CACHE
+ AssertCompile(RT_ELEMENTS(pThis->aDieAllocators) == 2);
+ pThis->aDieAllocators[0].cbMax = sizeof(RTDWARFDIE);
+ pThis->aDieAllocators[1].cbMax = sizeof(RTDWARFDIECOMPILEUNIT);
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_aTagDescs); i++)
+ if (g_aTagDescs[i].pDesc && g_aTagDescs[i].pDesc->cbDie > pThis->aDieAllocators[1].cbMax)
+ pThis->aDieAllocators[1].cbMax = (uint32_t)g_aTagDescs[i].pDesc->cbDie;
+ pThis->aDieAllocators[1].cbMax = RT_ALIGN_32(pThis->aDieAllocators[1].cbMax, sizeof(uint64_t));
+
+ for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aDieAllocators); i++)
+ {
+ int rc = RTMemCacheCreate(&pThis->aDieAllocators[i].hMemCache, pThis->aDieAllocators[i].cbMax, sizeof(uint64_t),
+ UINT32_MAX, NULL /*pfnCtor*/, NULL /*pfnDtor*/, NULL /*pvUser*/, 0 /*fFlags*/);
+ if (RT_FAILURE(rc))
+ {
+ while (i-- > 0)
+ RTMemCacheDestroy(pThis->aDieAllocators[i].hMemCache);
+ RTMemFree(pThis);
+ return rc;
+ }
+ }
+#endif
+
+ /*
+ * If the debug file name is set, let's see if it's an ELF image with DWARF
+ * inside it. In that case we'll have to deal with two image modules, one
+ * for segments and address translation and one for the debug information.
+ */
+ if (pMod->pszDbgFile != NULL)
+ rtDbgModDwarfTryOpenDbgFile(pMod, pThis, enmArch);
+
+ /*
+ * Enumerate the debug info in the module, looking for DWARF bits.
+ */
+ int rc = pThis->pDbgInfoMod->pImgVt->pfnEnumDbgInfo(pThis->pDbgInfoMod, rtDbgModDwarfEnumCallback, pThis);
+ if (RT_SUCCESS(rc))
+ {
+ if (pThis->aSections[krtDbgModDwarfSect_info].fPresent)
+ {
+ /*
+ * Extract / explode the data we want (symbols and line numbers)
+ * storing them in a container module.
+ */
+ rc = RTDbgModCreate(&pThis->hCnt, pMod->pszName, 0 /*cbSeg*/, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ pMod->pvDbgPriv = pThis;
+
+ rc = rtDbgModDwarfAddSegmentsFromImage(pThis);
+ if (RT_SUCCESS(rc))
+ rc = rtDwarfInfo_LoadAll(pThis);
+ if (RT_SUCCESS(rc))
+ rc = rtDwarfSyms_LoadAll(pThis);
+ if (RT_SUCCESS(rc))
+ rc = rtDwarfLine_ExplodeAll(pThis);
+ if (RT_SUCCESS(rc) && pThis->iWatcomPass == 1)
+ {
+ rc = rtDbgModDwarfAddSegmentsFromPass1(pThis);
+ pThis->iWatcomPass = 2;
+ if (RT_SUCCESS(rc))
+ rc = rtDwarfInfo_LoadAll(pThis);
+ if (RT_SUCCESS(rc))
+ rc = rtDwarfSyms_LoadAll(pThis);
+ if (RT_SUCCESS(rc))
+ rc = rtDwarfLine_ExplodeAll(pThis);
+ }
+
+ /*
+ * Free the cached abbreviations and unload all sections.
+ */
+ pThis->cCachedAbbrevsAlloced = 0;
+ RTMemFree(pThis->paCachedAbbrevs);
+ pThis->paCachedAbbrevs = NULL;
+
+ for (unsigned iSect = 0; iSect < RT_ELEMENTS(pThis->aSections); iSect++)
+ if (pThis->aSections[iSect].pv)
+ pThis->pDbgInfoMod->pImgVt->pfnUnmapPart(pThis->pDbgInfoMod, pThis->aSections[iSect].cb,
+ &pThis->aSections[iSect].pv);
+
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo Kill pThis->CompileUnitList and the alloc caches. */
+ return VINF_SUCCESS;
+ }
+
+ /* bail out. */
+ RTDbgModRelease(pThis->hCnt);
+ pMod->pvDbgPriv = NULL;
+ }
+ }
+ else
+ rc = VERR_DBG_NO_MATCHING_INTERPRETER;
+ }
+
+ if (pThis->paCachedAbbrevs)
+ RTMemFree(pThis->paCachedAbbrevs);
+ pThis->paCachedAbbrevs = NULL;
+
+ for (unsigned iSect = 0; iSect < RT_ELEMENTS(pThis->aSections); iSect++)
+ if (pThis->aSections[iSect].pv)
+ pThis->pDbgInfoMod->pImgVt->pfnUnmapPart(pThis->pDbgInfoMod, pThis->aSections[iSect].cb,
+ &pThis->aSections[iSect].pv);
+
+#ifdef RTDBGMODDWARF_WITH_MEM_CACHE
+ uint32_t i = RT_ELEMENTS(pThis->aDieAllocators);
+ while (i-- > 0)
+ {
+ RTMemCacheDestroy(pThis->aDieAllocators[i].hMemCache);
+ pThis->aDieAllocators[i].hMemCache = NIL_RTMEMCACHE;
+ }
+#endif
+
+ RTMemFree(pThis);
+
+ return rc;
+}
+
+
+
+/** Virtual function table for the DWARF debug info reader. */
+DECL_HIDDEN_CONST(RTDBGMODVTDBG) const g_rtDbgModVtDbgDwarf =
+{
+ /*.u32Magic = */ RTDBGMODVTDBG_MAGIC,
+ /*.fSupports = */ RT_DBGTYPE_DWARF,
+ /*.pszName = */ "dwarf",
+ /*.pfnTryOpen = */ rtDbgModDwarf_TryOpen,
+ /*.pfnClose = */ rtDbgModDwarf_Close,
+
+ /*.pfnRvaToSegOff = */ rtDbgModDwarf_RvaToSegOff,
+ /*.pfnImageSize = */ rtDbgModDwarf_ImageSize,
+
+ /*.pfnSegmentAdd = */ rtDbgModDwarf_SegmentAdd,
+ /*.pfnSegmentCount = */ rtDbgModDwarf_SegmentCount,
+ /*.pfnSegmentByIndex = */ rtDbgModDwarf_SegmentByIndex,
+
+ /*.pfnSymbolAdd = */ rtDbgModDwarf_SymbolAdd,
+ /*.pfnSymbolCount = */ rtDbgModDwarf_SymbolCount,
+ /*.pfnSymbolByOrdinal = */ rtDbgModDwarf_SymbolByOrdinal,
+ /*.pfnSymbolByName = */ rtDbgModDwarf_SymbolByName,
+ /*.pfnSymbolByAddr = */ rtDbgModDwarf_SymbolByAddr,
+
+ /*.pfnLineAdd = */ rtDbgModDwarf_LineAdd,
+ /*.pfnLineCount = */ rtDbgModDwarf_LineCount,
+ /*.pfnLineByOrdinal = */ rtDbgModDwarf_LineByOrdinal,
+ /*.pfnLineByAddr = */ rtDbgModDwarf_LineByAddr,
+
+ /*.pfnUnwindFrame = */ rtDbgModDwarf_UnwindFrame,
+
+ /*.u32EndMagic = */ RTDBGMODVTDBG_MAGIC
+};
+
diff --git a/src/VBox/Runtime/common/dbg/dbgmodexports.cpp b/src/VBox/Runtime/common/dbg/dbgmodexports.cpp
new file mode 100644
index 00000000..716c43e1
--- /dev/null
+++ b/src/VBox/Runtime/common/dbg/dbgmodexports.cpp
@@ -0,0 +1,183 @@
+/* $Id: dbgmodexports.cpp $ */
+/** @file
+ * IPRT - Debug Module Using Image Exports.
+ */
+
+/*
+ * Copyright (C) 2013-2023 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 *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DBG
+#include <iprt/dbg.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloca.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/string.h>
+#include "internal/dbgmod.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct RTDBGMODEXPORTARGS
+{
+ PRTDBGMODINT pDbgMod;
+ RTLDRADDR uImageBase;
+ RTLDRADDR uRvaNext;
+ uint32_t cSegs;
+} RTDBGMODEXPORTARGS;
+/** Pointer to an argument package. */
+typedef RTDBGMODEXPORTARGS *PRTDBGMODEXPORTARGS;
+
+
+/**
+ * @callback_method_impl{FNRTLDRENUMSYMS,
+ * Copies the symbols over into the container} */
+static DECLCALLBACK(int) rtDbgModExportsAddSymbolCallback(RTLDRMOD hLdrMod, const char *pszSymbol, unsigned uSymbol,
+ RTLDRADDR Value, void *pvUser)
+{
+ PRTDBGMODEXPORTARGS pArgs = (PRTDBGMODEXPORTARGS)pvUser;
+ NOREF(hLdrMod);
+
+ if (Value >= pArgs->uImageBase)
+ {
+ char szOrdinalNm[48];
+ if (!pszSymbol || *pszSymbol == '\0')
+ {
+ RTStrPrintf(szOrdinalNm, sizeof(szOrdinalNm), "Ordinal%u", uSymbol);
+ pszSymbol = szOrdinalNm;
+ }
+
+ int rc = RTDbgModSymbolAdd(pArgs->pDbgMod, pszSymbol, RTDBGSEGIDX_RVA, Value - pArgs->uImageBase,
+ 0 /*cb*/, 0 /* fFlags */, NULL /*piOrdinal*/);
+ Log(("Symbol #%05u %#018RTptr %s [%Rrc]\n", uSymbol, Value, pszSymbol, rc)); NOREF(rc);
+ }
+ else
+ Log(("Symbol #%05u %#018RTptr %s [SKIPPED - INVALID ADDRESS]\n", uSymbol, Value, pszSymbol));
+ return VINF_SUCCESS;
+}
+
+
+/** @callback_method_impl{FNRTLDRENUMSEGS,
+ * Copies the segments over into the container} */
+static DECLCALLBACK(int) rtDbgModExportsAddSegmentsCallback(RTLDRMOD hLdrMod, PCRTLDRSEG pSeg, void *pvUser)
+{
+ PRTDBGMODEXPORTARGS pArgs = (PRTDBGMODEXPORTARGS)pvUser;
+ Log(("Segment %.*s: LinkAddress=%#llx RVA=%#llx cb=%#llx\n",
+ pSeg->cchName, pSeg->pszName, (uint64_t)pSeg->LinkAddress, (uint64_t)pSeg->RVA, pSeg->cb));
+ NOREF(hLdrMod);
+
+ pArgs->cSegs++;
+
+ /* Add dummy segments for segments that doesn't get mapped. */
+ if ( pSeg->LinkAddress == NIL_RTLDRADDR
+ || pSeg->RVA == NIL_RTLDRADDR)
+ return RTDbgModSegmentAdd(pArgs->pDbgMod, 0, 0, pSeg->pszName, 0 /*fFlags*/, NULL);
+
+ RTLDRADDR uRva = pSeg->RVA;
+#if 0 /* Kluge for the .data..percpu segment in 64-bit linux kernels and similar. (Moved to ELF.) */
+ if (uRva < pArgs->uRvaNext)
+ uRva = RT_ALIGN_T(pArgs->uRvaNext, pSeg->Alignment, RTLDRADDR);
+#endif
+
+ /* Find the best base address for the module. */
+ if ( ( !pArgs->uImageBase
+ || pArgs->uImageBase > pSeg->LinkAddress)
+ && ( pSeg->LinkAddress != 0 /* .data..percpu again. */
+ || pArgs->cSegs == 1))
+ pArgs->uImageBase = pSeg->LinkAddress;
+
+ /* Add it. */
+ RTLDRADDR cb = RT_MAX(pSeg->cb, pSeg->cbMapped);
+ pArgs->uRvaNext = uRva + cb;
+ return RTDbgModSegmentAdd(pArgs->pDbgMod, uRva, cb, pSeg->pszName, 0 /*fFlags*/, NULL);
+}
+
+
+/**
+ * Creates the debug info side of affairs based on exports and segments found in
+ * the image part.
+ *
+ * The image part must be successfully initialized prior to the call, while the
+ * debug bits must not be present of course.
+ *
+ * @returns IPRT status code
+ * @param pDbgMod The debug module structure.
+ */
+DECLHIDDEN(int) rtDbgModCreateForExports(PRTDBGMODINT pDbgMod)
+{
+ AssertReturn(!pDbgMod->pDbgVt, VERR_DBG_MOD_IPE);
+ AssertReturn(pDbgMod->pImgVt, VERR_DBG_MOD_IPE);
+ RTUINTPTR cbImage = pDbgMod->pImgVt->pfnImageSize(pDbgMod);
+ AssertReturn(cbImage > 0, VERR_DBG_MOD_IPE);
+
+ /*
+ * We simply use a container type for this work.
+ */
+ int rc = rtDbgModContainerCreate(pDbgMod, 0);
+ if (RT_FAILURE(rc))
+ return rc;
+ pDbgMod->fExports = true;
+
+ /*
+ * Copy the segments and symbols.
+ */
+ RTDBGMODEXPORTARGS Args;
+ Args.pDbgMod = pDbgMod;
+ Args.uImageBase = 0;
+ Args.uRvaNext = 0;
+ Args.cSegs = 0;
+ rc = pDbgMod->pImgVt->pfnEnumSegments(pDbgMod, rtDbgModExportsAddSegmentsCallback, &Args);
+ if (RT_SUCCESS(rc))
+ {
+ rc = pDbgMod->pImgVt->pfnEnumSymbols(pDbgMod, RTLDR_ENUM_SYMBOL_FLAGS_ALL | RTLDR_ENUM_SYMBOL_FLAGS_NO_FWD,
+ Args.uImageBase ? Args.uImageBase : 0x10000,
+ rtDbgModExportsAddSymbolCallback, &Args);
+ if (RT_FAILURE(rc))
+ Log(("rtDbgModCreateForExports: Error during symbol enum: %Rrc\n", rc));
+ }
+ else
+ Log(("rtDbgModCreateForExports: Error during segment enum: %Rrc\n", rc));
+
+ /* This won't fail. */
+ if (RT_SUCCESS(rc))
+ rc = VINF_SUCCESS;
+ else
+ rc = -rc; /* Change it into a warning. */
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/common/dbg/dbgmodghidra.cpp b/src/VBox/Runtime/common/dbg/dbgmodghidra.cpp
new file mode 100644
index 00000000..d69f4640
--- /dev/null
+++ b/src/VBox/Runtime/common/dbg/dbgmodghidra.cpp
@@ -0,0 +1,526 @@
+/* $Id: dbgmodghidra.cpp $ */
+/** @file
+ * IPRT - Debug Info Reader for Ghidra XML files created with createPdbXmlFiles.bat/pdb.exe.
+ */
+
+/*
+ * Copyright (C) 2021-2023 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 <iprt/dbg.h>
+#include "internal/iprt.h"
+
+#include <iprt/err.h>
+#include <iprt/ctype.h>
+#include <iprt/mem.h>
+#include <iprt/sort.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/cpp/xml.h>
+#include "internal/dbgmod.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Temporary segment data.
+ */
+typedef struct RTDBGMODGHIDRASEG
+{
+ const char *pszNumber;
+ RTUINTPTR uRva;
+} RTDBGMODGHIDRASEG;
+typedef RTDBGMODGHIDRASEG *PRTDBGMODGHIDRASEG;
+typedef const RTDBGMODGHIDRASEG *PCRTDBGMODGHIDRASEG;
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnUnwindFrame} */
+static DECLCALLBACK(int) rtDbgModGhidra_UnwindFrame(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState)
+{
+ RT_NOREF(pMod, iSeg, off, pState);
+ return VERR_DBG_NO_UNWIND_INFO;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByAddr} */
+static DECLCALLBACK(int) rtDbgModGhidra_LineByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off,
+ PRTINTPTR poffDisp, PRTDBGLINE pLineInfo)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModLineByAddr(hCnt, iSeg, off, poffDisp, pLineInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByOrdinal} */
+static DECLCALLBACK(int) rtDbgModGhidra_LineByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModLineByOrdinal(hCnt, iOrdinal, pLineInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineCount} */
+static DECLCALLBACK(uint32_t) rtDbgModGhidra_LineCount(PRTDBGMODINT pMod)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModLineCount(hCnt);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineAdd} */
+static DECLCALLBACK(int) rtDbgModGhidra_LineAdd(PRTDBGMODINT pMod, const char *pszFile, size_t cchFile, uint32_t uLineNo,
+ uint32_t iSeg, RTUINTPTR off, uint32_t *piOrdinal)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ Assert(!pszFile[cchFile]); NOREF(cchFile);
+ return RTDbgModLineAdd(hCnt, pszFile, uLineNo, iSeg, off, piOrdinal);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByAddr} */
+static DECLCALLBACK(int) rtDbgModGhidra_SymbolByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags,
+ PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModSymbolByAddr(hCnt, iSeg, off, fFlags, poffDisp, pSymInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByName} */
+static DECLCALLBACK(int) rtDbgModGhidra_SymbolByName(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol,
+ PRTDBGSYMBOL pSymInfo)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ Assert(!pszSymbol[cchSymbol]); NOREF(cchSymbol);
+ return RTDbgModSymbolByName(hCnt, pszSymbol, pSymInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByOrdinal} */
+static DECLCALLBACK(int) rtDbgModGhidra_SymbolByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModSymbolByOrdinal(hCnt, iOrdinal, pSymInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolCount} */
+static DECLCALLBACK(uint32_t) rtDbgModGhidra_SymbolCount(PRTDBGMODINT pMod)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModSymbolCount(hCnt);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolAdd} */
+static DECLCALLBACK(int) rtDbgModGhidra_SymbolAdd(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol,
+ RTDBGSEGIDX iSeg, RTUINTPTR off, RTUINTPTR cb, uint32_t fFlags,
+ uint32_t *piOrdinal)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ Assert(!pszSymbol[cchSymbol]); NOREF(cchSymbol);
+ return RTDbgModSymbolAdd(hCnt, pszSymbol, iSeg, off, cb, fFlags, piOrdinal);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentByIndex} */
+static DECLCALLBACK(int) rtDbgModGhidra_SegmentByIndex(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModSegmentByIndex(hCnt, iSeg, pSegInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentCount} */
+static DECLCALLBACK(RTDBGSEGIDX) rtDbgModGhidra_SegmentCount(PRTDBGMODINT pMod)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModSegmentCount(hCnt);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentAdd} */
+static DECLCALLBACK(int) rtDbgModGhidra_SegmentAdd(PRTDBGMODINT pMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName,
+ size_t cchName, uint32_t fFlags, PRTDBGSEGIDX piSeg)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ Assert(!pszName[cchName]); NOREF(cchName);
+ return RTDbgModSegmentAdd(hCnt, uRva, cb, pszName, fFlags, piSeg);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnImageSize} */
+static DECLCALLBACK(RTUINTPTR) rtDbgModGhidra_ImageSize(PRTDBGMODINT pMod)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModImageSize(hCnt);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnRvaToSegOff} */
+static DECLCALLBACK(RTDBGSEGIDX) rtDbgModGhidra_RvaToSegOff(PRTDBGMODINT pMod, RTUINTPTR uRva, PRTUINTPTR poffSeg)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModRvaToSegOff(hCnt, uRva, poffSeg);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnClose} */
+static DECLCALLBACK(int) rtDbgModGhidra_Close(PRTDBGMODINT pMod)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ RTDbgModRelease(hCnt);
+ pMod->pvDbgPriv = NULL;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Returns the table with the given name from the given table list.
+ *
+ * @returns Pointer to the XML element node containing the given table or NULL if not found.
+ * @param pelmTables Pointer to the node containing the tables.
+ * @param pszName The table name to look for.
+ */
+static const xml::ElementNode *rtDbgModGhidraGetTableByName(const xml::ElementNode *pelmTables, const char *pszName)
+{
+ xml::NodesLoop nl1(*pelmTables, "table");
+ const xml::ElementNode *pelmTbl;
+ while ((pelmTbl = nl1.forAllNodes()))
+ {
+ const char *pszTblName = NULL;
+
+ if ( pelmTbl->getAttributeValue("name", &pszTblName)
+ && !strcmp(pszTblName, pszName))
+ return pelmTbl;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Adds the symbols from the given \"Symbols\" table.
+ *
+ * @returns IPRT status code.
+ * @param hCnt Debug module container handle.
+ * @param elmTbl Reference to the XML node containing the symbols.
+ */
+static int rtDbgModGhidraXmlParseSymbols(RTDBGMOD hCnt, const xml::ElementNode &elmTbl)
+{
+ xml::NodesLoop nlSym(elmTbl, "symbol");
+ const xml::ElementNode *pelmSym;
+ while ((pelmSym = nlSym.forAllNodes()))
+ {
+ /* Only parse Function and PublicSymbol tags. */
+ const char *pszTag = NULL;
+ if ( pelmSym->getAttributeValue("tag", &pszTag)
+ && ( !strcmp(pszTag, "PublicSymbol")
+ || !strcmp(pszTag, "Function")))
+ {
+ const char *pszSymName = NULL;
+ if ( !pelmSym->getAttributeValue("undecorated", &pszSymName)
+ || *pszSymName == '\0')
+ pelmSym->getAttributeValue("name", &pszSymName);
+
+ if ( pszSymName
+ && strlen(pszSymName) < RTDBG_SYMBOL_NAME_LENGTH)
+ {
+ uint64_t u64Addr = 0;
+ uint64_t u64Length = 0;
+ if ( pelmSym->getAttributeValue("address", &u64Addr)
+ && pelmSym->getAttributeValue("length", &u64Length))
+ {
+ int rc = RTDbgModSymbolAdd(hCnt, pszSymName, RTDBGSEGIDX_RVA, u64Addr, u64Length, 0 /*fFlags*/, NULL);
+ if ( RT_FAILURE(rc)
+ && rc != VERR_DBG_DUPLICATE_SYMBOL
+ && rc != VERR_DBG_ADDRESS_CONFLICT
+ && rc != VERR_DBG_INVALID_RVA) /* (don't be too strict) */
+ return rc;
+ }
+ }
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Adds the symbols from the given \"functions\" table.
+ *
+ * @returns IPRT status code.
+ * @param hCnt Debug module container handle.
+ * @param elmTbl Reference to the XML node containing the symbols.
+ */
+static int rtDbgModGhidraXmlParseFunctions(RTDBGMOD hCnt, const xml::ElementNode &elmTbl)
+{
+ xml::NodesLoop nlFun(elmTbl, "function");
+ const xml::ElementNode *pelmFun;
+ while ((pelmFun = nlFun.forAllNodes()))
+ {
+ xml::NodesLoop nlLn(*pelmFun, "line_number");
+ const xml::ElementNode *pelmLn;
+ while ((pelmLn = nlLn.forAllNodes()))
+ {
+ const char *pszFile = NULL;
+ uint32_t uLineNo = 0;
+ uint64_t off = 0;
+ if ( pelmLn->getAttributeValue("source_file", &pszFile)
+ && pelmLn->getAttributeValue("start", &uLineNo)
+ && pelmLn->getAttributeValue("addr", &off))
+ {
+ int rc = RTDbgModLineAdd(hCnt, pszFile, uLineNo, RTDBGSEGIDX_RVA, off, NULL /*piOrdinal*/);
+ if ( RT_FAILURE(rc)
+ && rc != VERR_DBG_DUPLICATE_SYMBOL
+ && rc != VERR_DBG_ADDRESS_CONFLICT
+ && rc != VERR_DBG_INVALID_RVA) /* (don't be too strict) */
+ return rc;
+ }
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @copydoc FNRTSORTCMP
+ */
+static DECLCALLBACK(int) rtDbgModGhidraSegmentsSortCmp(void const *pvElement1, void const *pvElement2, void *pvUser)
+{
+ RT_NOREF(pvUser);
+ PCRTDBGMODGHIDRASEG pSeg1 = (PCRTDBGMODGHIDRASEG)pvElement1;
+ PCRTDBGMODGHIDRASEG pSeg2 = (PCRTDBGMODGHIDRASEG)pvElement2;
+
+ if (pSeg1->uRva > pSeg2->uRva)
+ return 1;
+ if (pSeg1->uRva < pSeg2->uRva)
+ return -1;
+
+ return 0;
+}
+
+
+/**
+ * Adds the segments in the given \"SegmentMap\" table.
+ *
+ * @returns IPRT status code.
+ * @param hCnt Debug module container handle.
+ * @param elmTblSeg Reference to the XML node containing the segments.
+ */
+static int rtDbgModGhidraSegmentsAdd(RTDBGMOD hCnt, const xml::ElementNode &elmTblSeg)
+{
+ RTDBGMODGHIDRASEG aSegments[32];
+ uint32_t idxSeg = 0;
+
+ xml::NodesLoop nl1(elmTblSeg, "segment");
+ const xml::ElementNode *pelmSeg;
+ while ( (pelmSeg = nl1.forAllNodes())
+ && idxSeg < RT_ELEMENTS(aSegments))
+ {
+ const char *pszNumber = NULL;
+ RTUINTPTR uRva = 0;
+
+ if ( pelmSeg->getAttributeValue("number", &pszNumber)
+ && pelmSeg->getAttributeValue("address", &uRva))
+ {
+ aSegments[idxSeg].pszNumber = pszNumber;
+ aSegments[idxSeg].uRva = uRva;
+ idxSeg++;
+ }
+ }
+
+ /* Sort the segments by RVA so it is possible to deduce segment sizes. */
+ RTSortShell(&aSegments[0], idxSeg, sizeof(aSegments[0]), rtDbgModGhidraSegmentsSortCmp, NULL);
+
+ for (uint32_t i = 0; i < idxSeg - 1; i++)
+ {
+ int rc = RTDbgModSegmentAdd(hCnt, aSegments[i].uRva, aSegments[i + 1].uRva - aSegments[i].uRva,
+ aSegments[i].pszNumber, 0 /*fFlags*/, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /* Last segment for which we assume a size of 0 right now. */
+ int rc = RTDbgModSegmentAdd(hCnt, aSegments[idxSeg - 1].uRva, 0,
+ aSegments[idxSeg - 1].pszNumber, 0 /*fFlags*/, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ return rc;
+}
+
+
+/**
+ * Load the symbols from an XML document.
+ *
+ * @returns IPRT status code.
+ * @param hCnt Debug module container handle.
+ * @param a_pDoc Pointer to the XML document.
+ */
+static int rtDbgModGhidraXmlParse(RTDBGMOD hCnt, xml::Document *a_pDoc)
+{
+ /*
+ * Get the root element and check whether it looks like a valid Ghidra XML.
+ */
+ const xml::ElementNode *pelmRoot = a_pDoc->getRootElement();
+ if ( !pelmRoot
+ || strcmp(pelmRoot->getName(), "pdb") != 0)
+ return VERR_DBG_NO_MATCHING_INTERPRETER;
+
+ const xml::ElementNode *pelmTables = pelmRoot->findChildElement("tables");
+ if (!pelmTables)
+ return VERR_DBG_NO_MATCHING_INTERPRETER;
+
+ const xml::ElementNode *pelmTbl = rtDbgModGhidraGetTableByName(pelmTables, "SegmentMap");
+ if (pelmTbl)
+ {
+ int rc = rtDbgModGhidraSegmentsAdd(hCnt, *pelmTbl);
+ if (RT_SUCCESS(rc))
+ {
+ pelmTbl = rtDbgModGhidraGetTableByName(pelmTables, "Symbols");
+ if (pelmTbl)
+ {
+ rc = rtDbgModGhidraXmlParseSymbols(hCnt, *pelmTbl);
+ if (RT_SUCCESS(rc))
+ {
+ pelmTbl = pelmRoot->findChildElement("functions"); /* Might not be there. */
+ if (pelmTbl)
+ rc = rtDbgModGhidraXmlParseFunctions(hCnt, *pelmTbl);
+ return rc;
+ }
+ }
+ }
+ }
+
+ return VERR_DBG_NO_MATCHING_INTERPRETER;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnTryOpen} */
+static DECLCALLBACK(int) rtDbgModGhidra_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch)
+{
+ RT_NOREF(enmArch);
+
+ /*
+ * Fend off images.
+ */
+ if (!pMod->pszDbgFile)
+ return VERR_DBG_NO_MATCHING_INTERPRETER;
+ pMod->pvDbgPriv = NULL;
+
+ /*
+ * Try open the file and create an instance.
+ */
+ xml::Document Doc;
+ {
+ xml::XmlFileParser Parser;
+ try
+ {
+ Parser.read(pMod->pszDbgFile, Doc);
+ }
+ catch (xml::XmlError &rErr)
+ {
+ RT_NOREF(rErr);
+ return VERR_DBG_NO_MATCHING_INTERPRETER;
+ }
+ catch (xml::EIPRTFailure &rErr)
+ {
+ return rErr.rc();
+ }
+ }
+
+ RTDBGMOD hCnt;
+ int rc = RTDbgModCreate(&hCnt, pMod->pszName, 0 /*cbSeg*/, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Hand the xml doc over to the common code.
+ */
+ try
+ {
+ rc = rtDbgModGhidraXmlParse(hCnt, &Doc);
+ if (RT_SUCCESS(rc))
+ {
+ pMod->pvDbgPriv = hCnt;
+ return VINF_SUCCESS;
+ }
+ }
+ catch (RTCError &rXcpt) // includes all XML exceptions
+ {
+ RT_NOREF(rXcpt);
+ rc = VERR_DBG_NO_MATCHING_INTERPRETER;
+ }
+ RTDbgModRelease(hCnt);
+ }
+
+ return rc;
+}
+
+
+
+/** Virtual function table for the Ghidra XML file reader. */
+DECL_HIDDEN_CONST(RTDBGMODVTDBG) const g_rtDbgModVtDbgGhidra =
+{
+ /*.u32Magic = */ RTDBGMODVTDBG_MAGIC,
+ /*.fSupports = */ RT_DBGTYPE_OTHER | RT_DBGTYPE_MAP,
+ /*.pszName = */ "ghidra",
+ /*.pfnTryOpen = */ rtDbgModGhidra_TryOpen,
+ /*.pfnClose = */ rtDbgModGhidra_Close,
+
+ /*.pfnRvaToSegOff = */ rtDbgModGhidra_RvaToSegOff,
+ /*.pfnImageSize = */ rtDbgModGhidra_ImageSize,
+
+ /*.pfnSegmentAdd = */ rtDbgModGhidra_SegmentAdd,
+ /*.pfnSegmentCount = */ rtDbgModGhidra_SegmentCount,
+ /*.pfnSegmentByIndex = */ rtDbgModGhidra_SegmentByIndex,
+
+ /*.pfnSymbolAdd = */ rtDbgModGhidra_SymbolAdd,
+ /*.pfnSymbolCount = */ rtDbgModGhidra_SymbolCount,
+ /*.pfnSymbolByOrdinal = */ rtDbgModGhidra_SymbolByOrdinal,
+ /*.pfnSymbolByName = */ rtDbgModGhidra_SymbolByName,
+ /*.pfnSymbolByAddr = */ rtDbgModGhidra_SymbolByAddr,
+
+ /*.pfnLineAdd = */ rtDbgModGhidra_LineAdd,
+ /*.pfnLineCount = */ rtDbgModGhidra_LineCount,
+ /*.pfnLineByOrdinal = */ rtDbgModGhidra_LineByOrdinal,
+ /*.pfnLineByAddr = */ rtDbgModGhidra_LineByAddr,
+
+ /*.pfnUnwindFrame = */ rtDbgModGhidra_UnwindFrame,
+
+ /*.u32EndMagic = */ RTDBGMODVTDBG_MAGIC
+};
+
diff --git a/src/VBox/Runtime/common/dbg/dbgmodldr.cpp b/src/VBox/Runtime/common/dbg/dbgmodldr.cpp
new file mode 100644
index 00000000..b81f6d89
--- /dev/null
+++ b/src/VBox/Runtime/common/dbg/dbgmodldr.cpp
@@ -0,0 +1,286 @@
+/* $Id: dbgmodldr.cpp $ */
+/** @file
+ * IPRT - Debug Module Image Interpretation by RTLdr.
+ */
+
+/*
+ * Copyright (C) 2011-2023 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 <iprt/dbg.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/file.h>
+#include <iprt/ldr.h>
+#include <iprt/mem.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include "internal/dbgmod.h"
+#include "internal/ldr.h"
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The instance data of the RTLdr based image reader.
+ */
+typedef struct RTDBGMODLDR
+{
+ /** Magic value (RTDBGMODLDR_MAGIC). */
+ uint32_t u32Magic;
+ /** The loader handle. */
+ RTLDRMOD hLdrMod;
+} RTDBGMODLDR;
+/** Pointer to instance data NM map reader. */
+typedef RTDBGMODLDR *PRTDBGMODLDR;
+
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnUnwindFrame} */
+static DECLCALLBACK(int) rtDbgModLdr_UnwindFrame(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState)
+{
+ PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv;
+ Assert(pThis->u32Magic == RTDBGMODLDR_MAGIC);
+ return RTLdrUnwindFrame(pThis->hLdrMod, NULL, iSeg, off, pState);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnQueryProp} */
+static DECLCALLBACK(int) rtDbgModLdr_QueryProp(PRTDBGMODINT pMod, RTLDRPROP enmProp, void *pvBuf, size_t cbBuf, size_t *pcbRet)
+{
+ PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv;
+ Assert(pThis->u32Magic == RTDBGMODLDR_MAGIC);
+ return RTLdrQueryPropEx(pThis->hLdrMod, enmProp, NULL /*pvBits*/, pvBuf, cbBuf, pcbRet);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnGetArch} */
+static DECLCALLBACK(RTLDRARCH) rtDbgModLdr_GetArch(PRTDBGMODINT pMod)
+{
+ PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv;
+ Assert(pThis->u32Magic == RTDBGMODLDR_MAGIC);
+ return RTLdrGetArch(pThis->hLdrMod);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnGetFormat} */
+static DECLCALLBACK(RTLDRFMT) rtDbgModLdr_GetFormat(PRTDBGMODINT pMod)
+{
+ PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv;
+ Assert(pThis->u32Magic == RTDBGMODLDR_MAGIC);
+ return RTLdrGetFormat(pThis->hLdrMod);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnReadAt} */
+static DECLCALLBACK(int) rtDbgModLdr_ReadAt(PRTDBGMODINT pMod, uint32_t iDbgInfoHint, RTFOFF off, void *pvBuf, size_t cb)
+{
+ PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv;
+ Assert(pThis->u32Magic == RTDBGMODLDR_MAGIC);
+ RT_NOREF_PV(iDbgInfoHint);
+ return rtLdrReadAt(pThis->hLdrMod, pvBuf, UINT32_MAX /** @todo iDbgInfo*/, off, cb);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnUnmapPart} */
+static DECLCALLBACK(int) rtDbgModLdr_UnmapPart(PRTDBGMODINT pMod, size_t cb, void const **ppvMap)
+{
+ Assert(((PRTDBGMODLDR)pMod->pvImgPriv)->u32Magic == RTDBGMODLDR_MAGIC);
+ NOREF(pMod); NOREF(cb);
+ RTMemFree((void *)*ppvMap);
+ *ppvMap = NULL;
+ return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnMapPart} */
+static DECLCALLBACK(int) rtDbgModLdr_MapPart(PRTDBGMODINT pMod, uint32_t iDbgInfo, RTFOFF off, size_t cb, void const **ppvMap)
+{
+ PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv;
+ Assert(pThis->u32Magic == RTDBGMODLDR_MAGIC);
+
+ void *pvMap = RTMemAlloc(cb);
+ if (!pvMap)
+ return VERR_NO_MEMORY;
+
+ int rc = rtLdrReadAt(pThis->hLdrMod, pvMap, iDbgInfo, off, cb);
+ if (RT_SUCCESS(rc))
+ *ppvMap = pvMap;
+ else
+ {
+ RTMemFree(pvMap);
+ *ppvMap = NULL;
+ }
+ return rc;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnImageSize} */
+static DECLCALLBACK(RTUINTPTR) rtDbgModLdr_ImageSize(PRTDBGMODINT pMod)
+{
+ PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv;
+ Assert(pThis->u32Magic == RTDBGMODLDR_MAGIC);
+ return RTLdrSize(pThis->hLdrMod);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnRvaToSegOffset} */
+static DECLCALLBACK(int) rtDbgModLdr_RvaToSegOffset(PRTDBGMODINT pMod, RTLDRADDR Rva, PRTDBGSEGIDX piSeg, PRTLDRADDR poffSeg)
+{
+ PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv;
+ Assert(pThis->u32Magic == RTDBGMODLDR_MAGIC);
+ return RTLdrRvaToSegOffset(pThis->hLdrMod, Rva, piSeg, poffSeg);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnLinkAddressToSegOffset} */
+static DECLCALLBACK(int) rtDbgModLdr_LinkAddressToSegOffset(PRTDBGMODINT pMod, RTLDRADDR LinkAddress,
+ PRTDBGSEGIDX piSeg, PRTLDRADDR poffSeg)
+{
+ PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv;
+ Assert(pThis->u32Magic == RTDBGMODLDR_MAGIC);
+ return RTLdrLinkAddressToSegOffset(pThis->hLdrMod, LinkAddress, piSeg, poffSeg);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnEnumSymbols} */
+static DECLCALLBACK(int) rtDbgModLdr_EnumSymbols(PRTDBGMODINT pMod, uint32_t fFlags, RTLDRADDR BaseAddress,
+ PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
+{
+ PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv;
+ Assert(pThis->u32Magic == RTDBGMODLDR_MAGIC);
+ return RTLdrEnumSymbols(pThis->hLdrMod, fFlags, NULL /*pvBits*/, BaseAddress, pfnCallback, pvUser);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnEnumSegments} */
+static DECLCALLBACK(int) rtDbgModLdr_EnumSegments(PRTDBGMODINT pMod, PFNRTLDRENUMSEGS pfnCallback, void *pvUser)
+{
+ PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv;
+ Assert(pThis->u32Magic == RTDBGMODLDR_MAGIC);
+ return RTLdrEnumSegments(pThis->hLdrMod, pfnCallback, pvUser);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnEnumDbgInfo} */
+static DECLCALLBACK(int) rtDbgModLdr_EnumDbgInfo(PRTDBGMODINT pMod, PFNRTLDRENUMDBG pfnCallback, void *pvUser)
+{
+ PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv;
+ Assert(pThis->u32Magic == RTDBGMODLDR_MAGIC);
+ return RTLdrEnumDbgInfo(pThis->hLdrMod, NULL, pfnCallback, pvUser);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnClose} */
+static DECLCALLBACK(int) rtDbgModLdr_Close(PRTDBGMODINT pMod)
+{
+ PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv;
+ AssertPtr(pThis);
+ Assert(pThis->u32Magic == RTDBGMODLDR_MAGIC);
+
+ int rc = RTLdrClose(pThis->hLdrMod); AssertRC(rc);
+ pThis->hLdrMod = NIL_RTLDRMOD;
+ pThis->u32Magic = RTDBGMODLDR_MAGIC_DEAD;
+
+ RTMemFree(pThis);
+
+ return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTIMG,pfnTryOpen} */
+static DECLCALLBACK(int) rtDbgModLdr_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch, uint32_t fLdrFlags)
+{
+ RTLDRMOD hLdrMod;
+ int rc = RTLdrOpen(pMod->pszImgFile, RTLDR_O_FOR_DEBUG | fLdrFlags, enmArch, &hLdrMod);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtDbgModLdrOpenFromHandle(pMod, hLdrMod);
+ if (RT_FAILURE(rc))
+ RTLdrClose(hLdrMod);
+ }
+ return rc;
+}
+
+
+/** Virtual function table for the RTLdr based image reader. */
+DECL_HIDDEN_CONST(RTDBGMODVTIMG) const g_rtDbgModVtImgLdr =
+{
+ /*.u32Magic = */ RTDBGMODVTIMG_MAGIC,
+ /*.fReserved = */ 0,
+ /*.pszName = */ "RTLdr",
+ /*.pfnTryOpen = */ rtDbgModLdr_TryOpen,
+ /*.pfnClose = */ rtDbgModLdr_Close,
+ /*.pfnEnumDbgInfo = */ rtDbgModLdr_EnumDbgInfo,
+ /*.pfnEnumSegments = */ rtDbgModLdr_EnumSegments,
+ /*.pfnEnumSymbols = */ rtDbgModLdr_EnumSymbols,
+ /*.pfnImageSize = */ rtDbgModLdr_ImageSize,
+ /*.pfnLinkAddressToSegOffset = */ rtDbgModLdr_LinkAddressToSegOffset,
+ /*.pfnRvaToSegOffset= */ rtDbgModLdr_RvaToSegOffset,
+ /*.pfnMapPart = */ rtDbgModLdr_MapPart,
+ /*.pfnUnmapPart = */ rtDbgModLdr_UnmapPart,
+ /*.pfnReadAt = */ rtDbgModLdr_ReadAt,
+ /*.pfnGetFormat = */ rtDbgModLdr_GetFormat,
+ /*.pfnGetArch = */ rtDbgModLdr_GetArch,
+ /*.pfnQueryProp = */ rtDbgModLdr_QueryProp,
+ /*.pfnUnwindFrame = */ rtDbgModLdr_UnwindFrame,
+
+ /*.u32EndMagic = */ RTDBGMODVTIMG_MAGIC
+};
+
+
+/**
+ * Open PE-image trick.
+ *
+ * @returns IPRT status code
+ * @param pDbgMod The debug module instance.
+ * @param hLdrMod The module to open a image debug backend for.
+ */
+DECLHIDDEN(int) rtDbgModLdrOpenFromHandle(PRTDBGMODINT pDbgMod, RTLDRMOD hLdrMod)
+{
+ PRTDBGMODLDR pThis = (PRTDBGMODLDR)RTMemAllocZ(sizeof(RTDBGMODLDR));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ pThis->u32Magic = RTDBGMODLDR_MAGIC;
+ pThis->hLdrMod = hLdrMod;
+ pDbgMod->pvImgPriv = pThis;
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/common/dbg/dbgmodmapsym.cpp b/src/VBox/Runtime/common/dbg/dbgmodmapsym.cpp
new file mode 100644
index 00000000..8cbc0447
--- /dev/null
+++ b/src/VBox/Runtime/common/dbg/dbgmodmapsym.cpp
@@ -0,0 +1,622 @@
+/* $Id: dbgmodmapsym.cpp $ */
+/** @file
+ * IPRT - Debug Map Reader for MAPSYM files (used by SYMDBG from old MASM).
+ *
+ * MAPSYM is was the tool producing these files from linker map files for
+ * use with SYMDBG (which shipped with MASM 3.0 (possibly earlier)), the OS/2
+ * kernel debugger, and other tools. The format is very limited and they had
+ * to strip down the os2krnl.map file in later years to keep MAPSYM happy.
+ */
+
+/*
+ * Copyright (C) 2009-2023 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 *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DBG
+#include <iprt/dbg.h>
+#include "internal/iprt.h"
+
+#include <iprt/err.h>
+#include <iprt/ctype.h>
+#include <iprt/file.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include "internal/dbgmod.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** @name MAPSYM structures and constants.
+ * @{ */
+
+/** MAPSYM: Header structure. */
+typedef struct MAPSYMHDR
+{
+ uint16_t off16NextMap; /**< 0x00: Offset of the next map divided by 16. */
+ uint8_t bFlags; /**< 0x02: Who really knows... */
+ uint8_t bReserved; /**< 0x03: Reserved / unknown. */
+ uint16_t uSegEntry; /**< 0x04: Some entrypoint/segment thing we don't care about. */
+ uint16_t cConsts; /**< 0x06: Constants referenced by offConstDef. */
+ uint16_t offConstDef; /**< 0x08: Offset to head of constant chain. Not div 16? */
+ uint16_t cSegs; /**< 0x0a: Number of segments in the map. */
+ uint16_t off16SegDef; /**< 0x0c: Offset of the segment defintions divided by 16. */
+ uint8_t cchMaxSym; /**< 0x0e: Maximum symbol-name length. */
+ uint8_t cchModule; /**< 0x0f: Length of the module name. */
+ RT_FLEXIBLE_ARRAY_EXTENSION
+ char achModule[RT_FLEXIBLE_ARRAY]; /**< 0x10: Module name, length given by cchModule. */
+} MAPSYMHDR;
+
+/** MAPSYM: Tail structure. */
+typedef struct MAPSYMTAIL
+{
+ uint16_t offNextMap; /**< 0x00: Always zero (it's the tail, see). */
+ uint8_t bRelease; /**< 0x02: Minor version number. */
+ uint8_t bVersion; /**< 0x03: Major version number. */
+} MAPSYMTAIL;
+
+/** MAPSYM: Segment defintion. */
+typedef struct MAPSYMSEGDEF
+{
+ uint16_t off16NextSeg; /**< 0x00: Offset of the next segment divided by 16. */
+ uint16_t cSymbols; /**< 0x02: Number of symbol offsets . */
+ uint16_t offSymbolOffsets; /**< 0x04: Offset of the symbol offset table. Each entry is a 16-bit value giving
+ * the offset symbol relative to this structure. */
+ uint16_t au16Reserved0[4]; /**< 0x06: Reserved / unknown.
+ * First byte/word seems to be 1-based segment number. */
+ uint8_t bFlags; /**< 0x0e: MAPSYMSEGDEF_F_32BIT or zero. */
+ uint8_t bReserved1; /**< 0x0f: Reserved / unknown. */
+ uint16_t offLineDef; /**< 0x10: Offset to the line defintions. */
+ uint16_t u16Reserved2; /**< 0x12: Reserved / unknown. Often seen holding 0xff00. */
+ uint8_t cchSegName; /**< 0x14: Segment name length. */
+ RT_FLEXIBLE_ARRAY_EXTENSION
+ char achSegName[RT_FLEXIBLE_ARRAY]; /**< 0x15: Segment name, length given by cchSegName. */
+} MAPSYMSEGDEF;
+
+#define MAPSYMSEGDEF_F_32BIT UINT8_C(0x01) /**< Indicates 32-bit segment rather than 16-bit, relevant for symbols. */
+#define MAPSYMSEGDEF_F_UNKNOWN UINT8_C(0x02) /**< Set on all segments in os2krnlr.sym from ACP2. */
+
+/** MAPSYM: 16-bit symbol */
+typedef struct MAPSYMSYMDEF16
+{
+ uint16_t uValue; /**< 0x00: The symbol value (address). */
+ uint8_t cchName; /**< 0x02: Symbol name length. */
+ char achName[1]; /**< 0x03: The symbol name, length give by cchName. */
+} MAPSYMSYMDEF16;
+
+/** MAPSYM: 16-bit symbol */
+typedef struct MAPSYMSYMDEF32
+{
+ uint32_t uValue; /**< 0x00: The symbol value (address). */
+ uint8_t cchName; /**< 0x04: Symbol name length. */
+ char achName[1]; /**< 0x05: The symbol name, length give by cchName. */
+} MAPSYMSYMDEF32;
+
+/** MAPSYM: Line number defintions. */
+typedef struct MAPSYMLINEDEF
+{
+ uint16_t off16NextLine; /**< 0x00: Offset to the next line defintion divided by 16. */
+ uint16_t uSegment; /**< 0x02: Guessing this must be segment number. */
+ uint16_t offLines; /**< 0x04: Offset to the line number array, relative to this structure. */
+ uint16_t cLines; /**< 0x08: Number of line numbers in the array. */
+ uint8_t cchSrcFile; /**< 0x0a: Length of source filename. */
+ RT_FLEXIBLE_ARRAY_EXTENSION
+ char achSrcFile[RT_FLEXIBLE_ARRAY]; /**< 0x0b: Source filename, length given by cchSrcFile. */
+} MAPSYMLINEDEF;
+
+/** MAPSYM: 16-bit line numbers. */
+typedef struct MAPSYMLINENO16
+{
+ uint16_t offSeg;
+ uint16_t uLineNo;
+} MAPSYMLINENO16;
+
+/** @} */
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Maximum number of segments we expect in a MAPSYM file. */
+#define RTDBGMODMAPSYM_MAX_SEGMENTS 256
+
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnUnwindFrame} */
+static DECLCALLBACK(int) rtDbgModMapSym_UnwindFrame(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState)
+{
+ RT_NOREF(pMod, iSeg, off, pState);
+ return VERR_DBG_NO_UNWIND_INFO;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByAddr} */
+static DECLCALLBACK(int) rtDbgModMapSym_LineByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off,
+ PRTINTPTR poffDisp, PRTDBGLINE pLineInfo)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModLineByAddr(hCnt, iSeg, off, poffDisp, pLineInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByOrdinal} */
+static DECLCALLBACK(int) rtDbgModMapSym_LineByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModLineByOrdinal(hCnt, iOrdinal, pLineInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineCount} */
+static DECLCALLBACK(uint32_t) rtDbgModMapSym_LineCount(PRTDBGMODINT pMod)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModLineCount(hCnt);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineAdd} */
+static DECLCALLBACK(int) rtDbgModMapSym_LineAdd(PRTDBGMODINT pMod, const char *pszFile, size_t cchFile, uint32_t uLineNo,
+ uint32_t iSeg, RTUINTPTR off, uint32_t *piOrdinal)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ Assert(!pszFile[cchFile]); NOREF(cchFile);
+ return RTDbgModLineAdd(hCnt, pszFile, uLineNo, iSeg, off, piOrdinal);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByAddr} */
+static DECLCALLBACK(int) rtDbgModMapSym_SymbolByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags,
+ PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModSymbolByAddr(hCnt, iSeg, off, fFlags, poffDisp, pSymInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByName} */
+static DECLCALLBACK(int) rtDbgModMapSym_SymbolByName(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol,
+ PRTDBGSYMBOL pSymInfo)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ Assert(!pszSymbol[cchSymbol]); NOREF(cchSymbol);
+ return RTDbgModSymbolByName(hCnt, pszSymbol, pSymInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByOrdinal} */
+static DECLCALLBACK(int) rtDbgModMapSym_SymbolByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModSymbolByOrdinal(hCnt, iOrdinal, pSymInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolCount} */
+static DECLCALLBACK(uint32_t) rtDbgModMapSym_SymbolCount(PRTDBGMODINT pMod)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModSymbolCount(hCnt);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolAdd} */
+static DECLCALLBACK(int) rtDbgModMapSym_SymbolAdd(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol,
+ RTDBGSEGIDX iSeg, RTUINTPTR off, RTUINTPTR cb, uint32_t fFlags,
+ uint32_t *piOrdinal)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ Assert(!pszSymbol[cchSymbol]); NOREF(cchSymbol);
+ return RTDbgModSymbolAdd(hCnt, pszSymbol, iSeg, off, cb, fFlags, piOrdinal);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentByIndex} */
+static DECLCALLBACK(int) rtDbgModMapSym_SegmentByIndex(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModSegmentByIndex(hCnt, iSeg, pSegInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentCount} */
+static DECLCALLBACK(RTDBGSEGIDX) rtDbgModMapSym_SegmentCount(PRTDBGMODINT pMod)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModSegmentCount(hCnt);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentAdd} */
+static DECLCALLBACK(int) rtDbgModMapSym_SegmentAdd(PRTDBGMODINT pMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName,
+ size_t cchName, uint32_t fFlags, PRTDBGSEGIDX piSeg)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ Assert(!pszName[cchName]); NOREF(cchName);
+ return RTDbgModSegmentAdd(hCnt, uRva, cb, pszName, fFlags, piSeg);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnImageSize} */
+static DECLCALLBACK(RTUINTPTR) rtDbgModMapSym_ImageSize(PRTDBGMODINT pMod)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModImageSize(hCnt);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnRvaToSegOff} */
+static DECLCALLBACK(RTDBGSEGIDX) rtDbgModMapSym_RvaToSegOff(PRTDBGMODINT pMod, RTUINTPTR uRva, PRTUINTPTR poffSeg)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ return RTDbgModRvaToSegOff(hCnt, uRva, poffSeg);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnClose} */
+static DECLCALLBACK(int) rtDbgModMapSym_Close(PRTDBGMODINT pMod)
+{
+ RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;
+ RTDbgModRelease(hCnt);
+ pMod->pvDbgPriv = NULL;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Validate the module header.
+ *
+ * @returns true if valid, false if not.
+ * @param pHdr The header.
+ * @param cbAvail How much we've actually read.
+ * @param cbFile The file size (relative to module header).
+ */
+static bool rtDbgModMapSymIsValidHeader(MAPSYMHDR const *pHdr, size_t cbAvail, uint64_t cbFile)
+{
+ if (cbAvail <= RT_UOFFSETOF(MAPSYMHDR, achModule))
+ return false;
+
+ if (pHdr->cSegs == 0)
+ return false;
+ if (pHdr->cSegs > RTDBGMODMAPSYM_MAX_SEGMENTS)
+ return false;
+
+ if (pHdr->off16SegDef == 0)
+ return false;
+ if (pHdr->off16SegDef * (uint32_t)16 >= cbFile)
+ return false;
+
+ if (pHdr->cchModule == 0)
+ return false;
+ if (pHdr->cchModule > 128) /* Note must be smaller than abPadding below in caller */
+ return false;
+
+ size_t cchMaxName = cbAvail - RT_UOFFSETOF(MAPSYMHDR, achModule);
+ if (pHdr->cchModule > cchMaxName)
+ return false;
+
+ for (uint32_t i = 0; i < pHdr->cchModule; i++)
+ {
+ unsigned char const uch = pHdr->achModule[i];
+ if ( uch < 0x20
+ || uch >= 0x7f)
+ return false;
+ }
+
+ return true;
+}
+
+
+/**
+ * Validate the given segment definition.
+ *
+ * @returns true if valid, false if not.
+ * @param pSegDef The segment definition structure.
+ * @param cbMax Host many bytes are available starting with pSegDef.
+ */
+static bool rtDbgModMapSymIsValidSegDef(MAPSYMSEGDEF const *pSegDef, size_t cbMax)
+{
+ if (RT_UOFFSETOF(MAPSYMSEGDEF, achSegName) > cbMax)
+ return false;
+ if (pSegDef->cSymbols)
+ {
+ if (pSegDef->cSymbols > _32K)
+ {
+ Log(("rtDbgModMapSymIsValidSegDef: Too many symbols: %#x\n", pSegDef->cSymbols));
+ return false;
+ }
+
+ if (pSegDef->offSymbolOffsets + (uint32_t)2 * pSegDef->cSymbols > cbMax)
+ {
+ Log(("rtDbgModMapSymIsValidSegDef: Bad symbol offset/count: %#x/%#x\n", pSegDef->offSymbolOffsets, pSegDef->cSymbols));
+ return false;
+ }
+ }
+
+ size_t cchMaxName = cbMax - RT_UOFFSETOF(MAPSYMHDR, achModule);
+ if (pSegDef->cchSegName > cchMaxName)
+ {
+ Log(("rtDbgModMapSymIsValidSegDef: Bad segment name length\n"));
+ return false;
+ }
+
+ for (uint32_t i = 0; i < pSegDef->cchSegName; i++)
+ {
+ unsigned char uch = pSegDef->achSegName[i];
+ if ( uch < 0x20
+ || uch >= 0x7f)
+ {
+ Log(("rtDbgModMapSymIsValidSegDef: Bad segment name: %.*Rhxs\n", pSegDef->cchSegName, pSegDef->achSegName));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * Fills @a hCnt with segments and symbols from the MAPSYM file.
+ *
+ * @note We only support reading the first module, right now.
+ */
+static int rtDbgModMapSymReadIt(RTDBGMOD hCnt, uint8_t const *pbFile, size_t cbFile)
+{
+ /*
+ * Revalidate the header.
+ */
+ MAPSYMHDR const *pHdr = (MAPSYMHDR const *)pbFile;
+ if (!rtDbgModMapSymIsValidHeader(pHdr, cbFile, cbFile))
+ return VERR_DBG_NO_MATCHING_INTERPRETER;
+ Log(("rtDbgModMapSymReadIt: szModule='%.*s' cSegs=%u off16NextMap=%#x\n",
+ pHdr->cchModule, pHdr->achModule, pHdr->cSegs, pHdr->off16NextMap));
+
+ /*
+ * Load each segment.
+ */
+ uint32_t uRva = 0;
+ uint32_t const cSegs = pHdr->cSegs;
+ uint32_t offSegment = pHdr->off16SegDef * (uint32_t)16;
+ for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
+ {
+ if (offSegment >= cbFile)
+ return VERR_DBG_NO_MATCHING_INTERPRETER;
+
+ size_t const cbMax = cbFile - offSegment;
+ MAPSYMSEGDEF const *pSegDef = (MAPSYMSEGDEF const *)&pbFile[offSegment];
+ if (!rtDbgModMapSymIsValidSegDef(pSegDef, cbMax))
+ return VERR_DBG_NO_MATCHING_INTERPRETER;
+
+ Log(("rtDbgModMapSymReadIt: Segment #%u: flags=%#x name='%.*s' symbols=%#x @ %#x next=%#x lines=@%#x (reserved: %#x %#x %#x %#x %#x %#x)\n",
+ iSeg, pSegDef->bFlags, pSegDef->cchSegName, pSegDef->achSegName, pSegDef->cSymbols, pSegDef->offSymbolOffsets,
+ pSegDef->off16NextSeg, pSegDef->offLineDef, pSegDef->au16Reserved0[0], pSegDef->au16Reserved0[1],
+ pSegDef->au16Reserved0[2], pSegDef->au16Reserved0[3], pSegDef->bReserved1, pSegDef->u16Reserved2));
+
+ /*
+ * First symbol pass finds the largest symbol and uses that as the segment size.
+ */
+ uint32_t cbSegmentEst = 0;
+ uint32_t const cSymbols = pSegDef->cSymbols;
+ uint16_t const * const paoffSymbols = (uint16_t const *)&pbFile[offSegment + pSegDef->offSymbolOffsets];
+ bool const fIs32Bit = RT_BOOL(pSegDef->bFlags & MAPSYMSEGDEF_F_32BIT);
+ uint32_t const cbSymDef = fIs32Bit ? 4 + 1 : 2 + 1;
+ for (uint32_t iSymbol = 0; iSymbol < cSymbols; iSymbol++)
+ {
+ uint32_t off = paoffSymbols[iSymbol] + offSegment;
+ if (off + cbSymDef <= cbFile)
+ {
+ uint32_t uValue = fIs32Bit ? *(uint32_t const *)&pbFile[off] : (uint32_t)*(uint16_t const *)&pbFile[off];
+ if (uValue > cbSegmentEst)
+ cbSegmentEst = uValue;
+ }
+ else
+ Log(("rtDbgModMapSymReadIt: Bad symbol offset %#x\n", off));
+ }
+
+ /*
+ * Add the segment.
+ */
+ char szName[256];
+ memcpy(szName, pSegDef->achSegName, pSegDef->cchSegName);
+ szName[pSegDef->cchSegName] = '\0';
+ if (!pSegDef->cchSegName)
+ RTStrPrintf(szName, sizeof(szName), "seg%02u", iSeg);
+
+ RTDBGSEGIDX idxDbgSeg = iSeg;
+ int rc = RTDbgModSegmentAdd(hCnt, uRva, cbSegmentEst, szName, 0 /*fFlags*/, &idxDbgSeg);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ uRva += cbSegmentEst;
+
+ /*
+ * The second symbol pass loads the symbol values and names.
+ */
+ for (uint32_t iSymbol = 0; iSymbol < cSymbols; iSymbol++)
+ {
+ uint32_t off = paoffSymbols[iSymbol] + offSegment;
+ if (off + cbSymDef <= cbFile)
+ {
+ /* Get value: */
+ uint32_t uValue = RT_MAKE_U16(pbFile[off], pbFile[off + 1]);
+ off += 2;
+ if (fIs32Bit)
+ {
+ uValue |= RT_MAKE_U32_FROM_U8(0, 0, pbFile[off], pbFile[off + 1]);
+ off += 2;
+ }
+
+ /* Get name: */
+ uint8_t cchName = pbFile[off++];
+ if (off + cchName <= cbFile)
+ {
+ memcpy(szName, &pbFile[off], cchName);
+ szName[cchName] = '\0';
+ RTStrPurgeEncoding(szName);
+ }
+ else
+ cchName = 0;
+ if (cchName == 0)
+ RTStrPrintf(szName, sizeof(szName), "unknown_%u_%u", iSeg, iSymbol);
+
+ /* Try add it: */
+ rc = RTDbgModSymbolAdd(hCnt, szName, idxDbgSeg, uValue, 0 /*cb*/, 0 /*fFlags*/, NULL /*piOrdinal*/);
+ if (RT_SUCCESS(rc))
+ Log7(("rtDbgModMapSymReadIt: %02x:%06x %s\n", idxDbgSeg, uValue, szName));
+ else if ( rc == VERR_DBG_DUPLICATE_SYMBOL
+ || rc == VERR_DBG_ADDRESS_CONFLICT
+ || rc == VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE)
+ Log(("rtDbgModMapSymReadIt: %02x:%06x %s\n", idxDbgSeg, uValue, szName));
+ else
+ {
+ Log(("rtDbgModMapSymReadIt: Unexpected RTDbgModSymbolAdd failure: %Rrc - %02x:%06x %s\n",
+ rc, idxDbgSeg, uValue, szName));
+ return rc;
+ }
+ }
+ }
+
+ /* Next segment */
+ offSegment = pSegDef->off16NextSeg * (uint32_t)16;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnTryOpen} */
+static DECLCALLBACK(int) rtDbgModMapSym_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch)
+{
+ NOREF(enmArch);
+
+ /*
+ * Fend off images.
+ */
+ if ( !pMod->pszDbgFile
+ || pMod->pImgVt)
+ return VERR_DBG_NO_MATCHING_INTERPRETER;
+ pMod->pvDbgPriv = NULL;
+
+ /*
+ * Try open the file and check out the first header.
+ */
+ RTFILE hFile;
+ int rc = RTFileOpen(&hFile, pMod->pszDbgFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
+ if (RT_SUCCESS(rc))
+ {
+ uint64_t cbFile = 0;
+ rc = RTFileQuerySize(hFile, &cbFile);
+ if ( RT_SUCCESS(rc)
+ && cbFile < _2M)
+ {
+ union
+ {
+ MAPSYMHDR Hdr;
+ char abPadding[sizeof(MAPSYMHDR) + 257]; /* rtDbgModMapSymIsValidHeader makes size assumptions. */
+ } uBuf;
+ size_t cbToRead = (size_t)RT_MIN(cbFile, sizeof(uBuf));
+ rc = RTFileReadAt(hFile, 0, &uBuf, RT_MIN(cbFile, sizeof(uBuf)), NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if (rtDbgModMapSymIsValidHeader(&uBuf.Hdr, cbToRead, cbFile))
+ {
+ uBuf.Hdr.achModule[uBuf.Hdr.cchModule] = '\0';
+
+ /*
+ * Read the whole thing into memory, create an
+ * instance/container and load it with symbols.
+ */
+ void *pvFile = NULL;
+ size_t cbFile2 = 0;
+ rc = RTFileReadAllByHandle(hFile, &pvFile, &cbFile2);
+ if (RT_SUCCESS(rc))
+ {
+ RTDBGMOD hCnt;
+ rc = RTDbgModCreate(&hCnt, uBuf.Hdr.achModule, 0 /*cbSeg*/, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtDbgModMapSymReadIt(hCnt, (uint8_t const *)pvFile, cbFile2);
+ if (RT_SUCCESS(rc))
+ pMod->pvDbgPriv = hCnt;
+ else
+ RTDbgModRelease(hCnt);
+ }
+ RTFileReadAllFree(pvFile, cbFile2);
+ }
+ }
+ else
+ rc = VERR_DBG_NO_MATCHING_INTERPRETER;
+ }
+ }
+ RTFileClose(hFile);
+ }
+ Log(("rtDbgModMapSym_TryOpen: %s -> %Rrc, %p\n", pMod->pszDbgFile, rc, pMod->pvDbgPriv));
+ return rc;
+}
+
+
+
+/** Virtual function table for the MAPSYM file reader. */
+DECL_HIDDEN_CONST(RTDBGMODVTDBG) const g_rtDbgModVtDbgMapSym =
+{
+ /*.u32Magic = */ RTDBGMODVTDBG_MAGIC,
+ /*.fSupports = */ RT_DBGTYPE_SYM,
+ /*.pszName = */ "mapsym",
+ /*.pfnTryOpen = */ rtDbgModMapSym_TryOpen,
+ /*.pfnClose = */ rtDbgModMapSym_Close,
+
+ /*.pfnRvaToSegOff = */ rtDbgModMapSym_RvaToSegOff,
+ /*.pfnImageSize = */ rtDbgModMapSym_ImageSize,
+
+ /*.pfnSegmentAdd = */ rtDbgModMapSym_SegmentAdd,
+ /*.pfnSegmentCount = */ rtDbgModMapSym_SegmentCount,
+ /*.pfnSegmentByIndex = */ rtDbgModMapSym_SegmentByIndex,
+
+ /*.pfnSymbolAdd = */ rtDbgModMapSym_SymbolAdd,
+ /*.pfnSymbolCount = */ rtDbgModMapSym_SymbolCount,
+ /*.pfnSymbolByOrdinal = */ rtDbgModMapSym_SymbolByOrdinal,
+ /*.pfnSymbolByName = */ rtDbgModMapSym_SymbolByName,
+ /*.pfnSymbolByAddr = */ rtDbgModMapSym_SymbolByAddr,
+
+ /*.pfnLineAdd = */ rtDbgModMapSym_LineAdd,
+ /*.pfnLineCount = */ rtDbgModMapSym_LineCount,
+ /*.pfnLineByOrdinal = */ rtDbgModMapSym_LineByOrdinal,
+ /*.pfnLineByAddr = */ rtDbgModMapSym_LineByAddr,
+
+ /*.pfnUnwindFrame = */ rtDbgModMapSym_UnwindFrame,
+
+ /*.u32EndMagic = */ RTDBGMODVTDBG_MAGIC
+};
+
diff --git a/src/VBox/Runtime/common/dbg/dbgmodnm.cpp b/src/VBox/Runtime/common/dbg/dbgmodnm.cpp
new file mode 100644
index 00000000..04174c7b
--- /dev/null
+++ b/src/VBox/Runtime/common/dbg/dbgmodnm.cpp
@@ -0,0 +1,581 @@
+/* $Id: dbgmodnm.cpp $ */
+/** @file
+ * IPRT - Debug Map Reader For NM Like Mapfiles.
+ */
+
+/*
+ * Copyright (C) 2009-2023 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 <iprt/dbg.h>
+#include "internal/iprt.h"
+
+#include <iprt/err.h>
+#include <iprt/ctype.h>
+#include <iprt/mem.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include "internal/dbgmod.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Instance data.
+ */
+typedef struct RTDBGMODNM
+{
+ /** The debug container containing doing the real work. */
+ RTDBGMOD hCnt;
+} RTDBGMODNM;
+/** Pointer to instance data NM map reader. */
+typedef RTDBGMODNM *PRTDBGMODNM;
+
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnUnwindFrame} */
+static DECLCALLBACK(int) rtDbgModNm_UnwindFrame(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState)
+{
+ RT_NOREF(pMod, iSeg, off, pState);
+ return VERR_DBG_NO_UNWIND_INFO;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByAddr} */
+static DECLCALLBACK(int) rtDbgModNm_LineByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off,
+ PRTINTPTR poffDisp, PRTDBGLINE pLineInfo)
+{
+ PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv;
+ return RTDbgModLineByAddr(pThis->hCnt, iSeg, off, poffDisp, pLineInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByOrdinal} */
+static DECLCALLBACK(int) rtDbgModNm_LineByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo)
+{
+ PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv;
+ return RTDbgModLineByOrdinal(pThis->hCnt, iOrdinal, pLineInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineCount} */
+static DECLCALLBACK(uint32_t) rtDbgModNm_LineCount(PRTDBGMODINT pMod)
+{
+ PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv;
+ return RTDbgModLineCount(pThis->hCnt);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnLineAdd} */
+static DECLCALLBACK(int) rtDbgModNm_LineAdd(PRTDBGMODINT pMod, const char *pszFile, size_t cchFile, uint32_t uLineNo,
+ uint32_t iSeg, RTUINTPTR off, uint32_t *piOrdinal)
+{
+ PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv;
+ Assert(!pszFile[cchFile]); NOREF(cchFile);
+ return RTDbgModLineAdd(pThis->hCnt, pszFile, uLineNo, iSeg, off, piOrdinal);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByAddr} */
+static DECLCALLBACK(int) rtDbgModNm_SymbolByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags,
+ PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo)
+{
+ PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv;
+ return RTDbgModSymbolByAddr(pThis->hCnt, iSeg, off, fFlags, poffDisp, pSymInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByName} */
+static DECLCALLBACK(int) rtDbgModNm_SymbolByName(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol,
+ PRTDBGSYMBOL pSymInfo)
+{
+ PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv;
+ Assert(!pszSymbol[cchSymbol]); NOREF(cchSymbol);
+ return RTDbgModSymbolByName(pThis->hCnt, pszSymbol, pSymInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByOrdinal} */
+static DECLCALLBACK(int) rtDbgModNm_SymbolByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo)
+{
+ PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv;
+ return RTDbgModSymbolByOrdinal(pThis->hCnt, iOrdinal, pSymInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolCount} */
+static DECLCALLBACK(uint32_t) rtDbgModNm_SymbolCount(PRTDBGMODINT pMod)
+{
+ PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv;
+ return RTDbgModSymbolCount(pThis->hCnt);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolAdd} */
+static DECLCALLBACK(int) rtDbgModNm_SymbolAdd(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol,
+ RTDBGSEGIDX iSeg, RTUINTPTR off, RTUINTPTR cb, uint32_t fFlags,
+ uint32_t *piOrdinal)
+{
+ PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv;
+ Assert(!pszSymbol[cchSymbol]); NOREF(cchSymbol);
+ return RTDbgModSymbolAdd(pThis->hCnt, pszSymbol, iSeg, off, cb, fFlags, piOrdinal);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentByIndex} */
+static DECLCALLBACK(int) rtDbgModNm_SegmentByIndex(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo)
+{
+ PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv;
+ return RTDbgModSegmentByIndex(pThis->hCnt, iSeg, pSegInfo);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentCount} */
+static DECLCALLBACK(RTDBGSEGIDX) rtDbgModNm_SegmentCount(PRTDBGMODINT pMod)
+{
+ PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv;
+ return RTDbgModSegmentCount(pThis->hCnt);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentAdd} */
+static DECLCALLBACK(int) rtDbgModNm_SegmentAdd(PRTDBGMODINT pMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName,
+ size_t cchName, uint32_t fFlags, PRTDBGSEGIDX piSeg)
+{
+ PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv;
+ Assert(!pszName[cchName]); NOREF(cchName);
+ return RTDbgModSegmentAdd(pThis->hCnt, uRva, cb, pszName, fFlags, piSeg);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnImageSize} */
+static DECLCALLBACK(RTUINTPTR) rtDbgModNm_ImageSize(PRTDBGMODINT pMod)
+{
+ PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv;
+ return RTDbgModImageSize(pThis->hCnt);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnRvaToSegOff} */
+static DECLCALLBACK(RTDBGSEGIDX) rtDbgModNm_RvaToSegOff(PRTDBGMODINT pMod, RTUINTPTR uRva, PRTUINTPTR poffSeg)
+{
+ PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv;
+ return RTDbgModRvaToSegOff(pThis->hCnt, uRva, poffSeg);
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnClose} */
+static DECLCALLBACK(int) rtDbgModNm_Close(PRTDBGMODINT pMod)
+{
+ PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv;
+ RTDbgModRelease(pThis->hCnt);
+ pThis->hCnt = NIL_RTDBGMOD;
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Scans a NM-like map file.
+ *
+ * This implements both passes to avoid code duplication.
+ *
+ * @returns IPRT status code.
+ * @param pThis The instance data.
+ * @param pStrm The stream.
+ * @param fAddSymbols false in the first pass, true in the second.
+ */
+static int rtDbgModNmScanFile(PRTDBGMODNM pThis, PRTSTREAM pStrm, bool fAddSymbols)
+{
+ /*
+ * Try parse the stream.
+ */
+ RTUINTPTR SegZeroRva = fAddSymbols ? RTDbgModSegmentRva(pThis->hCnt, 0/*iSeg*/) : 0;
+ char szSym[RTDBG_SYMBOL_NAME_LENGTH] = "";
+ size_t cchMod = 0;
+ size_t offSym = 0;
+ unsigned cchAddr = 0;
+ uint64_t u64Low = UINT64_MAX;
+ uint64_t u64High = 0;
+ int fWithType = -1;
+ char szLine[512];
+ int rc;
+ while (RT_SUCCESS(rc = RTStrmGetLine(pStrm, szLine, sizeof(szLine))))
+ {
+ char chType;
+ if (RT_C_IS_XDIGIT(szLine[0]))
+ {
+ /*
+ * This is really what C was made for, string parsing.
+ */
+ /* The symbol value (address). */
+ uint64_t u64Addr;
+ char *psz;
+ rc = RTStrToUInt64Ex(szLine, &psz, 16, &u64Addr);
+ if (rc != VWRN_TRAILING_CHARS)
+ return VERR_DBG_NOT_NM_MAP_FILE;
+
+ /* Check the address width. */
+ if (cchAddr == 0)
+ cchAddr = psz == &szLine[8] ? 8 : 16;
+ if (psz != &szLine[cchAddr])
+ return VERR_DBG_NOT_NM_MAP_FILE;
+
+ /* Get the type and check for single space before symbol. */
+ char *pszName;
+ if (fWithType < 0)
+ fWithType = RT_C_IS_BLANK(szLine[cchAddr + 2]) ? 1 : 0; /* have type? Linux 2.4 /proc/ksyms doesn't. */
+ if (fWithType)
+ {
+ chType = szLine[cchAddr + 1];
+ pszName = &szLine[cchAddr + 3];
+ if ( RT_C_IS_BLANK(chType)
+ || !RT_C_IS_BLANK(szLine[cchAddr + 2])
+ || RT_C_IS_BLANK(szLine[cchAddr + 3]))
+ return VERR_DBG_NOT_NM_MAP_FILE;
+ }
+ else
+ {
+ chType = 'T';
+ pszName = &szLine[cchAddr + 1];
+ }
+
+ /* Find the end of the symbol name. */
+ char *pszNameEnd = pszName;
+ char ch;
+ while ((ch = *pszNameEnd) != '\0' && !RT_C_IS_SPACE(ch))
+ pszNameEnd++;
+
+ /* Any module name (linux /proc/kallsyms) following in brackets? */
+ char *pszModName = pszNameEnd;
+ char *pszModNameEnd = pszModName;
+ if (*pszModName)
+ {
+ *pszModName++ = '\0';
+ pszModNameEnd = pszModName = RTStrStripL(pszModName);
+ if (*pszModName != '\0')
+ {
+ if (*pszModName != '[')
+ return VERR_DBG_NOT_LINUX_KALLSYMS;
+ pszModNameEnd = ++pszModName;
+ while ((ch = *pszModNameEnd) != '\0' && ch != ']')
+ pszModNameEnd++;
+ if (ch != ']')
+ return VERR_DBG_NOT_LINUX_KALLSYMS;
+ char *pszEnd = pszModNameEnd + 1;
+ if ((size_t)(pszModNameEnd - pszModName) >= 128) /* lazy bird */
+ return VERR_DBG_NOT_LINUX_KALLSYMS;
+ *pszModNameEnd = '\0';
+ if (*pszEnd)
+ pszEnd = RTStrStripL(pszEnd);
+ if (*pszEnd)
+ return VERR_DBG_NOT_LINUX_KALLSYMS;
+ }
+ }
+
+ /*
+ * Did the module change? Then update the symbol prefix.
+ */
+ if ( cchMod != (size_t)(pszModNameEnd - pszModName)
+ || memcmp(pszModName, szSym, cchMod))
+ {
+ cchMod = pszModNameEnd - pszModName;
+ if (cchMod == 0)
+ offSym = 0;
+ else
+ {
+ memcpy(szSym, pszModName, cchMod);
+ szSym[cchMod] = '.';
+ offSym = cchMod + 1;
+ }
+ szSym[offSym] = '\0';
+ }
+
+ /*
+ * Validate the type and add the symbol if it's a type we care for.
+ */
+ uint32_t fFlags = 0;
+ RTDBGSEGIDX iSegSym = 0;
+ switch (chType)
+ {
+ /* absolute */
+ case 'a':
+ case '?': /* /proc/kallsyms */
+ iSegSym = RTDBGSEGIDX_ABS;
+ /// @todo fFlags |= RTDBG_SYM_FLAGS_LOCAL;
+ break;
+ case 'A':
+ iSegSym = RTDBGSEGIDX_ABS;
+ /// @todo fFlags |= RTDBG_SYM_FLAGS_PUBLIC;
+ break;
+
+ case 'b':
+ /// @todo fFlags |= RTDBG_SYM_FLAGS_LOCAL;
+ break;
+ case 'B':
+ /// @todo fFlags |= RTDBG_SYM_FLAGS_PUBLIC;
+ break;
+
+ case 'c':
+ /// @todo fFlags |= RTDBG_SYM_FLAGS_LOCAL | RTDBG_SYM_FLAGS_COMMON;
+ break;
+ case 'C':
+ /// @todo fFlags |= RTDBG_SYM_FLAGS_PUBLIC | RTDBG_SYM_FLAGS_COMMON;
+ break;
+
+ case 'd':
+ /// @todo fFlags |= RTDBG_SYM_FLAGS_LOCAL;
+ break;
+ case 'D':
+ /// @todo fFlags |= RTDBG_SYM_FLAGS_PUBLIC;
+ break;
+
+ case 'g':
+ /// @todo fFlags |= RTDBG_SYM_FLAGS_LOCAL;
+ break;
+ case 'G':
+ /// @todo fFlags |= RTDBG_SYM_FLAGS_PUBLIC;
+ break;
+
+ case 'i':
+ /// @todo fFlags |= RTDBG_SYM_FLAGS_LOCAL;
+ break;
+ case 'I':
+ /// @todo fFlags |= RTDBG_SYM_FLAGS_PUBLIC;
+ break;
+
+ case 'r':
+ /// @todo fFlags |= RTDBG_SYM_FLAGS_LOCAL | RTDBG_SYM_FLAGS_CONST;
+ break;
+ case 'R':
+ /// @todo fFlags |= RTDBG_SYM_FLAGS_PUBLIC | RTDBG_SYM_FLAGS_CONST;
+ break;
+
+ case 's':
+ /// @todo fFlags |= RTDBG_SYM_FLAGS_LOCAL;
+ break;
+ case 'S':
+ /// @todo fFlags |= RTDBG_SYM_FLAGS_PUBLIC;
+ break;
+
+ case 't':
+ /// @todo fFlags |= RTDBG_SYM_FLAGS_LOCAL | RTDBG_SYM_FLAGS_TEXT;
+ break;
+ case 'T':
+ /// @todo fFlags |= RTDBG_SYM_FLAGS_PUBLIC | RTDBG_SYM_FLAGS_TEXT;
+ break;
+
+ case 'w':
+ /// @todo fFlags |= RTDBG_SYM_FLAGS_WEAK | RTDBG_SYM_FLAGS_LOCAL; //???
+ break;
+ case 'W':
+ /// @todo fFlags |= RTDBG_SYM_FLAGS_WEAK | RTDBG_SYM_FLAGS_PUBLIC;
+ break;
+
+ case 'N': /* debug */
+ case 'n':
+ case '-': /* stabs */
+ case 'u': /* undefined (/proc/kallsyms) */
+ case 'U':
+ case 'v': /* weakext */
+ case 'V':
+ iSegSym = NIL_RTDBGSEGIDX;
+ break;
+
+ default:
+ return VERR_DBG_NOT_NM_MAP_FILE;
+ }
+
+ if (iSegSym != NIL_RTDBGSEGIDX)
+ {
+ if (fAddSymbols)
+ {
+ size_t cchName = pszNameEnd - pszName;
+ if (cchName >= sizeof(szSym) - offSym)
+ cchName = sizeof(szSym) - offSym - 1;
+ memcpy(&szSym[offSym], pszName, cchName + 1);
+ if (iSegSym == 0)
+ rc = RTDbgModSymbolAdd(pThis->hCnt, szSym, iSegSym, u64Addr - SegZeroRva, 0/*cb*/, fFlags, NULL);
+ else
+ rc = RTDbgModSymbolAdd(pThis->hCnt, szSym, iSegSym, u64Addr, 0/*cb*/, fFlags, NULL);
+ if ( RT_FAILURE(rc)
+ && rc != VERR_DBG_DUPLICATE_SYMBOL
+ && rc != VERR_DBG_ADDRESS_CONFLICT) /* (don't be too strict) */
+ return rc;
+ }
+
+ /* Track segment span. */
+ if (iSegSym == 0)
+ {
+ if (u64Low > u64Addr)
+ u64Low = u64Addr;
+ if (u64High < u64Addr)
+ u64High = u64Addr;
+ }
+ }
+ }
+ else
+ {
+ /*
+ * This is either a blank line or a symbol without an address.
+ */
+ RTStrStripR(szLine);
+ if (szLine[0])
+ {
+ size_t cch = strlen(szLine);
+ if (cchAddr == 0)
+ cchAddr = cch < 16+3 || szLine[8+1] != ' ' ? 8 : 16;
+ if (cch < cchAddr+3+1)
+ return VERR_DBG_NOT_NM_MAP_FILE;
+ chType = szLine[cchAddr + 1];
+ if ( chType != 'U'
+ && chType != 'w')
+ return VERR_DBG_NOT_NM_MAP_FILE;
+ char *pszType = RTStrStripL(szLine);
+ if (pszType != &szLine[cchAddr + 1])
+ return VERR_DBG_NOT_NM_MAP_FILE;
+ if (!RT_C_IS_BLANK(szLine[cchAddr + 2]))
+ return VERR_DBG_NOT_NM_MAP_FILE;
+ }
+ /* else: blank - ignored */
+ }
+ }
+
+ /*
+ * The final segment.
+ */
+ if (rc == VERR_EOF)
+ {
+ if (fAddSymbols)
+ rc = VINF_SUCCESS;
+ else
+ {
+ if ( u64Low != UINT64_MAX
+ || u64High != 0)
+ rc = RTDbgModSegmentAdd(pThis->hCnt, u64Low, u64High - u64Low + 1, "main", 0, NULL);
+ else /* No sensible symbols... throw an error instead? */
+ rc = RTDbgModSegmentAdd(pThis->hCnt, 0, 0, "main", 0, NULL);
+ }
+ }
+
+ return rc;
+}
+
+
+/** @interface_method_impl{RTDBGMODVTDBG,pfnTryOpen} */
+static DECLCALLBACK(int) rtDbgModNm_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch)
+{
+ NOREF(enmArch);
+
+ /*
+ * Fend off images.
+ */
+ if ( !pMod->pszDbgFile
+ || pMod->pImgVt)
+ return VERR_DBG_NO_MATCHING_INTERPRETER;
+
+ /*
+ * Try open the file and create an instance.
+ */
+ PRTSTREAM pStrm;
+ int rc = RTStrmOpen(pMod->pszDbgFile, "r", &pStrm);
+ if (RT_SUCCESS(rc))
+ {
+ PRTDBGMODNM pThis = (PRTDBGMODNM)RTMemAlloc(sizeof(*pThis));
+ if (pThis)
+ {
+ rc = RTDbgModCreate(&pThis->hCnt, pMod->pszName, 0 /*cbSeg*/, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Scan the file twice, first to figure the segment
+ * sizes, then to add the symbol.
+ */
+ rc = rtDbgModNmScanFile(pThis, pStrm, false /*fAddSymbols*/);
+ if (RT_SUCCESS(rc))
+ rc = RTStrmRewind(pStrm);
+ if (RT_SUCCESS(rc))
+ rc = rtDbgModNmScanFile(pThis, pStrm, true /*fAddSymbols*/);
+ if (RT_SUCCESS(rc))
+ {
+ RTStrmClose(pStrm);
+ pMod->pvDbgPriv = pThis;
+ return rc;
+ }
+ }
+ RTDbgModRelease(pThis->hCnt);
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ RTStrmClose(pStrm);
+ }
+ return rc;
+}
+
+
+
+/** Virtual function table for the NM-like map file reader. */
+DECL_HIDDEN_CONST(RTDBGMODVTDBG) const g_rtDbgModVtDbgNm =
+{
+ /*.u32Magic = */ RTDBGMODVTDBG_MAGIC,
+ /*.fSupports = */ RT_DBGTYPE_MAP,
+ /*.pszName = */ "nm",
+ /*.pfnTryOpen = */ rtDbgModNm_TryOpen,
+ /*.pfnClose = */ rtDbgModNm_Close,
+
+ /*.pfnRvaToSegOff = */ rtDbgModNm_RvaToSegOff,
+ /*.pfnImageSize = */ rtDbgModNm_ImageSize,
+
+ /*.pfnSegmentAdd = */ rtDbgModNm_SegmentAdd,
+ /*.pfnSegmentCount = */ rtDbgModNm_SegmentCount,
+ /*.pfnSegmentByIndex = */ rtDbgModNm_SegmentByIndex,
+
+ /*.pfnSymbolAdd = */ rtDbgModNm_SymbolAdd,
+ /*.pfnSymbolCount = */ rtDbgModNm_SymbolCount,
+ /*.pfnSymbolByOrdinal = */ rtDbgModNm_SymbolByOrdinal,
+ /*.pfnSymbolByName = */ rtDbgModNm_SymbolByName,
+ /*.pfnSymbolByAddr = */ rtDbgModNm_SymbolByAddr,
+
+ /*.pfnLineAdd = */ rtDbgModNm_LineAdd,
+ /*.pfnLineCount = */ rtDbgModNm_LineCount,
+ /*.pfnLineByOrdinal = */ rtDbgModNm_LineByOrdinal,
+ /*.pfnLineByAddr = */ rtDbgModNm_LineByAddr,
+
+ /*.pfnUnwindFrame = */ rtDbgModNm_UnwindFrame,
+
+ /*.u32EndMagic = */ RTDBGMODVTDBG_MAGIC
+};
+
diff --git a/src/VBox/Runtime/common/dbg/dbgstackdumpself-amd64-x86.asm b/src/VBox/Runtime/common/dbg/dbgstackdumpself-amd64-x86.asm
new file mode 100644
index 00000000..9c21438d
--- /dev/null
+++ b/src/VBox/Runtime/common/dbg/dbgstackdumpself-amd64-x86.asm
@@ -0,0 +1,157 @@
+; $Id: dbgstackdumpself-amd64-x86.asm $
+;; @file
+; IPRT - RTDbgStackDumpSelf assembly wrapper calling rtDbgStackDumpSelfWorker.
+;
+
+;
+; Copyright (C) 2006-2023 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 *
+;*********************************************************************************************************************************
+%define RT_ASM_WITH_SEH64
+%include "iprt/asmdefs.mac"
+
+
+;*********************************************************************************************************************************
+;* Extern Symbols *
+;*********************************************************************************************************************************
+extern NAME(rtDbgStackDumpSelfWorker)
+
+
+BEGINCODE
+
+;;
+; Collects register state and calls C worker.
+;
+RT_BEGINPROC RTDbgStackDumpSelf
+ push xBP
+ SEH64_PUSH_xBP
+ mov xBP, xSP
+ SEH64_SET_FRAME_xBP 0
+
+ ;
+ ; Push all GPRS in reserve order...
+ ;
+%ifdef RT_ARCH_AMD64
+ push r15
+ SEH64_PUSH_GREG r15
+ push r14
+ SEH64_PUSH_GREG r14
+ push r13
+ SEH64_PUSH_GREG r13
+ push r12
+ SEH64_PUSH_GREG r12
+ push r13
+ SEH64_PUSH_GREG r13
+ push r12
+ SEH64_PUSH_GREG r12
+ push r11
+ SEH64_PUSH_GREG r11
+ push r10
+ SEH64_PUSH_GREG r10
+ push r9
+ SEH64_PUSH_GREG r9
+ push r8
+ SEH64_PUSH_GREG r8
+%endif
+ push xDI
+ SEH64_PUSH_GREG xDI
+ push xSI
+ SEH64_PUSH_GREG xSI
+%ifdef RT_ARCH_AMD64
+ mov r10, [xBP] ; Caller RBP.
+ push r10
+ lea r10, [xBP + xCB * 2] ; Caller RSP.
+ push r10
+%else
+ push dword [xBP] ; Caller EBP
+ push xAX
+ lea xAX, [xBP + xCB * 2] ; Caller ESP.
+ xchg xAX, [xSP]
+%endif
+ push xBX
+ SEH64_PUSH_GREG xBX
+ push xDX
+ SEH64_PUSH_GREG xDX
+ push xCX
+ SEH64_PUSH_GREG xCX
+ push xAX
+ SEH64_PUSH_GREG xAX
+
+ ;
+ ; ... preceeded by the EIP/RIP.
+ ;
+%ifdef RT_ARCH_AMD64
+ mov r10, [xBP + xCB]
+ push r10
+%else
+ push dword [xBP + xCB]
+%endif
+
+ ;
+ ; Call the worker function passing the register array as the fourth argument.
+ ;
+%ifdef ASM_CALL64_GCC
+ mov rcx, rsp ; 4th parameter = register array.
+ sub rsp, 8h ; Align stack.
+ SEH64_ALLOCATE_STACK 28h
+%elifdef ASM_CALL64_MSC
+ mov r9, rsp ; 4th parameter = register array.
+ sub rsp, 28h ; Aling stack and allocate spill area.
+ SEH64_ALLOCATE_STACK 28h
+%else
+ mov eax, esp ; Save register array location
+ and xSP, ~15 ; Align the stack.
+%endif
+SEH64_END_PROLOGUE
+
+%ifndef RT_ARCH_AMD64
+ push eax
+ push dword [xBP + xCB * 4]
+ push dword [xBP + xCB * 3]
+ push dword [xBP + xCB * 2]
+%endif
+%ifdef ASM_FORMAT_ELF
+ %ifdef PIC
+ call NAME(rtDbgStackDumpSelfWorker) wrt ..plt
+ %else
+ call NAME(rtDbgStackDumpSelfWorker)
+ %endif
+%else
+ call NAME(rtDbgStackDumpSelfWorker)
+%endif
+
+ leave
+ ret
+ENDPROC RTDbgStackDumpSelf
+
diff --git a/src/VBox/Runtime/common/dbg/dbgstackdumpself.cpp b/src/VBox/Runtime/common/dbg/dbgstackdumpself.cpp
new file mode 100644
index 00000000..32ef86b3
--- /dev/null
+++ b/src/VBox/Runtime/common/dbg/dbgstackdumpself.cpp
@@ -0,0 +1,543 @@
+/* $Id: dbgstackdumpself.cpp $ */
+/** @file
+ * IPRT - Dump current thread stack to buffer.
+ */
+
+/*
+ * Copyright (C) 2006-2023 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;
+}
+