diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 03:01:46 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 03:01:46 +0000 |
commit | f8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch) | |
tree | 26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/Runtime/common/dbg/dbgas.cpp | |
parent | Initial commit. (diff) | |
download | virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.tar.xz virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.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.cpp | 1808 |
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); + |