dbgas.cpp revision c58f1213e628a545081c70e26c6b67a841cff880
/* $Id$ */
/** @file
* IPRT - Debug Address Space.
*/
/*
* Copyright (C) 2012 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_OFFSETOF(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;
}
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);
/**
* 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.
* @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.
* @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);
/**
* 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);
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)
{
if (phMod)
*phMod = NIL_RTDBGMOD;
return VERR_NOT_FOUND;
}
/*
* 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);
/**
* 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.
*/
DECLINLINE(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;
}
/**
* 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);
/**
* Query a line number 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.
*
* @param hDbgAs The address space handle.
* @param Addr The address which closest symbol is requested.
* @param poffDisp Where to return the distance between the line
* number and address.
* @param pLine Where to return the line number information.
*/
RTDECL(int) RTDbgAsLineByAddr(RTDBGAS hDbgAs, RTUINTPTR Addr, PRTINTPTR poffDisp, PRTDBGLINE pLine)
{
/*
* 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);
RTDbgModRelease(hMod);
return rc;
}
RT_EXPORT_SYMBOL(RTDbgAsLineByAddr);
/**
* Query a line number 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.
*
* @param hDbgAs The address space handle.
* @param Addr The address which closest symbol is requested.
* @param poffDisp Where to return the distance between the line
* number and address.
* @param ppLine Where to return the pointer to the allocated line
* number info. Always set. Free with RTDbgLineFree.
*/
RTDECL(int) RTDbgAsLineByAddrA(RTDBGAS hDbgAs, RTUINTPTR Addr, PRTINTPTR poffDisp, PRTDBGLINE *ppLine)
{
/*
* 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);
RTDbgModRelease(hMod);
return rc;
}
RT_EXPORT_SYMBOL(RTDbgAsLineByAddrA);