summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/dbg/dbgas.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
commitf8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch)
tree26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/Runtime/common/dbg/dbgas.cpp
parentInitial commit. (diff)
downloadvirtualbox-upstream.tar.xz
virtualbox-upstream.zip
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Runtime/common/dbg/dbgas.cpp')
-rw-r--r--src/VBox/Runtime/common/dbg/dbgas.cpp1808
1 files changed, 1808 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/dbg/dbgas.cpp b/src/VBox/Runtime/common/dbg/dbgas.cpp
new file mode 100644
index 00000000..ff95a6bd
--- /dev/null
+++ b/src/VBox/Runtime/common/dbg/dbgas.cpp
@@ -0,0 +1,1808 @@
+/* $Id: dbgas.cpp $ */
+/** @file
+ * IPRT - Debug Address Space.
+ */
+
+/*
+ * Copyright (C) 2009-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#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);
+
+
+/**
+ * Creates an empty address space.
+ *
+ * @returns IPRT status code.
+ *
+ * @param phDbgAs Where to store the address space handle on success.
+ * @param FirstAddr The first address in the address space.
+ * @param LastAddr The last address in the address space.
+ * @param pszName The name of the address space.
+ */
+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);
+
+
+/**
+ * Variant of RTDbgAsCreate that takes a name format string.
+ *
+ * @returns IPRT status code.
+ *
+ * @param phDbgAs Where to store the address space handle on success.
+ * @param FirstAddr The first address in the address space.
+ * @param LastAddr The last address in the address space.
+ * @param pszNameFmt The name format of the address space.
+ * @param va Format arguments.
+ */
+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);
+
+
+/**
+ * Variant of RTDbgAsCreate that takes a name format string.
+ *
+ * @returns IPRT status code.
+ *
+ * @param phDbgAs Where to store the address space handle on success.
+ * @param FirstAddr The first address in the address space.
+ * @param LastAddr The last address in the address space.
+ * @param pszNameFmt The name format of the address space.
+ * @param ... Format arguments.
+ */
+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 (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);
+}
+
+
+/**
+ * Retains another reference to the address space.
+ *
+ * @returns New reference count, UINT32_MAX on invalid handle (asserted).
+ *
+ * @param hDbgAs The address space handle.
+ *
+ * @remarks Will not take any locks.
+ */
+RTDECL(uint32_t) RTDbgAsRetain(RTDBGAS hDbgAs)
+{
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, UINT32_MAX);
+ return ASMAtomicIncU32(&pDbgAs->cRefs);
+}
+RT_EXPORT_SYMBOL(RTDbgAsRetain);
+
+
+/**
+ * Release a reference to the address space.
+ *
+ * When the reference count reaches zero, the address space is destroyed.
+ * That 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.
+ *
+ * @returns New reference count, UINT32_MAX on invalid handle (asserted).
+ *
+ * @param hDbgAs The address space handle. The NIL handle is quietly
+ * ignored and 0 is returned.
+ *
+ * @remarks Will not take any locks.
+ */
+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);
+
+
+/**
+ * Gets the name of an address space.
+ *
+ * @returns read only address space name.
+ * NULL if hDbgAs is invalid.
+ *
+ * @param hDbgAs The address space handle.
+ *
+ * @remarks Will not take any locks.
+ */
+RTDECL(const char *) RTDbgAsName(RTDBGAS hDbgAs)
+{
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, NULL);
+ return pDbgAs->szName;
+}
+RT_EXPORT_SYMBOL(RTDbgAsName);
+
+
+/**
+ * Gets the first address in an address space.
+ *
+ * @returns The address.
+ * 0 if hDbgAs is invalid.
+ *
+ * @param hDbgAs The address space handle.
+ *
+ * @remarks Will not take any locks.
+ */
+RTDECL(RTUINTPTR) RTDbgAsFirstAddr(RTDBGAS hDbgAs)
+{
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, 0);
+ return pDbgAs->FirstAddr;
+}
+RT_EXPORT_SYMBOL(RTDbgAsFirstAddr);
+
+
+/**
+ * Gets the last address in an address space.
+ *
+ * @returns The address.
+ * 0 if hDbgAs is invalid.
+ *
+ * @param hDbgAs The address space handle.
+ *
+ * @remarks Will not take any locks.
+ */
+RTDECL(RTUINTPTR) RTDbgAsLastAddr(RTDBGAS hDbgAs)
+{
+ PRTDBGASINT pDbgAs = hDbgAs;
+ RTDBGAS_VALID_RETURN_RC(pDbgAs, 0);
+ return pDbgAs->LastAddr;
+}
+RT_EXPORT_SYMBOL(RTDbgAsLastAddr);
+
+/**
+ * Gets the number of modules in the address space.
+ *
+ * This can be used together with RTDbgAsModuleByIndex
+ * to enumerate the modules.
+ *
+ * @returns The number of modules.
+ *
+ * @param hDbgAs The address space handle.
+ *
+ * @remarks Will not take any locks.
+ */
+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;
+}
+
+
+/**
+ * Links a module into the address space at the give address.
+ *
+ * The size of the mapping is determined using RTDbgModImageSize().
+ *
+ * @returns IPRT status code.
+ * @retval VERR_OUT_OF_RANGE if the specified address will put the module
+ * outside the address space.
+ * @retval VERR_ADDRESS_CONFLICT if the mapping clashes with existing mappings.
+ *
+ * @param hDbgAs The address space handle.
+ * @param hDbgMod The module handle of the module to be linked in.
+ * @param ImageAddr The address to link the module at.
+ * @param fFlags See RTDBGASLINK_FLAGS_*.
+ */
+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);
+
+
+/**
+ * Links a segment into the address space at the give address.
+ *
+ * The size of the mapping is determined using RTDbgModSegmentSize().
+ *
+ * @returns IPRT status code.
+ * @retval VERR_OUT_OF_RANGE if the specified address will put the module
+ * outside the address space.
+ * @retval VERR_ADDRESS_CONFLICT if the mapping clashes with existing mappings.
+ *
+ * @param hDbgAs The address space handle.
+ * @param hDbgMod The module handle.
+ * @param iSeg The segment number (0-based) of the segment to be
+ * linked in.
+ * @param SegAddr The address to link the segment at.
+ * @param fFlags See RTDBGASLINK_FLAGS_*.
+ */
+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);
+}
+
+
+/**
+ * Unlinks all the mappings of a module from the address space.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_NOT_FOUND if the module wasn't found.
+ *
+ * @param hDbgAs The address space handle.
+ * @param hDbgMod The module handle of the module to be unlinked.
+ */
+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);
+
+
+/**
+ * Unlinks the mapping at the specified address.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_NOT_FOUND if no module or segment is mapped at that address.
+ *
+ * @param hDbgAs The address space handle.
+ * @param Addr The address within the mapping to be unlinked.
+ */
+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);
+
+
+/**
+ * Get a the handle of a module in the address space by is index.
+ *
+ * @returns A retained handle to the specified module. The caller must release
+ * the returned reference.
+ * NIL_RTDBGMOD if invalid index or handle.
+ *
+ * @param hDbgAs The address space handle.
+ * @param iModule The index of the module to get.
+ *
+ * @remarks The module indexes may change after calls to RTDbgAsModuleLink,
+ * RTDbgAsModuleLinkSeg, RTDbgAsModuleUnlink and
+ * 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);
+
+
+/**
+ * Queries mapping module information by handle.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_NOT_FOUND if no mapping was found at the specified address.
+ *
+ * @param hDbgAs The address space handle.
+ * @param Addr Address within the mapping of the module or segment.
+ * @param phMod Where to the return the retained module handle.
+ * Optional.
+ * @param pAddr Where to return the base address of the mapping.
+ * Optional.
+ * @param piSeg Where to return the segment index. This is set to
+ * NIL if the entire module is mapped as a single
+ * mapping. Optional.
+ */
+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);
+
+
+/**
+ * Queries mapping module information by name.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_NOT_FOUND if no mapping was found at the specified address.
+ * @retval VERR_OUT_OF_RANGE if the name index was out of range.
+ *
+ * @param hDbgAs The address space handle.
+ * @param pszName The module name.
+ * @param iName There can be more than one module by the same name
+ * in an address space. This argument indicates which
+ * is meant. (0 based)
+ * @param phMod Where to the return the retained module handle.
+ */
+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);
+
+
+/**
+ * Queries mapping information for a module given by index.
+ *
+ * @returns IRPT status code.
+ * @retval VERR_INVALID_HANDLE if hDbgAs is invalid.
+ * @retval VERR_OUT_OF_RANGE if the name index was out of range.
+ * @retval VINF_BUFFER_OVERFLOW if the array is too small and the returned
+ * information is incomplete.
+ *
+ * @param hDbgAs The address space handle.
+ * @param iModule The index of the module to get.
+ * @param paMappings Where to return the mapping information. The buffer
+ * size is given by *pcMappings.
+ * @param pcMappings IN: Size of the paMappings array. OUT: The number of
+ * entries returned.
+ * @param fFlags Flags for reserved for future use. MBZ.
+ *
+ * @remarks See remarks for RTDbgAsModuleByIndex regarding the volatility of the
+ * iModule parameter.
+ */
+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);
+}
+
+
+/**
+ * Adds a symbol to a module in the address space.
+ *
+ * @returns IPRT status code. See RTDbgModSymbolAdd for more specific ones.
+ * @retval VERR_INVALID_HANDLE if hDbgAs is invalid.
+ * @retval VERR_NOT_FOUND if no module was found at the specified address.
+ * @retval VERR_NOT_SUPPORTED if the module interpret doesn't support adding
+ * custom symbols.
+ *
+ * @param hDbgAs The address space handle.
+ * @param pszSymbol The symbol name.
+ * @param Addr The address of the symbol.
+ * @param cb The size of the symbol.
+ * @param fFlags Symbol flags, RTDBGSYMBOLADD_F_XXX.
+ * @param piOrdinal Where to return the symbol ordinal on success. If
+ * the interpreter doesn't do ordinals, this will be set to
+ * UINT32_MAX. Optional
+ */
+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;
+}
+
+
+/**
+ * Query a symbol by address.
+ *
+ * @returns IPRT status code. See RTDbgModSymbolAddr for more specific ones.
+ * @retval VERR_INVALID_HANDLE if hDbgAs is invalid.
+ * @retval VERR_NOT_FOUND if the address couldn't be mapped to a module.
+ * @retval VERR_INVALID_PARAMETER if incorrect flags.
+ *
+ * @param hDbgAs The address space handle.
+ * @param Addr The address which closest symbol is requested.
+ * @param fFlags Symbol search flags, see RTDBGSYMADDR_FLAGS_XXX.
+ * @param poffDisp Where to return the distance between the symbol and
+ * address. Optional.
+ * @param pSymbol Where to return the symbol info.
+ * @param phMod Where to return the module handle. Optional.
+ */
+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);
+
+
+/**
+ * Query a symbol by address.
+ *
+ * @returns IPRT status code. See RTDbgModSymbolAddrA for more specific ones.
+ * @retval VERR_INVALID_HANDLE if hDbgAs is invalid.
+ * @retval VERR_NOT_FOUND if the address couldn't be mapped to a module.
+ * @retval VERR_INVALID_PARAMETER if incorrect flags.
+ *
+ * @param hDbgAs The address space handle.
+ * @param Addr The address which closest symbol is requested.
+ * @param fFlags Symbol search flags, see RTDBGSYMADDR_FLAGS_XXX.
+ * @param poffDisp Where to return the distance between the symbol
+ * and address. Optional.
+ * @param ppSymInfo Where to return the pointer to the allocated symbol
+ * info. Always set. Free with RTDbgSymbolFree.
+ * @param phMod Where to return the module handle. Optional.
+ */
+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;
+}
+
+
+/**
+ * Query a symbol by name.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_SYMBOL_NOT_FOUND if not found.
+ *
+ * @param hDbgAs The address space handle.
+ * @param pszSymbol The symbol name. It is possible to limit the scope
+ * of the search by prefixing the symbol with a module
+ * name pattern followed by a bang (!) character.
+ * RTStrSimplePatternNMatch is used for the matching.
+ * @param pSymbol Where to return the symbol info.
+ * @param phMod Where to return the module handle. Optional.
+ */
+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);
+
+
+/**
+ * Query a symbol by name, allocating the returned symbol structure.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_SYMBOL_NOT_FOUND if not found.
+ *
+ * @param hDbgAs The address space handle.
+ * @param pszSymbol The symbol name. See RTDbgAsSymbolByName for more.
+ * @param ppSymbol Where to return the pointer to the allocated
+ * symbol info. Always set. Free with RTDbgSymbolFree.
+ * @param phMod Where to return the module handle. Optional.
+ */
+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);
+
+
+/**
+ * Adds a line number to a module in the address space.
+ *
+ * @returns IPRT status code. See RTDbgModSymbolAdd for more specific ones.
+ * @retval VERR_INVALID_HANDLE if hDbgAs is invalid.
+ * @retval VERR_NOT_FOUND if no module was found at the specified address.
+ * @retval VERR_NOT_SUPPORTED if the module interpret doesn't support adding
+ * custom symbols.
+ *
+ * @param hDbgAs The address space handle.
+ * @param pszFile The file name.
+ * @param uLineNo The line number.
+ * @param Addr The address of the symbol.
+ * @param piOrdinal Where to return the line number ordinal on success.
+ * If the interpreter doesn't do ordinals, this will be
+ * set to UINT32_MAX. Optional.
+ */
+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);
+