diff options
Diffstat (limited to '')
-rw-r--r-- | src/VBox/Runtime/common/dbg/Makefile.kup | 0 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbg.cpp | 122 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgas.cpp | 1513 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgcfg.cpp | 2525 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgmod.cpp | 2317 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgmodcodeview.cpp | 3197 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgmodcontainer.cpp | 1050 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgmoddbghelp.cpp | 537 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgmoddeferred.cpp | 730 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgmoddwarf.cpp | 6287 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgmodexports.cpp | 183 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgmodghidra.cpp | 526 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgmodldr.cpp | 286 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgmodmapsym.cpp | 622 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgmodnm.cpp | 581 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgstackdumpself-amd64-x86.asm | 157 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgstackdumpself.cpp | 543 |
17 files changed, 21176 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/dbg/Makefile.kup b/src/VBox/Runtime/common/dbg/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/Runtime/common/dbg/Makefile.kup diff --git a/src/VBox/Runtime/common/dbg/dbg.cpp b/src/VBox/Runtime/common/dbg/dbg.cpp new file mode 100644 index 00000000..95b95353 --- /dev/null +++ b/src/VBox/Runtime/common/dbg/dbg.cpp @@ -0,0 +1,122 @@ +/* $Id: dbg.cpp $ */ +/** @file + * IPRT - Debug Misc. + */ + +/* + * Copyright (C) 2009-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/dbg.h> +#include "internal/iprt.h" + +#include <iprt/mem.h> + + + +/** + * Allocate a new symbol structure. + * + * @returns Pointer to a new structure on success, NULL on failure. + */ +RTDECL(PRTDBGSYMBOL) RTDbgSymbolAlloc(void) +{ + return (PRTDBGSYMBOL)RTMemAllocZ(sizeof(RTDBGSYMBOL)); +} +RT_EXPORT_SYMBOL(RTDbgSymbolAlloc); + + +/** + * Duplicates a symbol structure. + * + * @returns Pointer to duplicate on success, NULL on failure. + * + * @param pSymInfo The symbol info to duplicate. + */ +RTDECL(PRTDBGSYMBOL) RTDbgSymbolDup(PCRTDBGSYMBOL pSymInfo) +{ + return (PRTDBGSYMBOL)RTMemDup(pSymInfo, sizeof(*pSymInfo)); +} +RT_EXPORT_SYMBOL(RTDbgSymbolDup); + + +/** + * Free a symbol structure previously allocated by a RTDbg method. + * + * @param pSymInfo The symbol info to free. NULL is ignored. + */ +RTDECL(void) RTDbgSymbolFree(PRTDBGSYMBOL pSymInfo) +{ + RTMemFree(pSymInfo); +} +RT_EXPORT_SYMBOL(RTDbgSymbolFree); + + +/** + * Allocate a new line number structure. + * + * @returns Pointer to a new structure on success, NULL on failure. + */ +RTDECL(PRTDBGLINE) RTDbgLineAlloc(void) +{ + return (PRTDBGLINE)RTMemAllocZ(sizeof(RTDBGLINE)); +} +RT_EXPORT_SYMBOL(RTDbgLineAlloc); + + +/** + * Duplicates a line number structure. + * + * @returns Pointer to duplicate on success, NULL on failure. + * + * @param pLine The line number to duplicate. + */ +RTDECL(PRTDBGLINE) RTDbgLineDup(PCRTDBGLINE pLine) +{ + return (PRTDBGLINE)RTMemDup(pLine, sizeof(*pLine)); +} +RT_EXPORT_SYMBOL(RTDbgLineDup); + + +/** + * Free a line number structure previously allocated by a RTDbg method. + * + * @param pLine The line number to free. NULL is ignored. + */ +RTDECL(void) RTDbgLineFree(PRTDBGLINE pLine) +{ + RTMemFree(pLine); +} +RT_EXPORT_SYMBOL(RTDbgLineFree); + diff --git a/src/VBox/Runtime/common/dbg/dbgas.cpp b/src/VBox/Runtime/common/dbg/dbgas.cpp new file mode 100644 index 00000000..cd45a676 --- /dev/null +++ b/src/VBox/Runtime/common/dbg/dbgas.cpp @@ -0,0 +1,1513 @@ +/* $Id: dbgas.cpp $ */ +/** @file + * IPRT - Debug Address Space. + */ + +/* + * Copyright (C) 2009-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/dbg.h> +#include "internal/iprt.h" + +#include <iprt/asm.h> +#include <iprt/avl.h> +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/mem.h> +#include <iprt/param.h> +#include <iprt/string.h> +#include <iprt/semaphore.h> +#include "internal/magics.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** Pointer to a module table entry. */ +typedef struct RTDBGASMOD *PRTDBGASMOD; +/** Pointer to an address space mapping node. */ +typedef struct RTDBGASMAP *PRTDBGASMAP; +/** Pointer to a name head. */ +typedef struct RTDBGASNAME *PRTDBGASNAME; + +/** + * Module entry. + */ +typedef struct RTDBGASMOD +{ + /** Node core, the module handle is the key. */ + AVLPVNODECORE Core; + /** Pointer to the first mapping of the module or a segment within it. */ + PRTDBGASMAP pMapHead; + /** Pointer to the next module with an identical name. */ + PRTDBGASMOD pNextName; + /** The index into RTDBGASINT::papModules. */ + uint32_t iOrdinal; +} RTDBGASMOD; + +/** + * An address space mapping, either of a full module or a segment. + */ +typedef struct RTDBGASMAP +{ + /** The AVL node core. Contains the address range. */ + AVLRUINTPTRNODECORE Core; + /** Pointer to the next mapping of the module. */ + PRTDBGASMAP pNext; + /** Pointer to the module. */ + PRTDBGASMOD pMod; + /** Which segment in the module. + * This is NIL_RTDBGSEGIDX when the entire module is mapped. */ + RTDBGSEGIDX iSeg; +} RTDBGASMAP; + +/** + * Name in the address space. + */ +typedef struct RTDBGASNAME +{ + /** The string space node core.*/ + RTSTRSPACECORE StrCore; + /** The list of nodes */ + PRTDBGASMOD pHead; +} RTDBGASNAME; + +/** + * Debug address space instance. + */ +typedef struct RTDBGASINT +{ + /** Magic value (RTDBGAS_MAGIC). */ + uint32_t u32Magic; + /** The number of reference to this address space. */ + uint32_t volatile cRefs; + /** Handle of the read-write lock. */ + RTSEMRW hLock; + /** Number of modules in the module address space. */ + uint32_t cModules; + /** Pointer to the module table. + * The valid array length is given by cModules. */ + PRTDBGASMOD *papModules; + /** AVL tree translating module handles to module entries. */ + AVLPVTREE ModTree; + /** AVL tree mapping addresses to modules. */ + AVLRUINTPTRTREE MapTree; + /** Names of the modules in the name space. */ + RTSTRSPACE NameSpace; + /** The first address the AS. */ + RTUINTPTR FirstAddr; + /** The last address in the AS. */ + RTUINTPTR LastAddr; + /** The name of the address space. (variable length) */ + char szName[1]; +} RTDBGASINT; +/** Pointer to an a debug address space instance. */ +typedef RTDBGASINT *PRTDBGASINT; + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** Validates an address space handle and returns rc if not valid. */ +#define RTDBGAS_VALID_RETURN_RC(pDbgAs, rc) \ + do { \ + AssertPtrReturn((pDbgAs), (rc)); \ + AssertReturn((pDbgAs)->u32Magic == RTDBGAS_MAGIC, (rc)); \ + AssertReturn((pDbgAs)->cRefs > 0, (rc)); \ + } while (0) + +/** Locks the address space for reading. */ +#define RTDBGAS_LOCK_READ(pDbgAs) \ + do { \ + int rcLock = RTSemRWRequestRead((pDbgAs)->hLock, RT_INDEFINITE_WAIT); \ + AssertRC(rcLock); \ + } while (0) + +/** Unlocks the address space after reading. */ +#define RTDBGAS_UNLOCK_READ(pDbgAs) \ + do { \ + int rcLock = RTSemRWReleaseRead((pDbgAs)->hLock); \ + AssertRC(rcLock); \ + } while (0) + +/** Locks the address space for writing. */ +#define RTDBGAS_LOCK_WRITE(pDbgAs) \ + do { \ + int rcLock = RTSemRWRequestWrite((pDbgAs)->hLock, RT_INDEFINITE_WAIT); \ + AssertRC(rcLock); \ + } while (0) + +/** Unlocks the address space after writing. */ +#define RTDBGAS_UNLOCK_WRITE(pDbgAs) \ + do { \ + int rcLock = RTSemRWReleaseWrite((pDbgAs)->hLock); \ + AssertRC(rcLock); \ + } while (0) + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static void rtDbgAsModuleUnlinkMod(PRTDBGASINT pDbgAs, PRTDBGASMOD pMod); +static void rtDbgAsModuleUnlinkByMap(PRTDBGASINT pDbgAs, PRTDBGASMAP pMap); + + +RTDECL(int) RTDbgAsCreate(PRTDBGAS phDbgAs, RTUINTPTR FirstAddr, RTUINTPTR LastAddr, const char *pszName) +{ + /* + * Input validation. + */ + AssertPtrReturn(phDbgAs, VERR_INVALID_POINTER); + AssertPtrReturn(pszName, VERR_INVALID_POINTER); + AssertReturn(FirstAddr < LastAddr, VERR_INVALID_PARAMETER); + + /* + * Allocate memory for the instance data. + */ + size_t cchName = strlen(pszName); + PRTDBGASINT pDbgAs = (PRTDBGASINT)RTMemAllocVar(RT_UOFFSETOF_DYN(RTDBGASINT, szName[cchName + 1])); + if (!pDbgAs) + return VERR_NO_MEMORY; + + /* initialize it. */ + pDbgAs->u32Magic = RTDBGAS_MAGIC; + pDbgAs->cRefs = 1; + pDbgAs->hLock = NIL_RTSEMRW; + pDbgAs->cModules = 0; + pDbgAs->papModules = NULL; + pDbgAs->ModTree = NULL; + pDbgAs->MapTree = NULL; + pDbgAs->NameSpace = NULL; + pDbgAs->FirstAddr = FirstAddr; + pDbgAs->LastAddr = LastAddr; + memcpy(pDbgAs->szName, pszName, cchName + 1); + int rc = RTSemRWCreate(&pDbgAs->hLock); + if (RT_SUCCESS(rc)) + { + *phDbgAs = pDbgAs; + return VINF_SUCCESS; + } + + pDbgAs->u32Magic = 0; + RTMemFree(pDbgAs); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgAsCreate); + + +RTDECL(int) RTDbgAsCreateV(PRTDBGAS phDbgAs, RTUINTPTR FirstAddr, RTUINTPTR LastAddr, const char *pszNameFmt, va_list va) +{ + AssertPtrReturn(pszNameFmt, VERR_INVALID_POINTER); + + char *pszName; + RTStrAPrintfV(&pszName, pszNameFmt, va); + if (!pszName) + return VERR_NO_MEMORY; + + int rc = RTDbgAsCreate(phDbgAs, FirstAddr, LastAddr, pszName); + + RTStrFree(pszName); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgAsCreateV); + + +RTDECL(int) RTDbgAsCreateF(PRTDBGAS phDbgAs, RTUINTPTR FirstAddr, RTUINTPTR LastAddr, const char *pszNameFmt, ...) +{ + va_list va; + va_start(va, pszNameFmt); + int rc = RTDbgAsCreateV(phDbgAs, FirstAddr, LastAddr, pszNameFmt, va); + va_end(va); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgAsCreateF); + + +/** + * Callback used by RTDbgAsDestroy to free all mapping nodes. + * + * @returns 0 + * @param pNode The map node. + * @param pvUser NULL. + */ +static DECLCALLBACK(int) rtDbgAsDestroyMapCallback(PAVLRUINTPTRNODECORE pNode, void *pvUser) +{ + RTMemFree(pNode); + NOREF(pvUser); + return 0; +} + + +/** + * Callback used by RTDbgAsDestroy to free all name space nodes. + * + * @returns 0 + * @param pStr The name node. + * @param pvUser NULL. + */ +static DECLCALLBACK(int) rtDbgAsDestroyNameCallback(PRTSTRSPACECORE pStr, void *pvUser) +{ + RTMemFree(pStr); + NOREF(pvUser); + return 0; +} + + +/** + * Destroys the address space. + * + * This means unlinking all the modules it currently contains, potentially + * causing some or all of them to be destroyed as they are managed by + * reference counting. + * + * @param pDbgAs The address space instance to be destroyed. + */ +static void rtDbgAsDestroy(PRTDBGASINT pDbgAs) +{ + /* + * Mark the address space invalid and release all the modules. + */ + ASMAtomicWriteU32(&pDbgAs->u32Magic, ~RTDBGAS_MAGIC); + + RTAvlrUIntPtrDestroy(&pDbgAs->MapTree, rtDbgAsDestroyMapCallback, NULL); + RTStrSpaceDestroy(&pDbgAs->NameSpace, rtDbgAsDestroyNameCallback, NULL); + + uint32_t i = pDbgAs->cModules; + while (i-- > 0) + { + PRTDBGASMOD pMod = pDbgAs->papModules[i]; + AssertPtr(pMod); + if (RT_VALID_PTR(pMod)) + { + Assert(pMod->iOrdinal == i); + RTDbgModRelease((RTDBGMOD)pMod->Core.Key); + pMod->Core.Key = NIL_RTDBGMOD; + pMod->iOrdinal = UINT32_MAX; + RTMemFree(pMod); + } + pDbgAs->papModules[i] = NULL; + } + RTSemRWDestroy(pDbgAs->hLock); + pDbgAs->hLock = NIL_RTSEMRW; + RTMemFree(pDbgAs->papModules); + pDbgAs->papModules = NULL; + + RTMemFree(pDbgAs); +} + + +RTDECL(uint32_t) RTDbgAsRetain(RTDBGAS hDbgAs) +{ + PRTDBGASINT pDbgAs = hDbgAs; + RTDBGAS_VALID_RETURN_RC(pDbgAs, UINT32_MAX); + return ASMAtomicIncU32(&pDbgAs->cRefs); +} +RT_EXPORT_SYMBOL(RTDbgAsRetain); + + +RTDECL(uint32_t) RTDbgAsRelease(RTDBGAS hDbgAs) +{ + if (hDbgAs == NIL_RTDBGAS) + return 0; + PRTDBGASINT pDbgAs = hDbgAs; + RTDBGAS_VALID_RETURN_RC(pDbgAs, UINT32_MAX); + + uint32_t cRefs = ASMAtomicDecU32(&pDbgAs->cRefs); + if (!cRefs) + rtDbgAsDestroy(pDbgAs); + return cRefs; +} +RT_EXPORT_SYMBOL(RTDbgAsRelease); + + +RTDECL(int) RTDbgAsLockExcl(RTDBGAS hDbgAs) +{ + PRTDBGASINT pDbgAs = hDbgAs; + RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE); + RTDBGAS_LOCK_WRITE(pDbgAs); + return VINF_SUCCESS; +} +RT_EXPORT_SYMBOL(RTDbgAsLockExcl); + + +RTDECL(int) RTDbgAsUnlockExcl(RTDBGAS hDbgAs) +{ + PRTDBGASINT pDbgAs = hDbgAs; + RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE); + RTDBGAS_UNLOCK_WRITE(pDbgAs); + return VINF_SUCCESS; +} +RT_EXPORT_SYMBOL(RTDbgAsUnlockExcl); + + +RTDECL(const char *) RTDbgAsName(RTDBGAS hDbgAs) +{ + PRTDBGASINT pDbgAs = hDbgAs; + RTDBGAS_VALID_RETURN_RC(pDbgAs, NULL); + return pDbgAs->szName; +} +RT_EXPORT_SYMBOL(RTDbgAsName); + + +RTDECL(RTUINTPTR) RTDbgAsFirstAddr(RTDBGAS hDbgAs) +{ + PRTDBGASINT pDbgAs = hDbgAs; + RTDBGAS_VALID_RETURN_RC(pDbgAs, 0); + return pDbgAs->FirstAddr; +} +RT_EXPORT_SYMBOL(RTDbgAsFirstAddr); + + +RTDECL(RTUINTPTR) RTDbgAsLastAddr(RTDBGAS hDbgAs) +{ + PRTDBGASINT pDbgAs = hDbgAs; + RTDBGAS_VALID_RETURN_RC(pDbgAs, 0); + return pDbgAs->LastAddr; +} +RT_EXPORT_SYMBOL(RTDbgAsLastAddr); + + +RTDECL(uint32_t) RTDbgAsModuleCount(RTDBGAS hDbgAs) +{ + PRTDBGASINT pDbgAs = hDbgAs; + RTDBGAS_VALID_RETURN_RC(pDbgAs, 0); + return pDbgAs->cModules; +} +RT_EXPORT_SYMBOL(RTDbgAsModuleCount); + + +/** + * Common worker for RTDbgAsModuleLink and RTDbgAsModuleLinkSeg. + * + * @returns IPRT status code. + * @param pDbgAs Pointer to the address space instance data. + * @param hDbgMod The module to link. + * @param iSeg The segment to link or NIL if all. + * @param Addr The address we're linking it at. + * @param cb The size of what we're linking. + * @param pszName The name of the module. + * @param fFlags See RTDBGASLINK_FLAGS_*. + * + * @remarks The caller must have locked the address space for writing. + */ +int rtDbgAsModuleLinkCommon(PRTDBGASINT pDbgAs, RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, + RTUINTPTR Addr, RTUINTPTR cb, const char *pszName, uint32_t fFlags) +{ + /* + * Check that the requested space is undisputed. + */ + for (;;) + { + PRTDBGASMAP pAdjMod = (PRTDBGASMAP)RTAvlrUIntPtrGetBestFit(&pDbgAs->MapTree, Addr, false /* fAbove */); + if ( pAdjMod + && pAdjMod->Core.KeyLast >= Addr) + { + if (!(fFlags & RTDBGASLINK_FLAGS_REPLACE)) + return VERR_ADDRESS_CONFLICT; + rtDbgAsModuleUnlinkByMap(pDbgAs, pAdjMod); + continue; + } + pAdjMod = (PRTDBGASMAP)RTAvlrUIntPtrGetBestFit(&pDbgAs->MapTree, Addr, true /* fAbove */); + if ( pAdjMod + && pAdjMod->Core.Key <= Addr + cb - 1) + { + if (!(fFlags & RTDBGASLINK_FLAGS_REPLACE)) + return VERR_ADDRESS_CONFLICT; + rtDbgAsModuleUnlinkByMap(pDbgAs, pAdjMod); + continue; + } + break; + } + + /* + * First, create or find the module table entry. + */ + PRTDBGASMOD pMod = (PRTDBGASMOD)RTAvlPVGet(&pDbgAs->ModTree, hDbgMod); + if (!pMod) + { + /* + * Ok, we need a new entry. Grow the table if necessary. + */ + if (!(pDbgAs->cModules % 32)) + { + void *pvNew = RTMemRealloc(pDbgAs->papModules, sizeof(pDbgAs->papModules[0]) * (pDbgAs->cModules + 32)); + if (!pvNew) + return VERR_NO_MEMORY; + pDbgAs->papModules = (PRTDBGASMOD *)pvNew; + } + pMod = (PRTDBGASMOD)RTMemAlloc(sizeof(*pMod)); + if (!pMod) + return VERR_NO_MEMORY; + pMod->Core.Key = hDbgMod; + pMod->pMapHead = NULL; + pMod->pNextName = NULL; + if (RT_UNLIKELY(!RTAvlPVInsert(&pDbgAs->ModTree, &pMod->Core))) + { + AssertFailed(); + pDbgAs->cModules--; + RTMemFree(pMod); + return VERR_INTERNAL_ERROR; + } + pMod->iOrdinal = pDbgAs->cModules; + pDbgAs->papModules[pDbgAs->cModules] = pMod; + pDbgAs->cModules++; + RTDbgModRetain(hDbgMod); + + /* + * Add it to the name space. + */ + PRTDBGASNAME pName = (PRTDBGASNAME)RTStrSpaceGet(&pDbgAs->NameSpace, pszName); + if (!pName) + { + size_t cchName = strlen(pszName); + pName = (PRTDBGASNAME)RTMemAlloc(sizeof(*pName) + cchName + 1); + if (!pName) + { + RTDbgModRelease(hDbgMod); + pDbgAs->cModules--; + RTAvlPVRemove(&pDbgAs->ModTree, hDbgMod); + RTMemFree(pMod); + return VERR_NO_MEMORY; + } + pName->StrCore.cchString = cchName; + pName->StrCore.pszString = (char *)memcpy(pName + 1, pszName, cchName + 1); + pName->pHead = pMod; + if (!RTStrSpaceInsert(&pDbgAs->NameSpace, &pName->StrCore)) + AssertFailed(); + } + else + { + /* quick, but unfair. */ + pMod->pNextName = pName->pHead; + pName->pHead = pMod; + } + } + + /* + * Create a mapping node. + */ + int rc; + PRTDBGASMAP pMap = (PRTDBGASMAP)RTMemAlloc(sizeof(*pMap)); + if (pMap) + { + pMap->Core.Key = Addr; + pMap->Core.KeyLast = Addr + cb - 1; + pMap->pMod = pMod; + pMap->iSeg = iSeg; + if (RTAvlrUIntPtrInsert(&pDbgAs->MapTree, &pMap->Core)) + { + PRTDBGASMAP *pp = &pMod->pMapHead; + while (*pp && (*pp)->Core.Key < Addr) + pp = &(*pp)->pNext; + pMap->pNext = *pp; + *pp = pMap; + return VINF_SUCCESS; + } + + AssertFailed(); + RTMemFree(pMap); + rc = VERR_ADDRESS_CONFLICT; + } + else + rc = VERR_NO_MEMORY; + + /* + * Unlink the module if this was the only mapping. + */ + if (!pMod->pMapHead) + rtDbgAsModuleUnlinkMod(pDbgAs, pMod); + return rc; +} + + +RTDECL(int) RTDbgAsModuleLink(RTDBGAS hDbgAs, RTDBGMOD hDbgMod, RTUINTPTR ImageAddr, uint32_t fFlags) +{ + /* + * Validate input. + */ + PRTDBGASINT pDbgAs = hDbgAs; + RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE); + const char *pszName = RTDbgModName(hDbgMod); + if (!pszName) + return VERR_INVALID_HANDLE; + RTUINTPTR cb = RTDbgModImageSize(hDbgMod); + if (!cb) + return VERR_OUT_OF_RANGE; + if ( ImageAddr < pDbgAs->FirstAddr + || ImageAddr > pDbgAs->LastAddr + || ImageAddr + cb - 1 < pDbgAs->FirstAddr + || ImageAddr + cb - 1 > pDbgAs->LastAddr + || ImageAddr + cb - 1 < ImageAddr) + return VERR_OUT_OF_RANGE; + AssertReturn(!(fFlags & ~RTDBGASLINK_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER); + + /* + * Invoke worker common with RTDbgAsModuleLinkSeg. + */ + RTDBGAS_LOCK_WRITE(pDbgAs); + int rc = rtDbgAsModuleLinkCommon(pDbgAs, hDbgMod, NIL_RTDBGSEGIDX, ImageAddr, cb, pszName, fFlags); + RTDBGAS_UNLOCK_WRITE(pDbgAs); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgAsModuleLink); + + +RTDECL(int) RTDbgAsModuleLinkSeg(RTDBGAS hDbgAs, RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR SegAddr, uint32_t fFlags) +{ + /* + * Validate input. + */ + PRTDBGASINT pDbgAs = hDbgAs; + RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE); + const char *pszName = RTDbgModName(hDbgMod); + if (!pszName) + return VERR_INVALID_HANDLE; + RTUINTPTR cb = RTDbgModSegmentSize(hDbgMod, iSeg); + if (!cb) + return VERR_OUT_OF_RANGE; + if ( SegAddr < pDbgAs->FirstAddr + || SegAddr > pDbgAs->LastAddr + || SegAddr + cb - 1 < pDbgAs->FirstAddr + || SegAddr + cb - 1 > pDbgAs->LastAddr + || SegAddr + cb - 1 < SegAddr) + return VERR_OUT_OF_RANGE; + AssertReturn(!(fFlags & ~RTDBGASLINK_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER); + + /* + * Invoke worker common with RTDbgAsModuleLinkSeg. + */ + RTDBGAS_LOCK_WRITE(pDbgAs); + int rc = rtDbgAsModuleLinkCommon(pDbgAs, hDbgMod, iSeg, SegAddr, cb, pszName, fFlags); + RTDBGAS_UNLOCK_WRITE(pDbgAs); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgAsModuleLinkSeg); + + +/** + * Worker for RTDbgAsModuleUnlink, RTDbgAsModuleUnlinkByAddr and rtDbgAsModuleLinkCommon. + * + * @param pDbgAs Pointer to the address space instance data. + * @param pMod The module to unlink. + * + * @remarks The caller must have locked the address space for writing. + */ +static void rtDbgAsModuleUnlinkMod(PRTDBGASINT pDbgAs, PRTDBGASMOD pMod) +{ + Assert(!pMod->pMapHead); + + /* + * Unlink it from the name. + */ + const char *pszName = RTDbgModName((RTDBGMOD)pMod->Core.Key); + PRTDBGASNAME pName = (PRTDBGASNAME)RTStrSpaceGet(&pDbgAs->NameSpace, pszName); + AssertReturnVoid(pName); + + if (pName->pHead == pMod) + pName->pHead = pMod->pNextName; + else + for (PRTDBGASMOD pCur = pName->pHead; pCur; pCur = pCur->pNextName) + if (pCur->pNextName == pMod) + { + pCur->pNextName = pMod->pNextName; + break; + } + pMod->pNextName = NULL; + + /* + * Free the name if this was the last reference to it. + */ + if (!pName->pHead) + { + pName = (PRTDBGASNAME)RTStrSpaceRemove(&pDbgAs->NameSpace, pName->StrCore.pszString); + Assert(pName); + RTMemFree(pName); + } + + /* + * Remove it from the module handle tree. + */ + PAVLPVNODECORE pNode = RTAvlPVRemove(&pDbgAs->ModTree, pMod->Core.Key); + Assert(pNode == &pMod->Core); NOREF(pNode); + + /* + * Remove it from the module table by replacing it by the last entry. + */ + pDbgAs->cModules--; + uint32_t iMod = pMod->iOrdinal; + Assert(iMod <= pDbgAs->cModules); + if (iMod != pDbgAs->cModules) + { + PRTDBGASMOD pTailMod = pDbgAs->papModules[pDbgAs->cModules]; + pTailMod->iOrdinal = iMod; + pDbgAs->papModules[iMod] = pTailMod; + } + pMod->iOrdinal = UINT32_MAX; + + /* + * Free it. + */ + RTMemFree(pMod); +} + + +/** + * Worker for RTDbgAsModuleUnlink and RTDbgAsModuleUnlinkByAddr. + * + * @param pDbgAs Pointer to the address space instance data. + * @param pMap The map to unlink and free. + * + * @remarks The caller must have locked the address space for writing. + */ +static void rtDbgAsModuleUnlinkMap(PRTDBGASINT pDbgAs, PRTDBGASMAP pMap) +{ + /* remove from the tree */ + PAVLRUINTPTRNODECORE pNode = RTAvlrUIntPtrRemove(&pDbgAs->MapTree, pMap->Core.Key); + Assert(pNode == &pMap->Core); NOREF(pNode); + + /* unlink */ + PRTDBGASMOD pMod = pMap->pMod; + if (pMod->pMapHead == pMap) + pMod->pMapHead = pMap->pNext; + else + { + bool fFound = false; + for (PRTDBGASMAP pCur = pMod->pMapHead; pCur; pCur = pCur->pNext) + if (pCur->pNext == pMap) + { + pCur->pNext = pMap->pNext; + fFound = true; + break; + } + Assert(fFound); + } + + /* free it */ + pMap->Core.Key = pMap->Core.KeyLast = 0; + pMap->pNext = NULL; + pMap->pMod = NULL; + RTMemFree(pMap); +} + + +/** + * Worker for RTDbgAsModuleUnlinkByAddr and rtDbgAsModuleLinkCommon that + * unlinks a single mapping and releases the module if it's the last one. + * + * @param pDbgAs The address space instance. + * @param pMap The mapping to unlink. + * + * @remarks The caller must have locked the address space for writing. + */ +static void rtDbgAsModuleUnlinkByMap(PRTDBGASINT pDbgAs, PRTDBGASMAP pMap) +{ + /* + * Unlink it from the address space. + * Unlink the module as well if it's the last mapping it has. + */ + PRTDBGASMOD pMod = pMap->pMod; + rtDbgAsModuleUnlinkMap(pDbgAs, pMap); + if (!pMod->pMapHead) + rtDbgAsModuleUnlinkMod(pDbgAs, pMod); +} + + +RTDECL(int) RTDbgAsModuleUnlink(RTDBGAS hDbgAs, RTDBGMOD hDbgMod) +{ + /* + * Validate input. + */ + PRTDBGASINT pDbgAs = hDbgAs; + RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE); + if (hDbgMod == NIL_RTDBGMOD) + return VINF_SUCCESS; + + RTDBGAS_LOCK_WRITE(pDbgAs); + PRTDBGASMOD pMod = (PRTDBGASMOD)RTAvlPVGet(&pDbgAs->ModTree, hDbgMod); + if (!pMod) + { + RTDBGAS_UNLOCK_WRITE(pDbgAs); + return VERR_NOT_FOUND; + } + + /* + * Unmap all everything and release the module. + */ + while (pMod->pMapHead) + rtDbgAsModuleUnlinkMap(pDbgAs, pMod->pMapHead); + rtDbgAsModuleUnlinkMod(pDbgAs, pMod); + + RTDBGAS_UNLOCK_WRITE(pDbgAs); + return VINF_SUCCESS; +} +RT_EXPORT_SYMBOL(RTDbgAsModuleUnlink); + + +RTDECL(int) RTDbgAsModuleUnlinkByAddr(RTDBGAS hDbgAs, RTUINTPTR Addr) +{ + /* + * Validate input. + */ + PRTDBGASINT pDbgAs = hDbgAs; + RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE); + + RTDBGAS_LOCK_WRITE(pDbgAs); + PRTDBGASMAP pMap = (PRTDBGASMAP)RTAvlrUIntPtrRangeGet(&pDbgAs->MapTree, Addr); + if (!pMap) + { + RTDBGAS_UNLOCK_WRITE(pDbgAs); + return VERR_NOT_FOUND; + } + + /* + * Hand it to + */ + rtDbgAsModuleUnlinkByMap(pDbgAs, pMap); + + RTDBGAS_UNLOCK_WRITE(pDbgAs); + return VINF_SUCCESS; +} +RT_EXPORT_SYMBOL(RTDbgAsModuleUnlinkByAddr); + + +RTDECL(RTDBGMOD) RTDbgAsModuleByIndex(RTDBGAS hDbgAs, uint32_t iModule) +{ + /* + * Validate input. + */ + PRTDBGASINT pDbgAs = hDbgAs; + RTDBGAS_VALID_RETURN_RC(pDbgAs, NIL_RTDBGMOD); + + RTDBGAS_LOCK_READ(pDbgAs); + if (iModule >= pDbgAs->cModules) + { + RTDBGAS_UNLOCK_READ(pDbgAs); + return NIL_RTDBGMOD; + } + + /* + * Get, retain and return it. + */ + RTDBGMOD hMod = (RTDBGMOD)pDbgAs->papModules[iModule]->Core.Key; + RTDbgModRetain(hMod); + + RTDBGAS_UNLOCK_READ(pDbgAs); + return hMod; +} +RT_EXPORT_SYMBOL(RTDbgAsModuleByIndex); + + +RTDECL(int) RTDbgAsModuleByAddr(RTDBGAS hDbgAs, RTUINTPTR Addr, PRTDBGMOD phMod, PRTUINTPTR pAddr, PRTDBGSEGIDX piSeg) +{ + /* + * Validate input. + */ + PRTDBGASINT pDbgAs = hDbgAs; + RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE); + + RTDBGAS_LOCK_READ(pDbgAs); + PRTDBGASMAP pMap = (PRTDBGASMAP)RTAvlrUIntPtrRangeGet(&pDbgAs->MapTree, Addr); + if (!pMap) + { + RTDBGAS_UNLOCK_READ(pDbgAs); + return VERR_NOT_FOUND; + } + + /* + * Set up the return values. + */ + if (phMod) + { + RTDBGMOD hMod = (RTDBGMOD)pMap->pMod->Core.Key; + RTDbgModRetain(hMod); + *phMod = hMod; + } + if (pAddr) + *pAddr = pMap->Core.Key; + if (piSeg) + *piSeg = pMap->iSeg; + + RTDBGAS_UNLOCK_READ(pDbgAs); + return VINF_SUCCESS; +} +RT_EXPORT_SYMBOL(RTDbgAsModuleByAddr); + + +RTDECL(int) RTDbgAsModuleByName(RTDBGAS hDbgAs, const char *pszName, uint32_t iName, PRTDBGMOD phMod) +{ + /* + * Validate input. + */ + PRTDBGASINT pDbgAs = hDbgAs; + RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE); + AssertPtrReturn(phMod, VERR_INVALID_POINTER); + + RTDBGAS_LOCK_READ(pDbgAs); + PRTDBGASNAME pName = (PRTDBGASNAME)RTStrSpaceGet(&pDbgAs->NameSpace, pszName); + if (!pName) + { + RTDBGAS_UNLOCK_READ(pDbgAs); + return VERR_NOT_FOUND; + } + + PRTDBGASMOD pMod = pName->pHead; + while (iName-- > 0) + { + pMod = pMod->pNextName; + if (!pMod) + { + RTDBGAS_UNLOCK_READ(pDbgAs); + return VERR_OUT_OF_RANGE; + } + } + + /* + * Get, retain and return it. + */ + RTDBGMOD hMod = (RTDBGMOD)pMod->Core.Key; + RTDbgModRetain(hMod); + *phMod = hMod; + + RTDBGAS_UNLOCK_READ(pDbgAs); + return VINF_SUCCESS; +} +RT_EXPORT_SYMBOL(RTDbgAsModuleByName); + + +RTDECL(int) RTDbgAsModuleQueryMapByIndex(RTDBGAS hDbgAs, uint32_t iModule, PRTDBGASMAPINFO paMappings, uint32_t *pcMappings, uint32_t fFlags) +{ + /* + * Validate input. + */ + uint32_t const cMappings = *pcMappings; + PRTDBGASINT pDbgAs = hDbgAs; + RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE); + AssertReturn(!fFlags, VERR_INVALID_PARAMETER); + + RTDBGAS_LOCK_READ(pDbgAs); + if (iModule >= pDbgAs->cModules) + { + RTDBGAS_UNLOCK_READ(pDbgAs); + return VERR_OUT_OF_RANGE; + } + + /* + * Copy the mapping information about the module. + */ + int rc = VINF_SUCCESS; + PRTDBGASMAP pMap = pDbgAs->papModules[iModule]->pMapHead; + uint32_t cMaps = 0; + while (pMap) + { + if (cMaps >= cMappings) + { + rc = VINF_BUFFER_OVERFLOW; + break; + } + paMappings[cMaps].Address = pMap->Core.Key; + paMappings[cMaps].iSeg = pMap->iSeg; + cMaps++; + pMap = pMap->pNext; + } + + RTDBGAS_UNLOCK_READ(pDbgAs); + *pcMappings = cMaps; + return rc; +} +RT_EXPORT_SYMBOL(RTDbgAsModuleQueryMapByIndex); + + +/** + * Internal worker that looks up and retains a module. + * + * @returns Module handle, NIL_RTDBGMOD if not found. + * @param pDbgAs The address space instance data. + * @param Addr Address within the module. + * @param piSeg where to return the segment index. + * @param poffSeg Where to return the segment offset. + * @param pMapAddr The mapping address (RTDBGASMAP::Core.Key). + */ +DECLINLINE(RTDBGMOD) rtDbgAsModuleByAddr(PRTDBGASINT pDbgAs, RTUINTPTR Addr, PRTDBGSEGIDX piSeg, PRTUINTPTR poffSeg, PRTUINTPTR pMapAddr) +{ + RTDBGMOD hMod = NIL_RTDBGMOD; + + RTDBGAS_LOCK_READ(pDbgAs); + PRTDBGASMAP pMap = (PRTDBGASMAP)RTAvlrUIntPtrRangeGet(&pDbgAs->MapTree, Addr); + if (pMap) + { + hMod = (RTDBGMOD)pMap->pMod->Core.Key; + RTDbgModRetain(hMod); + *piSeg = pMap->iSeg != NIL_RTDBGSEGIDX ? pMap->iSeg : RTDBGSEGIDX_RVA; + *poffSeg = Addr - pMap->Core.Key; + if (pMapAddr) + *pMapAddr = pMap->Core.Key; + } + RTDBGAS_UNLOCK_READ(pDbgAs); + + return hMod; +} + + +/** + * Adjusts the address to correspond to the mapping of the module/segment. + * + * @param pAddr The address to adjust (in/out). + * @param iSeg The related segment. + * @param hDbgMod The module handle. + * @param MapAddr The mapping address. + * @param iMapSeg The segment that's mapped, NIL_RTDBGSEGIDX or + * RTDBGSEGIDX_RVA if the whole module is mapped here. + */ +DECLINLINE(void) rtDbgAsAdjustAddressByMapping(PRTUINTPTR pAddr, RTDBGSEGIDX iSeg, + RTDBGMOD hDbgMod, RTUINTPTR MapAddr, RTDBGSEGIDX iMapSeg) +{ + if (iSeg == RTDBGSEGIDX_ABS) + return; + + if (iSeg == RTDBGSEGIDX_RVA) + { + if ( iMapSeg == RTDBGSEGIDX_RVA + || iMapSeg == NIL_RTDBGSEGIDX) + *pAddr += MapAddr; + else + { + RTUINTPTR SegRva = RTDbgModSegmentRva(hDbgMod, iMapSeg); + AssertReturnVoid(SegRva != RTUINTPTR_MAX); + AssertMsg(SegRva <= *pAddr, ("SegRva=%RTptr *pAddr=%RTptr\n", SegRva, *pAddr)); + *pAddr += MapAddr - SegRva; + } + } + else + { + if ( iMapSeg != RTDBGSEGIDX_RVA + && iMapSeg != NIL_RTDBGSEGIDX) + { + Assert(iMapSeg == iSeg); + *pAddr += MapAddr; + } + else + { + RTUINTPTR SegRva = RTDbgModSegmentRva(hDbgMod, iSeg); + AssertReturnVoid(SegRva != RTUINTPTR_MAX); + *pAddr += MapAddr + SegRva; + } + } +} + + +/** + * Adjusts the symbol value to correspond to the mapping of the module/segment. + * + * @param pSymbol The returned symbol info. + * @param hDbgMod The module handle. + * @param MapAddr The mapping address. + * @param iMapSeg The segment that's mapped, NIL_RTDBGSEGIDX if the + * whole module is mapped here. + */ +DECLINLINE(void) rtDbgAsAdjustSymbolValue(PRTDBGSYMBOL pSymbol, RTDBGMOD hDbgMod, RTUINTPTR MapAddr, RTDBGSEGIDX iMapSeg) +{ + Assert(pSymbol->iSeg != NIL_RTDBGSEGIDX); + Assert(pSymbol->offSeg == pSymbol->Value); + rtDbgAsAdjustAddressByMapping(&pSymbol->Value, pSymbol->iSeg, hDbgMod, MapAddr, iMapSeg); +} + + +/** + * Adjusts the line number address to correspond to the mapping of the module/segment. + * + * @param pLine The returned line number info. + * @param hDbgMod The module handle. + * @param MapAddr The mapping address. + * @param iMapSeg The segment that's mapped, NIL_RTDBGSEGIDX if the + * whole module is mapped here. + */ +DECLINLINE(void) rtDbgAsAdjustLineAddress(PRTDBGLINE pLine, RTDBGMOD hDbgMod, RTUINTPTR MapAddr, RTDBGSEGIDX iMapSeg) +{ + Assert(pLine->iSeg != NIL_RTDBGSEGIDX); + Assert(pLine->offSeg == pLine->Address); + rtDbgAsAdjustAddressByMapping(&pLine->Address, pLine->iSeg, hDbgMod, MapAddr, iMapSeg); +} + + +RTDECL(int) RTDbgAsSymbolAdd(RTDBGAS hDbgAs, const char *pszSymbol, RTUINTPTR Addr, RTUINTPTR cb, uint32_t fFlags, uint32_t *piOrdinal) +{ + /* + * Validate input and resolve the address. + */ + PRTDBGASINT pDbgAs = hDbgAs; + RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE); + + RTDBGSEGIDX iSeg = NIL_RTDBGSEGIDX; /* shut up gcc */ + RTUINTPTR offSeg = 0; + RTDBGMOD hMod = rtDbgAsModuleByAddr(pDbgAs, Addr, &iSeg, &offSeg, NULL); + if (hMod == NIL_RTDBGMOD) + return VERR_NOT_FOUND; + + /* + * Forward the call. + */ + int rc = RTDbgModSymbolAdd(hMod, pszSymbol, iSeg, offSeg, cb, fFlags, piOrdinal); + RTDbgModRelease(hMod); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgAsSymbolAdd); + + +/** + * Creates a snapshot of the module table on the temporary heap. + * + * The caller must release all the module handles before freeing the table + * using RTMemTmpFree. + * + * @returns Module table snaphot. + * @param pDbgAs The address space instance data. + * @param pcModules Where to return the number of modules. + */ +static PRTDBGMOD rtDbgAsSnapshotModuleTable(PRTDBGASINT pDbgAs, uint32_t *pcModules) +{ + RTDBGAS_LOCK_READ(pDbgAs); + + uint32_t iMod = *pcModules = pDbgAs->cModules; + PRTDBGMOD pahModules = (PRTDBGMOD)RTMemTmpAlloc(sizeof(pahModules[0]) * RT_MAX(iMod, 1)); + if (pahModules) + { + while (iMod-- > 0) + { + RTDBGMOD hMod = (RTDBGMOD)pDbgAs->papModules[iMod]->Core.Key; + pahModules[iMod] = hMod; + RTDbgModRetain(hMod); + } + } + + RTDBGAS_UNLOCK_READ(pDbgAs); + return pahModules; +} + + +RTDECL(int) RTDbgAsSymbolByAddr(RTDBGAS hDbgAs, RTUINTPTR Addr, uint32_t fFlags, + PRTINTPTR poffDisp, PRTDBGSYMBOL pSymbol, PRTDBGMOD phMod) +{ + /* + * Validate input and resolve the address. + */ + PRTDBGASINT pDbgAs = hDbgAs; + RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE); + if (phMod) + *phMod = NIL_RTDBGMOD; + + RTDBGSEGIDX iSeg = NIL_RTDBGSEGIDX; /* shut up gcc */ + RTUINTPTR offSeg = 0; + RTUINTPTR MapAddr = 0; + RTDBGMOD hMod = rtDbgAsModuleByAddr(pDbgAs, Addr, &iSeg, &offSeg, &MapAddr); + if (hMod == NIL_RTDBGMOD) + { + /* + * Check for absolute symbols. Requires iterating all modules. + */ + if (fFlags & RTDBGSYMADDR_FLAGS_SKIP_ABS) + return VERR_NOT_FOUND; + + uint32_t cModules; + PRTDBGMOD pahModules = rtDbgAsSnapshotModuleTable(pDbgAs, &cModules); + if (!pahModules) + return VERR_NO_TMP_MEMORY; + + int rc; + RTINTPTR offBestDisp = RTINTPTR_MAX; + uint32_t iBest = UINT32_MAX; + for (uint32_t i = 0; i < cModules; i++) + { + RTINTPTR offDisp; + rc = RTDbgModSymbolByAddr(pahModules[i], RTDBGSEGIDX_ABS, Addr, fFlags, &offDisp, pSymbol); + if (RT_SUCCESS(rc) && RT_ABS(offDisp) < offBestDisp) + { + offBestDisp = RT_ABS(offDisp); + iBest = i; + } + } + + if (iBest == UINT32_MAX) + rc = VERR_NOT_FOUND; + else + { + hMod = pahModules[iBest]; + rc = RTDbgModSymbolByAddr(hMod, RTDBGSEGIDX_ABS, Addr, fFlags, poffDisp, pSymbol); + if (RT_SUCCESS(rc)) + { + rtDbgAsAdjustSymbolValue(pSymbol, hMod, MapAddr, iSeg); + if (phMod) + RTDbgModRetain(*phMod = hMod); + } + } + + for (uint32_t i = 0; i < cModules; i++) + RTDbgModRelease(pahModules[i]); + RTMemTmpFree(pahModules); + return rc; + } + + /* + * Forward the call. + */ + int rc = RTDbgModSymbolByAddr(hMod, iSeg, offSeg, fFlags, poffDisp, pSymbol); + if (RT_SUCCESS(rc)) + rtDbgAsAdjustSymbolValue(pSymbol, hMod, MapAddr, iSeg); + if (phMod) + *phMod = hMod; + else + RTDbgModRelease(hMod); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgAsSymbolByAddr); + + +RTDECL(int) RTDbgAsSymbolByAddrA(RTDBGAS hDbgAs, RTUINTPTR Addr, uint32_t fFlags, + PRTINTPTR poffDisp, PRTDBGSYMBOL *ppSymInfo, PRTDBGMOD phMod) +{ + /* + * Validate input and resolve the address. + */ + PRTDBGASINT pDbgAs = hDbgAs; + RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE); + + RTDBGSEGIDX iSeg = NIL_RTDBGSEGIDX; + RTUINTPTR offSeg = 0; + RTUINTPTR MapAddr = 0; + RTDBGMOD hMod = rtDbgAsModuleByAddr(pDbgAs, Addr, &iSeg, &offSeg, &MapAddr); + if (hMod == NIL_RTDBGMOD) + { + if (phMod) + *phMod = NIL_RTDBGMOD; + return VERR_NOT_FOUND; + } + + /* + * Forward the call. + */ + int rc = RTDbgModSymbolByAddrA(hMod, iSeg, offSeg, fFlags, poffDisp, ppSymInfo); + if (RT_SUCCESS(rc)) + rtDbgAsAdjustSymbolValue(*ppSymInfo, hMod, MapAddr, iSeg); + if (phMod) + *phMod = hMod; + else + RTDbgModRelease(hMod); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgAsSymbolByAddrA); + + +/** + * Attempts to find a mapping of the specified symbol/module and + * adjust it's Value field accordingly. + * + * @returns true / false success indicator. + * @param pDbgAs The address space. + * @param hDbgMod The module handle. + * @param pSymbol The symbol info. + */ +static bool rtDbgAsFindMappingAndAdjustSymbolValue(PRTDBGASINT pDbgAs, RTDBGMOD hDbgMod, PRTDBGSYMBOL pSymbol) +{ + /* + * Absolute segments needs no fixing. + */ + RTDBGSEGIDX const iSeg = pSymbol->iSeg; + if (iSeg == RTDBGSEGIDX_ABS) + return true; + + RTDBGAS_LOCK_READ(pDbgAs); + + /* + * Lookup up the module by it's handle and iterate the mappings looking for one + * that either encompasses the entire module or the segment in question. + */ + PRTDBGASMOD pMod = (PRTDBGASMOD)RTAvlPVGet(&pDbgAs->ModTree, hDbgMod); + if (pMod) + { + for (PRTDBGASMAP pMap = pMod->pMapHead; pMap; pMap = pMap->pNext) + { + /* Exact segment match or full-mapping. */ + if ( iSeg == pMap->iSeg + || pMap->iSeg == NIL_RTDBGSEGIDX) + { + RTUINTPTR MapAddr = pMap->Core.Key; + RTDBGSEGIDX iMapSeg = pMap->iSeg; + + RTDBGAS_UNLOCK_READ(pDbgAs); + rtDbgAsAdjustSymbolValue(pSymbol, hDbgMod, MapAddr, iMapSeg); + return true; + } + + /* Symbol uses RVA and the mapping doesn't, see if it's in the mapped segment. */ + if (iSeg == RTDBGSEGIDX_RVA) + { + Assert(pMap->iSeg != NIL_RTDBGSEGIDX); + RTUINTPTR SegRva = RTDbgModSegmentRva(hDbgMod, pMap->iSeg); + Assert(SegRva != RTUINTPTR_MAX); + RTUINTPTR cbSeg = RTDbgModSegmentSize(hDbgMod, pMap->iSeg); + if (SegRva - pSymbol->Value < cbSeg) + { + RTUINTPTR MapAddr = pMap->Core.Key; + RTDBGSEGIDX iMapSeg = pMap->iSeg; + + RTDBGAS_UNLOCK_READ(pDbgAs); + rtDbgAsAdjustSymbolValue(pSymbol, hDbgMod, MapAddr, iMapSeg); + return true; + } + } + } + } + /* else: Unmapped while we were searching. */ + + RTDBGAS_UNLOCK_READ(pDbgAs); + return false; +} + + +RTDECL(int) RTDbgAsSymbolByName(RTDBGAS hDbgAs, const char *pszSymbol, PRTDBGSYMBOL pSymbol, PRTDBGMOD phMod) +{ + /* + * Validate input. + */ + PRTDBGASINT pDbgAs = hDbgAs; + RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE); + AssertPtrReturn(pszSymbol, VERR_INVALID_POINTER); + AssertPtrReturn(pSymbol, VERR_INVALID_POINTER); + + /* + * Look for module pattern. + */ + const char *pachModPat = NULL; + size_t cchModPat = 0; + const char *pszBang = strchr(pszSymbol, '!'); + if (pszBang) + { + pachModPat = pszSymbol; + cchModPat = pszBang - pszSymbol; + pszSymbol = pszBang + 1; + if (!*pszSymbol) + return VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE; + /* Note! Zero length module -> no pattern -> escape for symbol with '!'. */ + } + + /* + * Iterate the modules, looking for the symbol. + */ + uint32_t cModules; + PRTDBGMOD pahModules = rtDbgAsSnapshotModuleTable(pDbgAs, &cModules); + if (!pahModules) + return VERR_NO_TMP_MEMORY; + + for (uint32_t i = 0; i < cModules; i++) + { + if ( cchModPat == 0 + || RTStrSimplePatternNMatch(pachModPat, cchModPat, RTDbgModName(pahModules[i]), RTSTR_MAX)) + { + int rc = RTDbgModSymbolByName(pahModules[i], pszSymbol, pSymbol); + if (RT_SUCCESS(rc)) + { + if (rtDbgAsFindMappingAndAdjustSymbolValue(pDbgAs, pahModules[i], pSymbol)) + { + if (phMod) + RTDbgModRetain(*phMod = pahModules[i]); + for (; i < cModules; i++) + RTDbgModRelease(pahModules[i]); + RTMemTmpFree(pahModules); + return rc; + } + } + } + RTDbgModRelease(pahModules[i]); + } + + RTMemTmpFree(pahModules); + return VERR_SYMBOL_NOT_FOUND; +} +RT_EXPORT_SYMBOL(RTDbgAsSymbolByName); + + +RTDECL(int) RTDbgAsSymbolByNameA(RTDBGAS hDbgAs, const char *pszSymbol, PRTDBGSYMBOL *ppSymbol, PRTDBGMOD phMod) +{ + /* + * Validate input. + */ + AssertPtrReturn(ppSymbol, VERR_INVALID_POINTER); + *ppSymbol = NULL; + PRTDBGASINT pDbgAs = hDbgAs; + RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE); + AssertPtrReturn(pszSymbol, VERR_INVALID_POINTER); + + /* + * Look for module pattern. + */ + const char *pachModPat = NULL; + size_t cchModPat = 0; + const char *pszBang = strchr(pszSymbol, '!'); + if (pszBang) + { + pachModPat = pszSymbol; + cchModPat = pszBang - pszSymbol; + pszSymbol = pszBang + 1; + if (!*pszSymbol) + return VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE; + /* Note! Zero length module -> no pattern -> escape for symbol with '!'. */ + } + + /* + * Iterate the modules, looking for the symbol. + */ + uint32_t cModules; + PRTDBGMOD pahModules = rtDbgAsSnapshotModuleTable(pDbgAs, &cModules); + if (!pahModules) + return VERR_NO_TMP_MEMORY; + + for (uint32_t i = 0; i < cModules; i++) + { + if ( cchModPat == 0 + || RTStrSimplePatternNMatch(pachModPat, cchModPat, RTDbgModName(pahModules[i]), RTSTR_MAX)) + { + int rc = RTDbgModSymbolByNameA(pahModules[i], pszSymbol, ppSymbol); + if (RT_SUCCESS(rc)) + { + if (rtDbgAsFindMappingAndAdjustSymbolValue(pDbgAs, pahModules[i], *ppSymbol)) + { + if (phMod) + RTDbgModRetain(*phMod = pahModules[i]); + for (; i < cModules; i++) + RTDbgModRelease(pahModules[i]); + RTMemTmpFree(pahModules); + return rc; + } + } + } + RTDbgModRelease(pahModules[i]); + } + + RTMemTmpFree(pahModules); + return VERR_SYMBOL_NOT_FOUND; +} +RT_EXPORT_SYMBOL(RTDbgAsSymbolByNameA); + + +RTDECL(int) RTDbgAsLineAdd(RTDBGAS hDbgAs, const char *pszFile, uint32_t uLineNo, RTUINTPTR Addr, uint32_t *piOrdinal) +{ + /* + * Validate input and resolve the address. + */ + PRTDBGASINT pDbgAs = hDbgAs; + RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE); + + RTDBGSEGIDX iSeg = NIL_RTDBGSEGIDX; /* shut up gcc */ + RTUINTPTR offSeg = 0; /* ditto */ + RTDBGMOD hMod = rtDbgAsModuleByAddr(pDbgAs, Addr, &iSeg, &offSeg, NULL); + if (hMod == NIL_RTDBGMOD) + return VERR_NOT_FOUND; + + /* + * Forward the call. + */ + int rc = RTDbgModLineAdd(hMod, pszFile, uLineNo, iSeg, offSeg, piOrdinal); + RTDbgModRelease(hMod); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgAsLineAdd); + + +RTDECL(int) RTDbgAsLineByAddr(RTDBGAS hDbgAs, RTUINTPTR Addr, PRTINTPTR poffDisp, PRTDBGLINE pLine, PRTDBGMOD phMod) +{ + /* + * Validate input and resolve the address. + */ + PRTDBGASINT pDbgAs = hDbgAs; + RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE); + + RTDBGSEGIDX iSeg = NIL_RTDBGSEGIDX; /* shut up gcc */ + RTUINTPTR offSeg = 0; + RTUINTPTR MapAddr = 0; + RTDBGMOD hMod = rtDbgAsModuleByAddr(pDbgAs, Addr, &iSeg, &offSeg, &MapAddr); + if (hMod == NIL_RTDBGMOD) + return VERR_NOT_FOUND; + + /* + * Forward the call. + */ + int rc = RTDbgModLineByAddr(hMod, iSeg, offSeg, poffDisp, pLine); + if (RT_SUCCESS(rc)) + { + rtDbgAsAdjustLineAddress(pLine, hMod, MapAddr, iSeg); + if (phMod) + *phMod = hMod; + else + RTDbgModRelease(hMod); + } + else + RTDbgModRelease(hMod); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgAsLineByAddr); + + +RTDECL(int) RTDbgAsLineByAddrA(RTDBGAS hDbgAs, RTUINTPTR Addr, PRTINTPTR poffDisp, PRTDBGLINE *ppLine, PRTDBGMOD phMod) +{ + /* + * Validate input and resolve the address. + */ + PRTDBGASINT pDbgAs = hDbgAs; + RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE); + + RTDBGSEGIDX iSeg = NIL_RTDBGSEGIDX; /* shut up gcc */ + RTUINTPTR offSeg = 0; + RTUINTPTR MapAddr = 0; + RTDBGMOD hMod = rtDbgAsModuleByAddr(pDbgAs, Addr, &iSeg, &offSeg, &MapAddr); + if (hMod == NIL_RTDBGMOD) + return VERR_NOT_FOUND; + + /* + * Forward the call. + */ + int rc = RTDbgModLineByAddrA(hMod, iSeg, offSeg, poffDisp, ppLine); + if (RT_SUCCESS(rc)) + { + rtDbgAsAdjustLineAddress(*ppLine, hMod, MapAddr, iSeg); + if (phMod) + *phMod = hMod; + else + RTDbgModRelease(hMod); + } + else + RTDbgModRelease(hMod); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgAsLineByAddrA); + diff --git a/src/VBox/Runtime/common/dbg/dbgcfg.cpp b/src/VBox/Runtime/common/dbg/dbgcfg.cpp new file mode 100644 index 00000000..f680f123 --- /dev/null +++ b/src/VBox/Runtime/common/dbg/dbgcfg.cpp @@ -0,0 +1,2525 @@ +/* $Id: dbgcfg.cpp $ */ +/** @file + * IPRT - Debugging Configuration. + */ + +/* + * Copyright (C) 2013-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_DBG +#include <iprt/dbg.h> +#include "internal/iprt.h" + +#include <iprt/alloca.h> +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/critsect.h> +#include <iprt/ctype.h> +#include <iprt/dir.h> +#include <iprt/err.h> +#include <iprt/env.h> +#include <iprt/file.h> +#ifdef IPRT_WITH_HTTP +# include <iprt/http.h> +#endif +#include <iprt/list.h> +#include <iprt/log.h> +#include <iprt/mem.h> +#include <iprt/path.h> +#include <iprt/process.h> +#include <iprt/semaphore.h> +#include <iprt/string.h> +#include <iprt/uuid.h> +#include "internal/magics.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * String list entry. + */ +typedef struct RTDBGCFGSTR +{ + /** List entry. */ + RTLISTNODE ListEntry; + /** Domain specific flags. */ + uint16_t fFlags; + /** The length of the string. */ + uint16_t cch; + /** The string. */ + char sz[1]; +} RTDBGCFGSTR; +/** Pointer to a string list entry. */ +typedef RTDBGCFGSTR *PRTDBGCFGSTR; + + +/** + * Configuration instance. + */ +typedef struct RTDBGCFGINT +{ + /** The magic value (RTDBGCFG_MAGIC). */ + uint32_t u32Magic; + /** Reference counter. */ + uint32_t volatile cRefs; + /** Flags, see RTDBGCFG_FLAGS_XXX. */ + uint64_t fFlags; + + /** List of paths to search for debug files and executable images. */ + RTLISTANCHOR PathList; + /** List of debug file suffixes. */ + RTLISTANCHOR SuffixList; + /** List of paths to search for source files. */ + RTLISTANCHOR SrcPathList; + +#ifdef RT_OS_WINDOWS + /** The _NT_ALT_SYMBOL_PATH and _NT_SYMBOL_PATH combined. */ + RTLISTANCHOR NtSymbolPathList; + /** The _NT_EXECUTABLE_PATH. */ + RTLISTANCHOR NtExecutablePathList; + /** The _NT_SOURCE_PATH. */ + RTLISTANCHOR NtSourcePath; +#endif + + /** Log callback function. */ + PFNRTDBGCFGLOG pfnLogCallback; + /** User argument to pass to the log callback. */ + void *pvLogUser; + + /** Critical section protecting the instance data. */ + RTCRITSECTRW CritSect; +} *PRTDBGCFGINT; + +/** + * Mnemonics map entry for a 64-bit unsigned property value. + */ +typedef struct RTDBGCFGU64MNEMONIC +{ + /** The flags to set or clear. */ + uint64_t fFlags; + /** The mnemonic. */ + const char *pszMnemonic; + /** The length of the mnemonic. */ + uint8_t cchMnemonic; + /** If @c true, the bits in fFlags will be set, if @c false they will be + * cleared. */ + bool fSet; +} RTDBGCFGU64MNEMONIC; +/** Pointer to a read only mnemonic map entry for a uint64_t property. */ +typedef RTDBGCFGU64MNEMONIC const *PCRTDBGCFGU64MNEMONIC; + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** Validates a debug module handle and returns rc if not valid. */ +#define RTDBGCFG_VALID_RETURN_RC(pThis, rc) \ + do { \ + AssertPtrReturn((pThis), (rc)); \ + AssertReturn((pThis)->u32Magic == RTDBGCFG_MAGIC, (rc)); \ + AssertReturn((pThis)->cRefs > 0, (rc)); \ + } while (0) + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Mnemonics map for RTDBGCFGPROP_FLAGS. */ +static const RTDBGCFGU64MNEMONIC g_aDbgCfgFlags[] = +{ + { RTDBGCFG_FLAGS_DEFERRED, RT_STR_TUPLE("deferred"), true }, + { RTDBGCFG_FLAGS_DEFERRED, RT_STR_TUPLE("nodeferred"), false }, + { RTDBGCFG_FLAGS_NO_SYM_SRV, RT_STR_TUPLE("symsrv"), false }, + { RTDBGCFG_FLAGS_NO_SYM_SRV, RT_STR_TUPLE("nosymsrv"), true }, + { RTDBGCFG_FLAGS_NO_SYSTEM_PATHS, RT_STR_TUPLE("syspaths"), false }, + { RTDBGCFG_FLAGS_NO_SYSTEM_PATHS, RT_STR_TUPLE("nosyspaths"), true }, + { RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH, RT_STR_TUPLE("rec"), false }, + { RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH, RT_STR_TUPLE("norec"), true }, + { RTDBGCFG_FLAGS_NO_RECURSIV_SRC_SEARCH, RT_STR_TUPLE("recsrc"), false }, + { RTDBGCFG_FLAGS_NO_RECURSIV_SRC_SEARCH, RT_STR_TUPLE("norecsrc"), true }, + { 0, NULL, 0, false } +}; + + +/** Interesting bundle suffixes. */ +static const char * const g_apszBundleSuffixes[] = +{ + ".kext", + ".app", + ".framework", + ".component", + ".action", + ".caction", + ".bundle", + ".sourcebundle", + ".menu", + ".plugin", + ".ppp", + ".monitorpanel", + ".scripting", + ".prefPane", + ".qlgenerator", + ".brailledriver", + ".saver", + ".SpeechVoice", + ".SpeechRecognizer", + ".SpeechSynthesizer", + ".mdimporter", + ".spreporter", + ".xpc", + NULL +}; + +/** Debug bundle suffixes. (Same as above + .dSYM) */ +static const char * const g_apszDSymBundleSuffixes[] = +{ + ".dSYM", + ".kext.dSYM", + ".app.dSYM", + ".framework.dSYM", + ".component.dSYM", + ".action.dSYM", + ".caction.dSYM", + ".bundle.dSYM", + ".sourcebundle.dSYM", + ".menu.dSYM", + ".plugin.dSYM", + ".ppp.dSYM", + ".monitorpanel.dSYM", + ".scripting.dSYM", + ".prefPane.dSYM", + ".qlgenerator.dSYM", + ".brailledriver.dSYM", + ".saver.dSYM", + ".SpeechVoice.dSYM", + ".SpeechRecognizer.dSYM", + ".SpeechSynthesizer.dSYM", + ".mdimporter.dSYM", + ".spreporter.dSYM", + ".xpc.dSYM", + NULL +}; + + + +/** + * Runtime logging, level 1. + * + * @param pThis The debug config instance data. + * @param pszFormat The message format string. + * @param ... Arguments references in the format string. + */ +static void rtDbgCfgLog1(PRTDBGCFGINT pThis, const char *pszFormat, ...) +{ + if (LogIsEnabled() || (pThis && pThis->pfnLogCallback)) + { + va_list va; + va_start(va, pszFormat); + char *pszMsg = RTStrAPrintf2V(pszFormat, va); + va_end(va); + + Log(("RTDbgCfg: %s", pszMsg)); + if (pThis && pThis->pfnLogCallback) + pThis->pfnLogCallback(pThis, 1, pszMsg, pThis->pvLogUser); + RTStrFree(pszMsg); + } +} + + +/** + * Runtime logging, level 2. + * + * @param pThis The debug config instance data. + * @param pszFormat The message format string. + * @param ... Arguments references in the format string. + */ +static void rtDbgCfgLog2(PRTDBGCFGINT pThis, const char *pszFormat, ...) +{ + if (LogIs2Enabled() || (pThis && pThis->pfnLogCallback)) + { + va_list va; + va_start(va, pszFormat); + char *pszMsg = RTStrAPrintf2V(pszFormat, va); + va_end(va); + + Log(("RTDbgCfg: %s", pszMsg)); + if (pThis && pThis->pfnLogCallback) + pThis->pfnLogCallback(pThis, 2, pszMsg, pThis->pvLogUser); + RTStrFree(pszMsg); + } +} + + +/** + * Checks if the file system at the given path is case insensitive or not. + * + * @returns true / false + * @param pszPath The path to query about. + */ +static int rtDbgCfgIsFsCaseInsensitive(const char *pszPath) +{ + RTFSPROPERTIES Props; + int rc = RTFsQueryProperties(pszPath, &Props); + if (RT_FAILURE(rc)) + return RT_OPSYS == RT_OPSYS_DARWIN + || RT_OPSYS == RT_OPSYS_DOS + || RT_OPSYS == RT_OPSYS_OS2 + || RT_OPSYS == RT_OPSYS_NT + || RT_OPSYS == RT_OPSYS_WINDOWS; + return !Props.fCaseSensitive; +} + + +/** + * Worker that does case sensitive file/dir searching. + * + * @returns true / false. + * @param pszPath The path buffer containing an existing directory and + * at @a offLastComp the name we're looking for. + * RTPATH_MAX in size. On success, this last component + * will have the correct case. On failure, the last + * component is stripped off. + * @param offLastComp The offset of the last component (for chopping it + * off). + * @param enmType What kind of thing we're looking for. + */ +static bool rtDbgCfgIsXxxxAndFixCaseWorker(char *pszPath, size_t offLastComp, RTDIRENTRYTYPE enmType) +{ + /** @todo IPRT should generalize this so we can use host specific tricks to + * speed it up. */ + + char *pszName = &pszPath[offLastComp]; + + /* Return straight away if the name isn't case foldable. */ + if (!RTStrIsCaseFoldable(pszName)) + { + *pszName = '\0'; + return false; + } + + /* + * Try some simple case folding games. + */ + RTStrToLower(pszName); + if (RTFileExists(pszPath)) + return true; + + RTStrToUpper(pszName); + if (RTFileExists(pszPath)) + return true; + + /* + * Open the directory and check each entry in it. + */ + char chSaved = *pszName; + *pszName = '\0'; + + RTDIR hDir; + int rc = RTDirOpen(&hDir, pszPath); + if (RT_FAILURE(rc)) + return false; + + *pszName = chSaved; + + for (;;) + { + /* Read the next entry. */ + union + { + RTDIRENTRY Entry; + uint8_t ab[_4K]; + } u; + size_t cbBuf = sizeof(u); + rc = RTDirRead(hDir, &u.Entry, &cbBuf); + if (RT_FAILURE(rc)) + break; + + if ( !RTStrICmp(pszName, u.Entry.szName) + && ( u.Entry.enmType == enmType + || u.Entry.enmType == RTDIRENTRYTYPE_UNKNOWN + || u.Entry.enmType == RTDIRENTRYTYPE_SYMLINK) ) + { + strcpy(pszName, u.Entry.szName); + if (u.Entry.enmType != enmType) + RTDirQueryUnknownType(pszPath, true /*fFollowSymlinks*/, &u.Entry.enmType); + if (u.Entry.enmType == enmType) + { + RTDirClose(hDir); + return true; + } + } + } + + RTDirClose(hDir); + *pszName = '\0'; + + return false; +} + + +/** + * Appends @a pszSubDir to @a pszPath and check whether it exists and is a + * directory. + * + * If @a fCaseInsensitive is set, we will do a case insensitive search for a + * matching sub directory. + * + * @returns true / false + * @param pszPath The path buffer containing an existing + * directory. RTPATH_MAX in size. + * @param pszSubDir The sub directory to append. + * @param fCaseInsensitive Whether case insensitive searching is required. + */ +static bool rtDbgCfgIsDirAndFixCase(char *pszPath, const char *pszSubDir, bool fCaseInsensitive) +{ + /* Save the length of the input path so we can restore it in the case + insensitive branch further down. */ + size_t const cchPath = strlen(pszPath); + + /* + * Append the sub directory and check if we got a hit. + */ + int rc = RTPathAppend(pszPath, RTPATH_MAX, pszSubDir); + if (RT_FAILURE(rc)) + return false; + + if (RTDirExists(pszPath)) + return true; + + /* + * Do case insensitive lookup if requested. + */ + if (fCaseInsensitive) + return rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, RTDIRENTRYTYPE_DIRECTORY); + + pszPath[cchPath] = '\0'; + return false; +} + + +/** + * Appends @a pszSubDir1 and @a pszSuffix to @a pszPath and check whether it + * exists and is a directory. + * + * If @a fCaseInsensitive is set, we will do a case insensitive search for a + * matching sub directory. + * + * @returns true / false + * @param pszPath The path buffer containing an existing + * directory. RTPATH_MAX in size. + * @param pszSubDir The sub directory to append. + * @param pszSuffix The suffix to append. + * @param fCaseInsensitive Whether case insensitive searching is required. + */ +static bool rtDbgCfgIsDirAndFixCase2(char *pszPath, const char *pszSubDir, const char *pszSuffix, bool fCaseInsensitive) +{ + Assert(!strpbrk(pszSuffix, ":/\\")); + + /* Save the length of the input path so we can restore it in the case + insensitive branch further down. */ + size_t const cchPath = strlen(pszPath); + + /* + * Append the subdirectory and suffix, then check if we got a hit. + */ + int rc = RTPathAppend(pszPath, RTPATH_MAX, pszSubDir); + if (RT_SUCCESS(rc)) + { + rc = RTStrCat(pszPath, RTPATH_MAX, pszSuffix); + if (RT_SUCCESS(rc)) + { + if (RTDirExists(pszPath)) + return true; + + /* + * Do case insensitive lookup if requested. + */ + if (fCaseInsensitive) + return rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, RTDIRENTRYTYPE_DIRECTORY); + } + } + + pszPath[cchPath] = '\0'; + return false; +} + + +/** + * Appends @a pszFilename to @a pszPath and check whether it exists and is a + * directory. + * + * If @a fCaseInsensitive is set, we will do a case insensitive search for a + * matching filename. + * + * @returns true / false + * @param pszPath The path buffer containing an existing + * directory. RTPATH_MAX in size. + * @param pszFilename The filename to append. + * @param pszSuffix Optional filename suffix to append. + * @param fCaseInsensitive Whether case insensitive searching is required. + * @param fMsCompressed Whether to look for the MS compressed file name + * variant. + * @param pfProbablyCompressed This is set to true if a MS compressed + * filename variant is returned. Optional. + */ +static bool rtDbgCfgIsFileAndFixCase(char *pszPath, const char *pszFilename, const char *pszSuffix, bool fCaseInsensitive, + bool fMsCompressed, bool *pfProbablyCompressed) +{ + /* Save the length of the input path so we can restore it in the case + insensitive branch further down. */ + size_t cchPath = strlen(pszPath); + if (pfProbablyCompressed) + *pfProbablyCompressed = false; + + /* + * Append the filename and optionally suffix, then check if we got a hit. + */ + int rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename); + if (RT_FAILURE(rc)) + return false; + if (pszSuffix) + { + Assert(!fMsCompressed); + rc = RTStrCat(pszPath, RTPATH_MAX, pszSuffix); + if (RT_FAILURE(rc)) + return false; + } + + if (RTFileExists(pszPath)) + return true; + + /* + * Do case insensitive file lookup if requested. + */ + if (fCaseInsensitive) + { + if (rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, RTDIRENTRYTYPE_FILE)) + return true; + } + + /* + * Look for MS compressed file if requested. + */ + if ( fMsCompressed + && (unsigned char)pszFilename[strlen(pszFilename) - 1] < 0x7f) + { + pszPath[cchPath] = '\0'; + rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename); + AssertRCReturn(rc, false); + pszPath[strlen(pszPath) - 1] = '_'; + + if (pfProbablyCompressed) + *pfProbablyCompressed = true; + + if ( RTFileExists(pszPath) + || ( fCaseInsensitive + && rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, RTDIRENTRYTYPE_FILE) )) + return true; + + if (pfProbablyCompressed) + *pfProbablyCompressed = false; + } + + pszPath[cchPath] = '\0'; + return false; +} + + +static int rtDbgCfgTryOpenDir(PRTDBGCFGINT pThis, char *pszPath, PRTPATHSPLIT pSplitFn, uint32_t fFlags, + PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + int rcRet = VWRN_NOT_FOUND; + int rc2; + + /* If the directory doesn't exist, just quit immediately. + Note! Our case insensitivity doesn't extend to the search dirs themselfs, + only to the bits under neath them. */ + if (!RTDirExists(pszPath)) + { + rtDbgCfgLog2(pThis, "Dir does not exist: '%s'\n", pszPath); + return rcRet; + } + + /* Figure out whether we have to do a case sensitive search or not. + Note! As a simplification, we don't ask for case settings in each + directory under the user specified path, we assume the file + systems that mounted there have compatible settings. Faster + that way. */ + bool const fCaseInsensitive = (fFlags & RTDBGCFG_O_CASE_INSENSITIVE) + && !rtDbgCfgIsFsCaseInsensitive(pszPath); + + size_t const cchPath = strlen(pszPath); + + /* + * Look for the file with less and less of the original path given. + */ + for (unsigned i = RTPATH_PROP_HAS_ROOT_SPEC(pSplitFn->fProps); i < pSplitFn->cComps; i++) + { + pszPath[cchPath] = '\0'; + + rc2 = VINF_SUCCESS; + for (unsigned j = i; j < pSplitFn->cComps - 1U && RT_SUCCESS(rc2); j++) + if (!rtDbgCfgIsDirAndFixCase(pszPath, pSplitFn->apszComps[i], fCaseInsensitive)) + rc2 = VERR_FILE_NOT_FOUND; + + if (RT_SUCCESS(rc2)) + { + if (rtDbgCfgIsFileAndFixCase(pszPath, pSplitFn->apszComps[pSplitFn->cComps - 1], NULL /*pszSuffix*/, + fCaseInsensitive, false, NULL)) + { + rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath); + rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN) + { + if (rc2 == VINF_CALLBACK_RETURN) + rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath); + else + rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath); + return rc2; + } + rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath); + if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet)) + rcRet = rc2; + } + } + } + + /* + * Do a recursive search if requested. + */ + if ( (fFlags & RTDBGCFG_O_RECURSIVE) + && pThis + && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH) ) + { + /** @todo Recursive searching will be done later. */ + } + + return rcRet; +} + +static int rtDbgCfgUnpackMsCacheFile(PRTDBGCFGINT pThis, char *pszPath, const char *pszFilename) +{ + rtDbgCfgLog2(pThis, "Unpacking '%s'...\n", pszPath); + + /* + * Duplicate the source file path, just for simplicity and restore the + * final character in the orignal. We cheerfully ignorining any + * possibility of multibyte UTF-8 sequences just like the caller did when + * setting it to '_'. + */ + char *pszSrcArchive = RTStrDup(pszPath); + if (!pszSrcArchive) + return VERR_NO_STR_MEMORY; + + pszPath[strlen(pszPath) - 1] = RT_C_TO_LOWER(pszFilename[strlen(pszFilename) - 1]); + + + /* + * Figuring out the argument list for the platform specific unpack util. + */ +#ifdef RT_OS_WINDOWS + RTPathChangeToDosSlashes(pszSrcArchive, false /*fForce*/); + RTPathChangeToDosSlashes(pszPath, false /*fForce*/); + const char *papszArgs[] = + { + "expand.exe", + pszSrcArchive, + pszPath, + NULL + }; + +#else + char szExtractDir[RTPATH_MAX]; + strcpy(szExtractDir, pszPath); + RTPathStripFilename(szExtractDir); + + const char *papszArgs[] = + { + "cabextract", + "-L", /* Lower case extracted files. */ + "-d", szExtractDir, /* Extraction path */ + pszSrcArchive, + NULL + }; +#endif + + /* + * Do the unpacking. + */ + RTPROCESS hChild; + int rc = RTProcCreate(papszArgs[0], papszArgs, RTENV_DEFAULT, +#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) + RTPROC_FLAGS_NO_WINDOW | RTPROC_FLAGS_HIDDEN | RTPROC_FLAGS_SEARCH_PATH, +#else + RTPROC_FLAGS_SEARCH_PATH, +#endif + &hChild); + if (RT_SUCCESS(rc)) + { + RTPROCSTATUS ProcStatus; + rc = RTProcWait(hChild, RTPROCWAIT_FLAGS_BLOCK, &ProcStatus); + if (RT_SUCCESS(rc)) + { + if ( ProcStatus.enmReason == RTPROCEXITREASON_NORMAL + && ProcStatus.iStatus == 0) + { + if (RTPathExists(pszPath)) + { + rtDbgCfgLog1(pThis, "Successfully unpacked '%s' to '%s'.\n", pszSrcArchive, pszPath); + rc = VINF_SUCCESS; + } + else + { + rtDbgCfgLog1(pThis, "Successfully ran unpacker on '%s', but '%s' is missing!\n", pszSrcArchive, pszPath); + rc = VERR_ZIP_ERROR; + } + } + else + { + rtDbgCfgLog2(pThis, "Unpacking '%s' failed: iStatus=%d enmReason=%d\n", + pszSrcArchive, ProcStatus.iStatus, ProcStatus.enmReason); + rc = VERR_ZIP_CORRUPTED; + } + } + else + rtDbgCfgLog1(pThis, "Error waiting for process: %Rrc\n", rc); + + } + else + rtDbgCfgLog1(pThis, "Error starting unpack process '%s': %Rrc\n", papszArgs[0], rc); + + return rc; +} + + +static int rtDbgCfgTryDownloadAndOpen(PRTDBGCFGINT pThis, const char *pszServer, char *pszPath, + const char *pszCacheSubDir, const char *pszUuidMappingSubDir, + PRTPATHSPLIT pSplitFn, const char *pszCacheSuffix, uint32_t fFlags, + PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + RT_NOREF_PV(pszUuidMappingSubDir); /** @todo do we bother trying pszUuidMappingSubDir? */ + RT_NOREF_PV(pszCacheSuffix); /** @todo do we bother trying pszUuidMappingSubDir? */ + RT_NOREF_PV(fFlags); + + if (pThis->fFlags & RTDBGCFG_FLAGS_NO_SYM_SRV) + return VWRN_NOT_FOUND; + if (!pszCacheSubDir || !*pszCacheSubDir) + return VWRN_NOT_FOUND; + if ( !(fFlags & RTDBGCFG_O_SYMSRV) + && !(fFlags & RTDBGCFG_O_DEBUGINFOD)) + return VWRN_NOT_FOUND; + + /* + * Create the path. + */ + size_t cchTmp = strlen(pszPath); + + int rc = RTDirCreateFullPath(pszPath, 0766); + if (!RTDirExists(pszPath)) + { + Log(("Error creating cache dir '%s': %Rrc\n", pszPath, rc)); + return rc; + } + + const char *pszFilename = pSplitFn->apszComps[pSplitFn->cComps - 1]; + rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename); + if (RT_FAILURE(rc)) + return rc; + RTStrToLower(&pszPath[cchTmp]); + if (!RTDirExists(pszPath)) + { + rc = RTDirCreate(pszPath, 0766, 0); + if (RT_FAILURE(rc)) + { + Log(("RTDirCreate(%s) -> %Rrc\n", pszPath, rc)); + } + } + + rc = RTPathAppend(pszPath, RTPATH_MAX, pszCacheSubDir); + if (RT_FAILURE(rc)) + return rc; + if (!RTDirExists(pszPath)) + { + rc = RTDirCreate(pszPath, 0766, 0); + if (RT_FAILURE(rc)) + { + Log(("RTDirCreate(%s) -> %Rrc\n", pszPath, rc)); + } + } + + /* Prepare the destination file name while we're here. */ + cchTmp = strlen(pszPath); + RTStrToLower(&pszPath[cchTmp]); + rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename); + if (RT_FAILURE(rc)) + return rc; + + /* + * Download/copy the file. + */ + char szUrl[_2K]; + /* Download URL? */ + if ( RTStrIStartsWith(pszServer, "http://") + || RTStrIStartsWith(pszServer, "https://") + || RTStrIStartsWith(pszServer, "ftp://") ) + { +#ifdef IPRT_WITH_HTTP + RTHTTP hHttp; + rc = RTHttpCreate(&hHttp); + if (RT_SUCCESS(rc)) + { + RTHttpUseSystemProxySettings(hHttp); + RTHttpSetFollowRedirects(hHttp, 8); + + static const char * const s_apszHeadersMsSymSrv[] = + { + "User-Agent: Microsoft-Symbol-Server/6.6.0999.9", + "Pragma: no-cache", + }; + + static const char * const s_apszHeadersDebuginfod[] = + { + "User-Agent: IPRT DbgCfg 1.0", + "Pragma: no-cache", + }; + + if (fFlags & RTDBGCFG_O_SYMSRV) + rc = RTHttpSetHeaders(hHttp, RT_ELEMENTS(s_apszHeadersMsSymSrv), s_apszHeadersMsSymSrv); + else /* Must be debuginfod. */ + rc = RTHttpSetHeaders(hHttp, RT_ELEMENTS(s_apszHeadersDebuginfod), s_apszHeadersDebuginfod); + if (RT_SUCCESS(rc)) + { + if (fFlags & RTDBGCFG_O_SYMSRV) + RTStrPrintf(szUrl, sizeof(szUrl), "%s/%s/%s/%s", pszServer, pszFilename, pszCacheSubDir, pszFilename); + else + RTStrPrintf(szUrl, sizeof(szUrl), "%s/buildid/%s/debuginfo", pszServer, pszCacheSubDir); + + /** @todo Use some temporary file name and rename it after the operation + * since not all systems support read-deny file sharing + * settings. */ + rtDbgCfgLog2(pThis, "Downloading '%s' to '%s'...\n", szUrl, pszPath); + rc = RTHttpGetFile(hHttp, szUrl, pszPath); + if (RT_FAILURE(rc)) + { + RTFileDelete(pszPath); + rtDbgCfgLog1(pThis, "%Rrc on URL '%s'\n", rc, szUrl); + } + if ( rc == VERR_HTTP_NOT_FOUND + && (fFlags & RTDBGCFG_O_SYMSRV)) + { + /* Try the compressed version of the file. */ + pszPath[strlen(pszPath) - 1] = '_'; + szUrl[strlen(szUrl) - 1] = '_'; + rtDbgCfgLog2(pThis, "Downloading '%s' to '%s'...\n", szUrl, pszPath); + rc = RTHttpGetFile(hHttp, szUrl, pszPath); + if (RT_SUCCESS(rc)) + rc = rtDbgCfgUnpackMsCacheFile(pThis, pszPath, pszFilename); + else + { + rtDbgCfgLog1(pThis, "%Rrc on URL '%s'\n", rc, pszPath); + RTFileDelete(pszPath); + } + } + } + + RTHttpDestroy(hHttp); + } +#else + rc = VWRN_NOT_FOUND; +#endif + } + /* No download, assume dir on server share. */ + else + { + if (RTStrIStartsWith(pszServer, "file:///")) + pszServer += 4 + 1 + 3 - 1; + + /* Compose the path to the uncompressed file on the server. */ + rc = RTPathJoin(szUrl, sizeof(szUrl), pszServer, pszFilename); + if (RT_SUCCESS(rc)) + rc = RTPathAppend(szUrl, sizeof(szUrl), pszCacheSubDir); + if (RT_SUCCESS(rc)) + rc = RTPathAppend(szUrl, sizeof(szUrl), pszFilename); + if (RT_SUCCESS(rc)) + { + rtDbgCfgLog2(pThis, "Copying '%s' to '%s'...\n", szUrl, pszPath); + rc = RTFileCopy(szUrl, pszPath); + if (RT_FAILURE(rc)) + { + RTFileDelete(pszPath); + rtDbgCfgLog1(pThis, "%Rrc on '%s'\n", rc, szUrl); + + /* Try the compressed version. */ + pszPath[strlen(pszPath) - 1] = '_'; + szUrl[strlen(szUrl) - 1] = '_'; + rtDbgCfgLog2(pThis, "Copying '%s' to '%s'...\n", szUrl, pszPath); + rc = RTFileCopy(szUrl, pszPath); + if (RT_SUCCESS(rc)) + rc = rtDbgCfgUnpackMsCacheFile(pThis, pszPath, pszFilename); + else + { + rtDbgCfgLog1(pThis, "%Rrc on '%s'\n", rc, pszPath); + RTFileDelete(pszPath); + } + } + } + } + if (RT_SUCCESS(rc)) + { + /* + * Succeeded in downloading it. Add UUID mapping? + */ + if (pszUuidMappingSubDir) + { + /** @todo UUID mapping when downloading. */ + } + + /* + * Give the file a try. + */ + Assert(RTFileExists(pszPath)); + rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath); + rc = pfnCallback(pThis, pszPath, pvUser1, pvUser2); + if (rc == VINF_CALLBACK_RETURN) + rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath); + else if (rc == VERR_CALLBACK_RETURN) + rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath); + else + rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc, pszPath); + } + + return rc; +} + + +static int rtDbgCfgCopyFileToCache(PRTDBGCFGINT pThis, char const *pszSrc, const char *pchCache, size_t cchCache, + const char *pszCacheSubDir, const char *pszUuidMappingSubDir, PRTPATHSPLIT pSplitFn) +{ + RT_NOREF_PV(pThis); RT_NOREF_PV(pszSrc); RT_NOREF_PV(pchCache); RT_NOREF_PV(cchCache); + RT_NOREF_PV(pszUuidMappingSubDir); RT_NOREF_PV(pSplitFn); + + if (!pszCacheSubDir || !*pszCacheSubDir) + return VINF_SUCCESS; + + /** @todo copy to cache */ + return VINF_SUCCESS; +} + + +static int rtDbgCfgTryOpenCache(PRTDBGCFGINT pThis, char *pszPath, size_t cchCachePath, + const char *pszCacheSubDir, const char *pszUuidMappingSubDir, + PCRTPATHSPLIT pSplitFn, const char *pszCacheSuffix, uint32_t fFlags, + PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + Assert(pszPath[cchCachePath] == '\0'); + + /* + * If the cache doesn't exist, fail right away. + */ + if (!pszCacheSubDir || !*pszCacheSubDir) + return VWRN_NOT_FOUND; + if (!RTDirExists(pszPath)) + { + rtDbgCfgLog2(pThis, "Cache does not exist: '%s'\n", pszPath); + return VWRN_NOT_FOUND; + } + + /* + * If we got a UUID mapping option, try it first as we can hopefully + * dispense with case folding. + */ + if (pszUuidMappingSubDir) + { + int rc = RTPathAppend(pszPath, RTPATH_MAX, pszUuidMappingSubDir); + if ( RT_SUCCESS(rc) + && RTFileExists(pszPath)) + { + /* Try resolve the path before presenting it to the client, a + 12 digit filename is of little worth. */ + char szBackup[RTPATH_MAX]; + strcpy(szBackup, pszPath); + rc = RTPathAbs(szBackup, pszPath, RTPATH_MAX); + if (RT_FAILURE(rc)) + strcpy(pszPath, szBackup); + + /* Do the callback thing. */ + rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath); + int rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN) + rtDbgCfgLog1(pThis, "Found '%s' via uuid mapping.\n", pszPath); + else if (rc2 == VERR_CALLBACK_RETURN) + rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath); + else + rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath); + if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN) + return rc2; + + /* Failed, restore the cache path. */ + memcpy(pszPath, szBackup, cchCachePath); + } + pszPath[cchCachePath] = '\0'; + } + + /* + * Carefully construct the cache path with case insensitivity in mind. + */ + bool const fCaseInsensitive = (fFlags & RTDBGCFG_O_CASE_INSENSITIVE) + && !rtDbgCfgIsFsCaseInsensitive(pszPath); + const char *pszFilename = pSplitFn->apszComps[pSplitFn->cComps - 1]; + + if (!rtDbgCfgIsDirAndFixCase(pszPath, pszFilename, fCaseInsensitive)) + return VWRN_NOT_FOUND; + + if (!rtDbgCfgIsDirAndFixCase(pszPath, pszCacheSubDir, fCaseInsensitive)) + return VWRN_NOT_FOUND; + + bool fProbablyCompressed = false; + if (!rtDbgCfgIsFileAndFixCase(pszPath, pszFilename, pszCacheSuffix, fCaseInsensitive, + RT_BOOL(fFlags & RTDBGCFG_O_MAYBE_COMPRESSED_MS), &fProbablyCompressed)) + return VWRN_NOT_FOUND; + if (fProbablyCompressed) + { + int rc = rtDbgCfgUnpackMsCacheFile(pThis, pszPath, pszFilename); + if (RT_FAILURE(rc)) + return VWRN_NOT_FOUND; + } + + rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath); + int rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN) + rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath); + else if (rc2 == VERR_CALLBACK_RETURN) + rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath); + else + rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath); + return rc2; +} + + +static int rtDbgCfgTryOpenList(PRTDBGCFGINT pThis, PRTLISTANCHOR pList, PRTPATHSPLIT pSplitFn, const char *pszCacheSubDir, + const char *pszUuidMappingSubDir, uint32_t fFlags, char *pszPath, + PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + int rcRet = VWRN_NOT_FOUND; + int rc2 = VINF_SUCCESS; + + const char *pchCache = NULL; + size_t cchCache = 0; + int rcCache = VWRN_NOT_FOUND; + + PRTDBGCFGSTR pCur; + RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry) + { + size_t cchDir = pCur->cch; + const char *pszDir = pCur->sz; + rtDbgCfgLog2(pThis, "Path list entry: '%s'\n", pszDir); + + /* This is very simplistic, but we have a unreasonably large path + buffer, so it'll work just fine and simplify things greatly below. */ + if (cchDir >= RTPATH_MAX - 8U) + { + if (RT_SUCCESS_NP(rcRet)) + rcRet = VERR_FILENAME_TOO_LONG; + continue; + } + + /* + * Process the path according to it's type. + */ + if (!RTStrNICmp(pszDir, RT_STR_TUPLE("srv*"))) + { + /* + * Symbol server. + */ + pszDir += sizeof("srv*") - 1; + cchDir -= sizeof("srv*") - 1; + bool fSearchCache = false; + const char *pszServer = (const char *)memchr(pszDir, '*', cchDir); + if (!pszServer) + pszServer = pszDir; + else if (pszServer == pszDir) + continue; + else + { + fSearchCache = true; + pchCache = pszDir; + cchCache = pszServer - pszDir; + pszServer++; + } + + /* We don't have any default cache directory, so skip if the cache is missing. */ + if (cchCache == 0) + continue; + + /* Search the cache first (if we haven't already done so). */ + if (fSearchCache) + { + memcpy(pszPath, pchCache, cchCache); + pszPath[cchCache] = '\0'; + RTPathChangeToUnixSlashes(pszPath, false); + + rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, cchCache, pszCacheSubDir, pszUuidMappingSubDir, + pSplitFn, NULL /*pszCacheSuffix*/, fFlags, pfnCallback, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN) + return rc2; + } + + /* Try downloading the file. */ + if (rcCache == VWRN_NOT_FOUND) + { + memcpy(pszPath, pchCache, cchCache); + pszPath[cchCache] = '\0'; + RTPathChangeToUnixSlashes(pszPath, false); + + rc2 = rtDbgCfgTryDownloadAndOpen(pThis, pszServer, pszPath, pszCacheSubDir, pszUuidMappingSubDir, + pSplitFn, NULL /*pszCacheSuffix*/, fFlags, pfnCallback, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN) + return rc2; + } + } + else if (!RTStrNICmp(pszDir, RT_STR_TUPLE("cache*"))) + { + /* + * Cache directory. + */ + pszDir += sizeof("cache*") - 1; + cchDir -= sizeof("cache*") - 1; + if (!cchDir) + continue; + pchCache = pszDir; + cchCache = cchDir; + + memcpy(pszPath, pchCache, cchCache); + pszPath[cchCache] = '\0'; + RTPathChangeToUnixSlashes(pszPath, false); + + rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, cchCache, pszCacheSubDir, pszUuidMappingSubDir, + pSplitFn, NULL /*pszCacheSuffix*/, fFlags, pfnCallback, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN) + return rc2; + } + else + { + /* + * Normal directory. Check for our own 'rec*' and 'norec*' prefix + * flags governing recursive searching. + */ + uint32_t fFlagsDir = fFlags; + if (!RTStrNICmp(pszDir, RT_STR_TUPLE("rec*"))) + { + pszDir += sizeof("rec*") - 1; + cchDir -= sizeof("rec*") - 1; + fFlagsDir |= RTDBGCFG_O_RECURSIVE; + } + else if (!RTStrNICmp(pszDir, RT_STR_TUPLE("norec*"))) + { + pszDir += sizeof("norec*") - 1; + cchDir -= sizeof("norec*") - 1; + fFlagsDir &= ~RTDBGCFG_O_RECURSIVE; + } + + /* Copy the path into the buffer and do the searching. */ + memcpy(pszPath, pszDir, cchDir); + pszPath[cchDir] = '\0'; + RTPathChangeToUnixSlashes(pszPath, false); + + rc2 = rtDbgCfgTryOpenDir(pThis, pszPath, pSplitFn, fFlagsDir, pfnCallback, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN) + { + if ( rc2 == VINF_CALLBACK_RETURN + && cchCache > 0) + rtDbgCfgCopyFileToCache(pThis, pszPath, pchCache, cchCache, + pszCacheSubDir, pszUuidMappingSubDir, pSplitFn); + return rc2; + } + } + + /* Propagate errors. */ + if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet)) + rcRet = rc2; + } + + return rcRet; +} + + +/** + * Common worker routine for Image and debug info opening. + * + * This will not search using for suffixes. + * + * @returns IPRT status code. + * @param hDbgCfg The debugging configuration handle. + * NIL_RTDBGCFG is accepted, but the result is + * that no paths will be searched beyond the + * given and the current directory. + * @param pszFilename The filename to search for. This may or may + * not include a full or partial path. + * @param pszCacheSubDir The cache subdirectory to look in. + * @param pszUuidMappingSubDir UUID mapping subdirectory to check, NULL if + * no mapping wanted. + * @param fFlags Flags and hints. + * @param pfnCallback The open callback routine. + * @param pvUser1 User parameter 1. + * @param pvUser2 User parameter 2. + */ +static int rtDbgCfgOpenWithSubDir(RTDBGCFG hDbgCfg, const char *pszFilename, const char *pszCacheSubDir, + const char *pszUuidMappingSubDir, uint32_t fFlags, + PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + int rcRet = VINF_SUCCESS; + int rc2; + + /* + * Do a little validating first. + */ + PRTDBGCFGINT pThis = hDbgCfg; + if (pThis != NIL_RTDBGCFG) + RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE); + else + pThis = NULL; + AssertPtrReturn(pszFilename, VERR_INVALID_POINTER); + AssertPtrReturn(pszCacheSubDir, VERR_INVALID_POINTER); + AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER); + AssertReturn(!(fFlags & ~RTDBGCFG_O_VALID_MASK), VERR_INVALID_FLAGS); + + /* + * Do some guessing as to the way we should parse the filename and whether + * it's case exact or not. + */ + bool fDosPath = RT_OPSYS_USES_DOS_PATHS(fFlags & RTDBGCFG_O_OPSYS_MASK) + || (fFlags & RTDBGCFG_O_CASE_INSENSITIVE) + || strchr(pszFilename, ':') != NULL + || strchr(pszFilename, '\\') != NULL; + if (fDosPath) + fFlags |= RTDBGCFG_O_CASE_INSENSITIVE; + + rtDbgCfgLog2(pThis, "Looking for '%s' w/ cache subdir '%s' and %#x flags...\n", pszFilename, pszCacheSubDir, fFlags); + + PRTPATHSPLIT pSplitFn; + rc2 = RTPathSplitA(pszFilename, &pSplitFn, fDosPath ? RTPATH_STR_F_STYLE_DOS : RTPATH_STR_F_STYLE_UNIX); + if (RT_FAILURE(rc2)) + return rc2; + AssertReturnStmt(pSplitFn->fProps & RTPATH_PROP_FILENAME, RTPathSplitFree(pSplitFn), VERR_IS_A_DIRECTORY); + + /* + * Try the stored file name first if it has a kind of absolute path. + */ + char szPath[RTPATH_MAX]; + if (RTPATH_PROP_HAS_ROOT_SPEC(pSplitFn->fProps)) + { + rc2 = RTPathSplitReassemble(pSplitFn, RTPATH_STR_F_STYLE_HOST, szPath, sizeof(szPath)); + if (RT_SUCCESS(rc2) && RTFileExists(szPath)) + { + RTPathChangeToUnixSlashes(szPath, false); + rtDbgCfgLog1(pThis, "Trying '%s'...\n", szPath); + rc2 = pfnCallback(pThis, szPath, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN) + rtDbgCfgLog1(pThis, "Found '%s'.\n", szPath); + else if (rc2 == VERR_CALLBACK_RETURN) + rtDbgCfgLog1(pThis, "Error opening '%s'.\n", szPath); + else + rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, szPath); + } + } + if ( rc2 != VINF_CALLBACK_RETURN + && rc2 != VERR_CALLBACK_RETURN) + { + /* + * Try the current directory (will take cover relative paths + * skipped above). + */ + rc2 = RTPathGetCurrent(szPath, sizeof(szPath)); + if (RT_FAILURE(rc2)) + strcpy(szPath, "."); + RTPathChangeToUnixSlashes(szPath, false); + + rc2 = rtDbgCfgTryOpenDir(pThis, szPath, pSplitFn, fFlags, pfnCallback, pvUser1, pvUser2); + if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet)) + rcRet = rc2; + + if ( rc2 != VINF_CALLBACK_RETURN + && rc2 != VERR_CALLBACK_RETURN + && pThis) + { + rc2 = RTCritSectRwEnterShared(&pThis->CritSect); + if (RT_SUCCESS(rc2)) + { + /* + * Run the applicable lists. + */ + rc2 = rtDbgCfgTryOpenList(pThis, &pThis->PathList, pSplitFn, pszCacheSubDir, + pszUuidMappingSubDir, fFlags, szPath, pfnCallback, pvUser1, pvUser2); + if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet)) + rcRet = rc2; + +#ifdef RT_OS_WINDOWS + if ( rc2 != VINF_CALLBACK_RETURN + && rc2 != VERR_CALLBACK_RETURN + && (fFlags & RTDBGCFG_O_EXECUTABLE_IMAGE) + && !(fFlags & RTDBGCFG_O_NO_SYSTEM_PATHS) + && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_SYSTEM_PATHS) ) + { + rc2 = rtDbgCfgTryOpenList(pThis, &pThis->NtExecutablePathList, pSplitFn, pszCacheSubDir, + pszUuidMappingSubDir, fFlags, szPath, pfnCallback, pvUser1, pvUser2); + if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet)) + rcRet = rc2; + } + + if ( rc2 != VINF_CALLBACK_RETURN + && rc2 != VERR_CALLBACK_RETURN + && !(fFlags & RTDBGCFG_O_NO_SYSTEM_PATHS) + && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_SYSTEM_PATHS) ) + { + rc2 = rtDbgCfgTryOpenList(pThis, &pThis->NtSymbolPathList, pSplitFn, pszCacheSubDir, + pszUuidMappingSubDir, fFlags, szPath, pfnCallback, pvUser1, pvUser2); + if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet)) + rcRet = rc2; + } +#endif + RTCritSectRwLeaveShared(&pThis->CritSect); + } + else if (RT_SUCCESS(rcRet)) + rcRet = rc2; + } + } + + RTPathSplitFree(pSplitFn); + if ( rc2 == VINF_CALLBACK_RETURN + || rc2 == VERR_CALLBACK_RETURN) + rcRet = rc2; + else if (RT_SUCCESS(rcRet)) + rcRet = VERR_NOT_FOUND; + return rcRet; +} + + +RTDECL(int) RTDbgCfgOpenEx(RTDBGCFG hDbgCfg, const char *pszFilename, const char *pszCacheSubDir, + const char *pszUuidMappingSubDir, uint32_t fFlags, + PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, pszCacheSubDir, pszUuidMappingSubDir, fFlags, + pfnCallback, pvUser1, pvUser2); +} + + + + +RTDECL(int) RTDbgCfgOpenPeImage(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp, + PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + char szSubDir[32]; + RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, cbImage); + return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL, + RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE + | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXECUTABLE_IMAGE, + pfnCallback, pvUser1, pvUser2); +} + + +RTDECL(int) RTDbgCfgOpenPdb70(RTDBGCFG hDbgCfg, const char *pszFilename, PCRTUUID pUuid, uint32_t uAge, + PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + char szSubDir[64]; + if (!pUuid) + szSubDir[0] = '\0'; + else + { + /* Stringify the UUID and remove the dashes. */ + int rc2 = RTUuidToStr(pUuid, szSubDir, sizeof(szSubDir)); + AssertRCReturn(rc2, rc2); + + char *pszSrc = szSubDir; + char *pszDst = szSubDir; + char ch; + while ((ch = *pszSrc++)) + if (ch != '-') + *pszDst++ = RT_C_TO_UPPER(ch); + + RTStrPrintf(pszDst, &szSubDir[sizeof(szSubDir)] - pszDst, "%X", uAge); + } + + return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL, + RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE + | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXT_DEBUG_FILE, + pfnCallback, pvUser1, pvUser2); +} + + +RTDECL(int) RTDbgCfgOpenPdb20(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp, uint32_t uAge, + PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + RT_NOREF_PV(cbImage); + /** @todo test this! */ + char szSubDir[32]; + RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, uAge); + return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL, + RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE + | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXT_DEBUG_FILE, + pfnCallback, pvUser1, pvUser2); +} + + +RTDECL(int) RTDbgCfgOpenDbg(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp, + PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + char szSubDir[32]; + RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, cbImage); + return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL, + RT_OPSYS_WINDOWS /* approx */ | RTDBGCFG_O_SYMSRV | RTDBGCFG_O_CASE_INSENSITIVE + | RTDBGCFG_O_MAYBE_COMPRESSED_MS | RTDBGCFG_O_EXT_DEBUG_FILE, + pfnCallback, pvUser1, pvUser2); +} + + +RTDECL(int) RTDbgCfgOpenDwo(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t uCrc32, + PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + char szSubDir[32]; + RTStrPrintf(szSubDir, sizeof(szSubDir), "%08x", uCrc32); + return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, NULL, + RT_OPSYS_UNKNOWN | RTDBGCFG_O_EXT_DEBUG_FILE, + pfnCallback, pvUser1, pvUser2); +} + + +RTDECL(int) RTDbgCfgOpenDwoBuildId(RTDBGCFG hDbgCfg, const char *pszFilename, const uint8_t *pbBuildId, + size_t cbBuildId, PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + char *pszSubDir = NULL; + int rc = RTStrAPrintf(&pszSubDir, "%#.*Rhxs", cbBuildId, pbBuildId); + if (RT_SUCCESS(rc)) + { + rc = rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, pszSubDir, NULL, + RTDBGCFG_O_DEBUGINFOD | RT_OPSYS_UNKNOWN | RTDBGCFG_O_EXT_DEBUG_FILE, + pfnCallback, pvUser1, pvUser2); + RTStrFree(pszSubDir); + } + + return rc; +} + + + +/* + * + * D a r w i n . d S Y M b u n d l e s + * D a r w i n . d S Y M b u n d l e s + * D a r w i n . d S Y M b u n d l e s + * + */ + +/** + * Very similar to rtDbgCfgTryOpenDir. + */ +static int rtDbgCfgTryOpenDsymBundleInDir(PRTDBGCFGINT pThis, char *pszPath, PRTPATHSPLIT pSplitFn, + const char * const *papszSuffixes, uint32_t fFlags, + PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + int rcRet = VWRN_NOT_FOUND; + int rc2; + + /* If the directory doesn't exist, just quit immediately. + Note! Our case insensitivity doesn't extend to the search dirs themselfs, + only to the bits under neath them. */ + if (!RTDirExists(pszPath)) + { + rtDbgCfgLog2(pThis, "Dir does not exist: '%s'\n", pszPath); + return rcRet; + } + + /* Figure out whether we have to do a case sensitive search or not. + Note! As a simplification, we don't ask for case settings in each + directory under the user specified path, we assume the file + systems that mounted there have compatible settings. Faster + that way. */ + bool const fCaseInsensitive = (fFlags & RTDBGCFG_O_CASE_INSENSITIVE) + && !rtDbgCfgIsFsCaseInsensitive(pszPath); + + size_t const cchPath = strlen(pszPath); + + /* + * Look for the file with less and less of the original path given. + * Also try out typical bundle extension variations. + */ + const char *pszName = pSplitFn->apszComps[pSplitFn->cComps - 1]; + for (unsigned i = RTPATH_PROP_HAS_ROOT_SPEC(pSplitFn->fProps); i < pSplitFn->cComps; i++) + { + pszPath[cchPath] = '\0'; + + rc2 = VINF_SUCCESS; + for (unsigned j = i; j < pSplitFn->cComps - 1U && RT_SUCCESS(rc2); j++) + if (!rtDbgCfgIsDirAndFixCase(pszPath, pSplitFn->apszComps[i], fCaseInsensitive)) + rc2 = VERR_FILE_NOT_FOUND; + if (RT_SUCCESS(rc2)) + { + for (uint32_t iSuffix = 0; papszSuffixes[iSuffix]; iSuffix++) + { + if ( !rtDbgCfgIsDirAndFixCase2(pszPath, pszName, papszSuffixes[iSuffix], fCaseInsensitive) + && !rtDbgCfgIsDirAndFixCase(pszPath, "Contents", fCaseInsensitive) + && !rtDbgCfgIsDirAndFixCase(pszPath, "Resources", fCaseInsensitive) + && !rtDbgCfgIsDirAndFixCase(pszPath, "DWARF", fCaseInsensitive)) + { + if (rtDbgCfgIsFileAndFixCase(pszPath, pszName, NULL /*pszSuffix*/, fCaseInsensitive, false, NULL)) + { + rtDbgCfgLog1(pThis, "Trying '%s'...\n", pszPath); + rc2 = pfnCallback(pThis, pszPath, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN) + { + if (rc2 == VINF_CALLBACK_RETURN) + rtDbgCfgLog1(pThis, "Found '%s'.\n", pszPath); + else + rtDbgCfgLog1(pThis, "Error opening '%s'.\n", pszPath); + return rc2; + } + rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, pszPath); + if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet)) + rcRet = rc2; + } + } + } + } + rc2 = VERR_FILE_NOT_FOUND; + } + + /* + * Do a recursive search if requested. + */ + if ( (fFlags & RTDBGCFG_O_RECURSIVE) + && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_RECURSIV_SEARCH) ) + { + /** @todo Recursive searching will be done later. */ + } + + return rcRet; +} + + +/** + * Very similar to rtDbgCfgTryOpenList. + */ +static int rtDbgCfgTryOpenBundleInList(PRTDBGCFGINT pThis, PRTLISTANCHOR pList, PRTPATHSPLIT pSplitFn, + const char * const *papszSuffixes, const char *pszCacheSubDir, + const char *pszCacheSuffix, const char *pszUuidMappingSubDir, + uint32_t fFlags, char *pszPath, + PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + int rcRet = VWRN_NOT_FOUND; + int rc2; + + const char *pchCache = NULL; + size_t cchCache = 0; + int rcCache = VWRN_NOT_FOUND; + + PRTDBGCFGSTR pCur; + RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry) + { + size_t cchDir = pCur->cch; + const char *pszDir = pCur->sz; + rtDbgCfgLog2(pThis, "Path list entry: '%s'\n", pszDir); + + /* This is very simplistic, but we have a unreasonably large path + buffer, so it'll work just fine and simplify things greatly below. */ + if (cchDir >= RTPATH_MAX - 8U) + { + if (RT_SUCCESS_NP(rcRet)) + rcRet = VERR_FILENAME_TOO_LONG; + continue; + } + + /* + * Process the path according to it's type. + */ + rc2 = VINF_SUCCESS; + if (!RTStrNICmp(pszDir, RT_STR_TUPLE("srv*"))) + { + /* + * Symbol server. + */ + pszDir += sizeof("srv*") - 1; + cchDir -= sizeof("srv*") - 1; + bool fSearchCache = false; + const char *pszServer = (const char *)memchr(pszDir, '*', cchDir); + if (!pszServer) + pszServer = pszDir; + else if (pszServer == pszDir) + continue; + else + { + fSearchCache = true; + pchCache = pszDir; + cchCache = pszServer - pszDir; + pszServer++; + } + + /* We don't have any default cache directory, so skip if the cache is missing. */ + if (cchCache == 0) + continue; + + /* Search the cache first (if we haven't already done so). */ + if (fSearchCache) + { + memcpy(pszPath, pchCache, cchCache); + pszPath[cchCache] = '\0'; + RTPathChangeToUnixSlashes(pszPath, false); + + rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, cchCache, pszCacheSubDir, pszUuidMappingSubDir, + pSplitFn, pszCacheSuffix, fFlags, pfnCallback, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN) + return rc2; + } + + /* Try downloading the file. */ + if (rcCache == VWRN_NOT_FOUND) + { + memcpy(pszPath, pchCache, cchCache); + pszPath[cchCache] = '\0'; + RTPathChangeToUnixSlashes(pszPath, false); + + rc2 = rtDbgCfgTryDownloadAndOpen(pThis, pszServer, pszPath, pszCacheSubDir, pszUuidMappingSubDir, + pSplitFn, pszCacheSuffix, fFlags, pfnCallback, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN) + return rc2; + } + } + else if (!RTStrNICmp(pszDir, RT_STR_TUPLE("cache*"))) + { + /* + * Cache directory. + */ + pszDir += sizeof("cache*") - 1; + cchDir -= sizeof("cache*") - 1; + if (!cchDir) + continue; + pchCache = pszDir; + cchCache = cchDir; + + memcpy(pszPath, pchCache, cchCache); + pszPath[cchCache] = '\0'; + RTPathChangeToUnixSlashes(pszPath, false); + + rcCache = rc2 = rtDbgCfgTryOpenCache(pThis, pszPath, cchCache, pszCacheSubDir, pszUuidMappingSubDir, + pSplitFn, pszCacheSuffix, fFlags, pfnCallback, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN) + return rc2; + } + else + { + /* + * Normal directory. Check for our own 'rec*' and 'norec*' prefix + * flags governing recursive searching. + */ + uint32_t fFlagsDir = fFlags; + if (!RTStrNICmp(pszDir, RT_STR_TUPLE("rec*"))) + { + pszDir += sizeof("rec*") - 1; + cchDir -= sizeof("rec*") - 1; + fFlagsDir |= RTDBGCFG_O_RECURSIVE; + } + else if (!RTStrNICmp(pszDir, RT_STR_TUPLE("norec*"))) + { + pszDir += sizeof("norec*") - 1; + cchDir -= sizeof("norec*") - 1; + fFlagsDir &= ~RTDBGCFG_O_RECURSIVE; + } + + /* Copy the path into the buffer and do the searching. */ + memcpy(pszPath, pszDir, cchDir); + pszPath[cchDir] = '\0'; + RTPathChangeToUnixSlashes(pszPath, false); + + rc2 = rtDbgCfgTryOpenDsymBundleInDir(pThis, pszPath, pSplitFn, papszSuffixes, fFlagsDir, + pfnCallback, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN) + { + if ( rc2 == VINF_CALLBACK_RETURN + && cchCache > 0) + rtDbgCfgCopyFileToCache(pThis, pszPath, pchCache, cchCache, + pszCacheSubDir, pszUuidMappingSubDir, pSplitFn); + return rc2; + } + } + + /* Propagate errors. */ + if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet)) + rcRet = rc2; + } + + return rcRet; +} + + +/** + * Creating a UUID mapping subdirectory path for use in caches. + * + * @returns IPRT status code. + * @param pszSubDir The output buffer. + * @param cbSubDir The size of the output buffer. (Top dir length + + * slash + UUID string len + extra dash.) + * @param pszTopDir The top level cache directory name. No slashes + * or other directory separators, please. + * @param pUuid The UUID. + */ +static int rtDbgCfgConstructUuidMappingSubDir(char *pszSubDir, size_t cbSubDir, const char *pszTopDir, PCRTUUID pUuid) +{ + Assert(!strpbrk(pszTopDir, ":/\\")); + + size_t cchTopDir = strlen(pszTopDir); + if (cchTopDir + 1 + 1 + RTUUID_STR_LENGTH + 1 > cbSubDir) + return VERR_BUFFER_OVERFLOW; + memcpy(pszSubDir, pszTopDir, cchTopDir); + + pszSubDir += cchTopDir; + *pszSubDir++ = RTPATH_SLASH; + cbSubDir -= cchTopDir + 1; + + /* ed5a8336-35c2-4892-9122-21d5572924a3 -> ED5A/8336/35C2/4892/9122/21D5572924A3 */ + int rc = RTUuidToStr(pUuid, pszSubDir + 1, cbSubDir - 1); AssertRCReturn(rc, rc); + RTStrToUpper(pszSubDir + 1); + memmove(pszSubDir, pszSubDir + 1, 4); + pszSubDir += 4; + *pszSubDir = RTPATH_SLASH; + pszSubDir += 5; + *pszSubDir = RTPATH_SLASH; + pszSubDir += 5; + *pszSubDir = RTPATH_SLASH; + pszSubDir += 5; + *pszSubDir = RTPATH_SLASH; + pszSubDir += 5; + *pszSubDir = RTPATH_SLASH; + + return VINF_SUCCESS; +} + + +static int rtDbgCfgOpenBundleFile(RTDBGCFG hDbgCfg, const char *pszImage, const char * const *papszSuffixes, + const char *pszBundleSubDir, PCRTUUID pUuid, const char *pszUuidMapDirName, + const char *pszCacheSuffix, bool fOpenImage, + PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + /* + * Bundles are directories, means we can forget about sharing code much + * with the other RTDbgCfgOpenXXX methods. Thus we're duplicating a lot of + * code from rtDbgCfgOpenWithSubDir with .dSYM/.kext/.dylib/.app/.* related + * adjustments, so, a bug found here or there probably means the other + * version needs updating. + */ + int rcRet = VINF_SUCCESS; + int rc2; + + /* + * Do a little validating first. + */ + PRTDBGCFGINT pThis = hDbgCfg; + if (pThis != NIL_RTDBGCFG) + RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE); + else + pThis = NULL; + AssertPtrReturn(pszImage, VERR_INVALID_POINTER); + AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER); + + /* + * Set up rtDbgCfgOpenWithSubDir and uuid map parameters. + */ + uint32_t fFlags = RTDBGCFG_O_EXT_DEBUG_FILE | RT_OPSYS_DARWIN; + const char *pszCacheSubDir = NULL; + char szCacheSubDir[RTUUID_STR_LENGTH]; + const char *pszUuidMappingSubDir = NULL; + char szUuidMappingSubDir[RTUUID_STR_LENGTH + 16]; + if (pUuid) + { + /* Since Mac debuggers uses UUID mappings, we just the slashing default + UUID string representation instead of stripping dashes like for PDB. */ + RTUuidToStr(pUuid, szCacheSubDir, sizeof(szCacheSubDir)); + pszCacheSubDir = szCacheSubDir; + + rc2 = rtDbgCfgConstructUuidMappingSubDir(szUuidMappingSubDir, sizeof(szUuidMappingSubDir), pszUuidMapDirName, pUuid); + AssertRCReturn(rc2, rc2); + pszUuidMappingSubDir = szUuidMappingSubDir; + } + + /* + * Do some guessing as to the way we should parse the filename and whether + * it's case exact or not. + */ + bool fDosPath = strchr(pszImage, ':') != NULL + || strchr(pszImage, '\\') != NULL + || RT_OPSYS_USES_DOS_PATHS(fFlags & RTDBGCFG_O_OPSYS_MASK) + || (fFlags & RTDBGCFG_O_CASE_INSENSITIVE); + if (fDosPath) + fFlags |= RTDBGCFG_O_CASE_INSENSITIVE; + + rtDbgCfgLog2(pThis, "Looking for '%s' with %#x flags...\n", pszImage, fFlags); + + PRTPATHSPLIT pSplitFn; + rc2 = RTPathSplitA(pszImage, &pSplitFn, fDosPath ? RTPATH_STR_F_STYLE_DOS : RTPATH_STR_F_STYLE_UNIX); + if (RT_FAILURE(rc2)) + return rc2; + AssertReturnStmt(pSplitFn->fProps & RTPATH_PROP_FILENAME, RTPathSplitFree(pSplitFn), VERR_IS_A_DIRECTORY); + + /* + * Try the image directory first. + */ + char szPath[RTPATH_MAX]; + if (pSplitFn->cComps > 0) + { + rc2 = RTPathSplitReassemble(pSplitFn, RTPATH_STR_F_STYLE_HOST, szPath, sizeof(szPath)); + if (fOpenImage && RT_SUCCESS(rc2)) + { + rc2 = RTStrCat(szPath, sizeof(szPath), papszSuffixes[0]); + if (RT_SUCCESS(rc2)) + rc2 = RTStrCat(szPath, sizeof(szPath), pszBundleSubDir); + if (RT_SUCCESS(rc2)) + rc2 = RTPathAppend(szPath, sizeof(szPath), pSplitFn->apszComps[pSplitFn->cComps - 1]); + } + if (RT_SUCCESS(rc2) && RTPathExists(szPath)) + { + RTPathChangeToUnixSlashes(szPath, false); + rtDbgCfgLog1(pThis, "Trying '%s'...\n", szPath); + rc2 = pfnCallback(pThis, szPath, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN) + rtDbgCfgLog1(pThis, "Found '%s'.\n", szPath); + else if (rc2 == VERR_CALLBACK_RETURN) + rtDbgCfgLog1(pThis, "Error opening '%s'.\n", szPath); + else + rtDbgCfgLog1(pThis, "Error %Rrc opening '%s'.\n", rc2, szPath); + } + } + if ( rc2 != VINF_CALLBACK_RETURN + && rc2 != VERR_CALLBACK_RETURN) + { + /* + * Try the current directory (will take cover relative paths + * skipped above). + */ + rc2 = RTPathGetCurrent(szPath, sizeof(szPath)); + if (RT_FAILURE(rc2)) + strcpy(szPath, "."); + RTPathChangeToUnixSlashes(szPath, false); + + rc2 = rtDbgCfgTryOpenDsymBundleInDir(pThis, szPath, pSplitFn, g_apszDSymBundleSuffixes, + fFlags, pfnCallback, pvUser1, pvUser2); + if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet)) + rcRet = rc2; + + if ( rc2 != VINF_CALLBACK_RETURN + && rc2 != VERR_CALLBACK_RETURN + && pThis) + { + rc2 = RTCritSectRwEnterShared(&pThis->CritSect); + if (RT_SUCCESS(rc2)) + { + /* + * Run the applicable lists. + */ + rc2 = rtDbgCfgTryOpenBundleInList(pThis, &pThis->PathList, pSplitFn, g_apszDSymBundleSuffixes, + pszCacheSubDir, pszCacheSuffix, + pszUuidMappingSubDir, fFlags, szPath, + pfnCallback, pvUser1, pvUser2); + if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet)) + rcRet = rc2; + + RTCritSectRwLeaveShared(&pThis->CritSect); + } + else if (RT_SUCCESS(rcRet)) + rcRet = rc2; + } + } + + RTPathSplitFree(pSplitFn); + if ( rc2 == VINF_CALLBACK_RETURN + || rc2 == VERR_CALLBACK_RETURN) + rcRet = rc2; + else if (RT_SUCCESS(rcRet)) + rcRet = VERR_NOT_FOUND; + return rcRet; +} + + +RTDECL(int) RTDbgCfgOpenDsymBundle(RTDBGCFG hDbgCfg, const char *pszImage, PCRTUUID pUuid, + PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + return rtDbgCfgOpenBundleFile(hDbgCfg, pszImage, g_apszDSymBundleSuffixes, + "Contents" RTPATH_SLASH_STR "Resources" RTPATH_SLASH_STR "DWARF", + pUuid, RTDBG_CACHE_UUID_MAP_DIR_DSYMS, RTDBG_CACHE_DSYM_FILE_SUFFIX, false /* fOpenImage */, + pfnCallback, pvUser1, pvUser2); +} + + +RTDECL(int) RTDbgCfgOpenMachOImage(RTDBGCFG hDbgCfg, const char *pszImage, PCRTUUID pUuid, + PFNRTDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + return rtDbgCfgOpenBundleFile(hDbgCfg, pszImage, g_apszBundleSuffixes, + "Contents" RTPATH_SLASH_STR "MacOS", + pUuid, RTDBG_CACHE_UUID_MAP_DIR_IMAGES, NULL /*pszCacheSuffix*/, true /* fOpenImage */, + pfnCallback, pvUser1, pvUser2); +} + + + +RTDECL(int) RTDbgCfgSetLogCallback(RTDBGCFG hDbgCfg, PFNRTDBGCFGLOG pfnCallback, void *pvUser) +{ + PRTDBGCFGINT pThis = hDbgCfg; + RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE); + AssertPtrNullReturn(pfnCallback, VERR_INVALID_POINTER); + + int rc = RTCritSectRwEnterExcl(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + if ( pThis->pfnLogCallback == NULL + || pfnCallback == NULL + || pfnCallback == pThis->pfnLogCallback) + { + pThis->pfnLogCallback = NULL; + pThis->pvLogUser = NULL; + ASMCompilerBarrier(); /* paranoia */ + pThis->pvLogUser = pvUser; + pThis->pfnLogCallback = pfnCallback; + rc = VINF_SUCCESS; + } + else + rc = VERR_ACCESS_DENIED; + RTCritSectRwLeaveExcl(&pThis->CritSect); + } + + return rc; +} + + +/** + * Frees a string list. + * + * @param pList The list to free. + */ +static void rtDbgCfgFreeStrList(PRTLISTANCHOR pList) +{ + PRTDBGCFGSTR pCur; + PRTDBGCFGSTR pNext; + RTListForEachSafe(pList, pCur, pNext, RTDBGCFGSTR, ListEntry) + { + RTListNodeRemove(&pCur->ListEntry); + RTMemFree(pCur); + } +} + + +/** + * Make changes to a string list, given a semicolon separated input string. + * + * @returns VINF_SUCCESS, VERR_FILENAME_TOO_LONG, VERR_NO_MEMORY + * @param pThis The config instance. + * @param enmOp The change operation. + * @param pszValue The input strings separated by semicolon. + * @param fPaths Indicates that this is a path list and that we + * should look for srv and cache prefixes. + * @param pList The string list anchor. + */ +static int rtDbgCfgChangeStringList(PRTDBGCFGINT pThis, RTDBGCFGOP enmOp, const char *pszValue, bool fPaths, + PRTLISTANCHOR pList) +{ + RT_NOREF_PV(pThis); RT_NOREF_PV(fPaths); + + if (enmOp == RTDBGCFGOP_SET) + rtDbgCfgFreeStrList(pList); + + PRTLISTNODE pPrependTo = pList; + while (*pszValue) + { + /* Skip separators. */ + while (*pszValue == ';') + pszValue++; + if (!*pszValue) + break; + + /* Find the end of this path. */ + const char *pchPath = pszValue++; + char ch; + while ((ch = *pszValue) && ch != ';') + pszValue++; + size_t cchPath = pszValue - pchPath; + if (cchPath >= UINT16_MAX) + return VERR_FILENAME_TOO_LONG; + + if (enmOp == RTDBGCFGOP_REMOVE) + { + /* + * Remove all occurences. + */ + PRTDBGCFGSTR pCur; + PRTDBGCFGSTR pNext; + RTListForEachSafe(pList, pCur, pNext, RTDBGCFGSTR, ListEntry) + { + if ( pCur->cch == cchPath + && !memcmp(pCur->sz, pchPath, cchPath)) + { + RTListNodeRemove(&pCur->ListEntry); + RTMemFree(pCur); + } + } + } + else + { + /* + * We're adding a new one. + */ + PRTDBGCFGSTR pNew = (PRTDBGCFGSTR)RTMemAlloc(RT_UOFFSETOF_DYN(RTDBGCFGSTR, sz[cchPath + 1])); + if (!pNew) + return VERR_NO_MEMORY; + pNew->cch = (uint16_t)cchPath; + pNew->fFlags = 0; + memcpy(pNew->sz, pchPath, cchPath); + pNew->sz[cchPath] = '\0'; + + if (enmOp == RTDBGCFGOP_PREPEND) + { + RTListNodeInsertAfter(pPrependTo, &pNew->ListEntry); + pPrependTo = &pNew->ListEntry; + } + else + RTListAppend(pList, &pNew->ListEntry); + } + } + + return VINF_SUCCESS; +} + + +/** + * Make changes to a 64-bit value + * + * @returns VINF_SUCCESS, VERR_DBG_CFG_INVALID_VALUE. + * @param pThis The config instance. + * @param enmOp The change operation. + * @param pszValue The input value. + * @param paMnemonics The mnemonics map for this value. + * @param puValue The value to change. + */ +static int rtDbgCfgChangeStringU64(PRTDBGCFGINT pThis, RTDBGCFGOP enmOp, const char *pszValue, + PCRTDBGCFGU64MNEMONIC paMnemonics, uint64_t *puValue) +{ + RT_NOREF_PV(pThis); + + uint64_t uNew = enmOp == RTDBGCFGOP_SET ? 0 : *puValue; + char ch; + while ((ch = *pszValue)) + { + /* skip whitespace and separators */ + while (RT_C_IS_SPACE(ch) || RT_C_IS_CNTRL(ch) || ch == ';' || ch == ':') + ch = *++pszValue; + if (!ch) + break; + + if (RT_C_IS_DIGIT(ch)) + { + uint64_t uTmp; + int rc = RTStrToUInt64Ex(pszValue, (char **)&pszValue, 0, &uTmp); + if (RT_FAILURE(rc) || rc == VWRN_NUMBER_TOO_BIG) + return VERR_DBG_CFG_INVALID_VALUE; + + if (enmOp != RTDBGCFGOP_REMOVE) + uNew |= uTmp; + else + uNew &= ~uTmp; + } + else + { + /* A mnemonic, find the end of it. */ + const char *pszMnemonic = pszValue - 1; + do + ch = *++pszValue; + while (ch && !RT_C_IS_SPACE(ch) && !RT_C_IS_CNTRL(ch) && ch != ';' && ch != ':'); + size_t cchMnemonic = pszValue - pszMnemonic; + + /* Look it up in the map and apply it. */ + unsigned i = 0; + while (paMnemonics[i].pszMnemonic) + { + if ( cchMnemonic == paMnemonics[i].cchMnemonic + && !memcmp(pszMnemonic, paMnemonics[i].pszMnemonic, cchMnemonic)) + { + if (paMnemonics[i].fSet ? enmOp != RTDBGCFGOP_REMOVE : enmOp == RTDBGCFGOP_REMOVE) + uNew |= paMnemonics[i].fFlags; + else + uNew &= ~paMnemonics[i].fFlags; + break; + } + i++; + } + + if (!paMnemonics[i].pszMnemonic) + return VERR_DBG_CFG_INVALID_VALUE; + } + } + + *puValue = uNew; + return VINF_SUCCESS; +} + + +RTDECL(int) RTDbgCfgChangeString(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, RTDBGCFGOP enmOp, const char *pszValue) +{ + PRTDBGCFGINT pThis = hDbgCfg; + RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE); + AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER); + AssertReturn(enmOp > RTDBGCFGOP_INVALID && enmOp < RTDBGCFGOP_END, VERR_INVALID_PARAMETER); + if (!pszValue) + pszValue = ""; + else + AssertPtrReturn(pszValue, VERR_INVALID_POINTER); + + int rc = RTCritSectRwEnterExcl(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + switch (enmProp) + { + case RTDBGCFGPROP_FLAGS: + rc = rtDbgCfgChangeStringU64(pThis, enmOp, pszValue, g_aDbgCfgFlags, &pThis->fFlags); + break; + case RTDBGCFGPROP_PATH: + rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, true, &pThis->PathList); + break; + case RTDBGCFGPROP_SUFFIXES: + rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, false, &pThis->SuffixList); + break; + case RTDBGCFGPROP_SRC_PATH: + rc = rtDbgCfgChangeStringList(pThis, enmOp, pszValue, true, &pThis->SrcPathList); + break; + default: + AssertFailed(); + rc = VERR_INTERNAL_ERROR_3; + } + + RTCritSectRwLeaveExcl(&pThis->CritSect); + } + + return rc; +} + + +RTDECL(int) RTDbgCfgChangeUInt(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, RTDBGCFGOP enmOp, uint64_t uValue) +{ + PRTDBGCFGINT pThis = hDbgCfg; + RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE); + AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER); + AssertReturn(enmOp > RTDBGCFGOP_INVALID && enmOp < RTDBGCFGOP_END, VERR_INVALID_PARAMETER); + + int rc = RTCritSectRwEnterExcl(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + uint64_t *puValue = NULL; + switch (enmProp) + { + case RTDBGCFGPROP_FLAGS: + puValue = &pThis->fFlags; + break; + default: + rc = VERR_DBG_CFG_NOT_UINT_PROP; + } + if (RT_SUCCESS(rc)) + { + switch (enmOp) + { + case RTDBGCFGOP_SET: + *puValue = uValue; + break; + case RTDBGCFGOP_APPEND: + case RTDBGCFGOP_PREPEND: + *puValue |= uValue; + break; + case RTDBGCFGOP_REMOVE: + *puValue &= ~uValue; + break; + default: + AssertFailed(); + rc = VERR_INTERNAL_ERROR_2; + } + } + + RTCritSectRwLeaveExcl(&pThis->CritSect); + } + + return rc; +} + + +/** + * Querys a string list as a single string (semicolon separators). + * + * @returns VINF_SUCCESS, VERR_BUFFER_OVERFLOW. + * @param hDbgCfg The config instance handle. + * @param pList The string list anchor. + * @param pszValue The output buffer. + * @param cbValue The size of the output buffer. + */ +static int rtDbgCfgQueryStringList(RTDBGCFG hDbgCfg, PRTLISTANCHOR pList, + char *pszValue, size_t cbValue) +{ + RT_NOREF_PV(hDbgCfg); + + /* + * Check the length first. + */ + size_t cbReq = 1; + PRTDBGCFGSTR pCur; + RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry) + cbReq += pCur->cch + 1; + if (cbReq > cbValue) + return VERR_BUFFER_OVERFLOW; + + /* + * Construct the string list in the buffer. + */ + char *psz = pszValue; + RTListForEach(pList, pCur, RTDBGCFGSTR, ListEntry) + { + if (psz != pszValue) + *psz++ = ';'; + memcpy(psz, pCur->sz, pCur->cch); + psz += pCur->cch; + } + *psz = '\0'; + + return VINF_SUCCESS; +} + + +/** + * Querys the string value of a 64-bit unsigned int. + * + * @returns VINF_SUCCESS, VERR_BUFFER_OVERFLOW. + * @param hDbgCfg The config instance handle. + * @param uValue The value to query. + * @param paMnemonics The mnemonics map for this value. + * @param pszValue The output buffer. + * @param cbValue The size of the output buffer. + */ +static int rtDbgCfgQueryStringU64(RTDBGCFG hDbgCfg, uint64_t uValue, PCRTDBGCFGU64MNEMONIC paMnemonics, + char *pszValue, size_t cbValue) +{ + RT_NOREF_PV(hDbgCfg); + + /* + * If no mnemonics, just return the hex value. + */ + if (!paMnemonics || paMnemonics[0].pszMnemonic) + { + char szTmp[64]; + size_t cch = RTStrPrintf(szTmp, sizeof(szTmp), "%#x", uValue); + if (cch + 1 > cbValue) + return VERR_BUFFER_OVERFLOW; + memcpy(pszValue, szTmp, cbValue); + return VINF_SUCCESS; + } + + /* + * Check that there is sufficient buffer space first. + */ + size_t cbReq = 1; + for (unsigned i = 0; paMnemonics[i].pszMnemonic; i++) + if ( paMnemonics[i].fSet + ? (paMnemonics[i].fFlags & uValue) + : !(paMnemonics[i].fFlags & uValue)) + cbReq += (cbReq != 1) + paMnemonics[i].cchMnemonic; + if (cbReq > cbValue) + return VERR_BUFFER_OVERFLOW; + + /* + * Construct the string. + */ + char *psz = pszValue; + for (unsigned i = 0; paMnemonics[i].pszMnemonic; i++) + if ( paMnemonics[i].fSet + ? (paMnemonics[i].fFlags & uValue) + : !(paMnemonics[i].fFlags & uValue)) + { + if (psz != pszValue) + *psz++ = ' '; + memcpy(psz, paMnemonics[i].pszMnemonic, paMnemonics[i].cchMnemonic); + psz += paMnemonics[i].cchMnemonic; + } + *psz = '\0'; + return VINF_SUCCESS; +} + + +RTDECL(int) RTDbgCfgQueryString(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, char *pszValue, size_t cbValue) +{ + PRTDBGCFGINT pThis = hDbgCfg; + RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE); + AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER); + AssertPtrReturn(pszValue, VERR_INVALID_POINTER); + + int rc = RTCritSectRwEnterShared(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + switch (enmProp) + { + case RTDBGCFGPROP_FLAGS: + rc = rtDbgCfgQueryStringU64(pThis, pThis->fFlags, g_aDbgCfgFlags, pszValue, cbValue); + break; + case RTDBGCFGPROP_PATH: + rc = rtDbgCfgQueryStringList(pThis, &pThis->PathList, pszValue, cbValue); + break; + case RTDBGCFGPROP_SUFFIXES: + rc = rtDbgCfgQueryStringList(pThis, &pThis->SuffixList, pszValue, cbValue); + break; + case RTDBGCFGPROP_SRC_PATH: + rc = rtDbgCfgQueryStringList(pThis, &pThis->SrcPathList, pszValue, cbValue); + break; + default: + AssertFailed(); + rc = VERR_INTERNAL_ERROR_3; + } + + RTCritSectRwLeaveShared(&pThis->CritSect); + } + + return rc; +} + + +RTDECL(int) RTDbgCfgQueryUInt(RTDBGCFG hDbgCfg, RTDBGCFGPROP enmProp, uint64_t *puValue) +{ + PRTDBGCFGINT pThis = hDbgCfg; + RTDBGCFG_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE); + AssertReturn(enmProp > RTDBGCFGPROP_INVALID && enmProp < RTDBGCFGPROP_END, VERR_INVALID_PARAMETER); + AssertPtrReturn(puValue, VERR_INVALID_POINTER); + + int rc = RTCritSectRwEnterShared(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + switch (enmProp) + { + case RTDBGCFGPROP_FLAGS: + *puValue = pThis->fFlags; + break; + default: + rc = VERR_DBG_CFG_NOT_UINT_PROP; + } + + RTCritSectRwLeaveShared(&pThis->CritSect); + } + + return rc; +} + +RTDECL(uint32_t) RTDbgCfgRetain(RTDBGCFG hDbgCfg) +{ + PRTDBGCFGINT pThis = hDbgCfg; + RTDBGCFG_VALID_RETURN_RC(pThis, UINT32_MAX); + + uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs); + Assert(cRefs < UINT32_MAX / 2); + return cRefs; +} + + +RTDECL(uint32_t) RTDbgCfgRelease(RTDBGCFG hDbgCfg) +{ + if (hDbgCfg == NIL_RTDBGCFG) + return 0; + + PRTDBGCFGINT pThis = hDbgCfg; + RTDBGCFG_VALID_RETURN_RC(pThis, UINT32_MAX); + + uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs); + if (!cRefs) + { + /* + * Last reference - free all memory. + */ + ASMAtomicWriteU32(&pThis->u32Magic, ~RTDBGCFG_MAGIC); + rtDbgCfgFreeStrList(&pThis->PathList); + rtDbgCfgFreeStrList(&pThis->SuffixList); + rtDbgCfgFreeStrList(&pThis->SrcPathList); +#ifdef RT_OS_WINDOWS + rtDbgCfgFreeStrList(&pThis->NtSymbolPathList); + rtDbgCfgFreeStrList(&pThis->NtExecutablePathList); + rtDbgCfgFreeStrList(&pThis->NtSourcePath); +#endif + RTCritSectRwDelete(&pThis->CritSect); + RTMemFree(pThis); + } + else + Assert(cRefs < UINT32_MAX / 2); + return cRefs; +} + + +RTDECL(int) RTDbgCfgCreate(PRTDBGCFG phDbgCfg, const char *pszEnvVarPrefix, bool fNativePaths) +{ + /* + * Validate input. + */ + AssertPtrReturn(phDbgCfg, VERR_INVALID_POINTER); + if (pszEnvVarPrefix) + { + AssertPtrReturn(pszEnvVarPrefix, VERR_INVALID_POINTER); + AssertReturn(*pszEnvVarPrefix, VERR_INVALID_PARAMETER); + } + + /* + * Allocate and initialize a new instance. + */ + PRTDBGCFGINT pThis = (PRTDBGCFGINT)RTMemAllocZ(sizeof(*pThis)); + if (!pThis) + return VERR_NO_MEMORY; + + pThis->u32Magic = RTDBGCFG_MAGIC; + pThis->cRefs = 1; + RTListInit(&pThis->PathList); + RTListInit(&pThis->SuffixList); + RTListInit(&pThis->SrcPathList); +#ifdef RT_OS_WINDOWS + RTListInit(&pThis->NtSymbolPathList); + RTListInit(&pThis->NtExecutablePathList); + RTListInit(&pThis->NtSourcePath); +#endif + + int rc = RTCritSectRwInit(&pThis->CritSect); + if (RT_FAILURE(rc)) + { + RTMemFree(pThis); + return rc; + } + + /* + * Read configurtion from the environment if requested to do so. + */ + if (pszEnvVarPrefix || fNativePaths) + { + const size_t cbEnvVar = 256; + const size_t cbEnvVal = 65536 - cbEnvVar; + char *pszEnvVar = (char *)RTMemTmpAlloc(cbEnvVar + cbEnvVal); + if (pszEnvVar) + { + char *pszEnvVal = pszEnvVar + cbEnvVar; + + if (pszEnvVarPrefix) + { + static struct + { + RTDBGCFGPROP enmProp; + const char *pszVar; + } const s_aProps[] = + { + { RTDBGCFGPROP_FLAGS, "FLAGS" }, + { RTDBGCFGPROP_PATH, "PATH" }, + { RTDBGCFGPROP_SUFFIXES, "SUFFIXES" }, + { RTDBGCFGPROP_SRC_PATH, "SRC_PATH" }, + }; + + for (unsigned i = 0; i < RT_ELEMENTS(s_aProps); i++) + { + size_t cchEnvVar = RTStrPrintf(pszEnvVar, cbEnvVar, "%s_%s", pszEnvVarPrefix, s_aProps[i].pszVar); + if (cchEnvVar >= cbEnvVar - 1) + { + rc = VERR_BUFFER_OVERFLOW; + break; + } + + rc = RTEnvGetEx(RTENV_DEFAULT, pszEnvVar, pszEnvVal, cbEnvVal, NULL); + if (RT_SUCCESS(rc)) + { + rc = RTDbgCfgChangeString(pThis, s_aProps[i].enmProp, RTDBGCFGOP_SET, pszEnvVal); + if (RT_FAILURE(rc)) + break; + } + else if (rc != VERR_ENV_VAR_NOT_FOUND) + break; + else + rc = VINF_SUCCESS; + } + } + + /* + * Pick up system specific search paths. + */ + if (RT_SUCCESS(rc) && fNativePaths) + { + struct + { + PRTLISTANCHOR pList; + const char *pszVar; + char chSep; + } aNativePaths[] = + { +#ifdef RT_OS_WINDOWS + { &pThis->NtExecutablePathList, "_NT_EXECUTABLE_PATH", ';' }, + { &pThis->NtSymbolPathList, "_NT_ALT_SYMBOL_PATH", ';' }, + { &pThis->NtSymbolPathList, "_NT_SYMBOL_PATH", ';' }, + { &pThis->NtSourcePath, "_NT_SOURCE_PATH", ';' }, +#endif + { NULL, NULL, 0 } + }; + for (unsigned i = 0; aNativePaths[i].pList; i++) + { + Assert(aNativePaths[i].chSep == ';'); /* fix when needed */ + rc = RTEnvGetEx(RTENV_DEFAULT, aNativePaths[i].pszVar, pszEnvVal, cbEnvVal, NULL); + if (RT_SUCCESS(rc)) + { + rc = rtDbgCfgChangeStringList(pThis, RTDBGCFGOP_APPEND, pszEnvVal, true, aNativePaths[i].pList); + if (RT_FAILURE(rc)) + break; + } + else if (rc != VERR_ENV_VAR_NOT_FOUND) + break; + else + rc = VINF_SUCCESS; + } + } + RTMemTmpFree(pszEnvVar); + } + else + rc = VERR_NO_TMP_MEMORY; + if (RT_FAILURE(rc)) + { + /* + * Error, bail out. + */ + RTDbgCfgRelease(pThis); + return rc; + } + } + + /* + * Returns successfully. + */ + *phDbgCfg = pThis; + + return VINF_SUCCESS; +} + diff --git a/src/VBox/Runtime/common/dbg/dbgmod.cpp b/src/VBox/Runtime/common/dbg/dbgmod.cpp new file mode 100644 index 00000000..ac08f297 --- /dev/null +++ b/src/VBox/Runtime/common/dbg/dbgmod.cpp @@ -0,0 +1,2317 @@ +/* $Id: dbgmod.cpp $ */ +/** @file + * IPRT - Debug Module Interpreter. + */ + +/* + * Copyright (C) 2009-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_DBG +#include <iprt/dbg.h> +#include "internal/iprt.h" + +#include <iprt/alloca.h> +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/avl.h> +#include <iprt/err.h> +#include <iprt/initterm.h> +#include <iprt/log.h> +#include <iprt/mem.h> +#include <iprt/once.h> +#include <iprt/param.h> +#include <iprt/path.h> +#include <iprt/semaphore.h> +#include <iprt/strcache.h> +#include <iprt/string.h> +#include <iprt/uuid.h> +#include "internal/dbgmod.h" +#include "internal/magics.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** Debug info interpreter registration record. */ +typedef struct RTDBGMODREGDBG +{ + /** Pointer to the next record. */ + struct RTDBGMODREGDBG *pNext; + /** Pointer to the virtual function table for the interpreter. */ + PCRTDBGMODVTDBG pVt; + /** Usage counter. */ + uint32_t volatile cUsers; +} RTDBGMODREGDBG; +typedef RTDBGMODREGDBG *PRTDBGMODREGDBG; + +/** Image interpreter registration record. */ +typedef struct RTDBGMODREGIMG +{ + /** Pointer to the next record. */ + struct RTDBGMODREGIMG *pNext; + /** Pointer to the virtual function table for the interpreter. */ + PCRTDBGMODVTIMG pVt; + /** Usage counter. */ + uint32_t volatile cUsers; +} RTDBGMODREGIMG; +typedef RTDBGMODREGIMG *PRTDBGMODREGIMG; + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** Validates a debug module handle and returns rc if not valid. */ +#define RTDBGMOD_VALID_RETURN_RC(pDbgMod, rc) \ + do { \ + AssertPtrReturn((pDbgMod), (rc)); \ + AssertReturn((pDbgMod)->u32Magic == RTDBGMOD_MAGIC, (rc)); \ + AssertReturn((pDbgMod)->cRefs > 0, (rc)); \ + } while (0) + +/** Locks the debug module. */ +#define RTDBGMOD_LOCK(pDbgMod) \ + do { \ + int rcLock = RTCritSectEnter(&(pDbgMod)->CritSect); \ + AssertRC(rcLock); \ + } while (0) + +/** Unlocks the debug module. */ +#define RTDBGMOD_UNLOCK(pDbgMod) \ + do { \ + int rcLock = RTCritSectLeave(&(pDbgMod)->CritSect); \ + AssertRC(rcLock); \ + } while (0) + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Init once object for lazy registration of the built-in image and debug + * info interpreters. */ +static RTONCE g_rtDbgModOnce = RTONCE_INITIALIZER; +/** Read/Write semaphore protecting the list of registered interpreters. */ +static RTSEMRW g_hDbgModRWSem = NIL_RTSEMRW; +/** List of registered image interpreters. */ +static PRTDBGMODREGIMG g_pImgHead; +/** List of registered debug infor interpreters. */ +static PRTDBGMODREGDBG g_pDbgHead; +/** String cache for the debug info interpreters. + * RTSTRCACHE is thread safe. */ +DECL_HIDDEN_DATA(RTSTRCACHE) g_hDbgModStrCache = NIL_RTSTRCACHE; + + + + + +/** + * Cleanup debug info interpreter globals. + * + * @param enmReason The cause of the termination. + * @param iStatus The meaning of this depends on enmReason. + * @param pvUser User argument, unused. + */ +static DECLCALLBACK(void) rtDbgModTermCallback(RTTERMREASON enmReason, int32_t iStatus, void *pvUser) +{ + NOREF(iStatus); NOREF(pvUser); + if (enmReason == RTTERMREASON_UNLOAD) + { + RTSemRWDestroy(g_hDbgModRWSem); + g_hDbgModRWSem = NIL_RTSEMRW; + + RTStrCacheDestroy(g_hDbgModStrCache); + g_hDbgModStrCache = NIL_RTSTRCACHE; + + PRTDBGMODREGDBG pDbg = g_pDbgHead; + g_pDbgHead = NULL; + while (pDbg) + { + PRTDBGMODREGDBG pNext = pDbg->pNext; + AssertMsg(pDbg->cUsers == 0, ("%#x %s\n", pDbg->cUsers, pDbg->pVt->pszName)); + RTMemFree(pDbg); + pDbg = pNext; + } + + PRTDBGMODREGIMG pImg = g_pImgHead; + g_pImgHead = NULL; + while (pImg) + { + PRTDBGMODREGIMG pNext = pImg->pNext; + AssertMsg(pImg->cUsers == 0, ("%#x %s\n", pImg->cUsers, pImg->pVt->pszName)); + RTMemFree(pImg); + pImg = pNext; + } + } +} + + +/** + * Internal worker for register a debug interpreter. + * + * Called while owning the write lock or when locking isn't required. + * + * @returns IPRT status code. + * @retval VERR_NO_MEMORY + * @retval VERR_ALREADY_EXISTS + * + * @param pVt The virtual function table of the debug + * module interpreter. + */ +static int rtDbgModDebugInterpreterRegister(PCRTDBGMODVTDBG pVt) +{ + /* + * Search or duplicate registration. + */ + PRTDBGMODREGDBG pPrev = NULL; + for (PRTDBGMODREGDBG pCur = g_pDbgHead; pCur; pCur = pCur->pNext) + { + if (pCur->pVt == pVt) + return VERR_ALREADY_EXISTS; + if (!strcmp(pCur->pVt->pszName, pVt->pszName)) + return VERR_ALREADY_EXISTS; + pPrev = pCur; + } + + /* + * Create a new record and add it to the end of the list. + */ + PRTDBGMODREGDBG pReg = (PRTDBGMODREGDBG)RTMemAlloc(sizeof(*pReg)); + if (!pReg) + return VERR_NO_MEMORY; + pReg->pVt = pVt; + pReg->cUsers = 0; + pReg->pNext = NULL; + if (pPrev) + pPrev->pNext = pReg; + else + g_pDbgHead = pReg; + return VINF_SUCCESS; +} + + +/** + * Internal worker for register a image interpreter. + * + * Called while owning the write lock or when locking isn't required. + * + * @returns IPRT status code. + * @retval VERR_NO_MEMORY + * @retval VERR_ALREADY_EXISTS + * + * @param pVt The virtual function table of the image + * interpreter. + */ +static int rtDbgModImageInterpreterRegister(PCRTDBGMODVTIMG pVt) +{ + /* + * Search or duplicate registration. + */ + PRTDBGMODREGIMG pPrev = NULL; + for (PRTDBGMODREGIMG pCur = g_pImgHead; pCur; pCur = pCur->pNext) + { + if (pCur->pVt == pVt) + return VERR_ALREADY_EXISTS; + if (!strcmp(pCur->pVt->pszName, pVt->pszName)) + return VERR_ALREADY_EXISTS; + pPrev = pCur; + } + + /* + * Create a new record and add it to the end of the list. + */ + PRTDBGMODREGIMG pReg = (PRTDBGMODREGIMG)RTMemAlloc(sizeof(*pReg)); + if (!pReg) + return VERR_NO_MEMORY; + pReg->pVt = pVt; + pReg->cUsers = 0; + pReg->pNext = NULL; + if (pPrev) + pPrev->pNext = pReg; + else + g_pImgHead = pReg; + return VINF_SUCCESS; +} + + +/** + * Do-once callback that initializes the read/write semaphore and registers + * the built-in interpreters. + * + * @returns IPRT status code. + * @param pvUser NULL. + */ +static DECLCALLBACK(int) rtDbgModInitOnce(void *pvUser) +{ + NOREF(pvUser); + + /* + * Create the semaphore and string cache. + */ + int rc = RTSemRWCreate(&g_hDbgModRWSem); + AssertRCReturn(rc, rc); + + rc = RTStrCacheCreate(&g_hDbgModStrCache, "RTDBGMOD"); + if (RT_SUCCESS(rc)) + { + /* + * Register the interpreters. + */ + rc = rtDbgModDebugInterpreterRegister(&g_rtDbgModVtDbgNm); + if (RT_SUCCESS(rc)) + rc = rtDbgModDebugInterpreterRegister(&g_rtDbgModVtDbgMapSym); + if (RT_SUCCESS(rc)) + rc = rtDbgModDebugInterpreterRegister(&g_rtDbgModVtDbgDwarf); + if (RT_SUCCESS(rc)) + rc = rtDbgModDebugInterpreterRegister(&g_rtDbgModVtDbgCodeView); +#ifdef IPRT_WITH_GHIDRA_DBG_MOD + if (RT_SUCCESS(rc)) + rc = rtDbgModDebugInterpreterRegister(&g_rtDbgModVtDbgGhidra); +#endif +#ifdef RT_OS_WINDOWS + if (RT_SUCCESS(rc)) + rc = rtDbgModDebugInterpreterRegister(&g_rtDbgModVtDbgDbgHelp); +#endif + if (RT_SUCCESS(rc)) + rc = rtDbgModImageInterpreterRegister(&g_rtDbgModVtImgLdr); + if (RT_SUCCESS(rc)) + { + /* + * Finally, register the IPRT cleanup callback. + */ + rc = RTTermRegisterCallback(rtDbgModTermCallback, NULL); + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + + /* bail out: use the termination callback. */ + } + } + else + g_hDbgModStrCache = NIL_RTSTRCACHE; + rtDbgModTermCallback(RTTERMREASON_UNLOAD, 0, NULL); + return rc; +} + + +/** + * Performs lazy init of our global variables. + * @returns IPRT status code. + */ +DECLINLINE(int) rtDbgModLazyInit(void) +{ + return RTOnce(&g_rtDbgModOnce, rtDbgModInitOnce, NULL); +} + + +RTDECL(int) RTDbgModCreate(PRTDBGMOD phDbgMod, const char *pszName, RTUINTPTR cbSeg, uint32_t fFlags) +{ + /* + * Input validation and lazy initialization. + */ + AssertPtrReturn(phDbgMod, VERR_INVALID_POINTER); + *phDbgMod = NIL_RTDBGMOD; + AssertPtrReturn(pszName, VERR_INVALID_POINTER); + AssertReturn(*pszName, VERR_INVALID_PARAMETER); + AssertReturn(fFlags == 0 || fFlags == RTDBGMOD_F_NOT_DEFERRED, VERR_INVALID_FLAGS); + + int rc = rtDbgModLazyInit(); + if (RT_FAILURE(rc)) + return rc; + + /* + * Allocate a new module instance. + */ + PRTDBGMODINT pDbgMod = (PRTDBGMODINT)RTMemAllocZ(sizeof(*pDbgMod)); + if (!pDbgMod) + return VERR_NO_MEMORY; + pDbgMod->u32Magic = RTDBGMOD_MAGIC; + pDbgMod->cRefs = 1; + rc = RTCritSectInit(&pDbgMod->CritSect); + if (RT_SUCCESS(rc)) + { + pDbgMod->pszImgFileSpecified = RTStrCacheEnter(g_hDbgModStrCache, pszName); + pDbgMod->pszName = RTStrCacheEnterLower(g_hDbgModStrCache, RTPathFilenameEx(pszName, RTPATH_STR_F_STYLE_DOS)); + if (pDbgMod->pszName) + { + rc = rtDbgModContainerCreate(pDbgMod, cbSeg); + if (RT_SUCCESS(rc)) + { + *phDbgMod = pDbgMod; + return rc; + } + RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFile); + RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszName); + } + RTCritSectDelete(&pDbgMod->CritSect); + } + + RTMemFree(pDbgMod); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgModCreate); + + +RTDECL(int) RTDbgModCreateFromMap(PRTDBGMOD phDbgMod, const char *pszFilename, const char *pszName, + RTUINTPTR uSubtrahend, RTDBGCFG hDbgCfg) +{ + RT_NOREF_PV(hDbgCfg); + + /* + * Input validation and lazy initialization. + */ + AssertPtrReturn(phDbgMod, VERR_INVALID_POINTER); + *phDbgMod = NIL_RTDBGMOD; + AssertPtrReturn(pszFilename, VERR_INVALID_POINTER); + AssertReturn(*pszFilename, VERR_INVALID_PARAMETER); + AssertPtrNullReturn(pszName, VERR_INVALID_POINTER); + AssertReturn(uSubtrahend == 0, VERR_NOT_IMPLEMENTED); /** @todo implement uSubtrahend. */ + + int rc = rtDbgModLazyInit(); + if (RT_FAILURE(rc)) + return rc; + + if (!pszName) + pszName = RTPathFilenameEx(pszFilename, RTPATH_STR_F_STYLE_DOS); + + /* + * Allocate a new module instance. + */ + PRTDBGMODINT pDbgMod = (PRTDBGMODINT)RTMemAllocZ(sizeof(*pDbgMod)); + if (!pDbgMod) + return VERR_NO_MEMORY; + pDbgMod->u32Magic = RTDBGMOD_MAGIC; + pDbgMod->cRefs = 1; + rc = RTCritSectInit(&pDbgMod->CritSect); + if (RT_SUCCESS(rc)) + { + pDbgMod->pszName = RTStrCacheEnterLower(g_hDbgModStrCache, pszName); + if (pDbgMod->pszName) + { + pDbgMod->pszDbgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename); + if (pDbgMod->pszDbgFile) + { + /* + * Try the map file readers. + */ + rc = RTSemRWRequestRead(g_hDbgModRWSem, RT_INDEFINITE_WAIT); + if (RT_SUCCESS(rc)) + { + for (PRTDBGMODREGDBG pCur = g_pDbgHead; pCur; pCur = pCur->pNext) + { + if (pCur->pVt->fSupports & RT_DBGTYPE_MAP) + { + pDbgMod->pDbgVt = pCur->pVt; + pDbgMod->pvDbgPriv = NULL; + rc = pCur->pVt->pfnTryOpen(pDbgMod, RTLDRARCH_WHATEVER); + if (RT_SUCCESS(rc)) + { + ASMAtomicIncU32(&pCur->cUsers); + RTSemRWReleaseRead(g_hDbgModRWSem); + + *phDbgMod = pDbgMod; + return rc; + } + } + } + + /* bail out */ + rc = VERR_DBG_NO_MATCHING_INTERPRETER; + RTSemRWReleaseRead(g_hDbgModRWSem); + } + RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszName); + } + else + rc = VERR_NO_STR_MEMORY; + RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszDbgFile); + } + else + rc = VERR_NO_STR_MEMORY; + RTCritSectDelete(&pDbgMod->CritSect); + } + + RTMemFree(pDbgMod); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgModCreateFromMap); + + + +/* + * + * E x e c u t a b l e I m a g e F i l e s + * E x e c u t a b l e I m a g e F i l e s + * E x e c u t a b l e I m a g e F i l e s + * + */ + + +/** + * Opens debug information for an image. + * + * @returns IPRT status code + * @param pDbgMod The debug module structure. + * + * @note This will generally not look for debug info stored in external + * files. rtDbgModFromPeImageExtDbgInfoCallback can help with that. + */ +static int rtDbgModOpenDebugInfoInsideImage(PRTDBGMODINT pDbgMod) +{ + AssertReturn(!pDbgMod->pDbgVt, VERR_DBG_MOD_IPE); + AssertReturn(pDbgMod->pImgVt, VERR_DBG_MOD_IPE); + + int rc = RTSemRWRequestRead(g_hDbgModRWSem, RT_INDEFINITE_WAIT); + if (RT_SUCCESS(rc)) + { + for (PRTDBGMODREGDBG pDbg = g_pDbgHead; pDbg; pDbg = pDbg->pNext) + { + pDbgMod->pDbgVt = pDbg->pVt; + pDbgMod->pvDbgPriv = NULL; + rc = pDbg->pVt->pfnTryOpen(pDbgMod, pDbgMod->pImgVt->pfnGetArch(pDbgMod)); + if (RT_SUCCESS(rc)) + { + /* + * That's it! + */ + ASMAtomicIncU32(&pDbg->cUsers); + RTSemRWReleaseRead(g_hDbgModRWSem); + return VINF_SUCCESS; + } + + pDbgMod->pDbgVt = NULL; + Assert(pDbgMod->pvDbgPriv == NULL); + } + RTSemRWReleaseRead(g_hDbgModRWSem); + } + + return VERR_DBG_NO_MATCHING_INTERPRETER; +} + + +/** @callback_method_impl{FNRTDBGCFGOPEN} */ +static DECLCALLBACK(int) rtDbgModExtDbgInfoOpenCallback(RTDBGCFG hDbgCfg, const char *pszFilename, void *pvUser1, void *pvUser2) +{ + PRTDBGMODINT pDbgMod = (PRTDBGMODINT)pvUser1; + PCRTLDRDBGINFO pDbgInfo = (PCRTLDRDBGINFO)pvUser2; + RT_NOREF_PV(pDbgInfo); /** @todo consider a more direct search for a interpreter. */ + RT_NOREF_PV(hDbgCfg); + + Assert(!pDbgMod->pDbgVt); + Assert(!pDbgMod->pvDbgPriv); + Assert(!pDbgMod->pszDbgFile); + Assert(pDbgMod->pImgVt); + + /* + * Set the debug file name and try possible interpreters. + */ + pDbgMod->pszDbgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename); + + int rc = RTSemRWRequestRead(g_hDbgModRWSem, RT_INDEFINITE_WAIT); + if (RT_SUCCESS(rc)) + { + for (PRTDBGMODREGDBG pDbg = g_pDbgHead; pDbg; pDbg = pDbg->pNext) + { + pDbgMod->pDbgVt = pDbg->pVt; + pDbgMod->pvDbgPriv = NULL; + rc = pDbg->pVt->pfnTryOpen(pDbgMod, pDbgMod->pImgVt->pfnGetArch(pDbgMod)); + if (RT_SUCCESS(rc)) + { + /* + * Got it! + */ + ASMAtomicIncU32(&pDbg->cUsers); + RTSemRWReleaseRead(g_hDbgModRWSem); + return VINF_CALLBACK_RETURN; + } + + pDbgMod->pDbgVt = NULL; + Assert(pDbgMod->pvDbgPriv == NULL); + } + RTSemRWReleaseRead(g_hDbgModRWSem); + } + + /* No joy. */ + RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszDbgFile); + pDbgMod->pszDbgFile = NULL; + return rc; +} + + +/** + * Argument package used by rtDbgModOpenDebugInfoExternalToImage. + */ +typedef struct RTDBGMODOPENDIETI +{ + PRTDBGMODINT pDbgMod; + RTDBGCFG hDbgCfg; +} RTDBGMODOPENDIETI; + + +/** @callback_method_impl{FNRTLDRENUMDBG} */ +static DECLCALLBACK(int) +rtDbgModOpenDebugInfoExternalToImageCallback(RTLDRMOD hLdrMod, PCRTLDRDBGINFO pDbgInfo, void *pvUser) +{ + RTDBGMODOPENDIETI *pArgs = (RTDBGMODOPENDIETI *)pvUser; + RT_NOREF_PV(hLdrMod); + + Assert(pDbgInfo->enmType > RTLDRDBGINFOTYPE_INVALID && pDbgInfo->enmType < RTLDRDBGINFOTYPE_END); + const char *pszExtFile = pDbgInfo->pszExtFile; + if (!pszExtFile) + { + /* + * If a external debug type comes without a file name, calculate a + * likely debug filename for it. (Hack for NT4 drivers.) + */ + const char *pszExt = NULL; + if (pDbgInfo->enmType == RTLDRDBGINFOTYPE_CODEVIEW_DBG) + pszExt = ".dbg"; + else if ( pDbgInfo->enmType == RTLDRDBGINFOTYPE_CODEVIEW_PDB20 + || pDbgInfo->enmType == RTLDRDBGINFOTYPE_CODEVIEW_PDB70) + pszExt = ".pdb"; + if (pszExt && pArgs->pDbgMod->pszName) + { + size_t cchName = strlen(pArgs->pDbgMod->pszName); + char *psz = (char *)alloca(cchName + strlen(pszExt) + 1); + if (psz) + { + memcpy(psz, pArgs->pDbgMod->pszName, cchName + 1); + RTPathStripSuffix(psz); + pszExtFile = strcat(psz, pszExt); + } + } + + if (!pszExtFile) + { + Log2(("rtDbgModOpenDebugInfoExternalToImageCallback: enmType=%d\n", pDbgInfo->enmType)); + return VINF_SUCCESS; + } + } + + /* + * Switch on type and call the appropriate search function. + */ + int rc; + switch (pDbgInfo->enmType) + { + case RTLDRDBGINFOTYPE_CODEVIEW_PDB70: + rc = RTDbgCfgOpenPdb70(pArgs->hDbgCfg, pszExtFile, + &pDbgInfo->u.Pdb70.Uuid, + pDbgInfo->u.Pdb70.uAge, + rtDbgModExtDbgInfoOpenCallback, pArgs->pDbgMod, (void *)pDbgInfo); + break; + + case RTLDRDBGINFOTYPE_CODEVIEW_PDB20: + rc = RTDbgCfgOpenPdb20(pArgs->hDbgCfg, pszExtFile, + pDbgInfo->u.Pdb20.cbImage, + pDbgInfo->u.Pdb20.uTimestamp, + pDbgInfo->u.Pdb20.uAge, + rtDbgModExtDbgInfoOpenCallback, pArgs->pDbgMod, (void *)pDbgInfo); + break; + + case RTLDRDBGINFOTYPE_CODEVIEW_DBG: + rc = RTDbgCfgOpenDbg(pArgs->hDbgCfg, pszExtFile, + pDbgInfo->u.Dbg.cbImage, + pDbgInfo->u.Dbg.uTimestamp, + rtDbgModExtDbgInfoOpenCallback, pArgs->pDbgMod, (void *)pDbgInfo); + break; + + case RTLDRDBGINFOTYPE_DWARF_DWO: + rc = RTDbgCfgOpenDwo(pArgs->hDbgCfg, pszExtFile, + pDbgInfo->u.Dwo.uCrc32, + rtDbgModExtDbgInfoOpenCallback, pArgs->pDbgMod, (void *)pDbgInfo); + break; + + default: + Log(("rtDbgModOpenDebugInfoExternalToImageCallback: Don't know how to handle enmType=%d and pszFileExt=%s\n", + pDbgInfo->enmType, pszExtFile)); + return VERR_DBG_TODO; + } + if (RT_SUCCESS(rc)) + { + LogFlow(("RTDbgMod: Successfully opened external debug info '%s' for '%s'\n", + pArgs->pDbgMod->pszDbgFile, pArgs->pDbgMod->pszImgFile)); + return VINF_CALLBACK_RETURN; + } + Log(("rtDbgModOpenDebugInfoExternalToImageCallback: '%s' (enmType=%d) for '%s' -> %Rrc\n", + pszExtFile, pDbgInfo->enmType, pArgs->pDbgMod->pszImgFile, rc)); + return rc; +} + + +/** + * Opens debug info listed in the image that is stored in a separate file. + * + * @returns IPRT status code + * @param pDbgMod The debug module. + * @param hDbgCfg The debug config. Can be NIL. + */ +static int rtDbgModOpenDebugInfoExternalToImage(PRTDBGMODINT pDbgMod, RTDBGCFG hDbgCfg) +{ + Assert(!pDbgMod->pDbgVt); + + RTDBGMODOPENDIETI Args; + Args.pDbgMod = pDbgMod; + Args.hDbgCfg = hDbgCfg; + int rc = pDbgMod->pImgVt->pfnEnumDbgInfo(pDbgMod, rtDbgModOpenDebugInfoExternalToImageCallback, &Args); + if (RT_SUCCESS(rc) && pDbgMod->pDbgVt) + return VINF_SUCCESS; + + LogFlow(("rtDbgModOpenDebugInfoExternalToImage: rc=%Rrc\n", rc)); + return VERR_NOT_FOUND; +} + + +/** @callback_method_impl{FNRTDBGCFGOPEN} */ +static DECLCALLBACK(int) rtDbgModExtDbgInfoOpenCallback2(RTDBGCFG hDbgCfg, const char *pszFilename, void *pvUser1, void *pvUser2) +{ + PRTDBGMODINT pDbgMod = (PRTDBGMODINT)pvUser1; + RT_NOREF_PV(pvUser2); /** @todo image matching string or smth. */ + RT_NOREF_PV(hDbgCfg); + + Assert(!pDbgMod->pDbgVt); + Assert(!pDbgMod->pvDbgPriv); + Assert(!pDbgMod->pszDbgFile); + Assert(pDbgMod->pImgVt); + + /* + * Set the debug file name and try possible interpreters. + */ + pDbgMod->pszDbgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename); + + int rc = RTSemRWRequestRead(g_hDbgModRWSem, RT_INDEFINITE_WAIT); + if (RT_SUCCESS(rc)) + { + for (PRTDBGMODREGDBG pDbg = g_pDbgHead; pDbg; pDbg = pDbg->pNext) + { + pDbgMod->pDbgVt = pDbg->pVt; + pDbgMod->pvDbgPriv = NULL; + rc = pDbg->pVt->pfnTryOpen(pDbgMod, pDbgMod->pImgVt->pfnGetArch(pDbgMod)); + if (RT_SUCCESS(rc)) + { + /* + * Got it! + */ + ASMAtomicIncU32(&pDbg->cUsers); + RTSemRWReleaseRead(g_hDbgModRWSem); + return VINF_CALLBACK_RETURN; + } + pDbgMod->pDbgVt = NULL; + Assert(pDbgMod->pvDbgPriv == NULL); + } + } + + /* No joy. */ + RTSemRWReleaseRead(g_hDbgModRWSem); + RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszDbgFile); + pDbgMod->pszDbgFile = NULL; + return rc; +} + + +/** + * Opens external debug info that is not listed in the image. + * + * @returns IPRT status code + * @param pDbgMod The debug module. + * @param hDbgCfg The debug config. Can be NIL. + */ +static int rtDbgModOpenDebugInfoExternalToImage2(PRTDBGMODINT pDbgMod, RTDBGCFG hDbgCfg) +{ + int rc; + Assert(!pDbgMod->pDbgVt); + Assert(pDbgMod->pImgVt); + + /* + * Figure out what to search for based on the image format. + */ + const char *pszzExts = NULL; + RTLDRFMT enmFmt = pDbgMod->pImgVt->pfnGetFormat(pDbgMod); + switch (enmFmt) + { + case RTLDRFMT_MACHO: + { + RTUUID Uuid; + PRTUUID pUuid = &Uuid; + rc = pDbgMod->pImgVt->pfnQueryProp(pDbgMod, RTLDRPROP_UUID, &Uuid, sizeof(Uuid), NULL); + if (RT_FAILURE(rc)) + pUuid = NULL; + + rc = RTDbgCfgOpenDsymBundle(hDbgCfg, pDbgMod->pszImgFile, pUuid, + rtDbgModExtDbgInfoOpenCallback2, pDbgMod, NULL /*pvUser2*/); + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + break; + } + +#if 0 /* Will be links in the image if these apply. .map readers for PE or ELF we don't have. */ + case RTLDRFMT_ELF: + pszzExts = ".debug\0.dwo\0"; + break; + case RTLDRFMT_PE: + pszzExts = ".map\0"; + break; +#endif +#if 0 /* Haven't implemented .sym or .map file readers for OS/2 yet. */ + case RTLDRFMT_LX: + pszzExts = ".sym\0.map\0"; + break; +#endif + default: + rc = VERR_NOT_IMPLEMENTED; + break; + } + + NOREF(pszzExts); +#if 0 /* Later */ + if (pszzExts) + { + + } +#endif + + LogFlow(("rtDbgModOpenDebugInfoExternalToImage2: rc=%Rrc\n", rc)); + return VERR_NOT_FOUND; +} + + +RTDECL(int) RTDbgModCreateFromImage(PRTDBGMOD phDbgMod, const char *pszFilename, const char *pszName, + RTLDRARCH enmArch, RTDBGCFG hDbgCfg) +{ + /* + * Input validation and lazy initialization. + */ + AssertPtrReturn(phDbgMod, VERR_INVALID_POINTER); + *phDbgMod = NIL_RTDBGMOD; + AssertPtrReturn(pszFilename, VERR_INVALID_POINTER); + AssertReturn(*pszFilename, VERR_INVALID_PARAMETER); + AssertPtrNullReturn(pszName, VERR_INVALID_POINTER); + AssertReturn(enmArch > RTLDRARCH_INVALID && enmArch < RTLDRARCH_END, VERR_INVALID_PARAMETER); + + int rc = rtDbgModLazyInit(); + if (RT_FAILURE(rc)) + return rc; + + if (!pszName) + pszName = RTPathFilenameEx(pszFilename, RTPATH_STR_F_STYLE_DOS); + + /* + * Allocate a new module instance. + */ + PRTDBGMODINT pDbgMod = (PRTDBGMODINT)RTMemAllocZ(sizeof(*pDbgMod)); + if (!pDbgMod) + return VERR_NO_MEMORY; + pDbgMod->u32Magic = RTDBGMOD_MAGIC; + pDbgMod->cRefs = 1; + rc = RTCritSectInit(&pDbgMod->CritSect); + if (RT_SUCCESS(rc)) + { + pDbgMod->pszName = RTStrCacheEnterLower(g_hDbgModStrCache, pszName); + if (pDbgMod->pszName) + { + pDbgMod->pszImgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename); + if (pDbgMod->pszImgFile) + { + RTStrCacheRetain(pDbgMod->pszImgFile); + pDbgMod->pszImgFileSpecified = pDbgMod->pszImgFile; + + /* + * Find an image reader which groks the file. + */ + rc = RTSemRWRequestRead(g_hDbgModRWSem, RT_INDEFINITE_WAIT); + if (RT_SUCCESS(rc)) + { + PRTDBGMODREGIMG pImg; + for (pImg = g_pImgHead; pImg; pImg = pImg->pNext) + { + pDbgMod->pImgVt = pImg->pVt; + pDbgMod->pvImgPriv = NULL; + /** @todo need to specify some arch stuff here. */ + rc = pImg->pVt->pfnTryOpen(pDbgMod, enmArch, 0 /*fLdrFlags*/); + if (RT_SUCCESS(rc)) + { + /* + * Image detected, but found no debug info we were + * able to understand. + */ + /** @todo some generic way of matching image and debug info, flexible signature + * of some kind. Apple uses UUIDs, microsoft uses a UUID+age or a + * size+timestamp, and GNU a CRC32 (last time I checked). */ + rc = rtDbgModOpenDebugInfoExternalToImage(pDbgMod, hDbgCfg); + if (RT_FAILURE(rc)) + rc = rtDbgModOpenDebugInfoInsideImage(pDbgMod); + if (RT_FAILURE(rc)) + rc = rtDbgModOpenDebugInfoExternalToImage2(pDbgMod, hDbgCfg); + if (RT_FAILURE(rc)) + rc = rtDbgModCreateForExports(pDbgMod); + if (RT_SUCCESS(rc)) + { + /* + * We're done! + */ + ASMAtomicIncU32(&pImg->cUsers); + RTSemRWReleaseRead(g_hDbgModRWSem); + + *phDbgMod = pDbgMod; + return VINF_SUCCESS; + } + + /* Failed, close up the shop. */ + pDbgMod->pImgVt->pfnClose(pDbgMod); + pDbgMod->pImgVt = NULL; + pDbgMod->pvImgPriv = NULL; + break; + } + } + + /* + * Could it be a file containing raw debug info? + */ + if (!pImg) + { + pDbgMod->pImgVt = NULL; + pDbgMod->pvImgPriv = NULL; + pDbgMod->pszDbgFile = pDbgMod->pszImgFile; + pDbgMod->pszImgFile = NULL; + + for (PRTDBGMODREGDBG pDbg = g_pDbgHead; pDbg; pDbg = pDbg->pNext) + { + pDbgMod->pDbgVt = pDbg->pVt; + pDbgMod->pvDbgPriv = NULL; + rc = pDbg->pVt->pfnTryOpen(pDbgMod, enmArch); + if (RT_SUCCESS(rc)) + { + /* + * That's it! + */ + ASMAtomicIncU32(&pDbg->cUsers); + RTSemRWReleaseRead(g_hDbgModRWSem); + + *phDbgMod = pDbgMod; + return rc; + } + } + + pDbgMod->pszImgFile = pDbgMod->pszDbgFile; + pDbgMod->pszDbgFile = NULL; + } + + /* bail out */ + rc = VERR_DBG_NO_MATCHING_INTERPRETER; + RTSemRWReleaseRead(g_hDbgModRWSem); + } + RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFileSpecified); + RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFile); + } + else + rc = VERR_NO_STR_MEMORY; + RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszName); + } + else + rc = VERR_NO_STR_MEMORY; + RTCritSectDelete(&pDbgMod->CritSect); + } + + RTMemFree(pDbgMod); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgModCreateFromImage); + + + + + +/* + * + * P E I M A G E + * P E I M A G E + * P E I M A G E + * + */ + + + +/** @callback_method_impl{FNRTDBGCFGOPEN} */ +static DECLCALLBACK(int) rtDbgModFromPeImageOpenCallback(RTDBGCFG hDbgCfg, const char *pszFilename, void *pvUser1, void *pvUser2) +{ + PRTDBGMODINT pDbgMod = (PRTDBGMODINT)pvUser1; + PRTDBGMODDEFERRED pDeferred = (PRTDBGMODDEFERRED)pvUser2; + LogFlow(("rtDbgModFromPeImageOpenCallback: %s\n", pszFilename)); + RT_NOREF_PV(hDbgCfg); + + Assert(pDbgMod->pImgVt == NULL); + Assert(pDbgMod->pvImgPriv == NULL); + Assert(pDbgMod->pDbgVt == NULL); + Assert(pDbgMod->pvDbgPriv == NULL); + + /* + * Replace the image file name while probing it. + */ + const char *pszNewImgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename); + if (!pszNewImgFile) + return VERR_NO_STR_MEMORY; + const char *pszOldImgFile = pDbgMod->pszImgFile; + pDbgMod->pszImgFile = pszNewImgFile; + + /* + * Find an image reader which groks the file. + */ + int rc = RTSemRWRequestRead(g_hDbgModRWSem, RT_INDEFINITE_WAIT); + if (RT_SUCCESS(rc)) + { + rc = VERR_DBG_NO_MATCHING_INTERPRETER; + PRTDBGMODREGIMG pImg; + for (pImg = g_pImgHead; pImg; pImg = pImg->pNext) + { + pDbgMod->pImgVt = pImg->pVt; + pDbgMod->pvImgPriv = NULL; + int rc2 = pImg->pVt->pfnTryOpen(pDbgMod, RTLDRARCH_WHATEVER, 0 /*fLdrFlags*/); + if (RT_SUCCESS(rc2)) + { + rc = rc2; + break; + } + pDbgMod->pImgVt = NULL; + Assert(pDbgMod->pvImgPriv == NULL); + } + RTSemRWReleaseRead(g_hDbgModRWSem); + if (RT_SUCCESS(rc)) + { + /* + * Check the deferred info. + */ + RTUINTPTR cbImage = pDbgMod->pImgVt->pfnImageSize(pDbgMod); + if ( pDeferred->cbImage == 0 + || pDeferred->cbImage == cbImage) + { + uint32_t uTimestamp = pDeferred->u.PeImage.uTimestamp; /** @todo add method for getting the timestamp. */ + if ( pDeferred->u.PeImage.uTimestamp == 0 + || pDeferred->u.PeImage.uTimestamp == uTimestamp) + { + Log(("RTDbgMod: Found matching PE image '%s'\n", pszFilename)); + + /* + * We found the executable image we need, now go find any + * debug info associated with it. For PE images, this is + * generally found in an external file, so we do a sweep + * for that first. + * + * Then try open debug inside the module, and finally + * falling back on exports. + */ + rc = rtDbgModOpenDebugInfoExternalToImage(pDbgMod, pDeferred->hDbgCfg); + if (RT_FAILURE(rc)) + rc = rtDbgModOpenDebugInfoInsideImage(pDbgMod); + if (RT_FAILURE(rc)) + rc = rtDbgModCreateForExports(pDbgMod); + if (RT_SUCCESS(rc)) + { + RTStrCacheRelease(g_hDbgModStrCache, pszOldImgFile); + return VINF_CALLBACK_RETURN; + } + + /* Something bad happened, just give up. */ + Log(("rtDbgModFromPeImageOpenCallback: rtDbgModCreateForExports failed: %Rrc\n", rc)); + } + else + { + LogFlow(("rtDbgModFromPeImageOpenCallback: uTimestamp mismatch (found %#x, expected %#x) - %s\n", + uTimestamp, pDeferred->u.PeImage.uTimestamp, pszFilename)); + rc = VERR_DBG_FILE_MISMATCH; + } + } + else + { + LogFlow(("rtDbgModFromPeImageOpenCallback: cbImage mismatch (found %#x, expected %#x) - %s\n", + cbImage, pDeferred->cbImage, pszFilename)); + rc = VERR_DBG_FILE_MISMATCH; + } + + pDbgMod->pImgVt->pfnClose(pDbgMod); + pDbgMod->pImgVt = NULL; + pDbgMod->pvImgPriv = NULL; + } + else + LogFlow(("rtDbgModFromPeImageOpenCallback: Failed %Rrc - %s\n", rc, pszFilename)); + } + + /* Restore image name. */ + pDbgMod->pszImgFile = pszOldImgFile; + RTStrCacheRelease(g_hDbgModStrCache, pszNewImgFile); + return rc; +} + + +/** @callback_method_impl{FNRTDBGMODDEFERRED} */ +static DECLCALLBACK(int) rtDbgModFromPeImageDeferredCallback(PRTDBGMODINT pDbgMod, PRTDBGMODDEFERRED pDeferred) +{ + int rc; + + Assert(pDbgMod->pszImgFile); + if (!pDbgMod->pImgVt) + rc = RTDbgCfgOpenPeImage(pDeferred->hDbgCfg, pDbgMod->pszImgFile, + pDeferred->cbImage, pDeferred->u.PeImage.uTimestamp, + rtDbgModFromPeImageOpenCallback, pDbgMod, pDeferred); + else + { + rc = rtDbgModOpenDebugInfoExternalToImage(pDbgMod, pDeferred->hDbgCfg); + if (RT_FAILURE(rc)) + rc = rtDbgModOpenDebugInfoInsideImage(pDbgMod); + if (RT_FAILURE(rc)) + rc = rtDbgModCreateForExports(pDbgMod); + } + return rc; +} + + +RTDECL(int) RTDbgModCreateFromPeImage(PRTDBGMOD phDbgMod, const char *pszFilename, const char *pszName, + PRTLDRMOD phLdrMod, uint32_t cbImage, uint32_t uTimestamp, RTDBGCFG hDbgCfg) +{ + /* + * Input validation and lazy initialization. + */ + AssertPtrReturn(phDbgMod, VERR_INVALID_POINTER); + *phDbgMod = NIL_RTDBGMOD; + AssertPtrReturn(pszFilename, VERR_INVALID_POINTER); + AssertReturn(*pszFilename, VERR_INVALID_PARAMETER); + if (!pszName) + pszName = RTPathFilenameEx(pszFilename, RTPATH_STR_F_STYLE_DOS); + AssertPtrReturn(pszName, VERR_INVALID_POINTER); + AssertPtrNullReturn(phLdrMod, VERR_INVALID_POINTER); + RTLDRMOD hLdrMod = phLdrMod ? *phLdrMod : NIL_RTLDRMOD; + AssertReturn(hLdrMod == NIL_RTLDRMOD || RTLdrSize(hLdrMod) != ~(size_t)0, VERR_INVALID_HANDLE); + + int rc = rtDbgModLazyInit(); + if (RT_FAILURE(rc)) + return rc; + + uint64_t fDbgCfg = 0; + if (hDbgCfg) + { + rc = RTDbgCfgQueryUInt(hDbgCfg, RTDBGCFGPROP_FLAGS, &fDbgCfg); + AssertRCReturn(rc, rc); + } + + /* + * Allocate a new module instance. + */ + PRTDBGMODINT pDbgMod = (PRTDBGMODINT)RTMemAllocZ(sizeof(*pDbgMod)); + if (!pDbgMod) + return VERR_NO_MEMORY; + pDbgMod->u32Magic = RTDBGMOD_MAGIC; + pDbgMod->cRefs = 1; + rc = RTCritSectInit(&pDbgMod->CritSect); + if (RT_SUCCESS(rc)) + { + pDbgMod->pszName = RTStrCacheEnterLower(g_hDbgModStrCache, pszName); + if (pDbgMod->pszName) + { + pDbgMod->pszImgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename); + if (pDbgMod->pszImgFile) + { + RTStrCacheRetain(pDbgMod->pszImgFile); + pDbgMod->pszImgFileSpecified = pDbgMod->pszImgFile; + + /* + * If we have a loader module, we must instantiate the loader + * side of things regardless of the deferred setting. + */ + if (hLdrMod != NIL_RTLDRMOD) + { + if (!cbImage) + cbImage = (uint32_t)RTLdrSize(hLdrMod); + pDbgMod->pImgVt = &g_rtDbgModVtImgLdr; + + rc = rtDbgModLdrOpenFromHandle(pDbgMod, hLdrMod); + } + if (RT_SUCCESS(rc)) + { + /* We now own the loader handle, so clear the caller variable. */ + if (phLdrMod) + *phLdrMod = NIL_RTLDRMOD; + + /* + * Do it now or procrastinate? + */ + if (!(fDbgCfg & RTDBGCFG_FLAGS_DEFERRED) || !cbImage) + { + RTDBGMODDEFERRED Deferred; + Deferred.cbImage = cbImage; + Deferred.hDbgCfg = hDbgCfg; + Deferred.u.PeImage.uTimestamp = uTimestamp; + rc = rtDbgModFromPeImageDeferredCallback(pDbgMod, &Deferred); + } + else + { + PRTDBGMODDEFERRED pDeferred; + rc = rtDbgModDeferredCreate(pDbgMod, rtDbgModFromPeImageDeferredCallback, cbImage, hDbgCfg, + 0 /*cbDeferred*/, 0 /*fFlags*/, &pDeferred); + if (RT_SUCCESS(rc)) + pDeferred->u.PeImage.uTimestamp = uTimestamp; + } + if (RT_SUCCESS(rc)) + { + *phDbgMod = pDbgMod; + return VINF_SUCCESS; + } + + /* Failed, bail out. */ + if (hLdrMod != NIL_RTLDRMOD) + { + Assert(pDbgMod->pImgVt); + pDbgMod->pImgVt->pfnClose(pDbgMod); + } + } + RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszName); + } + else + rc = VERR_NO_STR_MEMORY; + RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFileSpecified); + RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFile); + } + else + rc = VERR_NO_STR_MEMORY; + RTCritSectDelete(&pDbgMod->CritSect); + } + + RTMemFree(pDbgMod); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgModCreateFromPeImage); + + + + +/* + * + * M a c h - O I M A G E + * M a c h - O I M A G E + * M a c h - O I M A G E + * + */ + + +/** + * Argument package used when opening Mach-O images and .dSYMs files. + */ +typedef struct RTDBGMODMACHOARGS +{ + /** For use more internal use in file locator callbacks. */ + RTLDRARCH enmArch; + /** For use more internal use in file locator callbacks. */ + PCRTUUID pUuid; + /** For use more internal use in file locator callbacks. */ + bool fOpenImage; + /** RTDBGMOD_F_XXX. */ + uint32_t fFlags; +} RTDBGMODMACHOARGS; +/** Pointer to a const segment package. */ +typedef RTDBGMODMACHOARGS const *PCRTDBGMODMACHOARGS; + + + +/** @callback_method_impl{FNRTDBGCFGOPEN} */ +static DECLCALLBACK(int) +rtDbgModFromMachOImageOpenDsymMachOCallback(RTDBGCFG hDbgCfg, const char *pszFilename, void *pvUser1, void *pvUser2) +{ + PRTDBGMODINT pDbgMod = (PRTDBGMODINT)pvUser1; + PCRTDBGMODMACHOARGS pArgs = (PCRTDBGMODMACHOARGS)pvUser2; + RT_NOREF_PV(hDbgCfg); + + Assert(!pDbgMod->pDbgVt); + Assert(!pDbgMod->pvDbgPriv); + Assert(!pDbgMod->pszDbgFile); + Assert(!pDbgMod->pImgVt); + Assert(!pDbgMod->pvDbgPriv); + Assert(pDbgMod->pszImgFile); + Assert(pDbgMod->pszImgFileSpecified); + + const char *pszImgFileOrg = pDbgMod->pszImgFile; + pDbgMod->pszImgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename); + if (!pDbgMod->pszImgFile) + return VERR_NO_STR_MEMORY; + RTStrCacheRetain(pDbgMod->pszImgFile); + pDbgMod->pszDbgFile = pDbgMod->pszImgFile; + + /* + * Try image interpreters as the dwarf file inside the dSYM bundle is a + * Mach-O file with dwarf debug sections insides it and no code or data. + */ + int rc = RTSemRWRequestRead(g_hDbgModRWSem, RT_INDEFINITE_WAIT); + if (RT_SUCCESS(rc)) + { + rc = VERR_DBG_NO_MATCHING_INTERPRETER; + PRTDBGMODREGIMG pImg; + for (pImg = g_pImgHead; pImg; pImg = pImg->pNext) + { + pDbgMod->pImgVt = pImg->pVt; + pDbgMod->pvImgPriv = NULL; + int rc2 = pImg->pVt->pfnTryOpen(pDbgMod, pArgs->enmArch, + pArgs->fFlags & RTDBGMOD_F_MACHO_LOAD_LINKEDIT ? RTLDR_O_MACHO_LOAD_LINKEDIT : 0); + if (RT_SUCCESS(rc2)) + { + rc = rc2; + break; + } + pDbgMod->pImgVt = NULL; + Assert(pDbgMod->pvImgPriv == NULL); + } + + if (RT_SUCCESS(rc)) + { + /* + * Check the UUID if one was given. + */ + if (pArgs->pUuid) + { + RTUUID UuidOpened; + rc = pDbgMod->pImgVt->pfnQueryProp(pDbgMod, RTLDRPROP_UUID, &UuidOpened, sizeof(UuidOpened), NULL); + if (RT_SUCCESS(rc)) + { + if (RTUuidCompare(&UuidOpened, pArgs->pUuid) != 0) + rc = VERR_DBG_FILE_MISMATCH; + } + else if (rc == VERR_NOT_FOUND || rc == VERR_NOT_IMPLEMENTED) + rc = VERR_DBG_FILE_MISMATCH; + } + if (RT_SUCCESS(rc)) + { + /* + * Pass it to the DWARF reader(s). Careful to restrict this or + * the dbghelp wrapper may end up being overly helpful. + */ + for (PRTDBGMODREGDBG pDbg = g_pDbgHead; pDbg; pDbg = pDbg->pNext) + { + if (pDbg->pVt->fSupports & (RT_DBGTYPE_DWARF | RT_DBGTYPE_STABS | RT_DBGTYPE_WATCOM)) + + { + pDbgMod->pDbgVt = pDbg->pVt; + pDbgMod->pvDbgPriv = NULL; + rc = pDbg->pVt->pfnTryOpen(pDbgMod, pDbgMod->pImgVt->pfnGetArch(pDbgMod)); + if (RT_SUCCESS(rc)) + { + /* + * Got it! + */ + ASMAtomicIncU32(&pDbg->cUsers); + RTSemRWReleaseRead(g_hDbgModRWSem); + RTStrCacheRelease(g_hDbgModStrCache, pszImgFileOrg); + return VINF_CALLBACK_RETURN; + } + pDbgMod->pDbgVt = NULL; + Assert(pDbgMod->pvDbgPriv == NULL); + } + } + + /* + * Likely fallback for when opening image. + */ + if (pArgs->fOpenImage) + { + rc = rtDbgModCreateForExports(pDbgMod); + if (RT_SUCCESS(rc)) + { + /* + * Done. + */ + RTSemRWReleaseRead(g_hDbgModRWSem); + RTStrCacheRelease(g_hDbgModStrCache, pszImgFileOrg); + return VINF_CALLBACK_RETURN; + } + } + } + + pDbgMod->pImgVt->pfnClose(pDbgMod); + pDbgMod->pImgVt = NULL; + pDbgMod->pvImgPriv = NULL; + } + } + + /* No joy. */ + RTSemRWReleaseRead(g_hDbgModRWSem); + RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFile); + pDbgMod->pszImgFile = pszImgFileOrg; + RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszDbgFile); + pDbgMod->pszDbgFile = NULL; + return rc; +} + + +static int rtDbgModFromMachOImageWorker(PRTDBGMODINT pDbgMod, RTLDRARCH enmArch, uint32_t cbImage, + uint32_t cSegs, PCRTDBGSEGMENT paSegs, PCRTUUID pUuid, RTDBGCFG hDbgCfg, uint32_t fFlags) +{ + RT_NOREF_PV(cbImage); RT_NOREF_PV(cSegs); RT_NOREF_PV(paSegs); + + RTDBGMODMACHOARGS Args; + Args.enmArch = enmArch; + Args.pUuid = pUuid && RTUuidIsNull(pUuid) ? pUuid : NULL; + Args.fOpenImage = false; + Args.fFlags = fFlags; + + /* + * Search for the .dSYM bundle first, since that's generally all we need. + */ + int rc = RTDbgCfgOpenDsymBundle(hDbgCfg, pDbgMod->pszImgFile, pUuid, + rtDbgModFromMachOImageOpenDsymMachOCallback, pDbgMod, &Args); + if (RT_FAILURE(rc)) + { + /* + * If we cannot get at the .dSYM, try the executable image. + */ + Args.fOpenImage = true; + rc = RTDbgCfgOpenMachOImage(hDbgCfg, pDbgMod->pszImgFile, pUuid, + rtDbgModFromMachOImageOpenDsymMachOCallback, pDbgMod, &Args); + } + return rc; +} + + +/** @callback_method_impl{FNRTDBGMODDEFERRED} */ +static DECLCALLBACK(int) rtDbgModFromMachOImageDeferredCallback(PRTDBGMODINT pDbgMod, PRTDBGMODDEFERRED pDeferred) +{ + return rtDbgModFromMachOImageWorker(pDbgMod, pDeferred->u.MachO.enmArch, pDeferred->cbImage, + pDeferred->u.MachO.cSegs, pDeferred->u.MachO.aSegs, + &pDeferred->u.MachO.Uuid, pDeferred->hDbgCfg, pDeferred->fFlags); +} + + +RTDECL(int) RTDbgModCreateFromMachOImage(PRTDBGMOD phDbgMod, const char *pszFilename, const char *pszName, RTLDRARCH enmArch, + PRTLDRMOD phLdrModIn, uint32_t cbImage, uint32_t cSegs, PCRTDBGSEGMENT paSegs, + PCRTUUID pUuid, RTDBGCFG hDbgCfg, uint32_t fFlags) +{ + /* + * Input validation and lazy initialization. + */ + AssertPtrReturn(phDbgMod, VERR_INVALID_POINTER); + *phDbgMod = NIL_RTDBGMOD; + AssertPtrReturn(pszFilename, VERR_INVALID_POINTER); + AssertReturn(*pszFilename, VERR_INVALID_PARAMETER); + if (!pszName) + pszName = RTPathFilenameEx(pszFilename, RTPATH_STR_F_STYLE_HOST); + AssertPtrReturn(pszName, VERR_INVALID_POINTER); + if (cSegs) + { + AssertReturn(cSegs < 1024, VERR_INVALID_PARAMETER); + AssertPtrReturn(paSegs, VERR_INVALID_POINTER); + AssertReturn(!cbImage, VERR_INVALID_PARAMETER); + } + AssertPtrNullReturn(pUuid, VERR_INVALID_POINTER); + AssertReturn(!(fFlags & ~RTDBGMOD_F_VALID_MASK), VERR_INVALID_FLAGS); + + AssertPtrNullReturn(phLdrModIn, VERR_INVALID_POINTER); + RTLDRMOD hLdrModIn = phLdrModIn ? *phLdrModIn : NIL_RTLDRMOD; + AssertReturn(hLdrModIn == NIL_RTLDRMOD || RTLdrSize(hLdrModIn) != ~(size_t)0, VERR_INVALID_HANDLE); + + AssertReturn(cbImage || cSegs || hLdrModIn != NIL_RTLDRMOD, VERR_INVALID_PARAMETER); + + int rc = rtDbgModLazyInit(); + if (RT_FAILURE(rc)) + return rc; + + uint64_t fDbgCfg = 0; + if (hDbgCfg) + { + rc = RTDbgCfgQueryUInt(hDbgCfg, RTDBGCFGPROP_FLAGS, &fDbgCfg); + AssertRCReturn(rc, rc); + } + + /* + * If we got no UUID but the caller passed in a module handle, try + * query the UUID from it. + */ + RTUUID UuidFromImage = RTUUID_INITIALIZE_NULL; + if ((!pUuid || RTUuidIsNull(pUuid)) && hLdrModIn != NIL_RTLDRMOD) + { + rc = RTLdrQueryProp(hLdrModIn, RTLDRPROP_UUID, &UuidFromImage, sizeof(UuidFromImage)); + if (RT_SUCCESS(rc)) + pUuid = &UuidFromImage; + } + + /* + * Allocate a new module instance. + */ + PRTDBGMODINT pDbgMod = (PRTDBGMODINT)RTMemAllocZ(sizeof(*pDbgMod)); + if (!pDbgMod) + return VERR_NO_MEMORY; + pDbgMod->u32Magic = RTDBGMOD_MAGIC; + pDbgMod->cRefs = 1; + rc = RTCritSectInit(&pDbgMod->CritSect); + if (RT_SUCCESS(rc)) + { + pDbgMod->pszName = RTStrCacheEnterLower(g_hDbgModStrCache, pszName); + if (pDbgMod->pszName) + { + pDbgMod->pszImgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename); + if (pDbgMod->pszImgFile) + { + RTStrCacheRetain(pDbgMod->pszImgFile); + pDbgMod->pszImgFileSpecified = pDbgMod->pszImgFile; + + /* + * Load it immediately? + */ + if ( !(fDbgCfg & RTDBGCFG_FLAGS_DEFERRED) + || cSegs /* for the time being. */ + || (!cbImage && !cSegs) + || (fFlags & RTDBGMOD_F_NOT_DEFERRED) + || hLdrModIn != NIL_RTLDRMOD) + { + rc = rtDbgModFromMachOImageWorker(pDbgMod, enmArch, cbImage, cSegs, paSegs, pUuid, hDbgCfg, fFlags); + if (RT_FAILURE(rc) && hLdrModIn != NIL_RTLDRMOD) + { + /* + * Create module based on exports from hLdrModIn. + */ + if (!cbImage) + cbImage = (uint32_t)RTLdrSize(hLdrModIn); + pDbgMod->pImgVt = &g_rtDbgModVtImgLdr; + + rc = rtDbgModLdrOpenFromHandle(pDbgMod, hLdrModIn); + if (RT_SUCCESS(rc)) + { + /* We now own the loader handle, so clear the caller variable. */ + if (phLdrModIn) + *phLdrModIn = NIL_RTLDRMOD; + + /** @todo delayed exports stuff */ + rc = rtDbgModCreateForExports(pDbgMod); + } + } + } + else + { + /* + * Procrastinate. Need image size atm. + */ + PRTDBGMODDEFERRED pDeferred; + rc = rtDbgModDeferredCreate(pDbgMod, rtDbgModFromMachOImageDeferredCallback, cbImage, hDbgCfg, + RT_UOFFSETOF_DYN(RTDBGMODDEFERRED, u.MachO.aSegs[cSegs]), + 0 /*fFlags*/, &pDeferred); + if (RT_SUCCESS(rc)) + { + pDeferred->u.MachO.Uuid = *pUuid; + pDeferred->u.MachO.enmArch = enmArch; + pDeferred->u.MachO.cSegs = cSegs; + if (cSegs) + memcpy(&pDeferred->u.MachO.aSegs, paSegs, cSegs * sizeof(paSegs[0])); + } + } + if (RT_SUCCESS(rc)) + { + *phDbgMod = pDbgMod; + return VINF_SUCCESS; + } + + /* Failed, bail out. */ + RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszName); + } + else + rc = VERR_NO_STR_MEMORY; + RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFileSpecified); + RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFile); + } + else + rc = VERR_NO_STR_MEMORY; + RTCritSectDelete(&pDbgMod->CritSect); + } + + RTMemFree(pDbgMod); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgModCreateFromMachOImage); + + + +/** + * Destroys an module after the reference count has reached zero. + * + * @param pDbgMod The module instance. + */ +static void rtDbgModDestroy(PRTDBGMODINT pDbgMod) +{ + /* + * Close the debug info interpreter first, then the image interpret. + */ + RTCritSectEnter(&pDbgMod->CritSect); /* paranoia */ + + if (pDbgMod->pDbgVt) + { + pDbgMod->pDbgVt->pfnClose(pDbgMod); + pDbgMod->pDbgVt = NULL; + pDbgMod->pvDbgPriv = NULL; + } + + if (pDbgMod->pImgVt) + { + pDbgMod->pImgVt->pfnClose(pDbgMod); + pDbgMod->pImgVt = NULL; + pDbgMod->pvImgPriv = NULL; + } + + /* + * Free the resources. + */ + ASMAtomicWriteU32(&pDbgMod->u32Magic, ~RTDBGMOD_MAGIC); + RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszName); + RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFile); + RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFileSpecified); + RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszDbgFile); + RTCritSectLeave(&pDbgMod->CritSect); /* paranoia */ + RTCritSectDelete(&pDbgMod->CritSect); + RTMemFree(pDbgMod); +} + + +RTDECL(uint32_t) RTDbgModRetain(RTDBGMOD hDbgMod) +{ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, UINT32_MAX); + return ASMAtomicIncU32(&pDbgMod->cRefs); +} +RT_EXPORT_SYMBOL(RTDbgModRetain); + + +RTDECL(uint32_t) RTDbgModRelease(RTDBGMOD hDbgMod) +{ + if (hDbgMod == NIL_RTDBGMOD) + return 0; + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, UINT32_MAX); + + uint32_t cRefs = ASMAtomicDecU32(&pDbgMod->cRefs); + if (!cRefs) + rtDbgModDestroy(pDbgMod); + return cRefs; +} +RT_EXPORT_SYMBOL(RTDbgModRelease); + + +RTDECL(const char *) RTDbgModName(RTDBGMOD hDbgMod) +{ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, NULL); + return pDbgMod->pszName; +} +RT_EXPORT_SYMBOL(RTDbgModName); + + +RTDECL(const char *) RTDbgModDebugFile(RTDBGMOD hDbgMod) +{ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, NULL); + if (pDbgMod->fDeferred || pDbgMod->fExports) + return NULL; + return pDbgMod->pszDbgFile; +} +RT_EXPORT_SYMBOL(RTDbgModDebugFile); + + +RTDECL(const char *) RTDbgModImageFile(RTDBGMOD hDbgMod) +{ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, NULL); + return pDbgMod->pszImgFileSpecified; +} +RT_EXPORT_SYMBOL(RTDbgModImageFile); + + +RTDECL(const char *) RTDbgModImageFileUsed(RTDBGMOD hDbgMod) +{ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, NULL); + return pDbgMod->pszImgFile == pDbgMod->pszImgFileSpecified ? NULL : pDbgMod->pszImgFile; +} +RT_EXPORT_SYMBOL(RTDbgModImageFileUsed); + + +RTDECL(bool) RTDbgModIsDeferred(RTDBGMOD hDbgMod) +{ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, false); + return pDbgMod->fDeferred; +} + + +RTDECL(bool) RTDbgModIsExports(RTDBGMOD hDbgMod) +{ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, false); + return pDbgMod->fExports; +} + + +RTDECL(int) RTDbgModRemoveAll(RTDBGMOD hDbgMod, bool fLeaveSegments) +{ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE); + + RTDBGMOD_LOCK(pDbgMod); + + /* Only possible on container modules. */ + int rc = VINF_SUCCESS; + if (pDbgMod->pDbgVt != &g_rtDbgModVtDbgContainer) + { + if (fLeaveSegments) + { + rc = rtDbgModContainer_LineRemoveAll(pDbgMod); + if (RT_SUCCESS(rc)) + rc = rtDbgModContainer_SymbolRemoveAll(pDbgMod); + } + else + rc = rtDbgModContainer_RemoveAll(pDbgMod); + } + else + rc = VERR_ACCESS_DENIED; + + RTDBGMOD_UNLOCK(pDbgMod); + return rc; +} + + +RTDECL(RTDBGSEGIDX) RTDbgModRvaToSegOff(RTDBGMOD hDbgMod, RTUINTPTR uRva, PRTUINTPTR poffSeg) +{ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, NIL_RTDBGSEGIDX); + RTDBGMOD_LOCK(pDbgMod); + + RTDBGSEGIDX iSeg = pDbgMod->pDbgVt->pfnRvaToSegOff(pDbgMod, uRva, poffSeg); + + RTDBGMOD_UNLOCK(pDbgMod); + return iSeg; +} +RT_EXPORT_SYMBOL(RTDbgModRvaToSegOff); + + +RTDECL(uint64_t) RTDbgModGetTag(RTDBGMOD hDbgMod) +{ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, 0); + return pDbgMod->uTag; +} +RT_EXPORT_SYMBOL(RTDbgModGetTag); + + +RTDECL(int) RTDbgModSetTag(RTDBGMOD hDbgMod, uint64_t uTag) +{ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE); + RTDBGMOD_LOCK(pDbgMod); + + pDbgMod->uTag = uTag; + + RTDBGMOD_UNLOCK(pDbgMod); + return VINF_SUCCESS; +} +RT_EXPORT_SYMBOL(RTDbgModSetTag); + + +RTDECL(RTUINTPTR) RTDbgModImageSize(RTDBGMOD hDbgMod) +{ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, RTUINTPTR_MAX); + RTDBGMOD_LOCK(pDbgMod); + + RTUINTPTR cbImage = pDbgMod->pDbgVt->pfnImageSize(pDbgMod); + + RTDBGMOD_UNLOCK(pDbgMod); + return cbImage; +} +RT_EXPORT_SYMBOL(RTDbgModImageSize); + + +RTDECL(RTLDRFMT) RTDbgModImageGetFormat(RTDBGMOD hDbgMod) +{ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, RTLDRFMT_INVALID); + RTDBGMOD_LOCK(pDbgMod); + + RTLDRFMT enmFmt; + if ( pDbgMod->pImgVt + && pDbgMod->pImgVt->pfnGetFormat) + enmFmt = pDbgMod->pImgVt->pfnGetFormat(pDbgMod); + else + enmFmt = RTLDRFMT_INVALID; + + RTDBGMOD_UNLOCK(pDbgMod); + return enmFmt; +} +RT_EXPORT_SYMBOL(RTDbgModImageGetFormat); + + +RTDECL(RTLDRARCH) RTDbgModImageGetArch(RTDBGMOD hDbgMod) +{ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, RTLDRARCH_INVALID); + RTDBGMOD_LOCK(pDbgMod); + + RTLDRARCH enmArch; + if ( pDbgMod->pImgVt + && pDbgMod->pImgVt->pfnGetArch) + enmArch = pDbgMod->pImgVt->pfnGetArch(pDbgMod); + else + enmArch = RTLDRARCH_WHATEVER; + + RTDBGMOD_UNLOCK(pDbgMod); + return enmArch; +} +RT_EXPORT_SYMBOL(RTDbgModImageGetArch); + + +RTDECL(int) RTDbgModImageQueryProp(RTDBGMOD hDbgMod, RTLDRPROP enmProp, void *pvBuf, size_t cbBuf, size_t *pcbRet) +{ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE); + AssertPtrNullReturn(pcbRet, VERR_INVALID_POINTER); + RTDBGMOD_LOCK(pDbgMod); + + int rc; + if ( pDbgMod->pImgVt + && pDbgMod->pImgVt->pfnQueryProp) + rc = pDbgMod->pImgVt->pfnQueryProp(pDbgMod, enmProp, pvBuf, cbBuf, pcbRet); + else + rc = VERR_NOT_FOUND; + + RTDBGMOD_UNLOCK(pDbgMod); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgModImageQueryProp); + + +RTDECL(int) RTDbgModSegmentAdd(RTDBGMOD hDbgMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName, + uint32_t fFlags, PRTDBGSEGIDX piSeg) +{ + /* + * Validate input. + */ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE); + AssertMsgReturn(uRva + cb >= uRva, ("uRva=%RTptr cb=%RTptr\n", uRva, cb), VERR_DBG_ADDRESS_WRAP); + Assert(*pszName); + size_t cchName = strlen(pszName); + AssertReturn(cchName > 0, VERR_DBG_SEGMENT_NAME_OUT_OF_RANGE); + AssertReturn(cchName < RTDBG_SEGMENT_NAME_LENGTH, VERR_DBG_SEGMENT_NAME_OUT_OF_RANGE); + AssertMsgReturn(!fFlags, ("%#x\n", fFlags), VERR_INVALID_PARAMETER); + AssertPtrNull(piSeg); + AssertMsgReturn(!piSeg || *piSeg == NIL_RTDBGSEGIDX || *piSeg <= RTDBGSEGIDX_LAST, ("%#x\n", *piSeg), VERR_DBG_SPECIAL_SEGMENT); + + /* + * Do the deed. + */ + RTDBGMOD_LOCK(pDbgMod); + int rc = pDbgMod->pDbgVt->pfnSegmentAdd(pDbgMod, uRva, cb, pszName, cchName, fFlags, piSeg); + RTDBGMOD_UNLOCK(pDbgMod); + + return rc; + +} +RT_EXPORT_SYMBOL(RTDbgModSegmentAdd); + + +RTDECL(RTDBGSEGIDX) RTDbgModSegmentCount(RTDBGMOD hDbgMod) +{ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, NIL_RTDBGSEGIDX); + RTDBGMOD_LOCK(pDbgMod); + + RTDBGSEGIDX cSegs = pDbgMod->pDbgVt->pfnSegmentCount(pDbgMod); + + RTDBGMOD_UNLOCK(pDbgMod); + return cSegs; +} +RT_EXPORT_SYMBOL(RTDbgModSegmentCount); + + +RTDECL(int) RTDbgModSegmentByIndex(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo) +{ + AssertMsgReturn(iSeg <= RTDBGSEGIDX_LAST, ("%#x\n", iSeg), VERR_DBG_SPECIAL_SEGMENT); + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE); + RTDBGMOD_LOCK(pDbgMod); + + int rc = pDbgMod->pDbgVt->pfnSegmentByIndex(pDbgMod, iSeg, pSegInfo); + + RTDBGMOD_UNLOCK(pDbgMod); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgModSegmentByIndex); + + +RTDECL(RTUINTPTR) RTDbgModSegmentSize(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg) +{ + if (iSeg == RTDBGSEGIDX_RVA) + return RTDbgModImageSize(hDbgMod); + RTDBGSEGMENT SegInfo; + int rc = RTDbgModSegmentByIndex(hDbgMod, iSeg, &SegInfo); + return RT_SUCCESS(rc) ? SegInfo.cb : RTUINTPTR_MAX; +} +RT_EXPORT_SYMBOL(RTDbgModSegmentSize); + + +RTDECL(RTUINTPTR) RTDbgModSegmentRva(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg) +{ + RTDBGSEGMENT SegInfo; + int rc = RTDbgModSegmentByIndex(hDbgMod, iSeg, &SegInfo); + return RT_SUCCESS(rc) ? SegInfo.uRva : RTUINTPTR_MAX; +} +RT_EXPORT_SYMBOL(RTDbgModSegmentRva); + + +RTDECL(int) RTDbgModSymbolAdd(RTDBGMOD hDbgMod, const char *pszSymbol, RTDBGSEGIDX iSeg, RTUINTPTR off, + RTUINTPTR cb, uint32_t fFlags, uint32_t *piOrdinal) +{ + /* + * Validate input. + */ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE); + AssertPtrReturn(pszSymbol, VERR_INVALID_POINTER); + size_t cchSymbol = strlen(pszSymbol); + AssertReturn(cchSymbol, VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE); + AssertReturn(cchSymbol < RTDBG_SYMBOL_NAME_LENGTH, VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE); + AssertMsgReturn( iSeg <= RTDBGSEGIDX_LAST + || ( iSeg >= RTDBGSEGIDX_SPECIAL_FIRST + && iSeg <= RTDBGSEGIDX_SPECIAL_LAST), + ("%#x\n", iSeg), + VERR_DBG_INVALID_SEGMENT_INDEX); + AssertMsgReturn(off + cb >= off, ("off=%RTptr cb=%RTptr\n", off, cb), VERR_DBG_ADDRESS_WRAP); + AssertReturn(!(fFlags & ~RTDBGSYMBOLADD_F_VALID_MASK), VERR_INVALID_FLAGS); + + RTDBGMOD_LOCK(pDbgMod); + + /* + * Convert RVAs. + */ + if (iSeg == RTDBGSEGIDX_RVA) + { + iSeg = pDbgMod->pDbgVt->pfnRvaToSegOff(pDbgMod, off, &off); + if (iSeg == NIL_RTDBGSEGIDX) + { + RTDBGMOD_UNLOCK(pDbgMod); + return VERR_DBG_INVALID_RVA; + } + } + + /* + * Get down to business. + */ + int rc = pDbgMod->pDbgVt->pfnSymbolAdd(pDbgMod, pszSymbol, cchSymbol, iSeg, off, cb, fFlags, piOrdinal); + + RTDBGMOD_UNLOCK(pDbgMod); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgModSymbolAdd); + + +RTDECL(uint32_t) RTDbgModSymbolCount(RTDBGMOD hDbgMod) +{ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, UINT32_MAX); + RTDBGMOD_LOCK(pDbgMod); + + uint32_t cSymbols = pDbgMod->pDbgVt->pfnSymbolCount(pDbgMod); + + RTDBGMOD_UNLOCK(pDbgMod); + return cSymbols; +} +RT_EXPORT_SYMBOL(RTDbgModSymbolCount); + + +RTDECL(int) RTDbgModSymbolByOrdinal(RTDBGMOD hDbgMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo) +{ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE); + RTDBGMOD_LOCK(pDbgMod); + + int rc = pDbgMod->pDbgVt->pfnSymbolByOrdinal(pDbgMod, iOrdinal, pSymInfo); + + RTDBGMOD_UNLOCK(pDbgMod); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgModSymbolByOrdinal); + + +RTDECL(int) RTDbgModSymbolByOrdinalA(RTDBGMOD hDbgMod, uint32_t iOrdinal, PRTDBGSYMBOL *ppSymInfo) +{ + AssertPtr(ppSymInfo); + *ppSymInfo = NULL; + + PRTDBGSYMBOL pSymInfo = RTDbgSymbolAlloc(); + if (!pSymInfo) + return VERR_NO_MEMORY; + + int rc = RTDbgModSymbolByOrdinal(hDbgMod, iOrdinal, pSymInfo); + + if (RT_SUCCESS(rc)) + *ppSymInfo = pSymInfo; + else + RTDbgSymbolFree(pSymInfo); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgModSymbolByOrdinalA); + + +/** + * Return a segment number/name as symbol if we couldn't find any + * valid symbols within the segment. + */ +DECL_NO_INLINE(static, int) +rtDbgModSymbolByAddrTrySegments(PRTDBGMODINT pDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR off, + PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo) +{ + Assert(iSeg <= RTDBGSEGIDX_LAST); + RTDBGSEGMENT SegInfo; + int rc = pDbgMod->pDbgVt->pfnSegmentByIndex(pDbgMod, iSeg, &SegInfo); + if (RT_SUCCESS(rc)) + { + pSymInfo->Value = 0; + pSymInfo->cb = SegInfo.cb; + pSymInfo->offSeg = 0; + pSymInfo->iSeg = iSeg; + pSymInfo->fFlags = 0; + if (SegInfo.szName[0]) + RTStrPrintf(pSymInfo->szName, sizeof(pSymInfo->szName), "start_seg%u_%s", SegInfo.iSeg, SegInfo.szName); + else + RTStrPrintf(pSymInfo->szName, sizeof(pSymInfo->szName), "start_seg%u", SegInfo.iSeg); + if (poffDisp) + *poffDisp = off; + return VINF_SUCCESS; + } + return VERR_SYMBOL_NOT_FOUND; +} + + +RTDECL(int) RTDbgModSymbolByAddr(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags, + PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo) +{ + /* + * Validate input. + */ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE); + AssertPtrNull(poffDisp); + AssertPtr(pSymInfo); + AssertReturn(!(fFlags & ~RTDBGSYMADDR_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER); + + RTDBGMOD_LOCK(pDbgMod); + + /* + * Convert RVAs. + */ + if (iSeg == RTDBGSEGIDX_RVA) + { + iSeg = pDbgMod->pDbgVt->pfnRvaToSegOff(pDbgMod, off, &off); + if (iSeg == NIL_RTDBGSEGIDX) + { + RTDBGMOD_UNLOCK(pDbgMod); + return VERR_DBG_INVALID_RVA; + } + } + + /* + * Get down to business. + */ + int rc = pDbgMod->pDbgVt->pfnSymbolByAddr(pDbgMod, iSeg, off, fFlags, poffDisp, pSymInfo); + + /* If we failed to locate a symbol, try use the specified segment as a reference. */ + if ( rc == VERR_SYMBOL_NOT_FOUND + && iSeg <= RTDBGSEGIDX_LAST + && !(fFlags & RTDBGSYMADDR_FLAGS_GREATER_OR_EQUAL)) + rc = rtDbgModSymbolByAddrTrySegments(pDbgMod, iSeg, off, poffDisp, pSymInfo); + + RTDBGMOD_UNLOCK(pDbgMod); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgModSymbolByAddr); + + +RTDECL(int) RTDbgModSymbolByAddrA(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags, + PRTINTPTR poffDisp, PRTDBGSYMBOL *ppSymInfo) +{ + AssertPtr(ppSymInfo); + *ppSymInfo = NULL; + + PRTDBGSYMBOL pSymInfo = RTDbgSymbolAlloc(); + if (!pSymInfo) + return VERR_NO_MEMORY; + + int rc = RTDbgModSymbolByAddr(hDbgMod, iSeg, off, fFlags, poffDisp, pSymInfo); + + if (RT_SUCCESS(rc)) + *ppSymInfo = pSymInfo; + else + RTDbgSymbolFree(pSymInfo); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgModSymbolByAddrA); + + +RTDECL(int) RTDbgModSymbolByName(RTDBGMOD hDbgMod, const char *pszSymbol, PRTDBGSYMBOL pSymInfo) +{ + /* + * Validate input. + */ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE); + AssertPtr(pszSymbol); + size_t cchSymbol = strlen(pszSymbol); + AssertReturn(cchSymbol, VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE); + AssertReturn(cchSymbol < RTDBG_SYMBOL_NAME_LENGTH, VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE); + AssertPtr(pSymInfo); + + /* + * Make the query. + */ + RTDBGMOD_LOCK(pDbgMod); + int rc = pDbgMod->pDbgVt->pfnSymbolByName(pDbgMod, pszSymbol, cchSymbol, pSymInfo); + RTDBGMOD_UNLOCK(pDbgMod); + + return rc; +} +RT_EXPORT_SYMBOL(RTDbgModSymbolByName); + + +RTDECL(int) RTDbgModSymbolByNameA(RTDBGMOD hDbgMod, const char *pszSymbol, PRTDBGSYMBOL *ppSymInfo) +{ + AssertPtr(ppSymInfo); + *ppSymInfo = NULL; + + PRTDBGSYMBOL pSymInfo = RTDbgSymbolAlloc(); + if (!pSymInfo) + return VERR_NO_MEMORY; + + int rc = RTDbgModSymbolByName(hDbgMod, pszSymbol, pSymInfo); + + if (RT_SUCCESS(rc)) + *ppSymInfo = pSymInfo; + else + RTDbgSymbolFree(pSymInfo); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgModSymbolByNameA); + + +RTDECL(int) RTDbgModLineAdd(RTDBGMOD hDbgMod, const char *pszFile, uint32_t uLineNo, + RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t *piOrdinal) +{ + /* + * Validate input. + */ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE); + AssertPtr(pszFile); + size_t cchFile = strlen(pszFile); + AssertReturn(cchFile, VERR_DBG_FILE_NAME_OUT_OF_RANGE); + AssertReturn(cchFile < RTDBG_FILE_NAME_LENGTH, VERR_DBG_FILE_NAME_OUT_OF_RANGE); + AssertMsgReturn( iSeg <= RTDBGSEGIDX_LAST + || iSeg == RTDBGSEGIDX_RVA, + ("%#x\n", iSeg), + VERR_DBG_INVALID_SEGMENT_INDEX); + AssertReturn(uLineNo > 0 && uLineNo < UINT32_MAX, VERR_INVALID_PARAMETER); + + RTDBGMOD_LOCK(pDbgMod); + + /* + * Convert RVAs. + */ + if (iSeg == RTDBGSEGIDX_RVA) + { + iSeg = pDbgMod->pDbgVt->pfnRvaToSegOff(pDbgMod, off, &off); + if (iSeg == NIL_RTDBGSEGIDX) + { + RTDBGMOD_UNLOCK(pDbgMod); + return VERR_DBG_INVALID_RVA; + } + } + + /* + * Get down to business. + */ + int rc = pDbgMod->pDbgVt->pfnLineAdd(pDbgMod, pszFile, cchFile, uLineNo, iSeg, off, piOrdinal); + + RTDBGMOD_UNLOCK(pDbgMod); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgModLineAdd); + + +RTDECL(uint32_t) RTDbgModLineCount(RTDBGMOD hDbgMod) +{ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, UINT32_MAX); + RTDBGMOD_LOCK(pDbgMod); + + uint32_t cLineNumbers = pDbgMod->pDbgVt->pfnLineCount(pDbgMod); + + RTDBGMOD_UNLOCK(pDbgMod); + return cLineNumbers; +} +RT_EXPORT_SYMBOL(RTDbgModLineCount); + + +RTDECL(int) RTDbgModLineByOrdinal(RTDBGMOD hDbgMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo) +{ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE); + RTDBGMOD_LOCK(pDbgMod); + + int rc = pDbgMod->pDbgVt->pfnLineByOrdinal(pDbgMod, iOrdinal, pLineInfo); + + RTDBGMOD_UNLOCK(pDbgMod); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgModLineByOrdinal); + + +RTDECL(int) RTDbgModLineByOrdinalA(RTDBGMOD hDbgMod, uint32_t iOrdinal, PRTDBGLINE *ppLineInfo) +{ + AssertPtr(ppLineInfo); + *ppLineInfo = NULL; + + PRTDBGLINE pLineInfo = RTDbgLineAlloc(); + if (!pLineInfo) + return VERR_NO_MEMORY; + + int rc = RTDbgModLineByOrdinal(hDbgMod, iOrdinal, pLineInfo); + + if (RT_SUCCESS(rc)) + *ppLineInfo = pLineInfo; + else + RTDbgLineFree(pLineInfo); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgModLineByOrdinalA); + + +RTDECL(int) RTDbgModLineByAddr(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTINTPTR poffDisp, PRTDBGLINE pLineInfo) +{ + /* + * Validate input. + */ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE); + AssertPtrNull(poffDisp); + AssertPtr(pLineInfo); + + RTDBGMOD_LOCK(pDbgMod); + + /* + * Convert RVAs. + */ + if (iSeg == RTDBGSEGIDX_RVA) + { + iSeg = pDbgMod->pDbgVt->pfnRvaToSegOff(pDbgMod, off, &off); + if (iSeg == NIL_RTDBGSEGIDX) + { + RTDBGMOD_UNLOCK(pDbgMod); + return VERR_DBG_INVALID_RVA; + } + } + + int rc = pDbgMod->pDbgVt->pfnLineByAddr(pDbgMod, iSeg, off, poffDisp, pLineInfo); + + RTDBGMOD_UNLOCK(pDbgMod); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgModLineByAddr); + + +RTDECL(int) RTDbgModLineByAddrA(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTINTPTR poffDisp, PRTDBGLINE *ppLineInfo) +{ + AssertPtr(ppLineInfo); + *ppLineInfo = NULL; + + PRTDBGLINE pLineInfo = RTDbgLineAlloc(); + if (!pLineInfo) + return VERR_NO_MEMORY; + + int rc = RTDbgModLineByAddr(hDbgMod, iSeg, off, poffDisp, pLineInfo); + + if (RT_SUCCESS(rc)) + *ppLineInfo = pLineInfo; + else + RTDbgLineFree(pLineInfo); + return rc; +} +RT_EXPORT_SYMBOL(RTDbgModLineByAddrA); + + +RTDECL(int) RTDbgModUnwindFrame(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState) +{ + /* + * Validate input. + */ + PRTDBGMODINT pDbgMod = hDbgMod; + RTDBGMOD_VALID_RETURN_RC(pDbgMod, VERR_INVALID_HANDLE); + AssertPtr(pState); + AssertReturn(pState->u32Magic == RTDBGUNWINDSTATE_MAGIC, VERR_INVALID_MAGIC); + + RTDBGMOD_LOCK(pDbgMod); + + /* + * Convert RVAs. + */ + if (iSeg == RTDBGSEGIDX_RVA) + { + iSeg = pDbgMod->pDbgVt->pfnRvaToSegOff(pDbgMod, off, &off); + if (iSeg == NIL_RTDBGSEGIDX) + { + RTDBGMOD_UNLOCK(pDbgMod); + return VERR_DBG_INVALID_RVA; + } + } + + /* + * Try the debug module first, then the image. + */ + int rc = VERR_DBG_NO_UNWIND_INFO; + if (pDbgMod->pDbgVt->pfnUnwindFrame) + rc = pDbgMod->pDbgVt->pfnUnwindFrame(pDbgMod, iSeg, off, pState); + if ( ( rc == VERR_DBG_NO_UNWIND_INFO + || rc == VERR_DBG_UNWIND_INFO_NOT_FOUND) + && pDbgMod->pImgVt + && pDbgMod->pImgVt->pfnUnwindFrame) + { + if (rc == VERR_DBG_NO_UNWIND_INFO) + rc = pDbgMod->pImgVt->pfnUnwindFrame(pDbgMod, iSeg, off, pState); + else + { + rc = pDbgMod->pImgVt->pfnUnwindFrame(pDbgMod, iSeg, off, pState); + if (rc == VERR_DBG_NO_UNWIND_INFO) + rc = VERR_DBG_UNWIND_INFO_NOT_FOUND; + } + } + + RTDBGMOD_UNLOCK(pDbgMod); + return rc; + +} +RT_EXPORT_SYMBOL(RTDbgModUnwindFrame); + diff --git a/src/VBox/Runtime/common/dbg/dbgmodcodeview.cpp b/src/VBox/Runtime/common/dbg/dbgmodcodeview.cpp new file mode 100644 index 00000000..5ce270d0 --- /dev/null +++ b/src/VBox/Runtime/common/dbg/dbgmodcodeview.cpp @@ -0,0 +1,3197 @@ +/* $Id: dbgmodcodeview.cpp $ */ +/** @file + * IPRT - Debug Module Reader For Microsoft CodeView and COFF. + * + * Based on the following documentation (plus guess work and googling): + * + * - "Tools Interface Standard (TIS) Formats Specification for Windows", + * dated February 1993, version 1.0. + * + * - "Visual C++ 5.0 Symbolic Debug Information Specification" chapter of + * SPECS.CHM from MSDN Library October 2001. + * + * - "High Level Languages Debug Table Documentation", aka HLLDBG.HTML, aka + * IBMHLL.HTML, last changed 1996-07-08. + * + * Testcases using RTLdrFlt: + * - VBoxPcBios.sym at 0xf0000. + * - NT4 kernel PE image (coff syms). + */ + +/* + * Copyright (C) 2013-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_DBG +#include <iprt/dbg.h> +#include "internal/iprt.h" + +#include <iprt/alloca.h> +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/file.h> +#include <iprt/latin1.h> +#include <iprt/log.h> +#include <iprt/mem.h> +#include <iprt/param.h> +#include <iprt/path.h> +#include <iprt/sort.h> +#include <iprt/string.h> +#include <iprt/strcache.h> +#include "internal/dbgmod.h" +#include "internal/magics.h" + +#include <iprt/formats/codeview.h> +#include <iprt/formats/pecoff.h> + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * File type. + */ +typedef enum RTCVFILETYPE +{ + RTCVFILETYPE_INVALID = 0, + /** Executable image. */ + RTCVFILETYPE_IMAGE, + /** A DBG-file with a IMAGE_SEPARATE_DEBUG_HEADER. */ + RTCVFILETYPE_DBG, + /** A PDB file. */ + RTCVFILETYPE_PDB, + /** Some other kind of file with CV at the end. */ + RTCVFILETYPE_OTHER_AT_END, + /** The end of the valid values. */ + RTCVFILETYPE_END, + /** Type blowup. */ + RTCVFILETYPE_32BIT_HACK = 0x7fffffff +} RTCVFILETYPE; + + +/** + * CodeView debug info reader instance. + */ +typedef struct RTDBGMODCV +{ + /** Using a container for managing the debug info. */ + RTDBGMOD hCnt; + + /** @name Codeview details + * @{ */ + /** The code view magic (used as format indicator). */ + uint32_t u32CvMagic; + /** The offset of the CV debug info in the file. */ + uint32_t offBase; + /** The size of the CV debug info. */ + uint32_t cbDbgInfo; + /** The offset of the subsection directory (relative to offBase). */ + uint32_t offDir; + /** @} */ + + /** @name COFF details. + * @{ */ + /** Offset of the COFF header. */ + uint32_t offCoffDbgInfo; + /** The size of the COFF debug info. */ + uint32_t cbCoffDbgInfo; + /** The COFF debug info header. */ + IMAGE_COFF_SYMBOLS_HEADER CoffHdr; + /** @} */ + + /** The file type. */ + RTCVFILETYPE enmType; + /** The file handle (if external). */ + RTFILE hFile; + /** Pointer to the module (no reference retained). */ + PRTDBGMODINT pMod; + + /** The image size, if we know it. This is 0 if we don't know it. */ + uint32_t cbImage; + + /** Indicates that we've loaded segments intot he container already. */ + bool fHaveLoadedSegments; + /** Alternative address translation method for DOS frames. */ + bool fHaveDosFrames; + + /** @name Codeview Parsing state. + * @{ */ + /** Number of directory entries. */ + uint32_t cDirEnts; + /** The directory (converted to 32-bit). */ + PRTCVDIRENT32 paDirEnts; + /** Current debugging style when parsing modules. */ + uint16_t uCurStyle; + /** Current debugging style version (HLL only). */ + uint16_t uCurStyleVer; + + /** The segment map (if present). */ + PRTCVSEGMAP pSegMap; + /** Segment names. */ + char *pszzSegNames; + /** The size of the segment names. */ + uint32_t cbSegNames; + + /** Size of the block pchSrcStrings points to. */ + size_t cbSrcStrings; + /** Buffer space allocated for the source string table. */ + size_t cbSrcStringsAlloc; + /** Copy of the last CV8 source string table. */ + char *pchSrcStrings; + + /** The size of the current source information table. */ + size_t cbSrcInfo; + /** Buffer space allocated for the source information table. */ + size_t cbSrcInfoAlloc; + /** Copy of the last CV8 source information table. */ + uint8_t *pbSrcInfo; + + /** @} */ + +} RTDBGMODCV; +/** Pointer to a codeview debug info reader instance. */ +typedef RTDBGMODCV *PRTDBGMODCV; +/** Pointer to a const codeview debug info reader instance. */ +typedef RTDBGMODCV *PCRTDBGMODCV; + + + +/** + * Subsection callback. + * + * @returns IPRT status code. + * @param pThis The CodeView debug info reader instance. + * @param pvSubSect Pointer to the subsection data. + * @param cbSubSect The size of the subsection data. + * @param pDirEnt The directory entry. + */ +typedef DECLCALLBACKTYPE(int, FNDBGMODCVSUBSECTCALLBACK,(PRTDBGMODCV pThis, void const *pvSubSect, size_t cbSubSect, + PCRTCVDIRENT32 pDirEnt)); +/** Pointer to a subsection callback. */ +typedef FNDBGMODCVSUBSECTCALLBACK *PFNDBGMODCVSUBSECTCALLBACK; + + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** Light weight assert + return w/ fixed status code. */ +#define RTDBGMODCV_CHECK_RET_BF(a_Expr, a_LogArgs) \ + do { \ + if (!(a_Expr)) \ + { \ + Log(("RTDbgCv: Check failed on line %d: " #a_Expr "\n", __LINE__)); \ + Log(a_LogArgs); \ + /*AssertFailed();*/ \ + return VERR_CV_BAD_FORMAT; \ + } \ + } while (0) + + +/** Light weight assert + return w/ fixed status code. */ +#define RTDBGMODCV_CHECK_NOMSG_RET_BF(a_Expr) \ + do { \ + if (!(a_Expr)) \ + { \ + Log(("RTDbgCv: Check failed on line %d: " #a_Expr "\n", __LINE__)); \ + /*AssertFailed();*/ \ + return VERR_CV_BAD_FORMAT; \ + } \ + } while (0) + + + + + +/** + * Reads CodeView information. + * + * @returns IPRT status code. + * @param pThis The CodeView reader instance. + * @param off The offset to start reading at, relative to the + * CodeView base header. + * @param pvBuf The buffer to read into. + * @param cb How many bytes to read. + */ +static int rtDbgModCvReadAt(PRTDBGMODCV pThis, uint32_t off, void *pvBuf, size_t cb) +{ + int rc; + if (pThis->hFile == NIL_RTFILE) + rc = pThis->pMod->pImgVt->pfnReadAt(pThis->pMod, UINT32_MAX, off + pThis->offBase, pvBuf, cb); + else + rc = RTFileReadAt(pThis->hFile, off + pThis->offBase, pvBuf, cb, NULL); + return rc; +} + + +/** + * Reads CodeView information into an allocated buffer. + * + * @returns IPRT status code. + * @param pThis The CodeView reader instance. + * @param off The offset to start reading at, relative to the + * CodeView base header. + * @param ppvBuf Where to return the allocated buffer on success. + * @param cb How many bytes to read. + */ +static int rtDbgModCvReadAtAlloc(PRTDBGMODCV pThis, uint32_t off, void **ppvBuf, size_t cb) +{ + int rc; + void *pvBuf = *ppvBuf = RTMemAlloc(cb); + if (pvBuf) + { + if (pThis->hFile == NIL_RTFILE) + rc = pThis->pMod->pImgVt->pfnReadAt(pThis->pMod, UINT32_MAX, off + pThis->offBase, pvBuf, cb); + else + rc = RTFileReadAt(pThis->hFile, off + pThis->offBase, pvBuf, cb, NULL); + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + + RTMemFree(pvBuf); + *ppvBuf = NULL; + } + else + rc = VERR_NO_MEMORY; + return rc; +} + + +#ifdef LOG_ENABLED +/** + * Gets a name string for a subsection type. + * + * @returns Section name (read only). + * @param uSubSectType The subsection type. + */ +static const char *rtDbgModCvGetSubSectionName(uint16_t uSubSectType) +{ + switch (uSubSectType) + { + case kCvSst_OldModule: return "sstOldModule"; + case kCvSst_OldPublic: return "sstOldPublic"; + case kCvSst_OldTypes: return "sstOldTypes"; + case kCvSst_OldSymbols: return "sstOldSymbols"; + case kCvSst_OldSrcLines: return "sstOldSrcLines"; + case kCvSst_OldLibraries: return "sstOldLibraries"; + case kCvSst_OldImports: return "sstOldImports"; + case kCvSst_OldCompacted: return "sstOldCompacted"; + case kCvSst_OldSrcLnSeg: return "sstOldSrcLnSeg"; + case kCvSst_OldSrcLines3: return "sstOldSrcLines3"; + + case kCvSst_Module: return "sstModule"; + case kCvSst_Types: return "sstTypes"; + case kCvSst_Public: return "sstPublic"; + case kCvSst_PublicSym: return "sstPublicSym"; + case kCvSst_Symbols: return "sstSymbols"; + case kCvSst_AlignSym: return "sstAlignSym"; + case kCvSst_SrcLnSeg: return "sstSrcLnSeg"; + case kCvSst_SrcModule: return "sstSrcModule"; + case kCvSst_Libraries: return "sstLibraries"; + case kCvSst_GlobalSym: return "sstGlobalSym"; + case kCvSst_GlobalPub: return "sstGlobalPub"; + case kCvSst_GlobalTypes: return "sstGlobalTypes"; + case kCvSst_MPC: return "sstMPC"; + case kCvSst_SegMap: return "sstSegMap"; + case kCvSst_SegName: return "sstSegName"; + case kCvSst_PreComp: return "sstPreComp"; + case kCvSst_PreCompMap: return "sstPreCompMap"; + case kCvSst_OffsetMap16: return "sstOffsetMap16"; + case kCvSst_OffsetMap32: return "sstOffsetMap32"; + case kCvSst_FileIndex: return "sstFileIndex"; + case kCvSst_StaticSym: return "sstStaticSym"; + } + static char s_sz[32]; + RTStrPrintf(s_sz, sizeof(s_sz), "Unknown%#x", uSubSectType); + return s_sz; +} +#endif /* LOG_ENABLED */ + + +#ifdef LOG_ENABLED +/** + * Gets a name string for a symbol type. + * + * @returns symbol type name (read only). + * @param enmSymType The symbol type to name. + */ +static const char *rtDbgModCvSsSymTypeName(RTCVSYMTYPE enmSymType) +{ + switch (enmSymType) + { +# define CASE_RET_STR(Name) case kCvSymType_##Name: return #Name; + CASE_RET_STR(Compile); + CASE_RET_STR(Register); + CASE_RET_STR(Constant); + CASE_RET_STR(UDT); + CASE_RET_STR(SSearch); + CASE_RET_STR(End); + CASE_RET_STR(Skip); + CASE_RET_STR(CVReserve); + CASE_RET_STR(ObjName); + CASE_RET_STR(EndArg); + CASE_RET_STR(CobolUDT); + CASE_RET_STR(ManyReg); + CASE_RET_STR(Return); + CASE_RET_STR(EntryThis); + CASE_RET_STR(BpRel16); + CASE_RET_STR(LData16); + CASE_RET_STR(GData16); + CASE_RET_STR(Pub16); + CASE_RET_STR(LProc16); + CASE_RET_STR(GProc16); + CASE_RET_STR(Thunk16); + CASE_RET_STR(BLock16); + CASE_RET_STR(With16); + CASE_RET_STR(Label16); + CASE_RET_STR(CExModel16); + CASE_RET_STR(VftPath16); + CASE_RET_STR(RegRel16); + CASE_RET_STR(BpRel32); + CASE_RET_STR(LData32); + CASE_RET_STR(GData32); + CASE_RET_STR(Pub32); + CASE_RET_STR(LProc32); + CASE_RET_STR(GProc32); + CASE_RET_STR(Thunk32); + CASE_RET_STR(Block32); + CASE_RET_STR(With32); + CASE_RET_STR(Label32); + CASE_RET_STR(CExModel32); + CASE_RET_STR(VftPath32); + CASE_RET_STR(RegRel32); + CASE_RET_STR(LThread32); + CASE_RET_STR(GThread32); + CASE_RET_STR(LProcMips); + CASE_RET_STR(GProcMips); + CASE_RET_STR(ProcRef); + CASE_RET_STR(DataRef); + CASE_RET_STR(Align); + CASE_RET_STR(LProcRef); + CASE_RET_STR(V2_Register); + CASE_RET_STR(V2_Constant); + CASE_RET_STR(V2_Udt); + CASE_RET_STR(V2_CobolUdt); + CASE_RET_STR(V2_ManyReg); + CASE_RET_STR(V2_BpRel); + CASE_RET_STR(V2_LData); + CASE_RET_STR(V2_GData); + CASE_RET_STR(V2_Pub); + CASE_RET_STR(V2_LProc); + CASE_RET_STR(V2_GProc); + CASE_RET_STR(V2_VftTable); + CASE_RET_STR(V2_RegRel); + CASE_RET_STR(V2_LThread); + CASE_RET_STR(V2_GThread); + CASE_RET_STR(V2_Unknown_1010); + CASE_RET_STR(V2_Unknown_1011); + CASE_RET_STR(V2_FrameInfo); + CASE_RET_STR(V2_Compliand); + CASE_RET_STR(V3_Compliand); + CASE_RET_STR(V3_Thunk); + CASE_RET_STR(V3_Block); + CASE_RET_STR(V3_Unknown_1104); + CASE_RET_STR(V3_Label); + CASE_RET_STR(V3_Register); + CASE_RET_STR(V3_Constant); + CASE_RET_STR(V3_Udt); + CASE_RET_STR(V3_Unknown_1109); + CASE_RET_STR(V3_Unknown_110a); + CASE_RET_STR(V3_BpRel); + CASE_RET_STR(V3_LData); + CASE_RET_STR(V3_GData); + CASE_RET_STR(V3_Pub); + CASE_RET_STR(V3_LProc); + CASE_RET_STR(V3_GProc); + CASE_RET_STR(V3_RegRel); + CASE_RET_STR(V3_LThread); + CASE_RET_STR(V3_GThread); + CASE_RET_STR(V3_Unknown_1114); + CASE_RET_STR(V3_Unknown_1115); + CASE_RET_STR(V3_MSTool); + CASE_RET_STR(V3_PubFunc1); + CASE_RET_STR(V3_PubFunc2); + CASE_RET_STR(V3_SectInfo); + CASE_RET_STR(V3_SubSectInfo); + CASE_RET_STR(V3_Entrypoint); + CASE_RET_STR(V3_Unknown_1139); + CASE_RET_STR(V3_SecuCookie); + CASE_RET_STR(V3_Unknown_113b); + CASE_RET_STR(V3_MsToolInfo); + CASE_RET_STR(V3_MsToolEnv); + CASE_RET_STR(VS2013_Local); + CASE_RET_STR(VS2013_FpOff); + CASE_RET_STR(VS2013_LProc32); + CASE_RET_STR(VS2013_GProc32); +#undef CASE_RET_STR + case kCvSymType_EndOfValues: break; + } + return "<unknown type>"; +} +#endif /* LOG_ENABLED */ + + +/** + * Adds a string to the g_hDbgModStrCache after sanitizing it. + * + * IPRT only deals with UTF-8 strings, so the string will be forced to UTF-8 + * encoding. Also, codeview generally have length prefixed + * + * @returns String cached copy of the string. + * @param pch The string to copy to the cache. + * @param cch The length of the string. RTSTR_MAX if zero + * terminated. + */ +static const char *rtDbgModCvAddSanitizedStringToCache(const char *pch, size_t cch) +{ + /* + * If the string is valid UTF-8 and or the right length, we're good. + * This is usually the case. + */ + const char *pszRet; + int rc; + if (cch != RTSTR_MAX) + rc = RTStrValidateEncodingEx(pch, cch, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH); + else + rc = RTStrValidateEncodingEx(pch, cch, 0); + if (RT_SUCCESS(rc)) + pszRet = RTStrCacheEnterN(g_hDbgModStrCache, pch, cch); + else + { + /* + * Need to sanitize the string, so make a copy of it. + */ + char *pszCopy = (char *)RTMemDupEx(pch, cch, 1); + AssertPtrReturn(pszCopy, NULL); + + /* Deal with anyembedded zero chars. */ + char *psz = RTStrEnd(pszCopy, cch); + while (psz) + { + *psz = '_'; + psz = RTStrEnd(psz, cch - (psz - pszCopy)); + } + + /* Force valid UTF-8 encoding. */ + RTStrPurgeEncoding(pszCopy); + Assert(strlen(pszCopy) == cch); + + /* Enter it into the cache and free the temp copy. */ + pszRet = RTStrCacheEnterN(g_hDbgModStrCache, pszCopy, cch); + RTMemFree(pszCopy); + } + return pszRet; +} + + +/** + * Translates a codeview segment and offset into our segment layout. + * + * @returns + * @param pThis . + * @param piSeg . + * @param poff . + */ +DECLINLINE(int) rtDbgModCvAdjustSegAndOffset(PRTDBGMODCV pThis, uint32_t *piSeg, uint64_t *poff) +{ + uint32_t iSeg = *piSeg; + if (iSeg == 0) + iSeg = RTDBGSEGIDX_ABS; + else if (pThis->pSegMap) + { + if (pThis->fHaveDosFrames) + { + if ( iSeg > pThis->pSegMap->Hdr.cSegs + || iSeg == 0) + return VERR_CV_BAD_FORMAT; + if (*poff <= pThis->pSegMap->aDescs[iSeg - 1].cb + pThis->pSegMap->aDescs[iSeg - 1].off) + *poff -= pThis->pSegMap->aDescs[iSeg - 1].off; + else + { + /* Workaround for VGABIOS where _DATA symbols like vgafont8 are + reported in the VGAROM segment. */ + uint64_t uAddrSym = *poff + ((uint32_t)pThis->pSegMap->aDescs[iSeg - 1].iFrame << 4); + uint16_t j = pThis->pSegMap->Hdr.cSegs; + while (j-- > 0) + { + uint64_t uAddrFirst = (uint64_t)pThis->pSegMap->aDescs[j].off + + ((uint32_t)pThis->pSegMap->aDescs[j].iFrame << 4); + if (uAddrSym - uAddrFirst < pThis->pSegMap->aDescs[j].cb) + { + Log(("CV addr fix: %04x:%08x -> %04x:%08x\n", iSeg, *poff, j + 1, uAddrSym - uAddrFirst)); + *poff = uAddrSym - uAddrFirst; + iSeg = j + 1; + break; + } + } + if (j == UINT16_MAX) + return VERR_CV_BAD_FORMAT; + } + } + else + { + if ( iSeg > pThis->pSegMap->Hdr.cSegs + || iSeg == 0 + || *poff > pThis->pSegMap->aDescs[iSeg - 1].cb) + return VERR_CV_BAD_FORMAT; + *poff += pThis->pSegMap->aDescs[iSeg - 1].off; + } + if (pThis->pSegMap->aDescs[iSeg - 1].fFlags & RTCVSEGMAPDESC_F_ABS) + iSeg = RTDBGSEGIDX_ABS; + else + iSeg = pThis->pSegMap->aDescs[iSeg - 1].iGroup; + } + *piSeg = iSeg; + return VINF_SUCCESS; +} + + +/** + * Adds a symbol to the container. + * + * @returns IPRT status code + * @param pThis The CodeView debug info reader instance. + * @param iSeg Segment number. + * @param off Offset into the segment + * @param pchName The symbol name (not necessarily terminated). + * @param cchName The symbol name length. + * @param fFlags Flags reserved for future exploits, MBZ. + * @param cbSym Symbol size, 0 if not available. + */ +static int rtDbgModCvAddSymbol(PRTDBGMODCV pThis, uint32_t iSeg, uint64_t off, const char *pchName, + uint32_t cchName, uint32_t fFlags, uint32_t cbSym) +{ + RT_NOREF_PV(fFlags); + const char *pszName = rtDbgModCvAddSanitizedStringToCache(pchName, cchName); + int rc; + if (pszName) + { +#if 1 + Log2(("CV Sym: %04x:%08x %.*s\n", iSeg, off, cchName, pchName)); + rc = rtDbgModCvAdjustSegAndOffset(pThis, &iSeg, &off); + if (RT_SUCCESS(rc)) + { + rc = RTDbgModSymbolAdd(pThis->hCnt, pszName, iSeg, off, cbSym, RTDBGSYMBOLADD_F_ADJUST_SIZES_ON_CONFLICT, NULL); + + /* Simple duplicate symbol mangling, just to get more details. */ + if (rc == VERR_DBG_DUPLICATE_SYMBOL && cchName < _2K) + { + char szTmpName[_2K + 96]; + memcpy(szTmpName, pszName, cchName); + szTmpName[cchName] = '_'; + for (uint32_t i = 1; i < 32; i++) + { + RTStrFormatU32(&szTmpName[cchName + 1], 80, i, 10, 0, 0, 0); + rc = RTDbgModSymbolAdd(pThis->hCnt, szTmpName, iSeg, off, cbSym, 0 /*fFlags*/, NULL); + if (rc != VERR_DBG_DUPLICATE_SYMBOL) + break; + } + + } + else if (rc == VERR_DBG_ADDRESS_CONFLICT && cbSym) + rc = RTDbgModSymbolAdd(pThis->hCnt, pszName, iSeg, off, cbSym, + RTDBGSYMBOLADD_F_REPLACE_SAME_ADDR | RTDBGSYMBOLADD_F_ADJUST_SIZES_ON_CONFLICT, NULL); + + Log(("Symbol: %04x:%08x %.*s [%Rrc]\n", iSeg, off, cchName, pchName, rc)); + if (rc == VERR_DBG_ADDRESS_CONFLICT || rc == VERR_DBG_DUPLICATE_SYMBOL) + rc = VINF_SUCCESS; + } + else + Log(("Invalid segment index/offset %#06x:%08x for symbol %.*s\n", iSeg, off, cchName, pchName)); + +#else + Log(("Symbol: %04x:%08x %.*s\n", iSeg, off, cchName, pchName)); + rc = VINF_SUCCESS; +#endif + RTStrCacheRelease(g_hDbgModStrCache, pszName); + } + else + rc = VERR_NO_STR_MEMORY; + return rc; +} + + +/** + * Validates the a zero terminated string. + * + * @returns String length if valid, UINT16_MAX if invalid. + * @param pszString The string to validate. + * @param pvRec The pointer to the record containing the string. + * @param cbRec The record length. + */ +static uint16_t rtDbgModCvValidateZeroString(const char *pszString, void const *pvRec, uint16_t cbRec) +{ + size_t offStrMember = (uintptr_t)pszString - (uintptr_t)pvRec; + AssertReturn(offStrMember < _1K, UINT16_MAX); + AssertReturn(offStrMember <= cbRec, UINT16_MAX); + cbRec -= (uint16_t)offStrMember; + + const char *pchEnd = RTStrEnd(pszString, cbRec); + AssertReturn(pchEnd, UINT16_MAX); + + int rc = RTStrValidateEncoding(pszString); + AssertRCReturn(rc, UINT16_MAX); + + return (uint16_t)(pchEnd - pszString); +} + + +/** + * Parses a CV4 symbol table, adding symbols to the container. + * + * @returns IPRT status code + * @param pThis The CodeView debug info reader instance. + * @param pvSymTab The symbol table. + * @param cbSymTab The size of the symbol table. + * @param fFlags Flags reserved for future exploits, MBZ. + */ +static int rtDbgModCvSsProcessV4PlusSymTab(PRTDBGMODCV pThis, void const *pvSymTab, size_t cbSymTab, uint32_t fFlags) +{ + int rc = VINF_SUCCESS; + RTCPTRUNION uCursor; + uCursor.pv = pvSymTab; + + RT_NOREF_PV(fFlags); + + while (cbSymTab > 0 && RT_SUCCESS(rc)) + { + uint8_t const * const pbRecStart = uCursor.pu8; + uint16_t cbRec = *uCursor.pu16++; + if (cbRec >= 2) + { + uint16_t uSymType = *uCursor.pu16++; + + Log3((" %p: uSymType=%#06x LB %#x %s\n", + pbRecStart - (uint8_t *)pvSymTab, uSymType, cbRec, rtDbgModCvSsSymTypeName((RTCVSYMTYPE)uSymType))); + RTDBGMODCV_CHECK_RET_BF(cbRec >= 2 && cbRec <= cbSymTab, ("cbRec=%#x cbSymTab=%#x\n", cbRec, cbSymTab)); + + switch (uSymType) + { + case kCvSymType_LData16: + case kCvSymType_GData16: + case kCvSymType_Pub16: + { + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec > 2 + 2+2+2+1); + uint16_t off = *uCursor.pu16++; + uint16_t iSeg = *uCursor.pu16++; + /*uint16_t iType =*/ *uCursor.pu16++; + uint8_t cchName = *uCursor.pu8++; + RTDBGMODCV_CHECK_NOMSG_RET_BF(cchName > 0); + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= 2 + 2+2+2+1 + cchName); + + rc = rtDbgModCvAddSymbol(pThis, iSeg, off, uCursor.pch, cchName, 0, 0); + break; + } + + case kCvSymType_LData32: + case kCvSymType_GData32: + case kCvSymType_Pub32: + { + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec > 2 + 4+2+2+1); + uint32_t off = *uCursor.pu32++; + uint16_t iSeg = *uCursor.pu16++; + /*uint16_t iType =*/ *uCursor.pu16++; + uint8_t cchName = *uCursor.pu8++; + RTDBGMODCV_CHECK_NOMSG_RET_BF(cchName > 0); + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= 2 + 4+2+2+1 + cchName); + + rc = rtDbgModCvAddSymbol(pThis, iSeg, off, uCursor.pch, cchName, 0, 0); + break; + } + + case kCvSymType_LProc16: + case kCvSymType_GProc16: + { + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec > 2 + 4+4+4+2+2+2+2+2+2+1+1); + /*uint32_t uParent =*/ *uCursor.pu32++; + /*uint32_t uEnd =*/ *uCursor.pu32++; + /*uint32_t uNext =*/ *uCursor.pu32++; + uint16_t cbProc = *uCursor.pu16++; + /*uint16_t offDebugStart =*/ *uCursor.pu16++; + /*uint16_t offDebugEnd =*/ *uCursor.pu16++; + uint16_t off = *uCursor.pu16++; + uint16_t iSeg = *uCursor.pu16++; + /*uint16_t iProcType =*/ *uCursor.pu16++; + /*uint8_t fbType =*/ *uCursor.pu8++; + uint8_t cchName = *uCursor.pu8++; + RTDBGMODCV_CHECK_NOMSG_RET_BF(cchName > 0); + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= 2 + 4+4+4+2+2+2+2+2+2+1+1 + cchName); + + rc = rtDbgModCvAddSymbol(pThis, iSeg, off, uCursor.pch, cchName, 0, cbProc); + break; + } + + case kCvSymType_LProc32: + case kCvSymType_GProc32: + { + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec > 2 + 4+4+4+4+4+4+4+2+2+1+1); + /*uint32_t uParent =*/ *uCursor.pu32++; + /*uint32_t uEnd =*/ *uCursor.pu32++; + /*uint32_t uNext =*/ *uCursor.pu32++; + /*uint32_t cbProc =*/ *uCursor.pu32++; + /*uint32_t offDebugStart =*/ *uCursor.pu32++; + /*uint32_t offDebugEnd =*/ *uCursor.pu32++; + uint32_t off = *uCursor.pu32++; + uint16_t iSeg = *uCursor.pu16++; + /*uint16_t iProcType =*/ *uCursor.pu16++; + /*uint8_t fbType =*/ *uCursor.pu8++; + uint8_t cchName = *uCursor.pu8++; + RTDBGMODCV_CHECK_NOMSG_RET_BF(cchName > 0); + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= 2 + 4+4+4+4+4+4+4+2+2+1+1 + cchName); + + rc = rtDbgModCvAddSymbol(pThis, iSeg, off, uCursor.pch, cchName, 0, 0); + break; + } + + case kCvSymType_V3_Label: + { + PCRTCVSYMV3LABEL pLabel = (PCRTCVSYMV3LABEL)uCursor.pv; + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= sizeof(*pLabel)); + uint16_t cchName = rtDbgModCvValidateZeroString(pLabel->szName, pLabel, cbRec); + if (cchName != UINT16_MAX && cchName > 0) + rc = rtDbgModCvAddSymbol(pThis, pLabel->iSection, pLabel->offSection, pLabel->szName, cchName, 0, 0); + else + Log3((" cchName=%#x sec:off=%#x:%#x %.*Rhxs\n", + cchName, pLabel->iSection, pLabel->offSection, cbRec, pLabel)); + break; + } + + case kCvSymType_V3_LData: + case kCvSymType_V3_GData: + case kCvSymType_V3_Pub: + { + PCRTCVSYMV3TYPEDNAME pData = (PCRTCVSYMV3TYPEDNAME)uCursor.pv; + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= sizeof(*pData)); + uint16_t cchName = rtDbgModCvValidateZeroString(pData->szName, pData, cbRec); + if (cchName != UINT16_MAX && cchName > 0) + rc = rtDbgModCvAddSymbol(pThis, pData->iSection, pData->offSection, pData->szName, cchName, 0, 0); + else + Log3((" cchName=%#x sec:off=%#x:%#x idType=%#x %.*Rhxs\n", + cchName, pData->iSection, pData->offSection, pData->idType, cbRec, pData)); + break; + } + + case kCvSymType_V3_LProc: + case kCvSymType_V3_GProc: + { + PCRTCVSYMV3PROC pProc = (PCRTCVSYMV3PROC)uCursor.pv; + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= sizeof(*pProc)); + uint16_t cchName = rtDbgModCvValidateZeroString(pProc->szName, pProc, cbRec); + if (cchName != UINT16_MAX && cchName > 0) + rc = rtDbgModCvAddSymbol(pThis, pProc->iSection, pProc->offSection, pProc->szName, cchName, + 0, pProc->cbProc); + else + Log3((" cchName=%#x sec:off=%#x:%#x LB %#x\n", + cchName, pProc->iSection, pProc->offSection, pProc->cbProc)); + break; + } + + } + } + /*else: shorter records can be used for alignment, I guess. */ + + /* next */ + uCursor.pu8 = pbRecStart + cbRec + 2; + cbSymTab -= cbRec + 2; + } + return rc; +} + + +/** + * Makes a copy of the CV8 source string table. + * + * It will be references in a subsequent source information table, and again by + * line number tables thru that. + * + * @returns IPRT status code + * @param pThis The CodeView debug info reader instance. + * @param pvSrcStrings The source string table. + * @param cbSrcStrings The size of the source strings. + * @param fFlags Flags reserved for future exploits, MBZ. + */ +static int rtDbgModCvSsProcessV8SrcStrings(PRTDBGMODCV pThis, void const *pvSrcStrings, size_t cbSrcStrings, uint32_t fFlags) +{ + RT_NOREF_PV(fFlags); + + if (pThis->cbSrcStrings) + Log(("\n!!More than one source file string table for this module!!\n\n")); + + if (cbSrcStrings >= pThis->cbSrcStringsAlloc) + { + void *pvNew = RTMemRealloc(pThis->pchSrcStrings, cbSrcStrings + 1); + AssertReturn(pvNew, VERR_NO_MEMORY); + pThis->pchSrcStrings = (char *)pvNew; + pThis->cbSrcStringsAlloc = cbSrcStrings + 1; + } + memcpy(pThis->pchSrcStrings, pvSrcStrings, cbSrcStrings); + pThis->pchSrcStrings[cbSrcStrings] = '\0'; + pThis->cbSrcStrings = cbSrcStrings; + Log2((" saved %#x bytes of CV8 source strings\n", cbSrcStrings)); + + if (LogIs3Enabled()) + { + size_t iFile = 0; + size_t off = pThis->pchSrcStrings[0] != '\0' ? 0 : 1; + while (off < cbSrcStrings) + { + size_t cch = strlen(&pThis->pchSrcStrings[off]); + Log3((" %010zx #%03zu: %s\n", off, iFile, &pThis->pchSrcStrings[off])); + off += cch + 1; + iFile++; + } + } + + return VINF_SUCCESS; +} + + +/** + * Makes a copy of the CV8 source information table. + * + * It will be references in subsequent line number tables. + * + * @returns IPRT status code + * @param pThis The CodeView debug info reader instance. + * @param pvSrcInfo The source information table. + * @param cbSrcInfo The size of the source information table (bytes). + * @param fFlags Flags reserved for future exploits, MBZ. + */ +static int rtDbgModCvSsProcessV8SrcInfo(PRTDBGMODCV pThis, void const *pvSrcInfo, size_t cbSrcInfo, uint32_t fFlags) +{ + RT_NOREF_PV(fFlags); + + if (pThis->cbSrcInfo) + Log(("\n!!More than one source file info table for this module!!\n\n")); + + if (cbSrcInfo + sizeof(RTCV8SRCINFO) > pThis->cbSrcInfoAlloc) + { + void *pvNew = RTMemRealloc(pThis->pbSrcInfo, cbSrcInfo + sizeof(RTCV8SRCINFO)); + AssertReturn(pvNew, VERR_NO_MEMORY); + pThis->pbSrcInfo = (uint8_t *)pvNew; + pThis->cbSrcInfoAlloc = cbSrcInfo + sizeof(RTCV8SRCINFO); + } + memcpy(pThis->pbSrcInfo, pvSrcInfo, cbSrcInfo); + memset(&pThis->pbSrcInfo[cbSrcInfo], 0, sizeof(RTCV8SRCINFO)); + pThis->cbSrcInfo = cbSrcInfo; + Log2((" saved %#x bytes of CV8 source file info\n", cbSrcInfo)); + return VINF_SUCCESS; +} + + +/** + * Makes a copy of the CV8 source string table. + * + * It will be references in subsequent line number tables. + * + * @returns IPRT status code + * @param pThis The CodeView debug info reader instance. + * @param pvSectLines The section source line table. + * @param cbSectLines The size of the section source line table. + * @param fFlags Flags reserved for future exploits, MBZ. + */ +static int rtDbgModCvSsProcessV8SectLines(PRTDBGMODCV pThis, void const *pvSectLines, size_t cbSectLines, uint32_t fFlags) +{ + RT_NOREF_PV(fFlags); + + /* + * Starts with header. + */ + PCRTCV8LINESHDR pHdr = (PCRTCV8LINESHDR)pvSectLines; + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSectLines >= sizeof(*pHdr)); + cbSectLines -= sizeof(*pHdr); + Log2(("RTDbgModCv: seg #%u, off %#x LB %#x \n", pHdr->iSection, pHdr->offSection, pHdr->cbSectionCovered)); + + RTCPTRUNION uCursor; + uCursor.pv = pHdr + 1; + while (cbSectLines > 0) + { + /* Source file header. */ + PCRTCV8LINESSRCMAP pSrcHdr = (PCRTCV8LINESSRCMAP)uCursor.pv; + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSectLines >= sizeof(*pSrcHdr)); + RTDBGMODCV_CHECK_NOMSG_RET_BF(pSrcHdr->cb == pSrcHdr->cLines * sizeof(RTCV8LINEPAIR) + sizeof(RTCV8LINESSRCMAP)); + RTDBGMODCV_CHECK_RET_BF(!(pSrcHdr->offSourceInfo & 3), ("offSourceInfo=%#x\n", pSrcHdr->offSourceInfo)); + if (pSrcHdr->offSourceInfo + sizeof(uint32_t) <= pThis->cbSrcInfo) + { + PCRTCV8SRCINFO pSrcInfo = (PCRTCV8SRCINFO)&pThis->pbSrcInfo[pSrcHdr->offSourceInfo]; + const char *pszName = pSrcInfo->offSourceName < pThis->cbSrcStrings + ? &pThis->pchSrcStrings[pSrcInfo->offSourceName] : "unknown.c"; + pszName = rtDbgModCvAddSanitizedStringToCache(pszName, RTSTR_MAX); + Log2(("RTDbgModCv: #%u lines, %#x bytes, %#x=%s\n", pSrcHdr->cLines, pSrcHdr->cb, pSrcInfo->offSourceName, pszName)); + + if (pszName) + { + /* Process the line/offset pairs. */ + uint32_t cLeft = pSrcHdr->cLines; + PCRTCV8LINEPAIR pPair = (PCRTCV8LINEPAIR)(pSrcHdr + 1); + while (cLeft-- > 0) + { + uint32_t idxSeg = pHdr->iSection; + uint64_t off = pPair->offSection + pHdr->offSection; + int rc = rtDbgModCvAdjustSegAndOffset(pThis, &idxSeg, &off); + if (RT_SUCCESS(rc)) + rc = RTDbgModLineAdd(pThis->hCnt, pszName, pPair->uLineNumber, idxSeg, off, NULL); + if (RT_SUCCESS(rc)) + Log3(("RTDbgModCv: %#x:%#010llx %0u\n", idxSeg, off, pPair->uLineNumber)); + else + Log(( "RTDbgModCv: %#x:%#010llx %0u - rc=%Rrc!! (org: idxSeg=%#x off=%#x)\n", + idxSeg, off, pPair->uLineNumber, rc, pHdr->iSection, pPair->offSection)); + + /* next */ + pPair++; + } + Assert((uintptr_t)pPair - (uintptr_t)pSrcHdr == pSrcHdr->cb); + } + } + else + Log(("RTDbgModCv: offSourceInfo=%#x cbSrcInfo=%#x!\n", pSrcHdr->offSourceInfo, pThis->cbSrcInfo)); + + /* next */ + cbSectLines -= pSrcHdr->cb; + uCursor.pu8 += pSrcHdr->cb; + } + + return VINF_SUCCESS; +} + + + +/** + * Parses a CV8 symbol table, adding symbols to the container. + * + * @returns IPRT status code + * @param pThis The CodeView debug info reader instance. + * @param pvSymTab The symbol table. + * @param cbSymTab The size of the symbol table. + * @param fFlags Flags reserved for future exploits, MBZ. + */ +static int rtDbgModCvSsProcessV8SymTab(PRTDBGMODCV pThis, void const *pvSymTab, size_t cbSymTab, uint32_t fFlags) +{ + size_t const cbSymTabSaved = cbSymTab; + int rc = VINF_SUCCESS; + + /* + * First pass looks for source information and source strings tables. + * Microsoft puts the 0xf3 and 0xf4 last, usually with 0xf4 first. + * + * We ASSUME one string and one info table per module! + */ + RTCPTRUNION uCursor; + uCursor.pv = pvSymTab; + for (;;) + { + RTDBGMODCV_CHECK_RET_BF(cbSymTab > sizeof(RTCV8SYMBOLSBLOCK), ("cbSymTab=%zu\n", cbSymTab)); + PCRTCV8SYMBOLSBLOCK pBlockHdr = (PCRTCV8SYMBOLSBLOCK)uCursor.pv; + Log3((" %p: pass #1 uType=%#04x LB %#x\n", (uint8_t *)pBlockHdr - (uint8_t *)pvSymTab, pBlockHdr->uType, pBlockHdr->cb)); + RTDBGMODCV_CHECK_RET_BF(pBlockHdr->cb <= cbSymTab - sizeof(RTCV8SYMBOLSBLOCK), + ("cb=%#u cbSymTab=%zu\n", pBlockHdr->cb, cbSymTab)); + + switch (pBlockHdr->uType) + { + case RTCV8SYMBLOCK_TYPE_SRC_STR: + rc = rtDbgModCvSsProcessV8SrcStrings(pThis, pBlockHdr + 1, pBlockHdr->cb, fFlags); + break; + + case RTCV8SYMBLOCK_TYPE_SRC_INFO: + rc = rtDbgModCvSsProcessV8SrcInfo(pThis, pBlockHdr + 1, pBlockHdr->cb, fFlags); + break; + + case RTCV8SYMBLOCK_TYPE_SECT_LINES: + case RTCV8SYMBLOCK_TYPE_SYMBOLS: + break; + default: + Log(("rtDbgModCvSsProcessV8SymTab: Unknown block type %#x (LB %#x)\n", pBlockHdr->uType, pBlockHdr->cb)); + break; + } + uint32_t cbAligned = RT_ALIGN_32(sizeof(*pBlockHdr) + pBlockHdr->cb, 4); + if (RT_SUCCESS(rc) && cbSymTab > cbAligned) + { + uCursor.pu8 += cbAligned; + cbSymTab -= cbAligned; + } + else + break; + } + + /* + * Log the source info now that we've gathered both it and the strings. + */ + if (LogIs3Enabled() && pThis->cbSrcInfo) + { + Log3((" Source file info table:\n")); + size_t iFile = 0; + size_t off = 0; + while (off + 4 <= pThis->cbSrcInfo) + { + PCRTCV8SRCINFO pSrcInfo = (PCRTCV8SRCINFO)&pThis->pbSrcInfo[off]; +#ifdef LOG_ENABLED + const char *pszName = pSrcInfo->offSourceName < pThis->cbSrcStrings + ? &pThis->pchSrcStrings[pSrcInfo->offSourceName] : "out-of-bounds.c!"; + if (pSrcInfo->uDigestType == RTCV8SRCINFO_DIGEST_TYPE_MD5) + Log3((" %010zx #%03zu: %RTuuid %#x=%s\n", off, iFile, &pSrcInfo->Digest.md5, pSrcInfo->offSourceName, pszName)); + else if (pSrcInfo->uDigestType == RTCV8SRCINFO_DIGEST_TYPE_NONE) + Log3((" %010zx #%03zu: <none> %#x=%s\n", off, iFile, pSrcInfo->offSourceName, pszName)); + else + Log3((" %010zx #%03zu: !%#x! %#x=%s\n", off, iFile, pSrcInfo->uDigestType, pSrcInfo->offSourceName, pszName)); +#endif + off += pSrcInfo->uDigestType == RTCV8SRCINFO_DIGEST_TYPE_MD5 ? sizeof(*pSrcInfo) : 8; + iFile++; + } + } + + /* + * Second pass, process symbols and line numbers. + */ + uCursor.pv = pvSymTab; + cbSymTab = cbSymTabSaved; + for (;;) + { + RTDBGMODCV_CHECK_RET_BF(cbSymTab > sizeof(RTCV8SYMBOLSBLOCK), ("cbSymTab=%zu\n", cbSymTab)); + PCRTCV8SYMBOLSBLOCK pBlockHdr = (PCRTCV8SYMBOLSBLOCK)uCursor.pv; + Log3((" %p: pass #2 uType=%#04x LB %#x\n", (uint8_t *)pBlockHdr - (uint8_t *)pvSymTab, pBlockHdr->uType, pBlockHdr->cb)); + RTDBGMODCV_CHECK_RET_BF(pBlockHdr->cb <= cbSymTab - sizeof(RTCV8SYMBOLSBLOCK), + ("cb=%#u cbSymTab=%zu\n", pBlockHdr->cb, cbSymTab)); + + switch (pBlockHdr->uType) + { + case RTCV8SYMBLOCK_TYPE_SYMBOLS: + rc = rtDbgModCvSsProcessV4PlusSymTab(pThis, pBlockHdr + 1, pBlockHdr->cb, fFlags); + break; + + case RTCV8SYMBLOCK_TYPE_SECT_LINES: + rc = rtDbgModCvSsProcessV8SectLines(pThis, pBlockHdr + 1, pBlockHdr->cb, fFlags); + break; + + case RTCV8SYMBLOCK_TYPE_SRC_INFO: + case RTCV8SYMBLOCK_TYPE_SRC_STR: + break; + default: + Log(("rtDbgModCvSsProcessV8SymTab: Unknown block type %#x (LB %#x)\n", pBlockHdr->uType, pBlockHdr->cb)); + break; + } + uint32_t cbAligned = RT_ALIGN_32(sizeof(*pBlockHdr) + pBlockHdr->cb, 4); + if (RT_SUCCESS(rc) && cbSymTab > cbAligned) + { + uCursor.pu8 += cbAligned; + cbSymTab -= cbAligned; + } + else + break; + } + return rc; +} + + +/** @callback_method_impl{FNDBGMODCVSUBSECTCALLBACK, + * Parses kCvSst_GlobalPub\, kCvSst_GlobalSym and kCvSst_StaticSym subsections\, + * adding symbols it finds to the container.} */ +static DECLCALLBACK(int) +rtDbgModCvSs_GlobalPub_GlobalSym_StaticSym(PRTDBGMODCV pThis, void const *pvSubSect, size_t cbSubSect, PCRTCVDIRENT32 pDirEnt) +{ + PCRTCVGLOBALSYMTABHDR pHdr = (PCRTCVGLOBALSYMTABHDR)pvSubSect; + RT_NOREF_PV(pDirEnt); + + /* + * Quick data validation. + */ + Log2(("RTDbgModCv: %s: uSymHash=%#x uAddrHash=%#x cbSymbols=%#x cbSymHash=%#x cbAddrHash=%#x\n", + rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType), pHdr->uSymHash, + pHdr->uAddrHash, pHdr->cbSymbols, pHdr->cbSymHash, pHdr->cbAddrHash)); + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSubSect >= sizeof(RTCVGLOBALSYMTABHDR)); + RTDBGMODCV_CHECK_NOMSG_RET_BF((uint64_t)pHdr->cbSymbols + pHdr->cbSymHash + pHdr->cbAddrHash <= cbSubSect - sizeof(*pHdr)); + RTDBGMODCV_CHECK_NOMSG_RET_BF(pHdr->uSymHash < 0x20); + RTDBGMODCV_CHECK_NOMSG_RET_BF(pHdr->uAddrHash < 0x20); + if (!pHdr->cbSymbols) + return VINF_SUCCESS; + + /* + * Parse the symbols. + */ + return rtDbgModCvSsProcessV4PlusSymTab(pThis, pHdr + 1, pHdr->cbSymbols, 0); +} + + +/** @callback_method_impl{FNDBGMODCVSUBSECTCALLBACK, + * Parses kCvSst_Module subsection\, storing the debugging style in pThis.} */ +static DECLCALLBACK(int) +rtDbgModCvSs_Module(PRTDBGMODCV pThis, void const *pvSubSect, size_t cbSubSect, PCRTCVDIRENT32 pDirEnt) +{ + RT_NOREF_PV(pDirEnt); + + RTCPTRUNION uCursor; + uCursor.pv = pvSubSect; + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSubSect >= 2 + 2 + 2 + 2 + 0 + 1); + uint16_t iOverlay = *uCursor.pu16++; NOREF(iOverlay); + uint16_t iLib = *uCursor.pu16++; NOREF(iLib); + uint16_t cSegs = *uCursor.pu16++; + pThis->uCurStyle = *uCursor.pu16++; + if (pThis->uCurStyle == 0) + pThis->uCurStyle = RT_MAKE_U16('C', 'V'); + pThis->uCurStyleVer = 0; + pThis->cbSrcInfo = 0; + pThis->cbSrcStrings = 0; + uint8_t cchName = uCursor.pu8[cSegs * 12]; + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSubSect >= 2 + 2 + 2 + 2 + cSegs * 12U + 1 + cchName); + +#ifdef LOG_ENABLED + const char *pchName = (const char *)&uCursor.pu8[cSegs * 12 + 1]; + Log2(("RTDbgModCv: Module: iOverlay=%#x iLib=%#x cSegs=%#x Style=%c%c (%#x) %.*s\n", iOverlay, iLib, cSegs, + RT_BYTE1(pThis->uCurStyle), RT_BYTE2(pThis->uCurStyle), pThis->uCurStyle, cchName, pchName)); +#endif + RTDBGMODCV_CHECK_NOMSG_RET_BF(pThis->uCurStyle == RT_MAKE_U16('C', 'V')); + +#ifdef LOG_ENABLED + PCRTCVMODSEGINFO32 paSegs = (PCRTCVMODSEGINFO32)uCursor.pv; + for (uint16_t iSeg = 0; iSeg < cSegs; iSeg++) + Log2((" #%02u: %04x:%08x LB %08x\n", iSeg, paSegs[iSeg].iSeg, paSegs[iSeg].off, paSegs[iSeg].cb)); +#endif + + return VINF_SUCCESS; +} + + +/** @callback_method_impl{FNDBGMODCVSUBSECTCALLBACK, + * Parses kCvSst_Symbols\, kCvSst_PublicSym and kCvSst_AlignSym subsections\, + * adding symbols it finds to the container.} */ +static DECLCALLBACK(int) +rtDbgModCvSs_Symbols_PublicSym_AlignSym(PRTDBGMODCV pThis, void const *pvSubSect, size_t cbSubSect, PCRTCVDIRENT32 pDirEnt) +{ + RT_NOREF_PV(pDirEnt); + RTDBGMODCV_CHECK_NOMSG_RET_BF(pThis->uCurStyle == RT_MAKE_U16('C', 'V')); + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSubSect >= 8); + + uint32_t u32Signature = *(uint32_t const *)pvSubSect; + RTDBGMODCV_CHECK_RET_BF(u32Signature == RTCVSYMBOLS_SIGNATURE_CV4 || u32Signature == RTCVSYMBOLS_SIGNATURE_CV8, + ("%#x, expected %#x\n", u32Signature, RTCVSYMBOLS_SIGNATURE_CV4)); + if (u32Signature == RTCVSYMBOLS_SIGNATURE_CV8) + return rtDbgModCvSsProcessV8SymTab(pThis, (uint8_t const *)pvSubSect + 4, cbSubSect - 4, 0); + return rtDbgModCvSsProcessV4PlusSymTab(pThis, (uint8_t const *)pvSubSect + 4, cbSubSect - 4, 0); +} + + +/** @callback_method_impl{FNDBGMODCVSUBSECTCALLBACK, + * Parses kCvSst_SrcModule adding line numbers it finds to the container.} + */ +static DECLCALLBACK(int) +rtDbgModCvSs_SrcModule(PRTDBGMODCV pThis, void const *pvSubSect, size_t cbSubSect, PCRTCVDIRENT32 pDirEnt) +{ + RT_NOREF_PV(pDirEnt); + Log(("rtDbgModCvSs_SrcModule: uCurStyle=%#x\n%.*Rhxd\n", pThis->uCurStyle, cbSubSect, pvSubSect)); + + /* Check the header. */ + PCRTCVSRCMODULE pHdr = (PCRTCVSRCMODULE)pvSubSect; + AssertReturn(cbSubSect >= RT_UOFFSETOF(RTCVSRCMODULE, aoffSrcFiles), VERR_CV_BAD_FORMAT); + size_t cbHdr = sizeof(RTCVSRCMODULE) + + pHdr->cFiles * sizeof(uint32_t) + + pHdr->cSegs * sizeof(uint32_t) * 2 + + pHdr->cSegs * sizeof(uint16_t); + Log2(("RTDbgModCv: SrcModule: cFiles=%u cSegs=%u\n", pHdr->cFiles, pHdr->cFiles)); + RTDBGMODCV_CHECK_RET_BF(cbSubSect >= cbHdr, ("cbSubSect=%#x cbHdr=%zx\n", cbSubSect, cbHdr)); +#ifdef LOG_ENABLED + if (LogIs2Enabled()) + { + for (uint32_t i = 0; i < pHdr->cFiles; i++) + Log2(("RTDbgModCv: source file #%u: %#x\n", i, pHdr->aoffSrcFiles[i])); + PCRTCVSRCRANGE paSegRanges = (PCRTCVSRCRANGE)&pHdr->aoffSrcFiles[pHdr->cFiles]; + uint16_t const *paidxSegs = (uint16_t const *)&paSegRanges[pHdr->cSegs]; + for (uint32_t i = 0; i < pHdr->cSegs; i++) + Log2(("RTDbgModCv: seg #%u: %#010x-%#010x\n", paidxSegs[i], paSegRanges[i].offStart, paSegRanges[i].offEnd)); + } +#endif + + /* + * Work over the source files. + */ + for (uint32_t i = 0; i < pHdr->cFiles; i++) + { + uint32_t const offSrcFile = pHdr->aoffSrcFiles[i]; + RTDBGMODCV_CHECK_RET_BF(cbSubSect - RT_UOFFSETOF(RTCVSRCFILE, aoffSrcLines) >= offSrcFile, + ("cbSubSect=%#x (- %#x) aoffSrcFiles[%u]=%#x\n", + cbSubSect, RT_UOFFSETOF(RTCVSRCFILE, aoffSrcLines), i, offSrcFile)); + PCRTCVSRCFILE pSrcFile = (PCRTCVSRCFILE)((uint8_t const *)pvSubSect + offSrcFile); + size_t cbSrcFileHdr = RT_UOFFSETOF_DYN(RTCVSRCFILE, aoffSrcLines[pSrcFile->cSegs]) + + sizeof(RTCVSRCRANGE) * pSrcFile->cSegs + + sizeof(uint8_t); + RTDBGMODCV_CHECK_RET_BF(cbSubSect >= offSrcFile + cbSrcFileHdr && cbSubSect > cbSrcFileHdr, + ("cbSubSect=%#x aoffSrcFiles[%u]=%#x cbSrcFileHdr=%#x\n", cbSubSect, offSrcFile, i, cbSrcFileHdr)); + PCRTCVSRCRANGE paSegRanges = (PCRTCVSRCRANGE)&pSrcFile->aoffSrcLines[pSrcFile->cSegs]; + uint8_t const *pcchName = (uint8_t const *)&paSegRanges[pSrcFile->cSegs]; /** @todo TIS NB09 docs say 16-bit length... */ + const char *pchName = (const char *)(pcchName + 1); + RTDBGMODCV_CHECK_RET_BF(cbSubSect >= offSrcFile + cbSrcFileHdr + *pcchName, + ("cbSubSect=%#x offSrcFile=%#x cbSubSect=%#x *pcchName=%#x\n", + cbSubSect, offSrcFile, cbSubSect, *pcchName)); + Log2(("RTDbgModCv: source file #%u/%#x: cSegs=%#x '%.*s'\n", i, offSrcFile, pSrcFile->cSegs, *pcchName, pchName)); + const char *pszName = rtDbgModCvAddSanitizedStringToCache(pchName, *pcchName); + + /* + * Work the segments this source file contributes code to. + */ + for (uint32_t iSeg = 0; iSeg < pSrcFile->cSegs; iSeg++) + { + uint32_t const offSrcLine = pSrcFile->aoffSrcLines[iSeg]; + RTDBGMODCV_CHECK_RET_BF(cbSubSect - RT_UOFFSETOF(RTCVSRCLINE, aoffLines) >= offSrcLine, + ("cbSubSect=%#x (- %#x) aoffSrcFiles[%u]=%#x\n", + cbSubSect, RT_UOFFSETOF(RTCVSRCLINE, aoffLines), iSeg, offSrcLine)); + PCRTCVSRCLINE pSrcLine = (PCRTCVSRCLINE)((uint8_t const *)pvSubSect + offSrcLine); + size_t cbSrcLine = RT_UOFFSETOF_DYN(RTCVSRCLINE, aoffLines[pSrcLine->cPairs]) + + pSrcLine->cPairs * sizeof(uint16_t); + RTDBGMODCV_CHECK_RET_BF(cbSubSect >= offSrcLine + cbSrcLine, + ("cbSubSect=%#x aoffSrcFiles[%u]=%#x cbSrcLine=%#x\n", + cbSubSect, iSeg, offSrcLine, cbSrcLine)); + uint16_t const *paiLines = (uint16_t const *)&pSrcLine->aoffLines[pSrcLine->cPairs]; + Log2(("RTDbgModCv: seg #%u, %u pairs (off %#x)\n", pSrcLine->idxSeg, pSrcLine->cPairs, offSrcLine)); + for (uint32_t iPair = 0; iPair < pSrcLine->cPairs; iPair++) + { + + uint32_t idxSeg = pSrcLine->idxSeg; + uint64_t off = pSrcLine->aoffLines[iPair]; + int rc = rtDbgModCvAdjustSegAndOffset(pThis, &idxSeg, &off); + if (RT_SUCCESS(rc)) + rc = RTDbgModLineAdd(pThis->hCnt, pszName, paiLines[iPair], idxSeg, off, NULL); + if (RT_SUCCESS(rc)) + Log3(("RTDbgModCv: %#x:%#010llx %0u\n", idxSeg, off, paiLines[iPair])); + /* Note! Wlink produces the sstSrcModule subsections from LINNUM records, however the + CVGenLines() function assumes there is only one segment contributing to the + line numbers. So, when we do assembly that jumps between segments, it emits + the wrong addresses for some line numbers and we end up here, typically with + VERR_DBG_ADDRESS_CONFLICT. */ + else + Log(( "RTDbgModCv: %#x:%#010llx %0u - rc=%Rrc!! (org: idxSeg=%#x off=%#x)\n", + idxSeg, off, paiLines[iPair], rc, pSrcLine->idxSeg, pSrcLine->aoffLines[iPair])); + } + } + } + + return VINF_SUCCESS; +} + + +static int rtDbgModCvLoadSegmentMap(PRTDBGMODCV pThis) +{ + /* + * Search for the segment map and segment names. They will be at the end of the directory. + */ + uint32_t iSegMap = UINT32_MAX; + uint32_t iSegNames = UINT32_MAX; + uint32_t i = pThis->cDirEnts; + while (i-- > 0) + { + if ( pThis->paDirEnts[i].iMod != 0xffff + && pThis->paDirEnts[i].iMod != 0x0000) + break; + if (pThis->paDirEnts[i].uSubSectType == kCvSst_SegMap) + iSegMap = i; + else if (pThis->paDirEnts[i].uSubSectType == kCvSst_SegName) + iSegNames = i; + } + if (iSegMap == UINT32_MAX) + { + Log(("RTDbgModCv: No segment map present, using segment indexes as is then...\n")); + return VINF_SUCCESS; + } + RTDBGMODCV_CHECK_RET_BF(pThis->paDirEnts[iSegMap].cb >= sizeof(RTCVSEGMAPHDR), + ("Bad sstSegMap entry: cb=%#x\n", pThis->paDirEnts[iSegMap].cb)); + RTDBGMODCV_CHECK_NOMSG_RET_BF(iSegNames == UINT32_MAX || pThis->paDirEnts[iSegNames].cb > 0); + + /* + * Read them into memory. + */ + int rc = rtDbgModCvReadAtAlloc(pThis, pThis->paDirEnts[iSegMap].off, (void **)&pThis->pSegMap, + pThis->paDirEnts[iSegMap].cb); + if (iSegNames != UINT32_MAX && RT_SUCCESS(rc)) + { + pThis->cbSegNames = pThis->paDirEnts[iSegNames].cb; + rc = rtDbgModCvReadAtAlloc(pThis, pThis->paDirEnts[iSegNames].off, (void **)&pThis->pszzSegNames, + pThis->paDirEnts[iSegNames].cb); + } + if (RT_FAILURE(rc)) + return rc; + RTDBGMODCV_CHECK_NOMSG_RET_BF(!pThis->pszzSegNames || !pThis->pszzSegNames[pThis->cbSegNames - 1]); /* must be terminated */ + + /* Use local pointers to avoid lots of indirection and typing. */ + PCRTCVSEGMAPHDR pHdr = &pThis->pSegMap->Hdr; + PRTCVSEGMAPDESC paDescs = &pThis->pSegMap->aDescs[0]; + + /* + * If there are only logical segments, assume a direct mapping. + * PE images, like the NT4 kernel, does it like this. + */ + bool const fNoGroups = pHdr->cSegs == pHdr->cLogSegs; + + /* + * The PE image has an extra section/segment for the headers, the others + * doesn't. PE images doesn't have DOS frames. So, figure the image type now. + */ + RTLDRFMT enmImgFmt = RTLDRFMT_INVALID; + if (pThis->pMod->pImgVt) + enmImgFmt = pThis->pMod->pImgVt->pfnGetFormat(pThis->pMod); + + /* + * Validate and display it all. + */ + Log2(("RTDbgModCv: SegMap: cSegs=%#x cLogSegs=%#x (cbSegNames=%#x)\n", pHdr->cSegs, pHdr->cLogSegs, pThis->cbSegNames)); + RTDBGMODCV_CHECK_RET_BF(pThis->paDirEnts[iSegMap].cb >= sizeof(*pHdr) + pHdr->cSegs * sizeof(paDescs[0]), + ("SegMap is out of bounds: cbSubSect=%#x cSegs=%#x\n", pThis->paDirEnts[iSegMap].cb, pHdr->cSegs)); + RTDBGMODCV_CHECK_NOMSG_RET_BF(pHdr->cSegs >= pHdr->cLogSegs); + + Log2(("Logical segment descriptors: %u\n", pHdr->cLogSegs)); + + bool fHaveDosFrames = false; + for (i = 0; i < pHdr->cSegs; i++) + { + if (i == pHdr->cLogSegs) + Log2(("Group/Physical descriptors: %u\n", pHdr->cSegs - pHdr->cLogSegs)); + char szFlags[16]; + memset(szFlags, '-', sizeof(szFlags)); + if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_READ) + szFlags[0] = 'R'; + if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_WRITE) + szFlags[1] = 'W'; + if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_EXECUTE) + szFlags[2] = 'X'; + if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_32BIT) + szFlags[3] = '3', szFlags[4] = '2'; + if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_SEL) + szFlags[5] = 'S'; + if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_ABS) + szFlags[6] = 'A'; + if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_GROUP) + szFlags[7] = 'G'; + szFlags[8] = '\0'; + if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_RESERVED) + szFlags[8] = '!', szFlags[9] = '\0'; + Log2((" #%02u: %#010x LB %#010x flags=%#06x ovl=%#06x group=%#06x frame=%#06x iSegName=%#06x iClassName=%#06x %s\n", + i < pHdr->cLogSegs ? i : i - pHdr->cLogSegs, paDescs[i].off, paDescs[i].cb, paDescs[i].fFlags, paDescs[i].iOverlay, + paDescs[i].iGroup, paDescs[i].iFrame, paDescs[i].offSegName, paDescs[i].offClassName, szFlags)); + + RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].offSegName == UINT16_MAX || paDescs[i].offSegName < pThis->cbSegNames); + RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].offClassName == UINT16_MAX || paDescs[i].offClassName < pThis->cbSegNames); + const char *pszName = paDescs[i].offSegName != UINT16_MAX + ? pThis->pszzSegNames + paDescs[i].offSegName + : NULL; + const char *pszClass = paDescs[i].offClassName != UINT16_MAX + ? pThis->pszzSegNames + paDescs[i].offClassName + : NULL; + if (pszName || pszClass) + Log2((" pszName=%s pszClass=%s\n", pszName, pszClass)); + + /* Validate the group link. */ + RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].iGroup == 0 || !(paDescs[i].fFlags & RTCVSEGMAPDESC_F_GROUP)); + RTDBGMODCV_CHECK_NOMSG_RET_BF( paDescs[i].iGroup == 0 + || ( paDescs[i].iGroup >= pHdr->cLogSegs + && paDescs[i].iGroup < pHdr->cSegs)); + RTDBGMODCV_CHECK_NOMSG_RET_BF( paDescs[i].iGroup == 0 + || (paDescs[paDescs[i].iGroup].fFlags & RTCVSEGMAPDESC_F_GROUP)); + RTDBGMODCV_CHECK_NOMSG_RET_BF(!(paDescs[i].fFlags & RTCVSEGMAPDESC_F_GROUP) || paDescs[i].off == 0); /* assumed below */ + + if (fNoGroups) + { + RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].iGroup == 0); + if ( !fHaveDosFrames + && paDescs[i].iFrame != 0 + && (paDescs[i].fFlags & (RTCVSEGMAPDESC_F_SEL | RTCVSEGMAPDESC_F_ABS)) + && paDescs[i].iOverlay == 0 + && enmImgFmt != RTLDRFMT_PE + && pThis->enmType != RTCVFILETYPE_DBG) + fHaveDosFrames = true; /* BIOS, only groups with frames. */ + } + } + + /* + * Further valiations based on fHaveDosFrames or not. + */ + if (fNoGroups) + { + if (fHaveDosFrames) + for (i = 0; i < pHdr->cSegs; i++) + { + RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].iOverlay == 0); + RTDBGMODCV_CHECK_NOMSG_RET_BF( (paDescs[i].fFlags & (RTCVSEGMAPDESC_F_SEL | RTCVSEGMAPDESC_F_ABS)) + == RTCVSEGMAPDESC_F_SEL + || (paDescs[i].fFlags & (RTCVSEGMAPDESC_F_SEL | RTCVSEGMAPDESC_F_ABS)) + == RTCVSEGMAPDESC_F_ABS); + RTDBGMODCV_CHECK_NOMSG_RET_BF(!(paDescs[i].fFlags & RTCVSEGMAPDESC_F_ABS)); + } + else + for (i = 0; i < pHdr->cSegs; i++) + RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].off == 0); + } + + /* + * Modify the groups index to be the loader segment index instead, also + * add the segments to the container if we haven't done that already. + */ + + /* Guess work: Group can be implicit if used. Observed Visual C++ v1.5, + omitting the CODE group. */ + const char *pszGroup0 = NULL; + uint64_t cbGroup0 = 0; + if (!fNoGroups && !fHaveDosFrames) + { + for (i = 0; i < pHdr->cSegs; i++) + if ( !(paDescs[i].fFlags & (RTCVSEGMAPDESC_F_GROUP | RTCVSEGMAPDESC_F_ABS)) + && paDescs[i].iGroup == 0) + { + if (pszGroup0 == NULL && paDescs[i].offClassName != UINT16_MAX) + pszGroup0 = pThis->pszzSegNames + paDescs[i].offClassName; + uint64_t offEnd = (uint64_t)paDescs[i].off + paDescs[i].cb; + if (offEnd > cbGroup0) + cbGroup0 = offEnd; + } + } + + /* Add the segments. + Note! The RVAs derived from this exercise are all wrong. :-/ + Note! We don't have an image loader, so we cannot add any fake sections. */ + /** @todo Try see if we can figure something out from the frame value later. */ + if (!pThis->fHaveLoadedSegments) + { + uint16_t iSeg = 0; + if (!fHaveDosFrames) + { + Assert(!pThis->pMod->pImgVt); Assert(pThis->enmType != RTCVFILETYPE_DBG); + uint64_t uRva = 0; + if (cbGroup0 && !fNoGroups) + { + rc = RTDbgModSegmentAdd(pThis->hCnt, 0, cbGroup0, pszGroup0 ? pszGroup0 : "Seg00", 0 /*fFlags*/, NULL); + uRva += cbGroup0; + iSeg++; + } + + for (i = 0; RT_SUCCESS(rc) && i < pHdr->cSegs; i++) + if ((paDescs[i].fFlags & RTCVSEGMAPDESC_F_GROUP) || fNoGroups) + { + char szName[16]; + char *pszName = szName; + if (paDescs[i].offSegName != UINT16_MAX) + pszName = pThis->pszzSegNames + paDescs[i].offSegName; + else + RTStrPrintf(szName, sizeof(szName), "Seg%02u", iSeg); + rc = RTDbgModSegmentAdd(pThis->hCnt, uRva, paDescs[i].cb, pszName, 0 /*fFlags*/, NULL); + uRva += paDescs[i].cb; + iSeg++; + } + } + else + { + /* The map is not sorted by RVA, very annoying, but I'm countering + by being lazy and slow about it. :-) Btw. this is the BIOS case. */ + Assert(fNoGroups); +#if 1 /** @todo need more inputs */ + + /* Figure image base address. */ + uint64_t uImageBase = UINT64_MAX; + for (i = 0; RT_SUCCESS(rc) && i < pHdr->cSegs; i++) + { + uint64_t uAddr = (uint64_t)paDescs[i].off + ((uint32_t)paDescs[i].iFrame << 4); + if (uAddr < uImageBase) + uImageBase = uAddr; + } + + /* Add the segments. */ + uint64_t uMinAddr = uImageBase; + for (i = 0; RT_SUCCESS(rc) && i < pHdr->cSegs; i++) + { + /* Figure out the next one. */ + uint16_t cOverlaps = 0; + uint16_t iBest = UINT16_MAX; + uint64_t uBestAddr = UINT64_MAX; + for (uint16_t j = 0; j < pHdr->cSegs; j++) + { + uint64_t uAddr = (uint64_t)paDescs[j].off + ((uint32_t)paDescs[j].iFrame << 4); + if (uAddr >= uMinAddr && uAddr < uBestAddr) + { + uBestAddr = uAddr; + iBest = j; + } + else if (uAddr == uBestAddr) + { + cOverlaps++; + if (paDescs[j].cb > paDescs[iBest].cb) + { + uBestAddr = uAddr; + iBest = j; + } + } + } + if (iBest == UINT16_MAX && RT_SUCCESS(rc)) + { + rc = VERR_CV_IPE; + break; + } + + /* Add it. */ + char szName[16]; + char *pszName = szName; + if (paDescs[iBest].offSegName != UINT16_MAX) + pszName = pThis->pszzSegNames + paDescs[iBest].offSegName; + else + RTStrPrintf(szName, sizeof(szName), "Seg%02u", iSeg); + RTDBGSEGIDX idxDbgSeg = NIL_RTDBGSEGIDX; + rc = RTDbgModSegmentAdd(pThis->hCnt, uBestAddr - uImageBase, paDescs[iBest].cb, pszName, 0 /*fFlags*/, &idxDbgSeg); + Log(("CV: %#010x LB %#010x %s uRVA=%#010x iBest=%u cOverlaps=%u [idxDbgSeg=%#x iSeg=%#x]\n", + uBestAddr, paDescs[iBest].cb, szName, uBestAddr - uImageBase, iBest, cOverlaps, idxDbgSeg, idxDbgSeg)); + + /* Update translations. */ + paDescs[iBest].iGroup = iSeg; + if (cOverlaps > 0) + { + for (uint16_t j = 0; j < pHdr->cSegs; j++) + if ((uint64_t)paDescs[j].off + ((uint32_t)paDescs[j].iFrame << 4) == uBestAddr) + paDescs[iBest].iGroup = iSeg; + i += cOverlaps; + } + + /* Advance. */ + uMinAddr = uBestAddr + 1; + iSeg++; + } + + pThis->fHaveDosFrames = true; +#else + uint32_t iFrameFirst = UINT32_MAX; + uint16_t iSeg = 0; + uint32_t iFrameMin = 0; + do + { + /* Find next frame. */ + uint32_t iFrame = UINT32_MAX; + for (uint16_t j = 0; j < pHdr->cSegs; j++) + if (paDescs[j].iFrame >= iFrameMin && paDescs[j].iFrame < iFrame) + iFrame = paDescs[j].iFrame; + if (iFrame == UINT32_MAX) + break; + + /* Figure the frame span. */ + uint32_t offFirst = UINT32_MAX; + uint64_t offEnd = 0; + for (uint16_t j = 0; j < pHdr->cSegs; j++) + if (paDescs[j].iFrame == iFrame) + { + uint64_t offThisEnd = paDescs[j].off + paDescs[j].cb; + if (offThisEnd > offEnd) + offEnd = offThisEnd; + if (paDescs[j].off < offFirst) + offFirst = paDescs[j].off; + } + + if (offFirst < offEnd) + { + /* Add it. */ + char szName[16]; + RTStrPrintf(szName, sizeof(szName), "Frame_%04x", iFrame); + Log(("CV: %s offEnd=%#x offFirst=%#x\n", szName, offEnd, offFirst)); + if (iFrameFirst == UINT32_MAX) + iFrameFirst = iFrame; + rc = RTDbgModSegmentAdd(pThis->hCnt, (iFrame - iFrameFirst) << 4, offEnd, szName, 0 /*fFlags*/, NULL); + + /* Translation updates. */ + for (uint16_t j = 0; j < pHdr->cSegs; j++) + if (paDescs[j].iFrame == iFrame) + { + paDescs[j].iGroup = iSeg; + paDescs[j].off = 0; + paDescs[j].cb = offEnd > UINT32_MAX ? UINT32_MAX : (uint32_t)offEnd; + } + + iSeg++; + } + + iFrameMin = iFrame + 1; + } while (RT_SUCCESS(rc)); +#endif + } + + if (RT_FAILURE(rc)) + { + Log(("RTDbgModCv: %Rrc while adding segments from SegMap\n", rc)); + return rc; + } + + pThis->fHaveLoadedSegments = true; + + /* Skip the stuff below if we have DOS frames since we did it all above. */ + if (fHaveDosFrames) + return VINF_SUCCESS; + } + + /* Pass one: Fixate the group segment indexes. */ + uint16_t iSeg0 = enmImgFmt == RTLDRFMT_PE || pThis->enmType == RTCVFILETYPE_DBG ? 1 : 0; + uint16_t iSeg = iSeg0 + (cbGroup0 > 0); /** @todo probably wrong... */ + for (i = 0; i < pHdr->cSegs; i++) + if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_ABS) + paDescs[i].iGroup = (uint16_t)(RTDBGSEGIDX_ABS & UINT16_MAX); + else if ((paDescs[i].fFlags & RTCVSEGMAPDESC_F_GROUP) || fNoGroups) + paDescs[i].iGroup = iSeg++; + + /* Pass two: Resolve group references in to segment indexes. */ + Log2(("Mapped segments (both kinds):\n")); + for (i = 0; i < pHdr->cSegs; i++) + { + if (!fNoGroups && !(paDescs[i].fFlags & (RTCVSEGMAPDESC_F_GROUP | RTCVSEGMAPDESC_F_ABS))) + paDescs[i].iGroup = paDescs[i].iGroup == 0 ? iSeg0 : paDescs[paDescs[i].iGroup].iGroup; + + Log2((" #%02u: %#010x LB %#010x -> %#06x (flags=%#06x ovl=%#06x frame=%#06x)\n", + i, paDescs[i].off, paDescs[i].cb, paDescs[i].iGroup, + paDescs[i].fFlags, paDescs[i].iOverlay, paDescs[i].iFrame)); + } + + return VINF_SUCCESS; +} + + +/** + * @callback_method_impl{PFNRTSORTCMP, + * Used by rtDbgModCvLoadDirectory to sort the directory.} + */ +static DECLCALLBACK(int) rtDbgModCvDirEntCmp(void const *pvElement1, void const *pvElement2, void *pvUser) +{ + PRTCVDIRENT32 pEntry1 = (PRTCVDIRENT32)pvElement1; + PRTCVDIRENT32 pEntry2 = (PRTCVDIRENT32)pvElement2; + if (pEntry1->iMod < pEntry2->iMod) + return -1; + if (pEntry1->iMod > pEntry2->iMod) + return 1; + if (pEntry1->uSubSectType < pEntry2->uSubSectType) + return -1; + if (pEntry1->uSubSectType > pEntry2->uSubSectType) + return 1; + + RT_NOREF_PV(pvUser); + return 0; +} + + +/** + * Loads the directory into memory (RTDBGMODCV::paDirEnts and + * RTDBGMODCV::cDirEnts). + * + * Converting old format version into the newer format to simplifying the code + * using the directory. + * + * + * @returns IPRT status code. (May leave with paDirEnts allocated on failure.) + * @param pThis The CV reader instance. + */ +static int rtDbgModCvLoadDirectory(PRTDBGMODCV pThis) +{ + /* + * Read in the CV directory. + */ + int rc; + if ( pThis->u32CvMagic == RTCVHDR_MAGIC_NB00 + || pThis->u32CvMagic == RTCVHDR_MAGIC_NB02) + { + /* + * 16-bit type. + */ + RTCVDIRHDR16 DirHdr; + rc = rtDbgModCvReadAt(pThis, pThis->offDir, &DirHdr, sizeof(DirHdr)); + if (RT_SUCCESS(rc)) + { + if (DirHdr.cEntries > 2 && DirHdr.cEntries < _64K - 32U) + { + pThis->cDirEnts = DirHdr.cEntries; + pThis->paDirEnts = (PRTCVDIRENT32)RTMemAlloc(DirHdr.cEntries * sizeof(pThis->paDirEnts[0])); + if (pThis->paDirEnts) + { + rc = rtDbgModCvReadAt(pThis, pThis->offDir + sizeof(DirHdr), + pThis->paDirEnts, DirHdr.cEntries * sizeof(RTCVDIRENT16)); + if (RT_SUCCESS(rc)) + { + /* Convert the entries (from the end). */ + uint32_t cLeft = DirHdr.cEntries; + RTCVDIRENT32 volatile *pDst = pThis->paDirEnts + cLeft; + RTCVDIRENT16 volatile *pSrc = (RTCVDIRENT16 volatile *)pThis->paDirEnts + cLeft; + while (cLeft--) + { + pDst--; + pSrc--; + + pDst->cb = pSrc->cb; + pDst->off = RT_MAKE_U32(pSrc->offLow, pSrc->offHigh); + pDst->iMod = pSrc->iMod; + pDst->uSubSectType = pSrc->uSubSectType; + } + } + } + else + rc = VERR_NO_MEMORY; + } + else + { + Log(("Old CV directory count is out of considered valid range: %#x\n", DirHdr.cEntries)); + rc = VERR_CV_BAD_FORMAT; + } + } + } + else + { + /* + * 32-bit type (reading too much for NB04 is no problem). + * + * Note! The watcom linker (v1.9) seems to overwrite the directory + * header and more under some conditions. So, if this code fails + * you might be so lucky as to have reproduce that issue... + */ + RTCVDIRHDR32EX DirHdr; + rc = rtDbgModCvReadAt(pThis, pThis->offDir, &DirHdr, sizeof(DirHdr)); + if (RT_SUCCESS(rc)) + { + if ( DirHdr.Core.cbHdr != sizeof(DirHdr.Core) + && DirHdr.Core.cbHdr != sizeof(DirHdr)) + { + Log(("Unexpected CV directory size: %#x [wlink screwup?]\n", DirHdr.Core.cbHdr)); + rc = VERR_CV_BAD_FORMAT; + } + if ( DirHdr.Core.cbHdr == sizeof(DirHdr) + && ( DirHdr.offNextDir != 0 + || DirHdr.fFlags != 0) ) + { + Log(("Extended CV directory headers fields are not zero: fFlags=%#x offNextDir=%#x [wlink screwup?]\n", + DirHdr.fFlags, DirHdr.offNextDir)); + rc = VERR_CV_BAD_FORMAT; + } + if (DirHdr.Core.cbEntry != sizeof(RTCVDIRENT32)) + { + Log(("Unexpected CV directory entry size: %#x (expected %#x) [wlink screwup?]\n", DirHdr.Core.cbEntry, sizeof(RTCVDIRENT32))); + rc = VERR_CV_BAD_FORMAT; + } + if (DirHdr.Core.cEntries < 2 || DirHdr.Core.cEntries >= _512K) + { + Log(("CV directory count is out of considered valid range: %#x [wlink screwup?]\n", DirHdr.Core.cEntries)); + rc = VERR_CV_BAD_FORMAT; + } + if (RT_SUCCESS(rc)) + { + pThis->cDirEnts = DirHdr.Core.cEntries; + pThis->paDirEnts = (PRTCVDIRENT32)RTMemAlloc(DirHdr.Core.cEntries * sizeof(pThis->paDirEnts[0])); + if (pThis->paDirEnts) + rc = rtDbgModCvReadAt(pThis, pThis->offDir + DirHdr.Core.cbHdr, + pThis->paDirEnts, DirHdr.Core.cEntries * sizeof(RTCVDIRENT32)); + else + rc = VERR_NO_MEMORY; + } + } + } + + if (RT_SUCCESS(rc)) + { + uint32_t const cbDbgInfo = pThis->cbDbgInfo; + uint32_t const cDirEnts = pThis->cDirEnts; + + /* + * Just sort the directory in a way we like, no need to make + * complicated demands on the linker output. + */ + RTSortShell(pThis->paDirEnts, cDirEnts, sizeof(pThis->paDirEnts[0]), rtDbgModCvDirEntCmp, NULL); + + /* + * Basic info validation. + */ + uint16_t cGlobalMods = 0; + uint16_t cNormalMods = 0; + uint16_t iModLast = 0; + Log2(("RTDbgModCv: %u (%#x) directory entries:\n", cDirEnts, cDirEnts)); + for (uint32_t i = 0; i < cDirEnts; i++) + { + PCRTCVDIRENT32 pDirEnt = &pThis->paDirEnts[i]; + Log2((" #%04u mod=%#06x sst=%#06x at %#010x LB %#07x %s\n", + i, pDirEnt->iMod, pDirEnt->uSubSectType, pDirEnt->off, pDirEnt->cb, + rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType))); + + if ( pDirEnt->off >= cbDbgInfo + || pDirEnt->cb >= cbDbgInfo + || pDirEnt->off + pDirEnt->cb > cbDbgInfo) + { + Log(("CV directory entry #%u is out of bounds: %#x LB %#x, max %#x\n", i, pDirEnt->off, pDirEnt->cb, cbDbgInfo)); + rc = VERR_CV_BAD_FORMAT; + } + if ( pDirEnt->iMod == 0 + && pThis->u32CvMagic != RTCVHDR_MAGIC_NB04 + && pThis->u32CvMagic != RTCVHDR_MAGIC_NB02 + && pThis->u32CvMagic != RTCVHDR_MAGIC_NB00) + { + Log(("CV directory entry #%u uses module index 0 (uSubSectType=%#x)\n", i, pDirEnt->uSubSectType)); + rc = VERR_CV_BAD_FORMAT; + } + if (pDirEnt->iMod == 0 || pDirEnt->iMod == 0xffff) + cGlobalMods++; + else + { + if (pDirEnt->iMod > iModLast) + { + if ( pDirEnt->uSubSectType != kCvSst_Module + && pDirEnt->uSubSectType != kCvSst_OldModule) + { + Log(("CV directory entry #%u: expected module subsection first, found %s (%#x)\n", + i, rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType), pDirEnt->uSubSectType)); + rc = VERR_CV_BAD_FORMAT; + } + if (pDirEnt->iMod != iModLast + 1) + { + Log(("CV directory entry #%u: skips from mod %#x to %#x modules\n", i, iModLast, pDirEnt->iMod)); + rc = VERR_CV_BAD_FORMAT; + } + iModLast = pDirEnt->iMod; + } + cNormalMods++; + } + } + if (cGlobalMods == 0) + { + Log(("CV directory contains no global modules\n")); + rc = VERR_CV_BAD_FORMAT; + } + if (RT_SUCCESS(rc)) + { + Log(("CV dir stats: %u total, %u normal, %u special, iModLast=%#x (%u)\n", + cDirEnts, cNormalMods, cGlobalMods, iModLast, iModLast)); + +#if 0 /* skip this stuff */ + /* + * Validate the directory ordering. + */ + uint16_t i = 0; + + /* Normal modules. */ + if (pThis->enmDirOrder != RTCVDIRORDER_BY_SST_MOD) + { + uint16_t iEndNormalMods = cNormalMods + (pThis->enmDirOrder == RTCVDIRORDER_BY_MOD_0 ? cGlobalMods : 0); + while (i < iEndNormalMods) + { + if (pThis->paDirEnts[i].iMod == 0 || pThis->paDirEnts[i].iMod == 0xffff) + { + Log(("CV directory entry #%u: Unexpected global module entry.\n", i)); + rc = VERR_CV_BAD_FORMAT; + } + i++; + } + } + else + { + uint32_t fSeen = RT_BIT_32(kCvSst_Module - kCvSst_Module) + | RT_BIT_32(kCvSst_Libraries - kCvSst_Module) + | RT_BIT_32(kCvSst_GlobalSym - kCvSst_Module) + | RT_BIT_32(kCvSst_GlobalPub - kCvSst_Module) + | RT_BIT_32(kCvSst_GlobalTypes - kCvSst_Module) + | RT_BIT_32(kCvSst_SegName - kCvSst_Module) + | RT_BIT_32(kCvSst_SegMap - kCvSst_Module) + | RT_BIT_32(kCvSst_StaticSym - kCvSst_Module) + | RT_BIT_32(kCvSst_FileIndex - kCvSst_Module) + | RT_BIT_32(kCvSst_MPC - kCvSst_Module); + uint16_t iMod = 0; + uint16_t uSst = kCvSst_Module; + while (i < cNormalMods) + { + PCRTCVDIRENT32 pDirEnt = &pThis->paDirEnts[i]; + if ( pDirEnt->iMod > iMod + || pDirEnt->iMod == iMod) /* wlink subjected to MSVC 2010 /Z7 files with multiple .debug$S. */ + { + if (pDirEnt->uSubSectType != uSst) + { + Log(("CV directory entry #%u: Expected %s (%#x), found %s (%#x).\n", + i, rtDbgModCvGetSubSectionName(uSst), uSst, + rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType), pDirEnt->uSubSectType)); + rc = VERR_CV_BAD_FORMAT; + } + } + else + { + uint32_t iBit = pDirEnt->uSubSectType - kCvSst_Module; + if (iBit >= 32U || (fSeen & RT_BIT_32(iBit))) + { + Log(("CV directory entry #%u: SST %s (%#x) has already been seen or is for globals.\n", + i, rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType), pDirEnt->uSubSectType)); + rc = VERR_CV_BAD_FORMAT; + } + fSeen |= RT_BIT_32(iBit); + } + + uSst = pDirEnt->uSubSectType; + iMod = pDirEnt->iMod; + i++; + } + } + + /* New style with special modules at the end. */ + if (pThis->enmDirOrder != RTCVDIRORDER_BY_MOD_0) + while (i < cDirEnts) + { + if (pThis->paDirEnts[i].iMod != 0 && pThis->paDirEnts[i].iMod != 0xffff) + { + Log(("CV directory entry #%u: Expected global module entry, not %#x.\n", i, + pThis->paDirEnts[i].iMod)); + rc = VERR_CV_BAD_FORMAT; + } + i++; + } +#endif + } + } + + return rc; +} + + +static int rtDbgModCvLoadCodeViewInfo(PRTDBGMODCV pThis) +{ + /* + * Load the directory, the segment map (if any) and then scan for segments + * if necessary. + */ + int rc = rtDbgModCvLoadDirectory(pThis); + if (RT_SUCCESS(rc)) + rc = rtDbgModCvLoadSegmentMap(pThis); + if (RT_SUCCESS(rc) && !pThis->fHaveLoadedSegments) + { + rc = VERR_CV_TODO; /** @todo Scan anything containing address, in particular sstSegMap and sstModule, + * and reconstruct the segments from that information. */ + pThis->cbImage = 0x1000; + rc = VINF_SUCCESS; + } + + /* + * Process the directory. + */ + for (uint32_t i = 0; RT_SUCCESS(rc) && i < pThis->cDirEnts; i++) + { + PCRTCVDIRENT32 pDirEnt = &pThis->paDirEnts[i]; + Log3(("Processing module %#06x subsection #%04u %s\n", pDirEnt->iMod, i, rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType))); + PFNDBGMODCVSUBSECTCALLBACK pfnCallback = NULL; + switch (pDirEnt->uSubSectType) + { + case kCvSst_GlobalPub: + case kCvSst_GlobalSym: + case kCvSst_StaticSym: + pfnCallback = rtDbgModCvSs_GlobalPub_GlobalSym_StaticSym; + break; + case kCvSst_Module: + pfnCallback = rtDbgModCvSs_Module; + break; + case kCvSst_PublicSym: + case kCvSst_Symbols: + case kCvSst_AlignSym: + pfnCallback = rtDbgModCvSs_Symbols_PublicSym_AlignSym; + break; + + case kCvSst_OldModule: + case kCvSst_OldPublic: + case kCvSst_OldTypes: + case kCvSst_OldSymbols: + case kCvSst_OldSrcLines: + case kCvSst_OldLibraries: + case kCvSst_OldImports: + case kCvSst_OldCompacted: + case kCvSst_OldSrcLnSeg: + case kCvSst_OldSrcLines3: + /** @todo implement more. */ + break; + + case kCvSst_Types: + case kCvSst_Public: + case kCvSst_SrcLnSeg: + /** @todo implement more. */ + break; + case kCvSst_SrcModule: + pfnCallback = rtDbgModCvSs_SrcModule; + break; + case kCvSst_Libraries: + case kCvSst_GlobalTypes: + case kCvSst_MPC: + case kCvSst_PreComp: + case kCvSst_PreCompMap: + case kCvSst_OffsetMap16: + case kCvSst_OffsetMap32: + case kCvSst_FileIndex: + + default: + /** @todo implement more. */ + break; + + /* Skip because we've already processed them: */ + case kCvSst_SegMap: + case kCvSst_SegName: + pfnCallback = NULL; + break; + } + + if (pfnCallback) + { + void *pvSubSect; + rc = rtDbgModCvReadAtAlloc(pThis, pDirEnt->off, &pvSubSect, pDirEnt->cb); + if (RT_SUCCESS(rc)) + { + rc = pfnCallback(pThis, pvSubSect, pDirEnt->cb, pDirEnt); + RTMemFree(pvSubSect); + } + } + } + + /* + * Free temporary parsing objects. + */ + if (pThis->pbSrcInfo) + { + RTMemFree(pThis->pbSrcInfo); + pThis->pbSrcInfo = NULL; + pThis->cbSrcInfo = 0; + pThis->cbSrcInfoAlloc = 0; + } + if (pThis->pchSrcStrings) + { + RTMemFree(pThis->pchSrcStrings); + pThis->pchSrcStrings = NULL; + pThis->cbSrcStrings = 0; + pThis->cbSrcStringsAlloc = 0; + } + + return rc; +} + + +/* + * + * COFF Debug Info Parsing. + * COFF Debug Info Parsing. + * COFF Debug Info Parsing. + * + */ + +#ifdef LOG_ENABLED +static const char *rtDbgModCvGetCoffStorageClassName(uint8_t bStorageClass) +{ + switch (bStorageClass) + { + case IMAGE_SYM_CLASS_END_OF_FUNCTION: return "END_OF_FUNCTION"; + case IMAGE_SYM_CLASS_NULL: return "NULL"; + case IMAGE_SYM_CLASS_AUTOMATIC: return "AUTOMATIC"; + case IMAGE_SYM_CLASS_EXTERNAL: return "EXTERNAL"; + case IMAGE_SYM_CLASS_STATIC: return "STATIC"; + case IMAGE_SYM_CLASS_REGISTER: return "REGISTER"; + case IMAGE_SYM_CLASS_EXTERNAL_DEF: return "EXTERNAL_DEF"; + case IMAGE_SYM_CLASS_LABEL: return "LABEL"; + case IMAGE_SYM_CLASS_UNDEFINED_LABEL: return "UNDEFINED_LABEL"; + case IMAGE_SYM_CLASS_MEMBER_OF_STRUCT: return "MEMBER_OF_STRUCT"; + case IMAGE_SYM_CLASS_ARGUMENT: return "ARGUMENT"; + case IMAGE_SYM_CLASS_STRUCT_TAG: return "STRUCT_TAG"; + case IMAGE_SYM_CLASS_MEMBER_OF_UNION: return "MEMBER_OF_UNION"; + case IMAGE_SYM_CLASS_UNION_TAG: return "UNION_TAG"; + case IMAGE_SYM_CLASS_TYPE_DEFINITION: return "TYPE_DEFINITION"; + case IMAGE_SYM_CLASS_UNDEFINED_STATIC: return "UNDEFINED_STATIC"; + case IMAGE_SYM_CLASS_ENUM_TAG: return "ENUM_TAG"; + case IMAGE_SYM_CLASS_MEMBER_OF_ENUM: return "MEMBER_OF_ENUM"; + case IMAGE_SYM_CLASS_REGISTER_PARAM: return "REGISTER_PARAM"; + case IMAGE_SYM_CLASS_BIT_FIELD: return "BIT_FIELD"; + case IMAGE_SYM_CLASS_FAR_EXTERNAL: return "FAR_EXTERNAL"; + case IMAGE_SYM_CLASS_BLOCK: return "BLOCK"; + case IMAGE_SYM_CLASS_FUNCTION: return "FUNCTION"; + case IMAGE_SYM_CLASS_END_OF_STRUCT: return "END_OF_STRUCT"; + case IMAGE_SYM_CLASS_FILE: return "FILE"; + case IMAGE_SYM_CLASS_SECTION: return "SECTION"; + case IMAGE_SYM_CLASS_WEAK_EXTERNAL: return "WEAK_EXTERNAL"; + case IMAGE_SYM_CLASS_CLR_TOKEN: return "CLR_TOKEN"; + } + + static char s_szName[32]; + RTStrPrintf(s_szName, sizeof(s_szName), "Unknown%#04x", bStorageClass); + return s_szName; +} +#endif /* LOG_ENABLED */ + + +/** + * Adds a chunk of COFF line numbers. + * + * @param pThis The COFF/CodeView reader instance. + * @param pszFile The source file name. + * @param iSection The section number. + * @param paLines Pointer to the first line number table entry. + * @param cLines The number of line number table entries to add. + */ +static void rtDbgModCvAddCoffLineNumbers(PRTDBGMODCV pThis, const char *pszFile, uint32_t iSection, + PCIMAGE_LINENUMBER paLines, uint32_t cLines) +{ + RT_NOREF_PV(iSection); + Log4(("Adding %u line numbers in section #%u for %s\n", cLines, iSection, pszFile)); + PCIMAGE_LINENUMBER pCur = paLines; + while (cLines-- > 0) + { + if (pCur->Linenumber) + { + int rc = RTDbgModLineAdd(pThis->hCnt, pszFile, pCur->Linenumber, RTDBGSEGIDX_RVA, pCur->Type.VirtualAddress, NULL); + Log4((" %#010x: %u [%Rrc]\n", pCur->Type.VirtualAddress, pCur->Linenumber, rc)); NOREF(rc); + } + pCur++; + } +} + + +/** + * Adds a COFF symbol. + * + * @returns IPRT status (ignored) + * @param pThis The COFF/CodeView reader instance. + * @param idxSeg IPRT RVA or ABS segment index indicator. + * @param uValue The symbol value. + * @param pszName The symbol name. + */ +static int rtDbgModCvAddCoffSymbol(PRTDBGMODCV pThis, uint32_t idxSeg, uint32_t uValue, const char *pszName) +{ + int rc = RTDbgModSymbolAdd(pThis->hCnt, pszName, idxSeg, uValue, 0, 0 /*fFlags*/, NULL); + Log(("Symbol: %s:%08x %s [%Rrc]\n", idxSeg == RTDBGSEGIDX_RVA ? "rva" : "abs", uValue, pszName, rc)); + if (rc == VERR_DBG_ADDRESS_CONFLICT || rc == VERR_DBG_DUPLICATE_SYMBOL) + rc = VINF_SUCCESS; + return rc; +} + + +/** + * Processes the COFF symbol table. + * + * @returns IPRT status code + * @param pThis The COFF/CodeView reader instance. + * @param paSymbols Pointer to the symbol table. + * @param cSymbols The number of entries in the symbol table. + * @param paLines Pointer to the line number table. + * @param cLines The number of entires in the line number table. + * @param pszzStrTab Pointer to the string table. + * @param cbStrTab Size of the string table. + */ +static int rtDbgModCvProcessCoffSymbolTable(PRTDBGMODCV pThis, + PCIMAGE_SYMBOL paSymbols, uint32_t cSymbols, + PCIMAGE_LINENUMBER paLines, uint32_t cLines, + const char *pszzStrTab, uint32_t cbStrTab) +{ + Log3(("Processing COFF symbol table with %#x symbols\n", cSymbols)); + + /* + * Making some bold assumption that the line numbers for the section in + * the file are allocated sequentially, we do multiple passes until we've + * gathered them all. + */ + int rc = VINF_SUCCESS; + uint32_t cSections = 1; + uint32_t iLineSect = 1; + uint32_t iLine = 0; + do + { + /* + * Process the symbols. + */ + char szShort[9]; + char szFile[RTPATH_MAX]; + uint32_t iSymbol = 0; + szFile[0] = '\0'; + szShort[8] = '\0'; /* avoid having to terminate it all the time. */ + + while (iSymbol < cSymbols && RT_SUCCESS(rc)) + { + /* Copy the symbol in and hope it works around the misalignment + issues everywhere. */ + IMAGE_SYMBOL Sym; + memcpy(&Sym, &paSymbols[iSymbol], sizeof(Sym)); + RTDBGMODCV_CHECK_NOMSG_RET_BF(Sym.NumberOfAuxSymbols < cSymbols); + + /* Calc a zero terminated symbol name. */ + const char *pszName; + if (Sym.N.Name.Short) + pszName = (const char *)memcpy(szShort, &Sym.N, 8); + else + { + RTDBGMODCV_CHECK_NOMSG_RET_BF(Sym.N.Name.Long < cbStrTab); + pszName = pszzStrTab + Sym.N.Name.Long; + } + + /* Only log stuff and count sections the in the first pass.*/ + if (iLineSect == 1) + { + Log3(("%04x: s=%#06x v=%#010x t=%#06x a=%#04x c=%#04x (%s) name='%s'\n", + iSymbol, Sym.SectionNumber, Sym.Value, Sym.Type, Sym.NumberOfAuxSymbols, + Sym.StorageClass, rtDbgModCvGetCoffStorageClassName(Sym.StorageClass), pszName)); + if ((int16_t)cSections <= Sym.SectionNumber && Sym.SectionNumber > 0) + cSections = Sym.SectionNumber + 1; + } + + /* + * Use storage class to pick what we need (which isn't much because, + * MS only provides a very restricted set of symbols). + */ + IMAGE_AUX_SYMBOL Aux; + switch (Sym.StorageClass) + { + case IMAGE_SYM_CLASS_NULL: + /* a NOP */ + break; + + case IMAGE_SYM_CLASS_FILE: + { + /* Change the current file name (for line numbers). Pretend + ANSI and ISO-8859-1 are similar enough for out purposes... */ + RTDBGMODCV_CHECK_NOMSG_RET_BF(Sym.NumberOfAuxSymbols > 0); + const char *pszFile = (const char *)&paSymbols[iSymbol + 1]; + char *pszDst = szFile; + rc = RTLatin1ToUtf8Ex(pszFile, Sym.NumberOfAuxSymbols * sizeof(IMAGE_SYMBOL), &pszDst, sizeof(szFile), NULL); + if (RT_FAILURE(rc)) + Log(("Error converting COFF filename: %Rrc\n", rc)); + else if (iLineSect == 1) + Log3((" filename='%s'\n", szFile)); + break; + } + + case IMAGE_SYM_CLASS_STATIC: + if ( Sym.NumberOfAuxSymbols == 1 + && ( iLineSect == 1 + || Sym.SectionNumber == (int32_t)iLineSect) ) + { + memcpy(&Aux, &paSymbols[iSymbol + 1], sizeof(Aux)); + if (iLineSect == 1) + Log3((" section: cb=%#010x #relocs=%#06x #lines=%#06x csum=%#x num=%#x sel=%x rvd=%u\n", + Aux.Section.Length, Aux.Section.NumberOfRelocations, + Aux.Section.NumberOfLinenumbers, + Aux.Section.CheckSum, + RT_MAKE_U32(Aux.Section.Number, Aux.Section.HighNumber), + Aux.Section.Selection, + Aux.Section.bReserved)); + if ( Sym.SectionNumber == (int32_t)iLineSect + && Aux.Section.NumberOfLinenumbers > 0) + { + uint32_t cLinesToAdd = RT_MIN(Aux.Section.NumberOfLinenumbers, cLines - iLine); + if (iLine < cLines && szFile[0]) + rtDbgModCvAddCoffLineNumbers(pThis, szFile, iLineSect, &paLines[iLine], cLinesToAdd); + iLine += cLinesToAdd; + } + } + /* Not so sure about the quality here, but might be useful. */ + else if ( iLineSect == 1 + && Sym.NumberOfAuxSymbols == 0 + && Sym.SectionNumber != IMAGE_SYM_UNDEFINED + && Sym.SectionNumber != IMAGE_SYM_ABSOLUTE + && Sym.SectionNumber != IMAGE_SYM_DEBUG + && Sym.Value > 0 + && *pszName) + rtDbgModCvAddCoffSymbol(pThis, RTDBGSEGIDX_RVA, Sym.Value, pszName); + break; + + case IMAGE_SYM_CLASS_EXTERNAL: + /* Add functions (first pass only). */ + if ( iLineSect == 1 + && (ISFCN(Sym.Type) || Sym.Type == 0) + && Sym.NumberOfAuxSymbols == 0 + && *pszName ) + { + if (Sym.SectionNumber == IMAGE_SYM_ABSOLUTE) + rtDbgModCvAddCoffSymbol(pThis, RTDBGSEGIDX_ABS, Sym.Value, pszName); + else if ( Sym.SectionNumber != IMAGE_SYM_UNDEFINED + && Sym.SectionNumber != IMAGE_SYM_DEBUG) + rtDbgModCvAddCoffSymbol(pThis, RTDBGSEGIDX_RVA, Sym.Value, pszName); + } + break; + + case IMAGE_SYM_CLASS_FUNCTION: + /* Not sure this is really used. */ + break; + + case IMAGE_SYM_CLASS_END_OF_FUNCTION: + case IMAGE_SYM_CLASS_AUTOMATIC: + case IMAGE_SYM_CLASS_REGISTER: + case IMAGE_SYM_CLASS_EXTERNAL_DEF: + case IMAGE_SYM_CLASS_LABEL: + case IMAGE_SYM_CLASS_UNDEFINED_LABEL: + case IMAGE_SYM_CLASS_MEMBER_OF_STRUCT: + case IMAGE_SYM_CLASS_ARGUMENT: + case IMAGE_SYM_CLASS_STRUCT_TAG: + case IMAGE_SYM_CLASS_MEMBER_OF_UNION: + case IMAGE_SYM_CLASS_UNION_TAG: + case IMAGE_SYM_CLASS_TYPE_DEFINITION: + case IMAGE_SYM_CLASS_UNDEFINED_STATIC: + case IMAGE_SYM_CLASS_ENUM_TAG: + case IMAGE_SYM_CLASS_MEMBER_OF_ENUM: + case IMAGE_SYM_CLASS_REGISTER_PARAM: + case IMAGE_SYM_CLASS_BIT_FIELD: + case IMAGE_SYM_CLASS_FAR_EXTERNAL: + case IMAGE_SYM_CLASS_BLOCK: + case IMAGE_SYM_CLASS_END_OF_STRUCT: + case IMAGE_SYM_CLASS_SECTION: + case IMAGE_SYM_CLASS_WEAK_EXTERNAL: + case IMAGE_SYM_CLASS_CLR_TOKEN: + /* Not used by MS, I think. */ + break; + + default: + Log(("RTDbgCv: Unexpected COFF storage class %#x (%u)\n", Sym.StorageClass, Sym.StorageClass)); + break; + } + + /* next symbol */ + iSymbol += 1 + Sym.NumberOfAuxSymbols; + } + + /* Next section with line numbers. */ + iLineSect++; + } while (iLine < cLines && iLineSect < cSections && RT_SUCCESS(rc)); + + return rc; +} + + +/** + * Loads COFF debug information into the container. + * + * @returns IPRT status code. + * @param pThis The COFF/CodeView debug reader instance. + */ +static int rtDbgModCvLoadCoffInfo(PRTDBGMODCV pThis) +{ + /* + * Read the whole section into memory. + * Note! Cannot use rtDbgModCvReadAt or rtDbgModCvReadAtAlloc here. + */ + int rc; + uint8_t *pbDbgSect = (uint8_t *)RTMemAlloc(pThis->cbCoffDbgInfo); + if (pbDbgSect) + { + if (pThis->hFile == NIL_RTFILE) + rc = pThis->pMod->pImgVt->pfnReadAt(pThis->pMod, UINT32_MAX, pThis->offCoffDbgInfo, pbDbgSect, pThis->cbCoffDbgInfo); + else + rc = RTFileReadAt(pThis->hFile, pThis->offCoffDbgInfo, pbDbgSect, pThis->cbCoffDbgInfo, NULL); + if (RT_SUCCESS(rc)) + { + /* The string table follows after the symbol table. */ + const char *pszzStrTab = (const char *)( pbDbgSect + + pThis->CoffHdr.LvaToFirstSymbol + + pThis->CoffHdr.NumberOfSymbols * sizeof(IMAGE_SYMBOL)); + uint32_t cbStrTab = (uint32_t)((uintptr_t)(pbDbgSect + pThis->cbCoffDbgInfo) - (uintptr_t)pszzStrTab); + /** @todo The symbol table starts with a size. Read it and checking. Also verify + * that the symtab ends with a terminator character. */ + + rc = rtDbgModCvProcessCoffSymbolTable(pThis, + (PCIMAGE_SYMBOL)(pbDbgSect + pThis->CoffHdr.LvaToFirstSymbol), + pThis->CoffHdr.NumberOfSymbols, + (PCIMAGE_LINENUMBER)(pbDbgSect + pThis->CoffHdr.LvaToFirstLinenumber), + pThis->CoffHdr.NumberOfLinenumbers, + pszzStrTab, cbStrTab); + } + RTMemFree(pbDbgSect); + } + else + rc = VERR_NO_MEMORY; + return rc; +} + + + + + + +/* + * + * CodeView Debug module implementation. + * CodeView Debug module implementation. + * CodeView Debug module implementation. + * + */ + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnUnwindFrame} */ +static DECLCALLBACK(int) rtDbgModCv_UnwindFrame(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState) +{ + RT_NOREF(pMod, iSeg, off, pState); + return VERR_DBG_NO_UNWIND_INFO; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByAddr} */ +static DECLCALLBACK(int) rtDbgModCv_LineByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, + PRTINTPTR poffDisp, PRTDBGLINE pLineInfo) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + return RTDbgModLineByAddr(pThis->hCnt, iSeg, off, poffDisp, pLineInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByOrdinal} */ +static DECLCALLBACK(int) rtDbgModCv_LineByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + return RTDbgModLineByOrdinal(pThis->hCnt, iOrdinal, pLineInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineCount} */ +static DECLCALLBACK(uint32_t) rtDbgModCv_LineCount(PRTDBGMODINT pMod) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + return RTDbgModLineCount(pThis->hCnt); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineAdd} */ +static DECLCALLBACK(int) rtDbgModCv_LineAdd(PRTDBGMODINT pMod, const char *pszFile, size_t cchFile, uint32_t uLineNo, + uint32_t iSeg, RTUINTPTR off, uint32_t *piOrdinal) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + Assert(!pszFile[cchFile]); NOREF(cchFile); + return RTDbgModLineAdd(pThis->hCnt, pszFile, uLineNo, iSeg, off, piOrdinal); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByAddr} */ +static DECLCALLBACK(int) rtDbgModCv_SymbolByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags, + PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + return RTDbgModSymbolByAddr(pThis->hCnt, iSeg, off, fFlags, poffDisp, pSymInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByName} */ +static DECLCALLBACK(int) rtDbgModCv_SymbolByName(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol, + PRTDBGSYMBOL pSymInfo) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + Assert(!pszSymbol[cchSymbol]); RT_NOREF_PV(cchSymbol); + return RTDbgModSymbolByName(pThis->hCnt, pszSymbol/*, cchSymbol*/, pSymInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByOrdinal} */ +static DECLCALLBACK(int) rtDbgModCv_SymbolByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + return RTDbgModSymbolByOrdinal(pThis->hCnt, iOrdinal, pSymInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolCount} */ +static DECLCALLBACK(uint32_t) rtDbgModCv_SymbolCount(PRTDBGMODINT pMod) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + return RTDbgModSymbolCount(pThis->hCnt); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolAdd} */ +static DECLCALLBACK(int) rtDbgModCv_SymbolAdd(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol, + RTDBGSEGIDX iSeg, RTUINTPTR off, RTUINTPTR cb, uint32_t fFlags, + uint32_t *piOrdinal) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + Assert(!pszSymbol[cchSymbol]); NOREF(cchSymbol); + return RTDbgModSymbolAdd(pThis->hCnt, pszSymbol, iSeg, off, cb, fFlags, piOrdinal); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentByIndex} */ +static DECLCALLBACK(int) rtDbgModCv_SegmentByIndex(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + return RTDbgModSegmentByIndex(pThis->hCnt, iSeg, pSegInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentCount} */ +static DECLCALLBACK(RTDBGSEGIDX) rtDbgModCv_SegmentCount(PRTDBGMODINT pMod) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + return RTDbgModSegmentCount(pThis->hCnt); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentAdd} */ +static DECLCALLBACK(int) rtDbgModCv_SegmentAdd(PRTDBGMODINT pMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName, size_t cchName, + uint32_t fFlags, PRTDBGSEGIDX piSeg) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + Assert(!pszName[cchName]); NOREF(cchName); + return RTDbgModSegmentAdd(pThis->hCnt, uRva, cb, pszName, fFlags, piSeg); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnImageSize} */ +static DECLCALLBACK(RTUINTPTR) rtDbgModCv_ImageSize(PRTDBGMODINT pMod) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + if (pThis->cbImage) + return pThis->cbImage; + return RTDbgModImageSize(pThis->hCnt); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnRvaToSegOff} */ +static DECLCALLBACK(RTDBGSEGIDX) rtDbgModCv_RvaToSegOff(PRTDBGMODINT pMod, RTUINTPTR uRva, PRTUINTPTR poffSeg) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + return RTDbgModRvaToSegOff(pThis->hCnt, uRva, poffSeg); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnClose} */ +static DECLCALLBACK(int) rtDbgModCv_Close(PRTDBGMODINT pMod) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + + RTDbgModRelease(pThis->hCnt); + if (pThis->hFile != NIL_RTFILE) + RTFileClose(pThis->hFile); + RTMemFree(pThis->paDirEnts); + RTMemFree(pThis); + + pMod->pvDbgPriv = NULL; /* for internal use */ + return VINF_SUCCESS; +} + + +/* + * + * Probing code used by rtDbgModCv_TryOpen. + * Probing code used by rtDbgModCv_TryOpen. + * + */ + + + +/** + * @callback_method_impl{FNRTLDRENUMSEGS, Used to add segments from the image} + */ +static DECLCALLBACK(int) rtDbgModCvAddSegmentsCallback(RTLDRMOD hLdrMod, PCRTLDRSEG pSeg, void *pvUser) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pvUser; + Log(("Segment %s: LinkAddress=%#llx RVA=%#llx cb=%#llx\n", + pSeg->pszName, (uint64_t)pSeg->LinkAddress, (uint64_t)pSeg->RVA, pSeg->cb)); + NOREF(hLdrMod); + + /* If the segment doesn't have a mapping, just add a dummy so the indexing + works out correctly (same as for the image). */ + if (pSeg->RVA == NIL_RTLDRADDR) + return RTDbgModSegmentAdd(pThis->hCnt, 0, 0, pSeg->pszName, 0 /*fFlags*/, NULL); + + RTLDRADDR cb = RT_MAX(pSeg->cb, pSeg->cbMapped); + return RTDbgModSegmentAdd(pThis->hCnt, pSeg->RVA, cb, pSeg->pszName, 0 /*fFlags*/, NULL); +} + + +/** + * Copies the sections over from the DBG file. + * + * Called if we don't have an associated executable image. + * + * @returns IPRT status code. + * @param pThis The CV module instance. + * @param pDbgHdr The DBG file header. + * @param pszFilename The filename (for logging). + */ +static int rtDbgModCvAddSegmentsFromDbg(PRTDBGMODCV pThis, PCIMAGE_SEPARATE_DEBUG_HEADER pDbgHdr, const char *pszFilename) +{ + RT_NOREF_PV(pszFilename); + + /* + * Validate the header fields a little. + */ + if ( pDbgHdr->NumberOfSections < 1 + || pDbgHdr->NumberOfSections > 4096) + { + Log(("RTDbgModCv: Bad NumberOfSections: %d\n", pDbgHdr->NumberOfSections)); + return VERR_CV_BAD_FORMAT; + } + if (!RT_IS_POWER_OF_TWO(pDbgHdr->SectionAlignment)) + { + Log(("RTDbgModCv: Bad SectionAlignment: %#x\n", pDbgHdr->SectionAlignment)); + return VERR_CV_BAD_FORMAT; + } + + /* + * Read the section table. + */ + size_t cbShs = pDbgHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER); + PIMAGE_SECTION_HEADER paShs = (PIMAGE_SECTION_HEADER)RTMemAlloc(cbShs); + if (!paShs) + return VERR_NO_MEMORY; + int rc = RTFileReadAt(pThis->hFile, sizeof(*pDbgHdr), paShs, cbShs, NULL); + if (RT_SUCCESS(rc)) + { + /* + * Do some basic validation. + */ + uint32_t cbHeaders = 0; + uint32_t uRvaPrev = 0; + for (uint32_t i = 0; i < pDbgHdr->NumberOfSections; i++) + { + Log3(("RTDbgModCv: Section #%02u %#010x LB %#010x %.*s\n", + i, paShs[i].VirtualAddress, paShs[i].Misc.VirtualSize, sizeof(paShs[i].Name), paShs[i].Name)); + + if (paShs[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD) + continue; + + if (paShs[i].VirtualAddress < uRvaPrev) + { + Log(("RTDbgModCv: %s: Overlap or soring error, VirtualAddress=%#x uRvaPrev=%#x - section #%d '%.*s'!!!\n", + pszFilename, paShs[i].VirtualAddress, uRvaPrev, i, sizeof(paShs[i].Name), paShs[i].Name)); + rc = VERR_CV_BAD_FORMAT; + } + else if ( paShs[i].VirtualAddress > pDbgHdr->SizeOfImage + || paShs[i].Misc.VirtualSize > pDbgHdr->SizeOfImage + || paShs[i].VirtualAddress + paShs[i].Misc.VirtualSize > pDbgHdr->SizeOfImage) + { + Log(("RTDbgModCv: %s: VirtualAddress=%#x VirtualSize=%#x (total %x) - beyond image size (%#x) - section #%d '%.*s'!!!\n", + pszFilename, paShs[i].VirtualAddress, paShs[i].Misc.VirtualSize, + paShs[i].VirtualAddress + paShs[i].Misc.VirtualSize, + pThis->cbImage, i, sizeof(paShs[i].Name), paShs[i].Name)); + rc = VERR_CV_BAD_FORMAT; + } + else if (paShs[i].VirtualAddress & (pDbgHdr->SectionAlignment - 1)) + { + Log(("RTDbgModCv: %s: VirtualAddress=%#x misaligned (%#x) - section #%d '%.*s'!!!\n", + pszFilename, paShs[i].VirtualAddress, pDbgHdr->SectionAlignment, i, sizeof(paShs[i].Name), paShs[i].Name)); + rc = VERR_CV_BAD_FORMAT; + } + else + { + if (uRvaPrev == 0) + cbHeaders = paShs[i].VirtualAddress; + uRvaPrev = paShs[i].VirtualAddress + paShs[i].Misc.VirtualSize; + continue; + } + } + if (RT_SUCCESS(rc) && uRvaPrev == 0) + { + Log(("RTDbgModCv: %s: No loadable sections.\n", pszFilename)); + rc = VERR_CV_BAD_FORMAT; + } + if (RT_SUCCESS(rc) && cbHeaders == 0) + { + Log(("RTDbgModCv: %s: No space for PE headers.\n", pszFilename)); + rc = VERR_CV_BAD_FORMAT; + } + if (RT_SUCCESS(rc)) + { + /* + * Add sections. + */ + rc = RTDbgModSegmentAdd(pThis->hCnt, 0, cbHeaders, "NtHdrs", 0 /*fFlags*/, NULL); + for (uint32_t i = 0; RT_SUCCESS(rc) && i < pDbgHdr->NumberOfSections; i++) + { + char szName[sizeof(paShs[i].Name) + 1]; + memcpy(szName, paShs[i].Name, sizeof(paShs[i].Name)); + szName[sizeof(szName) - 1] = '\0'; + + if (paShs[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD) + rc = RTDbgModSegmentAdd(pThis->hCnt, 0, 0, szName, 0 /*fFlags*/, NULL); + else + rc = RTDbgModSegmentAdd(pThis->hCnt, paShs[i].VirtualAddress, paShs[i].Misc.VirtualSize, szName, + 0 /*fFlags*/, NULL); + } + if (RT_SUCCESS(rc)) + pThis->fHaveLoadedSegments = true; + } + } + + RTMemFree(paShs); + return rc; +} + + +/** + * Instantiates the CV/COFF reader. + * + * @returns IPRT status code + * @param pDbgMod The debug module instance. + * @param enmFileType The type of input file. + * @param hFile The file handle, NIL_RTFILE of image. + * @param ppThis Where to return the reader instance. + */ +static int rtDbgModCvCreateInstance(PRTDBGMODINT pDbgMod, RTCVFILETYPE enmFileType, RTFILE hFile, PRTDBGMODCV *ppThis) +{ + /* + * Do we already have an instance? Happens if we find multiple debug + * formats we support. + */ + PRTDBGMODCV pThis = (PRTDBGMODCV)pDbgMod->pvDbgPriv; + if (pThis) + { + Assert(pThis->enmType == enmFileType); + Assert(pThis->hFile == hFile); + Assert(pThis->pMod == pDbgMod); + *ppThis = pThis; + return VINF_SUCCESS; + } + + /* + * Create a new instance. + */ + pThis = (PRTDBGMODCV)RTMemAllocZ(sizeof(RTDBGMODCV)); + if (!pThis) + return VERR_NO_MEMORY; + int rc = RTDbgModCreate(&pThis->hCnt, pDbgMod->pszName, 0 /*cbSeg*/, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + pDbgMod->pvDbgPriv = pThis; + pThis->enmType = enmFileType; + pThis->hFile = hFile; + pThis->pMod = pDbgMod; + pThis->offBase = UINT32_MAX; + pThis->offCoffDbgInfo = UINT32_MAX; + *ppThis = pThis; + return VINF_SUCCESS; + } + RTMemFree(pThis); + return rc; +} + + +/** + * Common part of the COFF probing. + * + * @returns status code. + * @param pDbgMod The debug module instance. On success pvDbgPriv + * will point to a valid RTDBGMODCV. + * @param enmFileType The kind of file this is we're probing. + * @param hFile The file with debug info in it. + * @param off The offset where to expect CV debug info. + * @param cb The number of bytes of debug info. + * @param pszFilename The path to the file (for logging). + */ +static int rtDbgModCvProbeCoff(PRTDBGMODINT pDbgMod, RTCVFILETYPE enmFileType, RTFILE hFile, + uint32_t off, uint32_t cb, const char *pszFilename) +{ + RT_NOREF_PV(pszFilename); + + /* + * Check that there is sufficient data for a header, then read it. + */ + if (cb < sizeof(IMAGE_COFF_SYMBOLS_HEADER)) + { + Log(("RTDbgModCv: Not enough room for COFF header.\n")); + return VERR_BAD_EXE_FORMAT; + } + if (cb >= UINT32_C(128) * _1M) + { + Log(("RTDbgModCv: COFF debug information is to large (%'u bytes), max is 128MB\n", cb)); + return VERR_BAD_EXE_FORMAT; + } + + int rc; + IMAGE_COFF_SYMBOLS_HEADER Hdr; + if (hFile == NIL_RTFILE) + rc = pDbgMod->pImgVt->pfnReadAt(pDbgMod, UINT32_MAX, off, &Hdr, sizeof(Hdr)); + else + rc = RTFileReadAt(hFile, off, &Hdr, sizeof(Hdr), NULL); + if (RT_FAILURE(rc)) + { + Log(("RTDbgModCv: Error reading COFF header: %Rrc\n", rc)); + return rc; + } + + Log2(("RTDbgModCv: Found COFF debug info header at %#x (LB %#x) in %s\n", off, cb, pszFilename)); + Log2((" NumberOfSymbols = %#010x\n", Hdr.NumberOfSymbols)); + Log2((" LvaToFirstSymbol = %#010x\n", Hdr.LvaToFirstSymbol)); + Log2((" NumberOfLinenumbers = %#010x\n", Hdr.NumberOfLinenumbers)); + Log2((" LvaToFirstLinenumber = %#010x\n", Hdr.LvaToFirstLinenumber)); + Log2((" RvaToFirstByteOfCode = %#010x\n", Hdr.RvaToFirstByteOfCode)); + Log2((" RvaToLastByteOfCode = %#010x\n", Hdr.RvaToLastByteOfCode)); + Log2((" RvaToFirstByteOfData = %#010x\n", Hdr.RvaToFirstByteOfData)); + Log2((" RvaToLastByteOfData = %#010x\n", Hdr.RvaToLastByteOfData)); + + /* + * Validate the COFF header. + */ + if ( (uint64_t)Hdr.LvaToFirstSymbol + (uint64_t)Hdr.NumberOfSymbols * sizeof(IMAGE_SYMBOL) > cb + || (Hdr.LvaToFirstSymbol < sizeof(Hdr) && Hdr.NumberOfSymbols > 0)) + { + Log(("RTDbgModCv: Bad COFF symbol count or/and offset: LvaToFirstSymbol=%#x, NumberOfSymbols=%#x cbCoff=%#x\n", + Hdr.LvaToFirstSymbol, Hdr.NumberOfSymbols, cb)); + return VERR_BAD_EXE_FORMAT; + } + if ( (uint64_t)Hdr.LvaToFirstLinenumber + (uint64_t)Hdr.NumberOfLinenumbers * sizeof(IMAGE_LINENUMBER) > cb + || (Hdr.LvaToFirstLinenumber < sizeof(Hdr) && Hdr.NumberOfLinenumbers > 0)) + { + Log(("RTDbgModCv: Bad COFF symbol count or/and offset: LvaToFirstSymbol=%#x, NumberOfSymbols=%#x cbCoff=%#x\n", + Hdr.LvaToFirstSymbol, Hdr.NumberOfSymbols, cb)); + return VERR_BAD_EXE_FORMAT; + } + if (Hdr.NumberOfSymbols < 2) + { + Log(("RTDbgModCv: The COFF symbol table is too short to be of any worth... (%u syms)\n", Hdr.NumberOfSymbols)); + return VERR_NO_DATA; + } + + /* + * What we care about looks fine, use it. + */ + PRTDBGMODCV pThis; + rc = rtDbgModCvCreateInstance(pDbgMod, enmFileType, hFile, &pThis); + if (RT_SUCCESS(rc)) + { + pThis->offCoffDbgInfo = off; + pThis->cbCoffDbgInfo = cb; + pThis->CoffHdr = Hdr; + } + + return rc; +} + + +/** + * Common part of the CodeView probing. + * + * @returns status code. + * @param pDbgMod The debug module instance. On success pvDbgPriv + * will point to a valid RTDBGMODCV. + * @param pCvHdr The CodeView base header. + * @param enmFileType The kind of file this is we're probing. + * @param hFile The file with debug info in it. + * @param off The offset where to expect CV debug info. + * @param cb The number of bytes of debug info. + * @param enmArch The desired image architecture. + * @param pszFilename The path to the file (for logging). + */ +static int rtDbgModCvProbeCommon(PRTDBGMODINT pDbgMod, PRTCVHDR pCvHdr, RTCVFILETYPE enmFileType, RTFILE hFile, + uint32_t off, uint32_t cb, RTLDRARCH enmArch, const char *pszFilename) +{ + int rc = VERR_DBG_NO_MATCHING_INTERPRETER; + RT_NOREF_PV(enmArch); RT_NOREF_PV(pszFilename); + + /* Is a codeview format we (wish to) support? */ + if ( pCvHdr->u32Magic == RTCVHDR_MAGIC_NB11 + || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB09 + || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB08 + || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB05 + || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB04 + || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB02 + || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB00 + ) + { + /* We're assuming it's a base header, so the offset must be within + the area defined by the debug info we got from the loader. */ + if (pCvHdr->off < cb && pCvHdr->off >= sizeof(*pCvHdr)) + { + Log(("RTDbgModCv: Found %c%c%c%c at %#x - size %#x, directory at %#x. file type %d\n", + RT_BYTE1(pCvHdr->u32Magic), RT_BYTE2(pCvHdr->u32Magic), RT_BYTE3(pCvHdr->u32Magic), RT_BYTE4(pCvHdr->u32Magic), + off, cb, pCvHdr->off, enmFileType)); + + /* + * Create a module instance, if not already done. + */ + PRTDBGMODCV pThis; + rc = rtDbgModCvCreateInstance(pDbgMod, enmFileType, hFile, &pThis); + if (RT_SUCCESS(rc)) + { + pThis->u32CvMagic = pCvHdr->u32Magic; + pThis->offBase = off; + pThis->cbDbgInfo = cb; + pThis->offDir = pCvHdr->off; + return VINF_SUCCESS; + } + } + } + + return rc; +} + + +/** @callback_method_impl{FNRTLDRENUMDBG} */ +static DECLCALLBACK(int) rtDbgModCvEnumCallback(RTLDRMOD hLdrMod, PCRTLDRDBGINFO pDbgInfo, void *pvUser) +{ + PRTDBGMODINT pDbgMod = (PRTDBGMODINT)pvUser; + Assert(!pDbgMod->pvDbgPriv); + RT_NOREF_PV(hLdrMod); + + /* Skip external files, RTDbgMod will deal with those + via RTDBGMODINT::pszDbgFile. */ + if (pDbgInfo->pszExtFile) + return VINF_SUCCESS; + + /* We only handle the codeview sections. */ + if (pDbgInfo->enmType == RTLDRDBGINFOTYPE_CODEVIEW) + { + /* Read the specified header and check if we like it. */ + RTCVHDR CvHdr; + int rc = pDbgMod->pImgVt->pfnReadAt(pDbgMod, pDbgInfo->iDbgInfo, pDbgInfo->offFile, &CvHdr, sizeof(CvHdr)); + if (RT_SUCCESS(rc)) + rc = rtDbgModCvProbeCommon(pDbgMod, &CvHdr, RTCVFILETYPE_IMAGE, NIL_RTFILE, pDbgInfo->offFile, pDbgInfo->cb, + pDbgMod->pImgVt->pfnGetArch(pDbgMod), pDbgMod->pszImgFile); + } + else if (pDbgInfo->enmType == RTLDRDBGINFOTYPE_COFF) + { + /* Join paths with the DBG code. */ + rtDbgModCvProbeCoff(pDbgMod, RTCVFILETYPE_IMAGE, NIL_RTFILE, pDbgInfo->offFile, pDbgInfo->cb, pDbgMod->pszImgFile); + } + + return VINF_SUCCESS; +} + + +/** + * Part two of the external file probing. + * + * @returns status code. + * @param pThis The debug module instance. On success pvDbgPriv + * will point to a valid RTDBGMODCV. + * @param enmFileType The kind of file this is we're probing. + * @param hFile The file with debug info in it. + * @param off The offset where to expect CV debug info. + * @param cb The number of bytes of debug info. + * @param enmArch The desired image architecture. + * @param pszFilename The path to the file (for logging). + */ +static int rtDbgModCvProbeFile2(PRTDBGMODINT pThis, RTCVFILETYPE enmFileType, RTFILE hFile, uint32_t off, uint32_t cb, + RTLDRARCH enmArch, const char *pszFilename) +{ + RTCVHDR CvHdr; + int rc = RTFileReadAt(hFile, off, &CvHdr, sizeof(CvHdr), NULL); + if (RT_SUCCESS(rc)) + rc = rtDbgModCvProbeCommon(pThis, &CvHdr, enmFileType, hFile, off, cb, enmArch, pszFilename); + return rc; +} + + +/** + * Probes an external file for CodeView information. + * + * @returns status code. + * @param pDbgMod The debug module instance. On success pvDbgPriv + * will point to a valid RTDBGMODCV. + * @param pszFilename The path to the file to probe. + * @param enmArch The desired image architecture. + */ +static int rtDbgModCvProbeFile(PRTDBGMODINT pDbgMod, const char *pszFilename, RTLDRARCH enmArch) +{ + RTFILE hFile; + int rc = RTFileOpen(&hFile, pszFilename, RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN); + if (RT_FAILURE(rc)) + return rc; + + /* + * Check for .DBG file + */ + IMAGE_SEPARATE_DEBUG_HEADER DbgHdr; + rc = RTFileReadAt(hFile, 0, &DbgHdr, sizeof(DbgHdr), NULL); + if ( RT_SUCCESS(rc) + && DbgHdr.Signature == IMAGE_SEPARATE_DEBUG_SIGNATURE) + { + Log2(("RTDbgModCv: Found separate debug header in %s:\n", pszFilename)); + Log2((" Flags = %#x\n", DbgHdr.Flags)); + Log2((" Machine = %#x\n", DbgHdr.Machine)); + Log2((" Characteristics = %#x\n", DbgHdr.Characteristics)); + Log2((" TimeDateStamp = %#x\n", DbgHdr.TimeDateStamp)); + Log2((" CheckSum = %#x\n", DbgHdr.CheckSum)); + Log2((" ImageBase = %#x\n", DbgHdr.ImageBase)); + Log2((" SizeOfImage = %#x\n", DbgHdr.SizeOfImage)); + Log2((" NumberOfSections = %#x\n", DbgHdr.NumberOfSections)); + Log2((" ExportedNamesSize = %#x\n", DbgHdr.ExportedNamesSize)); + Log2((" DebugDirectorySize = %#x\n", DbgHdr.DebugDirectorySize)); + Log2((" SectionAlignment = %#x\n", DbgHdr.SectionAlignment)); + + /* + * Match up the architecture if specified. + */ + switch (enmArch) + { + case RTLDRARCH_X86_32: + if (DbgHdr.Machine != IMAGE_FILE_MACHINE_I386) + rc = VERR_LDR_ARCH_MISMATCH; + break; + case RTLDRARCH_AMD64: + if (DbgHdr.Machine != IMAGE_FILE_MACHINE_AMD64) + rc = VERR_LDR_ARCH_MISMATCH; + break; + + default: + case RTLDRARCH_HOST: + AssertFailed(); + case RTLDRARCH_WHATEVER: + break; + } + if (RT_FAILURE(rc)) + { + RTFileClose(hFile); + return rc; + } + + /* + * Probe for readable debug info in the debug directory. + */ + uint32_t offDbgDir = sizeof(DbgHdr) + + DbgHdr.NumberOfSections * sizeof(IMAGE_SECTION_HEADER) + + DbgHdr.ExportedNamesSize; + + uint32_t cEntries = DbgHdr.DebugDirectorySize / sizeof(IMAGE_DEBUG_DIRECTORY); + for (uint32_t i = 0; i < cEntries; i++, offDbgDir += sizeof(IMAGE_DEBUG_DIRECTORY)) + { + IMAGE_DEBUG_DIRECTORY DbgDir; + rc = RTFileReadAt(hFile, offDbgDir, &DbgDir, sizeof(DbgDir), NULL); + if (RT_FAILURE(rc)) + break; + if (DbgDir.Type == IMAGE_DEBUG_TYPE_CODEVIEW) + rc = rtDbgModCvProbeFile2(pDbgMod, RTCVFILETYPE_DBG, hFile, + DbgDir.PointerToRawData, DbgDir.SizeOfData, + enmArch, pszFilename); + else if (DbgDir.Type == IMAGE_DEBUG_TYPE_COFF) + rc = rtDbgModCvProbeCoff(pDbgMod, RTCVFILETYPE_DBG, hFile, + DbgDir.PointerToRawData, DbgDir.SizeOfData, pszFilename); + } + + /* + * If we get down here with an instance, it prooves that we've found + * something, regardless of any errors. Add the sections and such. + */ + PRTDBGMODCV pThis = (PRTDBGMODCV)pDbgMod->pvDbgPriv; + if (pThis) + { + pThis->cbImage = DbgHdr.SizeOfImage; + if (pDbgMod->pImgVt) + rc = VINF_SUCCESS; + else + { + rc = rtDbgModCvAddSegmentsFromDbg(pThis, &DbgHdr, pszFilename); + if (RT_FAILURE(rc)) + rtDbgModCv_Close(pDbgMod); + } + return rc; + } + + /* Failed to find CV or smth, look at the end of the file just to be sure... */ + } + + /* + * Look for CV tail header. + */ + uint64_t cbFile; + rc = RTFileSeek(hFile, -(RTFOFF)sizeof(RTCVHDR), RTFILE_SEEK_END, &cbFile); + if (RT_SUCCESS(rc)) + { + cbFile += sizeof(RTCVHDR); + RTCVHDR CvHdr; + rc = RTFileRead(hFile, &CvHdr, sizeof(CvHdr), NULL); + if (RT_SUCCESS(rc)) + rc = rtDbgModCvProbeFile2(pDbgMod, RTCVFILETYPE_OTHER_AT_END, hFile, + cbFile - CvHdr.off, CvHdr.off, enmArch, pszFilename); + } + + if (RT_FAILURE(rc)) + RTFileClose(hFile); + return rc; +} + + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnTryOpen} */ +static DECLCALLBACK(int) rtDbgModCv_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch) +{ + /* + * Look for debug info. + */ + int rc = VERR_DBG_NO_MATCHING_INTERPRETER; + if (pMod->pszDbgFile) + rc = rtDbgModCvProbeFile(pMod, pMod->pszDbgFile, enmArch); + + if (!pMod->pvDbgPriv && pMod->pImgVt) + { + int rc2 = pMod->pImgVt->pfnEnumDbgInfo(pMod, rtDbgModCvEnumCallback, pMod); + if (RT_FAILURE(rc2)) + rc = rc2; + + if (!pMod->pvDbgPriv) + { + /* Try the executable in case it has a NBxx tail header. */ + rc2 = rtDbgModCvProbeFile(pMod, pMod->pszImgFile, enmArch); + if (RT_FAILURE(rc2) && (RT_SUCCESS(rc) || rc == VERR_DBG_NO_MATCHING_INTERPRETER)) + rc = rc2; + } + } + + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + if (!pThis) + return RT_SUCCESS_NP(rc) ? VERR_DBG_NO_MATCHING_INTERPRETER : rc; + Assert(pThis->offBase != UINT32_MAX || pThis->offCoffDbgInfo != UINT32_MAX); + + /* + * Load the debug info. + */ + if (pMod->pImgVt) + { + rc = pMod->pImgVt->pfnEnumSegments(pMod, rtDbgModCvAddSegmentsCallback, pThis); + pThis->fHaveLoadedSegments = true; + } + if (RT_SUCCESS(rc) && pThis->offBase != UINT32_MAX) + rc = rtDbgModCvLoadCodeViewInfo(pThis); + if (RT_SUCCESS(rc) && pThis->offCoffDbgInfo != UINT32_MAX) + rc = rtDbgModCvLoadCoffInfo(pThis); + if (RT_SUCCESS(rc)) + { + Log(("RTDbgCv: Successfully loaded debug info\n")); + return VINF_SUCCESS; + } + + Log(("RTDbgCv: Debug info load error %Rrc\n", rc)); + rtDbgModCv_Close(pMod); + return rc; +} + + + + + +/** Virtual function table for the CodeView debug info reader. */ +DECL_HIDDEN_CONST(RTDBGMODVTDBG) const g_rtDbgModVtDbgCodeView = +{ + /*.u32Magic = */ RTDBGMODVTDBG_MAGIC, + /*.fSupports = */ RT_DBGTYPE_CODEVIEW, + /*.pszName = */ "codeview", + /*.pfnTryOpen = */ rtDbgModCv_TryOpen, + /*.pfnClose = */ rtDbgModCv_Close, + + /*.pfnRvaToSegOff = */ rtDbgModCv_RvaToSegOff, + /*.pfnImageSize = */ rtDbgModCv_ImageSize, + + /*.pfnSegmentAdd = */ rtDbgModCv_SegmentAdd, + /*.pfnSegmentCount = */ rtDbgModCv_SegmentCount, + /*.pfnSegmentByIndex = */ rtDbgModCv_SegmentByIndex, + + /*.pfnSymbolAdd = */ rtDbgModCv_SymbolAdd, + /*.pfnSymbolCount = */ rtDbgModCv_SymbolCount, + /*.pfnSymbolByOrdinal = */ rtDbgModCv_SymbolByOrdinal, + /*.pfnSymbolByName = */ rtDbgModCv_SymbolByName, + /*.pfnSymbolByAddr = */ rtDbgModCv_SymbolByAddr, + + /*.pfnLineAdd = */ rtDbgModCv_LineAdd, + /*.pfnLineCount = */ rtDbgModCv_LineCount, + /*.pfnLineByOrdinal = */ rtDbgModCv_LineByOrdinal, + /*.pfnLineByAddr = */ rtDbgModCv_LineByAddr, + + /*.pfnUnwindFrame = */ rtDbgModCv_UnwindFrame, + + /*.u32EndMagic = */ RTDBGMODVTDBG_MAGIC +}; + diff --git a/src/VBox/Runtime/common/dbg/dbgmodcontainer.cpp b/src/VBox/Runtime/common/dbg/dbgmodcontainer.cpp new file mode 100644 index 00000000..cf09ce75 --- /dev/null +++ b/src/VBox/Runtime/common/dbg/dbgmodcontainer.cpp @@ -0,0 +1,1050 @@ +/* $Id: dbgmodcontainer.cpp $ */ +/** @file + * IPRT - Debug Info Container. + */ + +/* + * Copyright (C) 2009-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_DBG +#include <iprt/dbg.h> +#include "internal/iprt.h" + +#include <iprt/avl.h> +#include <iprt/err.h> +#include <iprt/log.h> +#include <iprt/mem.h> +#define RTDBGMODCNT_WITH_MEM_CACHE +#ifdef RTDBGMODCNT_WITH_MEM_CACHE +# include <iprt/memcache.h> +#endif +#include <iprt/string.h> +#include <iprt/strcache.h> +#include "internal/dbgmod.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Symbol entry. + */ +typedef struct RTDBGMODCTNSYMBOL +{ + /** The address core. */ + AVLRUINTPTRNODECORE AddrCore; + /** The name space core. */ + RTSTRSPACECORE NameCore; + /** The ordinal number core. */ + AVLU32NODECORE OrdinalCore; + /** The segment index. */ + RTDBGSEGIDX iSeg; + /** The symbol flags. */ + uint32_t fFlags; + /** The symbol size. + * This may be zero while the range in AddrCore indicates 0. */ + RTUINTPTR cb; +} RTDBGMODCTNSYMBOL; +/** Pointer to a symbol entry in the debug info container. */ +typedef RTDBGMODCTNSYMBOL *PRTDBGMODCTNSYMBOL; +/** Pointer to a const symbol entry in the debug info container. */ +typedef RTDBGMODCTNSYMBOL const *PCRTDBGMODCTNSYMBOL; + +/** + * Line number entry. + */ +typedef struct RTDBGMODCTNLINE +{ + /** The address core. + * The Key is the address of the line number. */ + AVLUINTPTRNODECORE AddrCore; + /** The ordinal number core. */ + AVLU32NODECORE OrdinalCore; + /** Pointer to the file name (in string cache). */ + const char *pszFile; + /** The line number. */ + uint32_t uLineNo; + /** The segment index. */ + RTDBGSEGIDX iSeg; +} RTDBGMODCTNLINE; +/** Pointer to a line number entry. */ +typedef RTDBGMODCTNLINE *PRTDBGMODCTNLINE; +/** Pointer to const a line number entry. */ +typedef RTDBGMODCTNLINE const *PCRTDBGMODCTNLINE; + +/** + * Segment entry. + */ +typedef struct RTDBGMODCTNSEGMENT +{ + /** The symbol address space tree. */ + AVLRUINTPTRTREE SymAddrTree; + /** The line number address space tree. */ + AVLUINTPTRTREE LineAddrTree; + /** The segment offset. */ + RTUINTPTR off; + /** The segment size. */ + RTUINTPTR cb; + /** The segment flags. */ + uint32_t fFlags; + /** The segment name. */ + const char *pszName; +} RTDBGMODCTNSEGMENT; +/** Pointer to a segment entry in the debug info container. */ +typedef RTDBGMODCTNSEGMENT *PRTDBGMODCTNSEGMENT; +/** Pointer to a const segment entry in the debug info container. */ +typedef RTDBGMODCTNSEGMENT const *PCRTDBGMODCTNSEGMENT; + +/** + * Instance data. + */ +typedef struct RTDBGMODCTN +{ + /** The name space. */ + RTSTRSPACE Names; + /** Tree containing any absolute addresses. */ + AVLRUINTPTRTREE AbsAddrTree; + /** Tree organizing the symbols by ordinal number. */ + AVLU32TREE SymbolOrdinalTree; + /** Tree organizing the line numbers by ordinal number. */ + AVLU32TREE LineOrdinalTree; + /** Segment table. */ + PRTDBGMODCTNSEGMENT paSegs; + /** The number of segments in the segment table. */ + RTDBGSEGIDX cSegs; + /** The image size. 0 means unlimited. */ + RTUINTPTR cb; + /** The next symbol ordinal. */ + uint32_t iNextSymbolOrdinal; + /** The next line number ordinal. */ + uint32_t iNextLineOrdinal; +#ifdef RTDBGMODCNT_WITH_MEM_CACHE + /** Line number allocator. + * Using a cache is a bit overkill since we normally won't free them, but + * it's a construct that exists and does the job relatively efficiently. */ + RTMEMCACHE hLineNumAllocator; +#endif +} RTDBGMODCTN; +/** Pointer to instance data for the debug info container. */ +typedef RTDBGMODCTN *PRTDBGMODCTN; + + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnUnwindFrame} */ +static DECLCALLBACK(int) +rtDbgModContainer_UnwindFrame(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState) +{ + RT_NOREF(pMod, iSeg, off, pState); + return VERR_DBG_NO_UNWIND_INFO; +} + + +/** + * Fills in a RTDBGSYMBOL structure. + * + * @returns VINF_SUCCESS. + * @param pMySym Our internal symbol representation. + * @param pExtSym The external symbol representation. + */ +DECLINLINE(int) rtDbgModContainerReturnSymbol(PCRTDBGMODCTNSYMBOL pMySym, PRTDBGSYMBOL pExtSym) +{ + pExtSym->Value = pMySym->AddrCore.Key; + pExtSym->offSeg = pMySym->AddrCore.Key; + pExtSym->iSeg = pMySym->iSeg; + pExtSym->fFlags = pMySym->fFlags; + pExtSym->cb = pMySym->cb; + pExtSym->iOrdinal = pMySym->OrdinalCore.Key; + Assert(pMySym->NameCore.cchString < sizeof(pExtSym->szName)); + memcpy(pExtSym->szName, pMySym->NameCore.pszString, pMySym->NameCore.cchString + 1); + return VINF_SUCCESS; +} + + + +/** @copydoc RTDBGMODVTDBG::pfnLineByAddr */ +static DECLCALLBACK(int) rtDbgModContainer_LineByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, + PRTINTPTR poffDisp, PRTDBGLINE pLineInfo) +{ + PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv; + + /* + * Validate the input address. + */ + AssertMsgReturn(iSeg < pThis->cSegs, + ("iSeg=%#x cSegs=%#x\n", iSeg, pThis->cSegs), + VERR_DBG_INVALID_SEGMENT_INDEX); + AssertMsgReturn(off < pThis->paSegs[iSeg].cb, + ("off=%RTptr cbSeg=%RTptr\n", off, pThis->paSegs[iSeg].cb), + VERR_DBG_INVALID_SEGMENT_OFFSET); + + /* + * Lookup the nearest line number with an address less or equal to the specified address. + */ + PAVLUINTPTRNODECORE pAvlCore = RTAvlUIntPtrGetBestFit(&pThis->paSegs[iSeg].LineAddrTree, off, false /*fAbove*/); + if (!pAvlCore) + return pThis->iNextLineOrdinal + ? VERR_DBG_LINE_NOT_FOUND + : VERR_DBG_NO_LINE_NUMBERS; + PCRTDBGMODCTNLINE pMyLine = RT_FROM_MEMBER(pAvlCore, RTDBGMODCTNLINE const, AddrCore); + pLineInfo->Address = pMyLine->AddrCore.Key; + pLineInfo->offSeg = pMyLine->AddrCore.Key; + pLineInfo->iSeg = iSeg; + pLineInfo->uLineNo = pMyLine->uLineNo; + pLineInfo->iOrdinal = pMyLine->OrdinalCore.Key; + strcpy(pLineInfo->szFilename, pMyLine->pszFile); + if (poffDisp) + *poffDisp = off - pMyLine->AddrCore.Key; + return VINF_SUCCESS; +} + + +/** @copydoc RTDBGMODVTDBG::pfnLineByOrdinal */ +static DECLCALLBACK(int) rtDbgModContainer_LineByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo) +{ + PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv; + + /* + * Look it up. + */ + if (iOrdinal >= pThis->iNextLineOrdinal) + return pThis->iNextLineOrdinal + ? VERR_DBG_LINE_NOT_FOUND + : VERR_DBG_NO_LINE_NUMBERS; + PAVLU32NODECORE pAvlCore = RTAvlU32Get(&pThis->LineOrdinalTree, iOrdinal); + AssertReturn(pAvlCore, VERR_DBG_LINE_NOT_FOUND); + PCRTDBGMODCTNLINE pMyLine = RT_FROM_MEMBER(pAvlCore, RTDBGMODCTNLINE const, OrdinalCore); + pLineInfo->Address = pMyLine->AddrCore.Key; + pLineInfo->offSeg = pMyLine->AddrCore.Key; + pLineInfo->iSeg = pMyLine->iSeg; + pLineInfo->uLineNo = pMyLine->uLineNo; + pLineInfo->iOrdinal = pMyLine->OrdinalCore.Key; + strcpy(pLineInfo->szFilename, pMyLine->pszFile); + return VINF_SUCCESS; +} + + +/** @copydoc RTDBGMODVTDBG::pfnLineCount */ +static DECLCALLBACK(uint32_t) rtDbgModContainer_LineCount(PRTDBGMODINT pMod) +{ + PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv; + + /* Note! The ordinal numbers are 0-based. */ + return pThis->iNextLineOrdinal; +} + + +/** @copydoc RTDBGMODVTDBG::pfnLineAdd */ +static DECLCALLBACK(int) rtDbgModContainer_LineAdd(PRTDBGMODINT pMod, const char *pszFile, size_t cchFile, uint32_t uLineNo, + uint32_t iSeg, RTUINTPTR off, uint32_t *piOrdinal) +{ + PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv; + + /* + * Validate the input address. + */ + AssertMsgReturn(iSeg < pThis->cSegs, ("iSeg=%#x cSegs=%#x\n", iSeg, pThis->cSegs), + VERR_DBG_INVALID_SEGMENT_INDEX); + AssertMsgReturn(off <= pThis->paSegs[iSeg].cb, ("off=%RTptr cbSeg=%RTptr\n", off, pThis->paSegs[iSeg].cb), + VERR_DBG_INVALID_SEGMENT_OFFSET); + + /* + * Create a new entry. + */ +#ifdef RTDBGMODCNT_WITH_MEM_CACHE + PRTDBGMODCTNLINE pLine = (PRTDBGMODCTNLINE)RTMemCacheAlloc(pThis->hLineNumAllocator); +#else + PRTDBGMODCTNLINE pLine = (PRTDBGMODCTNLINE)RTMemAllocZ(sizeof(*pLine)); +#endif + if (!pLine) + return VERR_NO_MEMORY; + pLine->AddrCore.Key = off; + pLine->OrdinalCore.Key = pThis->iNextLineOrdinal; + pLine->uLineNo = uLineNo; + pLine->iSeg = iSeg; + pLine->pszFile = RTStrCacheEnterN(g_hDbgModStrCache, pszFile, cchFile); + int rc; + if (pLine->pszFile) + { + if (RTAvlUIntPtrInsert(&pThis->paSegs[iSeg].LineAddrTree, &pLine->AddrCore)) + { + if (RTAvlU32Insert(&pThis->LineOrdinalTree, &pLine->OrdinalCore)) + { + if (piOrdinal) + *piOrdinal = pThis->iNextLineOrdinal; + pThis->iNextLineOrdinal++; + return VINF_SUCCESS; + } + + rc = VERR_INTERNAL_ERROR_5; + RTAvlUIntPtrRemove(&pThis->paSegs[iSeg].LineAddrTree, pLine->AddrCore.Key); + } + + /* bail out */ + rc = VERR_DBG_ADDRESS_CONFLICT; + RTStrCacheRelease(g_hDbgModStrCache, pLine->pszFile); + } + else + rc = VERR_NO_MEMORY; +#ifdef RTDBGMODCNT_WITH_MEM_CACHE + RTMemCacheFree(pThis->hLineNumAllocator, pLine); +#else + RTMemFree(pLine); +#endif + return rc; +} + + +/** @copydoc RTDBGMODVTDBG::pfnSymbolByAddr */ +static DECLCALLBACK(int) rtDbgModContainer_SymbolByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags, + PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo) +{ + PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv; + + /* + * Validate the input address. + */ + AssertMsgReturn( iSeg == RTDBGSEGIDX_ABS + || iSeg < pThis->cSegs, + ("iSeg=%#x cSegs=%#x\n", iSeg, pThis->cSegs), + VERR_DBG_INVALID_SEGMENT_INDEX); + AssertMsgReturn( iSeg >= RTDBGSEGIDX_SPECIAL_FIRST + || off <= pThis->paSegs[iSeg].cb, + ("off=%RTptr cbSeg=%RTptr\n", off, pThis->paSegs[iSeg].cb), + VERR_DBG_INVALID_SEGMENT_OFFSET); + + /* + * Lookup the nearest symbol with an address less or equal to the specified address. + */ + PAVLRUINTPTRNODECORE pAvlCore = RTAvlrUIntPtrGetBestFit( iSeg == RTDBGSEGIDX_ABS + ? &pThis->AbsAddrTree + : &pThis->paSegs[iSeg].SymAddrTree, + off, + fFlags == RTDBGSYMADDR_FLAGS_GREATER_OR_EQUAL /*fAbove*/); + if (!pAvlCore) + return VERR_SYMBOL_NOT_FOUND; + PCRTDBGMODCTNSYMBOL pMySym = RT_FROM_MEMBER(pAvlCore, RTDBGMODCTNSYMBOL const, AddrCore); + if (poffDisp) + *poffDisp = off - pMySym->AddrCore.Key; + return rtDbgModContainerReturnSymbol(pMySym, pSymInfo); +} + + +/** @copydoc RTDBGMODVTDBG::pfnSymbolByName */ +static DECLCALLBACK(int) rtDbgModContainer_SymbolByName(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol, PRTDBGSYMBOL pSymInfo) +{ + PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv; + NOREF(cchSymbol); + + /* + * Look it up in the name space. + */ + PRTSTRSPACECORE pStrCore = RTStrSpaceGet(&pThis->Names, pszSymbol); + if (!pStrCore) + return VERR_SYMBOL_NOT_FOUND; + PCRTDBGMODCTNSYMBOL pMySym = RT_FROM_MEMBER(pStrCore, RTDBGMODCTNSYMBOL const, NameCore); + return rtDbgModContainerReturnSymbol(pMySym, pSymInfo); +} + + +/** @copydoc RTDBGMODVTDBG::pfnSymbolByOrdinal */ +static DECLCALLBACK(int) rtDbgModContainer_SymbolByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo) +{ + PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv; + + /* + * Look it up in the ordinal tree. + */ + if (iOrdinal >= pThis->iNextSymbolOrdinal) + return pThis->iNextSymbolOrdinal + ? VERR_DBG_NO_SYMBOLS + : VERR_SYMBOL_NOT_FOUND; + PAVLU32NODECORE pAvlCore = RTAvlU32Get(&pThis->SymbolOrdinalTree, iOrdinal); + AssertReturn(pAvlCore, VERR_SYMBOL_NOT_FOUND); + PCRTDBGMODCTNSYMBOL pMySym = RT_FROM_MEMBER(pAvlCore, RTDBGMODCTNSYMBOL const, OrdinalCore); + return rtDbgModContainerReturnSymbol(pMySym, pSymInfo); +} + + +/** @copydoc RTDBGMODVTDBG::pfnSymbolCount */ +static DECLCALLBACK(uint32_t) rtDbgModContainer_SymbolCount(PRTDBGMODINT pMod) +{ + PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv; + + /* Note! The ordinal numbers are 0-based. */ + return pThis->iNextSymbolOrdinal; +} + + +/** + * Worker for rtDbgModContainer_SymbolAdd that removes a symbol to resolve + * address conflicts. + * + * We don't shift ordinals up as that could be very expensive, instead we move + * the last one up to take the place of the one we're removing. Caller must + * take this into account. + * + * @param pThis The container. + * @param pAddrTree The address tree to remove from. + * @param pToRemove The conflicting symbol to be removed. + */ +static void rtDbgModContainer_SymbolReplace(PRTDBGMODCTN pThis, PAVLRUINTPTRTREE pAddrTree, PRTDBGMODCTNSYMBOL pToRemove) +{ + Log(("rtDbgModContainer_SymbolReplace: pToRemove=%p ordinal=%u %04x:%08RX64 %s\n", + pToRemove, pToRemove->OrdinalCore.Key, pToRemove->iSeg, pToRemove->AddrCore.Key, pToRemove->NameCore.pszString)); + + /* Unlink it. */ + PRTSTRSPACECORE pRemovedName = RTStrSpaceRemove(&pThis->Names, pToRemove->NameCore.pszString); + Assert(pRemovedName); RT_NOREF_PV(pRemovedName); + pToRemove->NameCore.pszString = NULL; + + PAVLRUINTPTRNODECORE pRemovedAddr = RTAvlrUIntPtrRemove(pAddrTree, pToRemove->AddrCore.Key); + Assert(pRemovedAddr); RT_NOREF_PV(pRemovedAddr); + pToRemove->AddrCore.Key = 0; + pToRemove->AddrCore.KeyLast = 0; + + uint32_t const iOrdinal = pToRemove->OrdinalCore.Key; + PAVLU32NODECORE pRemovedOrdinal = RTAvlU32Remove(&pThis->SymbolOrdinalTree, iOrdinal); + Assert(pRemovedOrdinal); RT_NOREF_PV(pRemovedOrdinal); + + RTMemFree(pToRemove); + + /* Jump the last symbol ordinal to take its place, unless pToRemove is the last one. */ + if (iOrdinal >= pThis->iNextSymbolOrdinal - 1) + pThis->iNextSymbolOrdinal -= 1; + else + { + PAVLU32NODECORE pLastOrdinal = RTAvlU32Remove(&pThis->SymbolOrdinalTree, pThis->iNextSymbolOrdinal - 1); + AssertReturnVoid(pLastOrdinal); + + pThis->iNextSymbolOrdinal -= 1; + pLastOrdinal->Key = iOrdinal; + bool fInsert = RTAvlU32Insert(&pThis->SymbolOrdinalTree, pLastOrdinal); + Assert(fInsert); RT_NOREF_PV(fInsert); + } +} + + +/** @copydoc RTDBGMODVTDBG::pfnSymbolAdd */ +static DECLCALLBACK(int) rtDbgModContainer_SymbolAdd(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol, + RTDBGSEGIDX iSeg, RTUINTPTR off, RTUINTPTR cb, uint32_t fFlags, + uint32_t *piOrdinal) +{ + PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv; + + /* + * Address validation. The other arguments have already been validated. + */ + AssertMsgReturn( iSeg == RTDBGSEGIDX_ABS + || iSeg < pThis->cSegs, + ("iSeg=%#x cSegs=%#x\n", iSeg, pThis->cSegs), + VERR_DBG_INVALID_SEGMENT_INDEX); + AssertMsgReturn( iSeg >= RTDBGSEGIDX_SPECIAL_FIRST + || off <= pThis->paSegs[iSeg].cb, + ("off=%RTptr cb=%RTptr cbSeg=%RTptr\n", off, cb, pThis->paSegs[iSeg].cb), + VERR_DBG_INVALID_SEGMENT_OFFSET); + + /* Be a little relaxed wrt to the symbol size. */ + int rc = VINF_SUCCESS; + if (iSeg != RTDBGSEGIDX_ABS && off + cb > pThis->paSegs[iSeg].cb) + { + cb = pThis->paSegs[iSeg].cb - off; + rc = VINF_DBG_ADJUSTED_SYM_SIZE; + } + + /* + * Create a new entry. + */ + PRTDBGMODCTNSYMBOL pSymbol = (PRTDBGMODCTNSYMBOL)RTMemAllocZ(sizeof(*pSymbol)); + if (!pSymbol) + return VERR_NO_MEMORY; + + pSymbol->AddrCore.Key = off; + pSymbol->AddrCore.KeyLast = off + (cb ? cb - 1 : 0); + pSymbol->OrdinalCore.Key = pThis->iNextSymbolOrdinal; + pSymbol->iSeg = iSeg; + pSymbol->cb = cb; + pSymbol->fFlags = fFlags; + pSymbol->NameCore.pszString = RTStrCacheEnterN(g_hDbgModStrCache, pszSymbol, cchSymbol); + if (pSymbol->NameCore.pszString) + { + if (RTStrSpaceInsert(&pThis->Names, &pSymbol->NameCore)) + { + PAVLRUINTPTRTREE pAddrTree = iSeg == RTDBGSEGIDX_ABS + ? &pThis->AbsAddrTree + : &pThis->paSegs[iSeg].SymAddrTree; + if (RTAvlrUIntPtrInsert(pAddrTree, &pSymbol->AddrCore)) + { + if (RTAvlU32Insert(&pThis->SymbolOrdinalTree, &pSymbol->OrdinalCore)) + { + /* + * Success. + */ + if (piOrdinal) + *piOrdinal = pThis->iNextSymbolOrdinal; + Log12(("rtDbgModContainer_SymbolAdd: ordinal=%u %04x:%08RX64 LB %#RX64 %s\n", + pThis->iNextSymbolOrdinal, iSeg, off, cb, pSymbol->NameCore.pszString)); + pThis->iNextSymbolOrdinal++; + return rc; + } + + /* bail out */ + rc = VERR_INTERNAL_ERROR_5; + RTAvlrUIntPtrRemove(pAddrTree, pSymbol->AddrCore.Key); + } + /* + * Did the caller specify a conflict resolution method? + */ + else if (fFlags & ( RTDBGSYMBOLADD_F_REPLACE_SAME_ADDR + | RTDBGSYMBOLADD_F_REPLACE_ANY + | RTDBGSYMBOLADD_F_ADJUST_SIZES_ON_CONFLICT)) + { + /* + * Handle anything at or before the start address first: + */ + AssertCompileMemberOffset(RTDBGMODCTNSYMBOL, AddrCore, 0); + PRTDBGMODCTNSYMBOL pConflict = (PRTDBGMODCTNSYMBOL)RTAvlrUIntPtrRangeGet(pAddrTree, pSymbol->AddrCore.Key); + if (pConflict) + { + if (pConflict->AddrCore.Key == pSymbol->AddrCore.Key) + { + /* Same address, only option is replacing it. */ + if (fFlags & (RTDBGSYMBOLADD_F_REPLACE_SAME_ADDR | RTDBGSYMBOLADD_F_REPLACE_ANY)) + rtDbgModContainer_SymbolReplace(pThis, pAddrTree, pConflict); + else + rc = VERR_DBG_ADDRESS_CONFLICT; + } + else if (fFlags & RTDBGSYMBOLADD_F_ADJUST_SIZES_ON_CONFLICT) + { + /* Reduce the size of the symbol before us, adopting the size if we've got none. */ + Assert(pConflict->AddrCore.Key < pSymbol->AddrCore.Key); + if (!pSymbol->cb) + { + pSymbol->AddrCore.KeyLast = pSymbol->AddrCore.KeyLast; + pSymbol->cb = pSymbol->AddrCore.KeyLast - pConflict->AddrCore.Key + 1; + rc = VINF_DBG_ADJUSTED_SYM_SIZE; + } + pConflict->AddrCore.KeyLast = pSymbol->AddrCore.Key - 1; + pConflict->cb = pSymbol->AddrCore.Key - pConflict->AddrCore.Key; + } + else if (fFlags & RTDBGSYMBOLADD_F_REPLACE_ANY) + rtDbgModContainer_SymbolReplace(pThis, pAddrTree, pConflict); + else + rc = VERR_DBG_ADDRESS_CONFLICT; + } + + /* + * Try insert again and deal with symbols in the range. + */ + while (RT_SUCCESS(rc)) + { + if (RTAvlrUIntPtrInsert(pAddrTree, &pSymbol->AddrCore)) + { + pSymbol->OrdinalCore.Key = pThis->iNextSymbolOrdinal; + if (RTAvlU32Insert(&pThis->SymbolOrdinalTree, &pSymbol->OrdinalCore)) + { + /* + * Success. + */ + if (piOrdinal) + *piOrdinal = pThis->iNextSymbolOrdinal; + pThis->iNextSymbolOrdinal++; + Log12(("rtDbgModContainer_SymbolAdd: ordinal=%u %04x:%08RX64 LB %#RX64 %s [replace codepath]\n", + pThis->iNextSymbolOrdinal, iSeg, off, cb, pSymbol->NameCore.pszString)); + return rc; + } + + rc = VERR_INTERNAL_ERROR_5; + RTAvlrUIntPtrRemove(pAddrTree, pSymbol->AddrCore.Key); + break; + } + + /* Get the first symbol above us and see if we can do anything about it (or ourselves). */ + AssertCompileMemberOffset(RTDBGMODCTNSYMBOL, AddrCore, 0); + pConflict = (PRTDBGMODCTNSYMBOL)RTAvlrUIntPtrGetBestFit(pAddrTree, pSymbol->AddrCore.Key, true /*fAbove*/); + AssertBreakStmt(pConflict, rc = VERR_DBG_ADDRESS_CONFLICT); + Assert(pSymbol->AddrCore.Key != pConflict->AddrCore.Key); + Assert(pSymbol->AddrCore.KeyLast >= pConflict->AddrCore.Key); + + if (fFlags & RTDBGSYMBOLADD_F_ADJUST_SIZES_ON_CONFLICT) + { + Assert(pSymbol->cb > 0); + pSymbol->AddrCore.Key = pConflict->AddrCore.Key - 1; + pSymbol->cb = pConflict->AddrCore.Key - pSymbol->AddrCore.Key; + rc = VINF_DBG_ADJUSTED_SYM_SIZE; + } + else if (fFlags & RTDBGSYMBOLADD_F_REPLACE_ANY) + rtDbgModContainer_SymbolReplace(pThis, pAddrTree, pConflict); + else + rc = VERR_DBG_ADDRESS_CONFLICT; + } + } + else + rc = VERR_DBG_ADDRESS_CONFLICT; + RTStrSpaceRemove(&pThis->Names, pSymbol->NameCore.pszString); + } + else + rc = VERR_DBG_DUPLICATE_SYMBOL; + RTStrCacheRelease(g_hDbgModStrCache, pSymbol->NameCore.pszString); + } + else + rc = VERR_NO_MEMORY; + RTMemFree(pSymbol); + return rc; +} + + +/** @copydoc RTDBGMODVTDBG::pfnSegmentByIndex */ +static DECLCALLBACK(int) rtDbgModContainer_SegmentByIndex(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo) +{ + PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv; + if (iSeg >= pThis->cSegs) + return VERR_DBG_INVALID_SEGMENT_INDEX; + pSegInfo->Address = RTUINTPTR_MAX; + pSegInfo->uRva = pThis->paSegs[iSeg].off; + pSegInfo->cb = pThis->paSegs[iSeg].cb; + pSegInfo->fFlags = pThis->paSegs[iSeg].fFlags; + pSegInfo->iSeg = iSeg; + strcpy(pSegInfo->szName, pThis->paSegs[iSeg].pszName); + return VINF_SUCCESS; +} + + +/** @copydoc RTDBGMODVTDBG::pfnSegmentCount */ +static DECLCALLBACK(RTDBGSEGIDX) rtDbgModContainer_SegmentCount(PRTDBGMODINT pMod) +{ + PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv; + return pThis->cSegs; +} + + +/** @copydoc RTDBGMODVTDBG::pfnSegmentAdd */ +static DECLCALLBACK(int) rtDbgModContainer_SegmentAdd(PRTDBGMODINT pMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName, size_t cchName, + uint32_t fFlags, PRTDBGSEGIDX piSeg) +{ + PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv; + + /* + * Input validation (the bits the caller cannot do). + */ + /* Overlapping segments are not yet supported. Will use flags to deal with it if it becomes necessary. */ + RTUINTPTR uRvaLast = uRva + RT_MAX(cb, 1) - 1; + RTUINTPTR uRvaLastMax = uRvaLast; + RTDBGSEGIDX iSeg = pThis->cSegs; + while (iSeg-- > 0) + { + RTUINTPTR uCurRva = pThis->paSegs[iSeg].off; + RTUINTPTR uCurRvaLast = uCurRva + RT_MAX(pThis->paSegs[iSeg].cb, 1) - 1; + if ( uRva <= uCurRvaLast + && uRvaLast >= uCurRva + && ( /* HACK ALERT! Allow empty segments to share space (bios/watcom, elf). */ + (cb != 0 && pThis->paSegs[iSeg].cb != 0) + || ( cb == 0 + && uRva != uCurRva + && uRva != uCurRvaLast) + || ( pThis->paSegs[iSeg].cb == 0 + && uCurRva != uRva + && uCurRva != uRvaLast) + ) + ) + AssertMsgFailedReturn(("uRva=%RTptr uRvaLast=%RTptr (cb=%RTptr) \"%s\";\n" + "uRva=%RTptr uRvaLast=%RTptr (cb=%RTptr) \"%s\" iSeg=%#x\n", + uRva, uRvaLast, cb, pszName, + uCurRva, uCurRvaLast, pThis->paSegs[iSeg].cb, pThis->paSegs[iSeg].pszName, iSeg), + VERR_DBG_SEGMENT_INDEX_CONFLICT); + if (uRvaLastMax < uCurRvaLast) + uRvaLastMax = uCurRvaLast; + } + /* Strict ordered segment addition at the moment. */ + iSeg = pThis->cSegs; + AssertMsgReturn(!piSeg || *piSeg == NIL_RTDBGSEGIDX || *piSeg == iSeg, + ("iSeg=%#x *piSeg=%#x\n", iSeg, *piSeg), + VERR_DBG_INVALID_SEGMENT_INDEX); + + /* + * Add an entry to the segment table, extending it if necessary. + */ + if (!(iSeg % 8)) + { + void *pvSegs = RTMemRealloc(pThis->paSegs, sizeof(RTDBGMODCTNSEGMENT) * (iSeg + 8)); + if (!pvSegs) + return VERR_NO_MEMORY; + pThis->paSegs = (PRTDBGMODCTNSEGMENT)pvSegs; + } + + pThis->paSegs[iSeg].SymAddrTree = NULL; + pThis->paSegs[iSeg].LineAddrTree = NULL; + pThis->paSegs[iSeg].off = uRva; + pThis->paSegs[iSeg].cb = cb; + pThis->paSegs[iSeg].fFlags = fFlags; + pThis->paSegs[iSeg].pszName = RTStrCacheEnterN(g_hDbgModStrCache, pszName, cchName); + if (pThis->paSegs[iSeg].pszName) + { + if (piSeg) + *piSeg = iSeg; + pThis->cSegs++; + pThis->cb = uRvaLastMax + 1; + if (!pThis->cb) + pThis->cb = RTUINTPTR_MAX; + return VINF_SUCCESS; + } + return VERR_NO_MEMORY; +} + + +/** @copydoc RTDBGMODVTDBG::pfnImageSize */ +static DECLCALLBACK(RTUINTPTR) rtDbgModContainer_ImageSize(PRTDBGMODINT pMod) +{ + PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv; + return pThis->cb; +} + + +/** @copydoc RTDBGMODVTDBG::pfnRvaToSegOff */ +static DECLCALLBACK(RTDBGSEGIDX) rtDbgModContainer_RvaToSegOff(PRTDBGMODINT pMod, RTUINTPTR uRva, PRTUINTPTR poffSeg) +{ + PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv; + PCRTDBGMODCTNSEGMENT paSeg = pThis->paSegs; + uint32_t const cSegs = pThis->cSegs; +#if 0 + if (cSegs <= 7) +#endif + { + /* + * Linear search. + */ + for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++) + { + RTUINTPTR offSeg = uRva - paSeg[iSeg].off; + if (offSeg < paSeg[iSeg].cb) + { + if (poffSeg) + *poffSeg = offSeg; + return iSeg; + } + } + } +#if 0 /** @todo binary search doesn't work if we've got empty segments in the list */ + else + { + /* + * Binary search. + */ + uint32_t iFirst = 0; + uint32_t iLast = cSegs - 1; + for (;;) + { + uint32_t iSeg = iFirst + (iLast - iFirst) / 2; + RTUINTPTR offSeg = uRva - paSeg[iSeg].off; + if (offSeg < paSeg[iSeg].cb) + { +#if 0 /* Enable if we change the above test. */ + if (offSeg == 0 && paSeg[iSeg].cb == 0) + while ( iSeg > 0 + && paSeg[iSeg - 1].cb == 0 + && paSeg[iSeg - 1].off == uRva) + iSeg--; +#endif + + if (poffSeg) + *poffSeg = offSeg; + return iSeg; + } + + /* advance */ + if (uRva < paSeg[iSeg].off) + { + /* between iFirst and iSeg. */ + if (iSeg == iFirst) + break; + iLast = iSeg - 1; + } + else + { + /* between iSeg and iLast. paSeg[iSeg].cb == 0 ends up here too. */ + if (iSeg == iLast) + break; + iFirst = iSeg + 1; + } + } + } +#endif + + /* Invalid. */ + return NIL_RTDBGSEGIDX; +} + + +/** Destroy a line number node. */ +static DECLCALLBACK(int) rtDbgModContainer_DestroyTreeLineNode(PAVLU32NODECORE pNode, void *pvUser) +{ + PRTDBGMODCTN pThis = (PRTDBGMODCTN)pvUser; + PRTDBGMODCTNLINE pLine = RT_FROM_MEMBER(pNode, RTDBGMODCTNLINE, OrdinalCore); + RTStrCacheRelease(g_hDbgModStrCache, pLine->pszFile); + pLine->pszFile = NULL; +#ifdef RTDBGMODCNT_WITH_MEM_CACHE + RTMemCacheFree(pThis->hLineNumAllocator, pLine); +#else + RTMemFree(pLine); NOREF(pThis); +#endif + return 0; +} + + +/** Destroy a symbol node. */ +static DECLCALLBACK(int) rtDbgModContainer_DestroyTreeNode(PAVLRUINTPTRNODECORE pNode, void *pvUser) +{ + PRTDBGMODCTNSYMBOL pSym = RT_FROM_MEMBER(pNode, RTDBGMODCTNSYMBOL, AddrCore); + RTStrCacheRelease(g_hDbgModStrCache, pSym->NameCore.pszString); + pSym->NameCore.pszString = NULL; + +#if 0 + //PRTDBGMODCTN pThis = (PRTDBGMODCTN)pvUser; + //PAVLU32NODECORE pRemoved = RTAvlU32Remove(&pThis->SymbolOrdinalTree, pSym->OrdinalCore.Key); + //Assert(pRemoved == &pSym->OrdinalCore); RT_NOREF_PV(pRemoved); +#else + RT_NOREF_PV(pvUser); +#endif + + RTMemFree(pSym); + return 0; +} + + +/** @copydoc RTDBGMODVTDBG::pfnClose */ +static DECLCALLBACK(int) rtDbgModContainer_Close(PRTDBGMODINT pMod) +{ + PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv; + + /* + * Destroy the symbols and instance data. + */ + for (uint32_t iSeg = 0; iSeg < pThis->cSegs; iSeg++) + { + RTAvlrUIntPtrDestroy(&pThis->paSegs[iSeg].SymAddrTree, rtDbgModContainer_DestroyTreeNode, pThis); + RTStrCacheRelease(g_hDbgModStrCache, pThis->paSegs[iSeg].pszName); + pThis->paSegs[iSeg].pszName = NULL; + } + + RTAvlrUIntPtrDestroy(&pThis->AbsAddrTree, rtDbgModContainer_DestroyTreeNode, pThis); + + pThis->Names = NULL; + +#ifdef RTDBGMODCNT_WITH_MEM_CACHE + RTMemCacheDestroy(pThis->hLineNumAllocator); + pThis->hLineNumAllocator = NIL_RTMEMCACHE; +#else + RTAvlU32Destroy(&pThis->LineOrdinalTree, rtDbgModContainer_DestroyTreeLineNode, pThis); +#endif + + RTMemFree(pThis->paSegs); + pThis->paSegs = NULL; + + RTMemFree(pThis); + + return VINF_SUCCESS; +} + + +/** @copydoc RTDBGMODVTDBG::pfnTryOpen */ +static DECLCALLBACK(int) rtDbgModContainer_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch) +{ + NOREF(pMod); NOREF(enmArch); + return VERR_INTERNAL_ERROR_5; +} + + + +/** Virtual function table for the debug info container. */ +DECL_HIDDEN_CONST(RTDBGMODVTDBG) const g_rtDbgModVtDbgContainer = +{ + /*.u32Magic = */ RTDBGMODVTDBG_MAGIC, + /*.fSupports = */ 0, /* (Don't call my TryOpen, please.) */ + /*.pszName = */ "container", + /*.pfnTryOpen = */ rtDbgModContainer_TryOpen, + /*.pfnClose = */ rtDbgModContainer_Close, + + /*.pfnRvaToSegOff = */ rtDbgModContainer_RvaToSegOff, + /*.pfnImageSize = */ rtDbgModContainer_ImageSize, + + /*.pfnSegmentAdd = */ rtDbgModContainer_SegmentAdd, + /*.pfnSegmentCount = */ rtDbgModContainer_SegmentCount, + /*.pfnSegmentByIndex = */ rtDbgModContainer_SegmentByIndex, + + /*.pfnSymbolAdd = */ rtDbgModContainer_SymbolAdd, + /*.pfnSymbolCount = */ rtDbgModContainer_SymbolCount, + /*.pfnSymbolByOrdinal = */ rtDbgModContainer_SymbolByOrdinal, + /*.pfnSymbolByName = */ rtDbgModContainer_SymbolByName, + /*.pfnSymbolByAddr = */ rtDbgModContainer_SymbolByAddr, + + /*.pfnLineAdd = */ rtDbgModContainer_LineAdd, + /*.pfnLineCount = */ rtDbgModContainer_LineCount, + /*.pfnLineByOrdinal = */ rtDbgModContainer_LineByOrdinal, + /*.pfnLineByAddr = */ rtDbgModContainer_LineByAddr, + + /*.pfnUnwindFrame = */ rtDbgModContainer_UnwindFrame, + + /*.u32EndMagic = */ RTDBGMODVTDBG_MAGIC +}; + + + +/** + * Special container operation for removing all symbols. + * + * @returns IPRT status code. + * @param pMod The module instance. + */ +DECLHIDDEN(int) rtDbgModContainer_SymbolRemoveAll(PRTDBGMODINT pMod) +{ + PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv; + + for (uint32_t iSeg = 0; iSeg < pThis->cSegs; iSeg++) + { + RTAvlrUIntPtrDestroy(&pThis->paSegs[iSeg].SymAddrTree, rtDbgModContainer_DestroyTreeNode, NULL); + Assert(pThis->paSegs[iSeg].SymAddrTree == NULL); + } + + RTAvlrUIntPtrDestroy(&pThis->AbsAddrTree, rtDbgModContainer_DestroyTreeNode, NULL); + Assert(pThis->AbsAddrTree == NULL); + + pThis->Names = NULL; + pThis->iNextSymbolOrdinal = 0; + + return VINF_SUCCESS; +} + + +/** + * Special container operation for removing all line numbers. + * + * @returns IPRT status code. + * @param pMod The module instance. + */ +DECLHIDDEN(int) rtDbgModContainer_LineRemoveAll(PRTDBGMODINT pMod) +{ + PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv; + + for (uint32_t iSeg = 0; iSeg < pThis->cSegs; iSeg++) + pThis->paSegs[iSeg].LineAddrTree = NULL; + + RTAvlU32Destroy(&pThis->LineOrdinalTree, rtDbgModContainer_DestroyTreeLineNode, pThis); + Assert(pThis->LineOrdinalTree == NULL); + + pThis->iNextLineOrdinal = 0; + + return VINF_SUCCESS; +} + + +/** + * Special container operation for removing everything. + * + * @returns IPRT status code. + * @param pMod The module instance. + */ +DECLHIDDEN(int) rtDbgModContainer_RemoveAll(PRTDBGMODINT pMod) +{ + PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv; + + rtDbgModContainer_LineRemoveAll(pMod); + rtDbgModContainer_SymbolRemoveAll(pMod); + + for (uint32_t iSeg = 0; iSeg < pThis->cSegs; iSeg++) + { + RTStrCacheRelease(g_hDbgModStrCache, pThis->paSegs[iSeg].pszName); + pThis->paSegs[iSeg].pszName = NULL; + } + + pThis->cSegs = 0; + pThis->cb = 0; + + return VINF_SUCCESS; +} + + +/** + * Creates a generic debug info container and associates it with the module. + * + * @returns IPRT status code. + * @param pMod The module instance. + * @param cbSeg The size of the initial segment. 0 if segments are to be + * created manually later on. + */ +DECLHIDDEN(int) rtDbgModContainerCreate(PRTDBGMODINT pMod, RTUINTPTR cbSeg) +{ + PRTDBGMODCTN pThis = (PRTDBGMODCTN)RTMemAlloc(sizeof(*pThis)); + if (!pThis) + return VERR_NO_MEMORY; + + pThis->Names = NULL; + pThis->AbsAddrTree = NULL; + pThis->SymbolOrdinalTree = NULL; + pThis->LineOrdinalTree = NULL; + pThis->paSegs = NULL; + pThis->cSegs = 0; + pThis->cb = 0; + pThis->iNextSymbolOrdinal = 0; + pThis->iNextLineOrdinal = 0; + + pMod->pDbgVt = &g_rtDbgModVtDbgContainer; + pMod->pvDbgPriv = pThis; + +#ifdef RTDBGMODCNT_WITH_MEM_CACHE + int rc = RTMemCacheCreate(&pThis->hLineNumAllocator, sizeof(RTDBGMODCTNLINE), sizeof(void *), UINT32_MAX, + NULL /*pfnCtor*/, NULL /*pfnDtor*/, NULL /*pvUser*/, 0 /*fFlags*/); +#else + int rc = VINF_SUCCESS; +#endif + if (RT_SUCCESS(rc)) + { + /* + * Add the initial segment. + */ + if (cbSeg) + rc = rtDbgModContainer_SegmentAdd(pMod, 0, cbSeg, "default", sizeof("default") - 1, 0, NULL); + if (RT_SUCCESS(rc)) + return rc; + +#ifdef RTDBGMODCNT_WITH_MEM_CACHE + RTMemCacheDestroy(pThis->hLineNumAllocator); +#endif + } + + RTMemFree(pThis); + pMod->pDbgVt = NULL; + pMod->pvDbgPriv = NULL; + return rc; +} + diff --git a/src/VBox/Runtime/common/dbg/dbgmoddbghelp.cpp b/src/VBox/Runtime/common/dbg/dbgmoddbghelp.cpp new file mode 100644 index 00000000..831bce8c --- /dev/null +++ b/src/VBox/Runtime/common/dbg/dbgmoddbghelp.cpp @@ -0,0 +1,537 @@ +/* $Id: dbgmoddbghelp.cpp $ */ +/** @file + * IPRT - Debug Info Reader Using DbgHelp.dll if Present. + */ + +/* + * Copyright (C) 2013-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_DBG +#include <iprt/dbg.h> +#include "internal/iprt.h" + +#include <iprt/asm.h> +#include <iprt/ctype.h> +#include <iprt/err.h> +#include <iprt/list.h> +#include <iprt/log.h> +#include <iprt/mem.h> +#include <iprt/path.h> +#include <iprt/string.h> +#include <iprt/utf16.h> +#include "internal/dbgmod.h" + +#include <iprt/win/windows.h> +#include <iprt/win/dbghelp.h> +#include <iprt/win/lazy-dbghelp.h> + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** For passing arguments to DbgHelp.dll callback. */ +typedef struct RTDBGMODBGHELPARGS +{ + RTDBGMOD hCnt; + PRTDBGMODINT pMod; + uint64_t uModAddr; + RTLDRADDR uNextRva; + + /** UTF-8 version of the previous file name. */ + char *pszPrev; + /** Copy of the previous file name. */ + PRTUTF16 pwszPrev; + /** Number of bytes pwszPrev points to. */ + size_t cbPrevUtf16Alloc; +} RTDBGMODBGHELPARGS; + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnUnwindFrame} */ +static DECLCALLBACK(int) rtDbgModDbgHelp_UnwindFrame(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState) +{ + RT_NOREF(pMod, iSeg, off, pState); + return VERR_DBG_NO_UNWIND_INFO; +} + + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByAddr} */ +static DECLCALLBACK(int) rtDbgModDbgHelp_LineByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, + PRTINTPTR poffDisp, PRTDBGLINE pLineInfo) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModLineByAddr(hCnt, iSeg, off, poffDisp, pLineInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByOrdinal} */ +static DECLCALLBACK(int) rtDbgModDbgHelp_LineByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModLineByOrdinal(hCnt, iOrdinal, pLineInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineCount} */ +static DECLCALLBACK(uint32_t) rtDbgModDbgHelp_LineCount(PRTDBGMODINT pMod) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModLineCount(hCnt); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineAdd} */ +static DECLCALLBACK(int) rtDbgModDbgHelp_LineAdd(PRTDBGMODINT pMod, const char *pszFile, size_t cchFile, uint32_t uLineNo, + uint32_t iSeg, RTUINTPTR off, uint32_t *piOrdinal) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + Assert(!pszFile[cchFile]); NOREF(cchFile); + return RTDbgModLineAdd(hCnt, pszFile, uLineNo, iSeg, off, piOrdinal); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByAddr} */ +static DECLCALLBACK(int) rtDbgModDbgHelp_SymbolByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags, + PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModSymbolByAddr(hCnt, iSeg, off, fFlags, poffDisp, pSymInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByName} */ +static DECLCALLBACK(int) rtDbgModDbgHelp_SymbolByName(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol, + PRTDBGSYMBOL pSymInfo) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + Assert(!pszSymbol[cchSymbol]); RT_NOREF_PV(cchSymbol); + return RTDbgModSymbolByName(hCnt, pszSymbol/*, cchSymbol*/, pSymInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByOrdinal} */ +static DECLCALLBACK(int) rtDbgModDbgHelp_SymbolByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModSymbolByOrdinal(hCnt, iOrdinal, pSymInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolCount} */ +static DECLCALLBACK(uint32_t) rtDbgModDbgHelp_SymbolCount(PRTDBGMODINT pMod) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModSymbolCount(hCnt); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolAdd} */ +static DECLCALLBACK(int) rtDbgModDbgHelp_SymbolAdd(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol, + RTDBGSEGIDX iSeg, RTUINTPTR off, RTUINTPTR cb, uint32_t fFlags, + uint32_t *piOrdinal) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + Assert(!pszSymbol[cchSymbol]); NOREF(cchSymbol); + return RTDbgModSymbolAdd(hCnt, pszSymbol, iSeg, off, cb, fFlags, piOrdinal); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentByIndex} */ +static DECLCALLBACK(int) rtDbgModDbgHelp_SegmentByIndex(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModSegmentByIndex(hCnt, iSeg, pSegInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentCount} */ +static DECLCALLBACK(RTDBGSEGIDX) rtDbgModDbgHelp_SegmentCount(PRTDBGMODINT pMod) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModSegmentCount(hCnt); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentAdd} */ +static DECLCALLBACK(int) rtDbgModDbgHelp_SegmentAdd(PRTDBGMODINT pMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName, size_t cchName, + uint32_t fFlags, PRTDBGSEGIDX piSeg) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + Assert(!pszName[cchName]); NOREF(cchName); + return RTDbgModSegmentAdd(hCnt, uRva, cb, pszName, fFlags, piSeg); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnImageSize} */ +static DECLCALLBACK(RTUINTPTR) rtDbgModDbgHelp_ImageSize(PRTDBGMODINT pMod) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + RTUINTPTR cb1 = RTDbgModImageSize(hCnt); + RTUINTPTR cb2 = pMod->pImgVt->pfnImageSize(pMod); + return RT_MAX(cb1, cb2); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnRvaToSegOff} */ +static DECLCALLBACK(RTDBGSEGIDX) rtDbgModDbgHelp_RvaToSegOff(PRTDBGMODINT pMod, RTUINTPTR uRva, PRTUINTPTR poffSeg) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModRvaToSegOff(hCnt, uRva, poffSeg); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnClose} */ +static DECLCALLBACK(int) rtDbgModDbgHelp_Close(PRTDBGMODINT pMod) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv;; + RTDbgModRelease(hCnt); + pMod->pvDbgPriv = NULL; + return VINF_SUCCESS; +} + + +/** + * SymEnumLinesW callback that adds a line number to the container. + * + * @returns TRUE, FALSE if we're out of memory. + * @param pLineInfo Line number information. + * @param pvUser Pointer to a RTDBGMODBGHELPARGS structure. + */ +static BOOL CALLBACK rtDbgModDbgHelpCopyLineNumberCallback(PSRCCODEINFOW pLineInfo, PVOID pvUser) +{ + RTDBGMODBGHELPARGS *pArgs = (RTDBGMODBGHELPARGS *)pvUser; + + if (pLineInfo->Address < pArgs->uModAddr) + { + Log((" %#018RX64 %05u %s [SKIPPED - INVALID ADDRESS!]\n", pLineInfo->Address, pLineInfo->LineNumber)); + return TRUE; + } + + /* + * To save having to call RTUtf16ToUtf8 every time, we keep a copy of the + * previous file name both as UTF-8 and UTF-16. + */ + /** @todo we could combine RTUtf16Len and memcmp... */ + size_t cbLen = (RTUtf16Len(pLineInfo->FileName) + 1) * sizeof(RTUTF16); + if ( !pArgs->pwszPrev + || pArgs->cbPrevUtf16Alloc < cbLen + || memcmp(pArgs->pwszPrev, pLineInfo->FileName, cbLen) ) + { + if (pArgs->cbPrevUtf16Alloc >= cbLen) + memcpy(pArgs->pwszPrev, pLineInfo->FileName, cbLen); + else + { + RTMemFree(pArgs->pwszPrev); + pArgs->cbPrevUtf16Alloc = cbLen; + pArgs->pwszPrev = (PRTUTF16)RTMemDupEx(pLineInfo->FileName, cbLen, pArgs->cbPrevUtf16Alloc - cbLen); + if (!pArgs->pwszPrev) + pArgs->cbPrevUtf16Alloc = 0; + } + + RTStrFree(pArgs->pszPrev); + pArgs->pszPrev = NULL; + int rc = RTUtf16ToUtf8(pLineInfo->FileName, &pArgs->pszPrev); + if (RT_FAILURE(rc)) + { + SetLastError(ERROR_OUTOFMEMORY); + Log(("rtDbgModDbgHelpCopyLineNumberCallback: Out of memory\n")); + return FALSE; + } + } + + /* + * Add the line number to the container. + */ + int rc = RTDbgModLineAdd(pArgs->hCnt, pArgs->pszPrev, pLineInfo->LineNumber, + RTDBGSEGIDX_RVA, pLineInfo->Address - pArgs->uModAddr, NULL); + Log((" %#018RX64 %05u %s [%Rrc]\n", pLineInfo->Address, pLineInfo->LineNumber, pArgs->pszPrev, rc)); + NOREF(rc); + + return TRUE; +} + + +/** + * Copies the line numbers into the container. + * + * @returns IPRT status code. + * @param pMod The debug module. + * @param hCnt The container that will keep the symbols. + * @param hFake The fake process handle. + * @param uModAddr The module load address. + */ +static int rtDbgModDbgHelpCopyLineNumbers(PRTDBGMODINT pMod, RTDBGMOD hCnt, HANDLE hFake, uint64_t uModAddr) +{ + RTDBGMODBGHELPARGS Args; + Args.hCnt = hCnt; + Args.pMod = pMod; + Args.uModAddr = uModAddr; + Args.pszPrev = NULL; + Args.pwszPrev = NULL; + Args.cbPrevUtf16Alloc = 0; + + int rc; + if (SymEnumLinesW(hFake, uModAddr, NULL /*pszObj*/, NULL /*pszFile*/, rtDbgModDbgHelpCopyLineNumberCallback, &Args)) + rc = VINF_SUCCESS; + else + { + rc = RTErrConvertFromWin32(GetLastError()); + Log(("Line number enum: %Rrc (%u)\n", rc, GetLastError())); + if (rc == VERR_NOT_SUPPORTED) + rc = VINF_SUCCESS; + } + + RTStrFree(Args.pszPrev); + RTMemFree(Args.pwszPrev); + return rc; +} + + +/** + * SymEnumSymbols callback that adds a symbol to the container. + * + * @returns TRUE + * @param pSymInfo The symbol information. + * @param cbSymbol The symbol size (estimated). + * @param pvUser Pointer to a RTDBGMODBGHELPARGS structure. + */ +static BOOL CALLBACK rtDbgModDbgHelpCopySymbolsCallback(PSYMBOL_INFO pSymInfo, ULONG cbSymbol, PVOID pvUser) +{ + RTDBGMODBGHELPARGS *pArgs = (RTDBGMODBGHELPARGS *)pvUser; + if (pSymInfo->Address < pArgs->uModAddr) /* NT4 SP1 ntfs.dbg */ + { + Log((" %#018RX64 LB %#07x %s [SKIPPED - INVALID ADDRESS!]\n", pSymInfo->Address, cbSymbol, pSymInfo->Name)); + return TRUE; + } + if (pSymInfo->NameLen >= RTDBG_SYMBOL_NAME_LENGTH) + { + Log((" %#018RX64 LB %#07x %s [SKIPPED - TOO LONG (%u > %u)!]\n", pSymInfo->Address, cbSymbol, pSymInfo->Name, + pSymInfo->NameLen, RTDBG_SYMBOL_NAME_LENGTH)); + return TRUE; + } + + /* ASSUMES the symbol name is ASCII. */ + int rc = RTDbgModSymbolAdd(pArgs->hCnt, pSymInfo->Name, RTDBGSEGIDX_RVA, + pSymInfo->Address - pArgs->uModAddr, cbSymbol, 0, NULL); + Log((" %#018RX64 LB %#07x %s [%Rrc]\n", pSymInfo->Address, cbSymbol, pSymInfo->Name, rc)); + NOREF(rc); + + return TRUE; +} + + +/** + * Copies the symbols into the container. + * + * @returns IPRT status code. + * @param pMod The debug module. + * @param hCnt The container that will keep the symbols. + * @param hFake The fake process handle. + * @param uModAddr The module load address. + */ +static int rtDbgModDbgHelpCopySymbols(PRTDBGMODINT pMod, RTDBGMOD hCnt, HANDLE hFake, uint64_t uModAddr) +{ + RTDBGMODBGHELPARGS Args; + Args.hCnt = hCnt; + Args.pMod = pMod; + Args.uModAddr = uModAddr; + int rc; + if (SymEnumSymbols(hFake, uModAddr, NULL, rtDbgModDbgHelpCopySymbolsCallback, &Args)) + rc = VINF_SUCCESS; + else + { + rc = RTErrConvertFromWin32(GetLastError()); + Log(("SymEnumSymbols: %Rrc (%u)\n", rc, GetLastError())); + } + return rc; +} + + +/** @callback_method_impl{FNRTLDRENUMSEGS, + * Copies the PE segments over into the container.} */ +static DECLCALLBACK(int) rtDbgModDbgHelpAddSegmentsCallback(RTLDRMOD hLdrMod, PCRTLDRSEG pSeg, void *pvUser) +{ + RTDBGMODBGHELPARGS *pArgs = (RTDBGMODBGHELPARGS *)pvUser; + RT_NOREF_PV(hLdrMod); + + Log(("Segment %.*s: LinkAddress=%#llx RVA=%#llx cb=%#llx\n", + pSeg->cchName, pSeg->pszName, (uint64_t)pSeg->LinkAddress, (uint64_t)pSeg->RVA, pSeg->cb)); + + Assert(pSeg->cchName > 0); + Assert(!pSeg->pszName[pSeg->cchName]); + + RTLDRADDR cb = RT_MAX(pSeg->cb, pSeg->cbMapped); + RTLDRADDR uRva = pSeg->RVA; + if (!uRva) + pArgs->uModAddr = pSeg->LinkAddress; + else if (uRva == NIL_RTLDRADDR) + { + cb = 0; + uRva = pArgs->uNextRva; + } + pArgs->uNextRva = uRva + cb; + + return RTDbgModSegmentAdd(pArgs->hCnt, uRva, cb, pSeg->pszName, 0 /*fFlags*/, NULL); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnTryOpen} */ +static DECLCALLBACK(int) rtDbgModDbgHelp_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch) +{ + NOREF(enmArch); + + /* + * Currently only support external files with a executable already present. + */ + if (!pMod->pszDbgFile) + return VERR_DBG_NO_MATCHING_INTERPRETER; + if (!pMod->pImgVt) + return VERR_DBG_NO_MATCHING_INTERPRETER; + + /* + * Create a container for copying the information into. We do this early + * so we can determine the image base address. + */ + RTDBGMOD hCnt; + int rc = RTDbgModCreate(&hCnt, pMod->pszName, 0 /*cbSeg*/, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + RTDBGMODBGHELPARGS Args; + RT_ZERO(Args); + Args.hCnt = hCnt; + rc = pMod->pImgVt->pfnEnumSegments(pMod, rtDbgModDbgHelpAddSegmentsCallback, &Args); + if (RT_SUCCESS(rc)) + { + uint32_t cbImage = pMod->pImgVt->pfnImageSize(pMod); + uint64_t uImageBase = Args.uModAddr ? Args.uModAddr : 0x4000000; + + /* + * Try load the module into an empty address space. + */ + static uint32_t volatile s_uFakeHandle = 0x3940000; + HANDLE hFake; + do + hFake = (HANDLE)(uintptr_t)ASMAtomicIncU32(&s_uFakeHandle); + while (hFake == NULL || hFake == INVALID_HANDLE_VALUE); + + LogFlow(("rtDbgModDbgHelp_TryOpen: \n")); + if (SymInitialize(hFake, NULL /*SearchPath*/, FALSE /*fInvalidProcess*/)) + { + SymSetOptions(SYMOPT_LOAD_LINES | SymGetOptions()); + + PRTUTF16 pwszDbgFile; + rc = RTStrToUtf16(pMod->pszDbgFile, &pwszDbgFile); + if (RT_SUCCESS(rc)) + { + uint64_t uModAddr = SymLoadModuleExW(hFake, NULL /*hFile*/, pwszDbgFile, NULL /*pszModName*/, + uImageBase, cbImage, NULL /*pModData*/, 0 /*fFlags*/); + if (uModAddr != 0) + { + rc = rtDbgModDbgHelpCopySymbols(pMod, hCnt, hFake, uModAddr); + if (RT_SUCCESS(rc)) + rc = rtDbgModDbgHelpCopyLineNumbers(pMod, hCnt, hFake, uModAddr); + if (RT_SUCCESS(rc)) + { + pMod->pvDbgPriv = hCnt; + pMod->pDbgVt = &g_rtDbgModVtDbgDbgHelp; + hCnt = NIL_RTDBGMOD; + LogFlow(("rtDbgModDbgHelp_TryOpen: Successfully loaded '%s' at %#llx\n", + pMod->pszDbgFile, (uint64_t)uImageBase)); + } + + SymUnloadModule64(hFake, uModAddr); + } + else + { + rc = RTErrConvertFromWin32(GetLastError()); + if (RT_SUCCESS_NP(rc)) + rc = VERR_DBG_NO_MATCHING_INTERPRETER; + LogFlow(("rtDbgModDbgHelp_TryOpen: Error loading the module '%s' at %#llx: %Rrc (%u)\n", + pMod->pszDbgFile, (uint64_t)uImageBase, rc, GetLastError())); + } + RTUtf16Free(pwszDbgFile); + } + else + LogFlow(("rtDbgModDbgHelp_TryOpen: Unicode version issue: %Rrc\n", rc)); + + BOOL fRc2 = SymCleanup(hFake); Assert(fRc2); NOREF(fRc2); + } + else + { + rc = RTErrConvertFromWin32(GetLastError()); + if (RT_SUCCESS_NP(rc)) + rc = VERR_DBG_NO_MATCHING_INTERPRETER; + LogFlow(("rtDbgModDbgHelp_TryOpen: SymInitialize failed: %Rrc (%u)\n", rc, GetLastError())); + } + } + RTDbgModRelease(hCnt); + } + return rc; +} + + + +/** Virtual function table for the DBGHELP debug info reader. */ +DECL_HIDDEN_CONST(RTDBGMODVTDBG) const g_rtDbgModVtDbgDbgHelp = +{ + /*.u32Magic = */ RTDBGMODVTDBG_MAGIC, + /*.fSupports = */ RT_DBGTYPE_CODEVIEW, + /*.pszName = */ "dbghelp", + /*.pfnTryOpen = */ rtDbgModDbgHelp_TryOpen, + /*.pfnClose = */ rtDbgModDbgHelp_Close, + + /*.pfnRvaToSegOff = */ rtDbgModDbgHelp_RvaToSegOff, + /*.pfnImageSize = */ rtDbgModDbgHelp_ImageSize, + + /*.pfnSegmentAdd = */ rtDbgModDbgHelp_SegmentAdd, + /*.pfnSegmentCount = */ rtDbgModDbgHelp_SegmentCount, + /*.pfnSegmentByIndex = */ rtDbgModDbgHelp_SegmentByIndex, + + /*.pfnSymbolAdd = */ rtDbgModDbgHelp_SymbolAdd, + /*.pfnSymbolCount = */ rtDbgModDbgHelp_SymbolCount, + /*.pfnSymbolByOrdinal = */ rtDbgModDbgHelp_SymbolByOrdinal, + /*.pfnSymbolByName = */ rtDbgModDbgHelp_SymbolByName, + /*.pfnSymbolByAddr = */ rtDbgModDbgHelp_SymbolByAddr, + + /*.pfnLineAdd = */ rtDbgModDbgHelp_LineAdd, + /*.pfnLineCount = */ rtDbgModDbgHelp_LineCount, + /*.pfnLineByOrdinal = */ rtDbgModDbgHelp_LineByOrdinal, + /*.pfnLineByAddr = */ rtDbgModDbgHelp_LineByAddr, + + /*.pfnUnwindFrame = */ rtDbgModDbgHelp_UnwindFrame, + + /*.u32EndMagic = */ RTDBGMODVTDBG_MAGIC +}; + diff --git a/src/VBox/Runtime/common/dbg/dbgmoddeferred.cpp b/src/VBox/Runtime/common/dbg/dbgmoddeferred.cpp new file mode 100644 index 00000000..5dbdc5e2 --- /dev/null +++ b/src/VBox/Runtime/common/dbg/dbgmoddeferred.cpp @@ -0,0 +1,730 @@ +/* $Id: dbgmoddeferred.cpp $ */ +/** @file + * IPRT - Debug Module Deferred Loading Stub. + */ + +/* + * Copyright (C) 2013-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/dbg.h> +#include "internal/iprt.h" + +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/mem.h> +#include <iprt/param.h> +#include <iprt/path.h> +#include <iprt/string.h> +#include "internal/dbgmod.h" +#include "internal/magics.h" + + + +/** + * Releases the instance data. + * + * @param pThis The instance data. + */ +static void rtDbgModDeferredReleaseInstanceData(PRTDBGMODDEFERRED pThis) +{ + AssertPtr(pThis); + Assert(pThis->u32Magic == RTDBGMODDEFERRED_MAGIC); + uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs); Assert(cRefs < 8); + if (!cRefs) + { + RTDbgCfgRelease(pThis->hDbgCfg); + pThis->hDbgCfg = NIL_RTDBGCFG; + pThis->u32Magic = RTDBGMODDEFERRED_MAGIC_DEAD; + RTMemFree(pThis); + } +} + + +/** + * Does the deferred loading of the real data (image and/or debug info). + * + * @returns VINF_SUCCESS or VERR_DBG_DEFERRED_LOAD_FAILED. + * @param pMod The generic module instance data. + * @param fForcedRetry Whether it's a forced retry by one of the + * pfnTryOpen methods. + */ +static int rtDbgModDeferredDoIt(PRTDBGMODINT pMod, bool fForcedRetry) +{ + RTCritSectEnter(&pMod->CritSect); + + int rc; + if (!pMod->fDeferredFailed || fForcedRetry) + { + bool const fDbgVt = pMod->pDbgVt == &g_rtDbgModVtDbgDeferred; + bool const fImgVt = pMod->pImgVt == &g_rtDbgModVtImgDeferred; + AssertReturnStmt(fDbgVt || fImgVt, RTCritSectLeave(&pMod->CritSect), VERR_INTERNAL_ERROR_5); + + PRTDBGMODDEFERRED pThis = (PRTDBGMODDEFERRED)(fDbgVt ? pMod->pvDbgPriv : pMod->pvImgPriv); + + /* Reset the method tables and private data pointes so the deferred loading + procedure can figure out what to do and won't get confused. */ + if (fDbgVt) + { + pMod->pvDbgPriv = NULL; + pMod->pDbgVt = NULL; + } + + if (fImgVt) + { + pMod->pvImgPriv = NULL; + pMod->pImgVt = NULL; + } + + /* Do the deferred loading. */ + rc = pThis->pfnDeferred(pMod, pThis); + if (RT_SUCCESS(rc)) + { + Assert(!fDbgVt || pMod->pDbgVt != NULL); + Assert(!fImgVt || pMod->pImgVt != NULL); + + pMod->fDeferred = false; + pMod->fDeferredFailed = false; + + rtDbgModDeferredReleaseInstanceData(pThis); + if (fImgVt && fDbgVt) + rtDbgModDeferredReleaseInstanceData(pThis); + } + else + { + /* Failed, bail out and restore the deferred setup. */ + pMod->fDeferredFailed = true; + + if (fDbgVt) + { + Assert(!pMod->pDbgVt); + pMod->pDbgVt = &g_rtDbgModVtDbgDeferred; + pMod->pvDbgPriv = pThis; + } + + if (fImgVt) + { + Assert(!pMod->pImgVt); + pMod->pImgVt = &g_rtDbgModVtImgDeferred; + pMod->pvImgPriv = pThis; + } + } + } + else + rc = VERR_DBG_DEFERRED_LOAD_FAILED; + + RTCritSectLeave(&pMod->CritSect); + return rc; +} + + + + +/* + * + * D e b u g I n f o M e t h o d s + * D e b u g I n f o M e t h o d s + * D e b u g I n f o M e t h o d s + * + */ + +/** @interface_method_impl{RTDBGMODVTDBG,pfnUnwindFrame} */ +static DECLCALLBACK(int) +rtDbgModDeferredDbg_UnwindFrame(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState) +{ + Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC); + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + rc = pMod->pDbgVt->pfnUnwindFrame(pMod, iSeg, off, pState); + return rc; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByAddr} */ +static DECLCALLBACK(int) rtDbgModDeferredDbg_LineByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, + PRTINTPTR poffDisp, PRTDBGLINE pLineInfo) +{ + Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC); + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + rc = pMod->pDbgVt->pfnLineByAddr(pMod, iSeg, off, poffDisp, pLineInfo); + return rc; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByOrdinal} */ +static DECLCALLBACK(int) rtDbgModDeferredDbg_LineByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo) +{ + Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC); + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + rc = pMod->pDbgVt->pfnLineByOrdinal(pMod, iOrdinal, pLineInfo); + return rc; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineCount} */ +static DECLCALLBACK(uint32_t) rtDbgModDeferredDbg_LineCount(PRTDBGMODINT pMod) +{ + Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC); + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + return pMod->pDbgVt->pfnLineCount(pMod); + return 0; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineAdd} */ +static DECLCALLBACK(int) rtDbgModDeferredDbg_LineAdd(PRTDBGMODINT pMod, const char *pszFile, size_t cchFile, uint32_t uLineNo, + uint32_t iSeg, RTUINTPTR off, uint32_t *piOrdinal) +{ + Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC); + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + rc = pMod->pDbgVt->pfnLineAdd(pMod, pszFile, cchFile, uLineNo, iSeg, off, piOrdinal); + return rc; +} + + +/** + * Fill in symbol info for the fake start symbol. + * + * @returns VINF_SUCCESS + * @param pThis The deferred load data. + * @param pSymInfo The output structure. + */ +static int rtDbgModDeferredDbgSymInfo_Start(PRTDBGMODDEFERRED pThis, PRTDBGSYMBOL pSymInfo) +{ + pSymInfo->Value = 0; + pSymInfo->cb = pThis->cbImage; + pSymInfo->offSeg = 0; + pSymInfo->iSeg = 0; + pSymInfo->fFlags = 0; + pSymInfo->iOrdinal = 0; + strcpy(pSymInfo->szName, "DeferredStart"); + return VINF_SUCCESS; +} + + +/** + * Fill in symbol info for the fake last symbol. + * + * @returns VINF_SUCCESS + * @param pThis The deferred load data. + * @param pSymInfo The output structure. + */ +static int rtDbgModDeferredDbgSymInfo_Last(PRTDBGMODDEFERRED pThis, PRTDBGSYMBOL pSymInfo) +{ + pSymInfo->Value = pThis->cbImage - 1; + pSymInfo->cb = 0; + pSymInfo->offSeg = pThis->cbImage - 1; + pSymInfo->iSeg = 0; + pSymInfo->fFlags = 0; + pSymInfo->iOrdinal = 1; + strcpy(pSymInfo->szName, "DeferredLast"); + return VINF_SUCCESS; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByAddr} */ +static DECLCALLBACK(int) rtDbgModDeferredDbg_SymbolByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags, + PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo) +{ + Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC); + if ( (fFlags & RTDBGSYMADDR_FLAGS_SKIP_ABS_IN_DEFERRED) + && iSeg == RTDBGSEGIDX_ABS) + return VERR_SYMBOL_NOT_FOUND; + + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + rc = pMod->pDbgVt->pfnSymbolByAddr(pMod, iSeg, off, fFlags, poffDisp, pSymInfo); + else + { + PRTDBGMODDEFERRED pThis = (PRTDBGMODDEFERRED)pMod->pvDbgPriv; + if (off == 0) + rc = rtDbgModDeferredDbgSymInfo_Start(pThis, pSymInfo); + else if (off >= pThis->cbImage - 1 || (fFlags & RTDBGSYMADDR_FLAGS_GREATER_OR_EQUAL)) + rc = rtDbgModDeferredDbgSymInfo_Last(pThis, pSymInfo); + else + rc = rtDbgModDeferredDbgSymInfo_Start(pThis, pSymInfo); + if (poffDisp) + *poffDisp = off - pSymInfo->offSeg; + } + return rc; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByName} */ +static DECLCALLBACK(int) rtDbgModDeferredDbg_SymbolByName(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol, + PRTDBGSYMBOL pSymInfo) +{ + Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC); + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + rc = pMod->pDbgVt->pfnSymbolByName(pMod, pszSymbol, cchSymbol, pSymInfo); + else + { + PRTDBGMODDEFERRED pThis = (PRTDBGMODDEFERRED)pMod->pvDbgPriv; + if ( cchSymbol == sizeof("DeferredStart") - 1 + && !memcmp(pszSymbol, RT_STR_TUPLE("DeferredStart"))) + rc = rtDbgModDeferredDbgSymInfo_Start(pThis, pSymInfo); + else if ( cchSymbol == sizeof("DeferredLast") - 1 + && !memcmp(pszSymbol, RT_STR_TUPLE("DeferredLast"))) + rc = rtDbgModDeferredDbgSymInfo_Last(pThis, pSymInfo); + else + rc = VERR_SYMBOL_NOT_FOUND; + } + return rc; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByOrdinal} */ +static DECLCALLBACK(int) rtDbgModDeferredDbg_SymbolByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo) +{ + Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC); + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + rc = pMod->pDbgVt->pfnSymbolByOrdinal(pMod, iOrdinal, pSymInfo); + else + { + PRTDBGMODDEFERRED pThis = (PRTDBGMODDEFERRED)pMod->pvDbgPriv; + if (iOrdinal == 0) + rc = rtDbgModDeferredDbgSymInfo_Start(pThis, pSymInfo); + else if (iOrdinal == 1) + rc = rtDbgModDeferredDbgSymInfo_Last(pThis, pSymInfo); + else + rc = VERR_SYMBOL_NOT_FOUND; + } + return rc; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolCount} */ +static DECLCALLBACK(uint32_t) rtDbgModDeferredDbg_SymbolCount(PRTDBGMODINT pMod) +{ + Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC); + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + return pMod->pDbgVt->pfnSymbolCount(pMod); + return 2; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolAdd} */ +static DECLCALLBACK(int) rtDbgModDeferredDbg_SymbolAdd(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol, + RTDBGSEGIDX iSeg, RTUINTPTR off, RTUINTPTR cb, uint32_t fFlags, + uint32_t *piOrdinal) +{ + Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC); + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + rc = pMod->pDbgVt->pfnSymbolAdd(pMod, pszSymbol, cchSymbol, iSeg, off, cb, fFlags, piOrdinal); + return rc; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentByIndex} */ +static DECLCALLBACK(int) rtDbgModDeferredDbg_SegmentByIndex(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo) +{ + Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC); + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + rc = pMod->pDbgVt->pfnSegmentByIndex(pMod, iSeg, pSegInfo); + else if (iSeg == 0) + { + PRTDBGMODDEFERRED pThis = (PRTDBGMODDEFERRED)pMod->pvDbgPriv; + pSegInfo->Address = 0; + pSegInfo->uRva = 0; + pSegInfo->cb = pThis->cbImage; + pSegInfo->fFlags = 0; + pSegInfo->iSeg = 0; + memcpy(pSegInfo->szName, RT_STR_TUPLE("LATER")); + rc = VINF_SUCCESS; + } + else + rc = VERR_DBG_INVALID_SEGMENT_INDEX; + return rc; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentCount} */ +static DECLCALLBACK(RTDBGSEGIDX) rtDbgModDeferredDbg_SegmentCount(PRTDBGMODINT pMod) +{ + Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC); + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + return pMod->pDbgVt->pfnSegmentCount(pMod); + return 1; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentAdd} */ +static DECLCALLBACK(int) rtDbgModDeferredDbg_SegmentAdd(PRTDBGMODINT pMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName, + size_t cchName, uint32_t fFlags, PRTDBGSEGIDX piSeg) +{ + Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC); + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + rc = pMod->pDbgVt->pfnSegmentAdd(pMod, uRva, cb, pszName, cchName, fFlags, piSeg); + return rc; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnImageSize} */ +static DECLCALLBACK(RTUINTPTR) rtDbgModDeferredDbg_ImageSize(PRTDBGMODINT pMod) +{ + PRTDBGMODDEFERRED pThis = (PRTDBGMODDEFERRED)pMod->pvDbgPriv; + Assert(pThis->u32Magic == RTDBGMODDEFERRED_MAGIC); + return pThis->cbImage; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnRvaToSegOff} */ +static DECLCALLBACK(RTDBGSEGIDX) rtDbgModDeferredDbg_RvaToSegOff(PRTDBGMODINT pMod, RTUINTPTR uRva, PRTUINTPTR poffSeg) +{ + Assert(((PRTDBGMODDEFERRED)pMod->pvDbgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC); + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + return pMod->pDbgVt->pfnRvaToSegOff(pMod, uRva, poffSeg); + return 0; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnClose} */ +static DECLCALLBACK(int) rtDbgModDeferredDbg_Close(PRTDBGMODINT pMod) +{ + rtDbgModDeferredReleaseInstanceData((PRTDBGMODDEFERRED)pMod->pvDbgPriv); + return VINF_SUCCESS; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnTryOpen} */ +static DECLCALLBACK(int) rtDbgModDeferredDbg_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch) +{ + NOREF(enmArch); + return rtDbgModDeferredDoIt(pMod, true /*fForceRetry*/); +} + + + +/** Virtual function table for the deferred debug info reader. */ +DECL_HIDDEN_CONST(RTDBGMODVTDBG) const g_rtDbgModVtDbgDeferred = +{ + /*.u32Magic = */ RTDBGMODVTDBG_MAGIC, + /*.fSupports = */ RT_DBGTYPE_MAP, + /*.pszName = */ "deferred", + /*.pfnTryOpen = */ rtDbgModDeferredDbg_TryOpen, + /*.pfnClose = */ rtDbgModDeferredDbg_Close, + + /*.pfnRvaToSegOff = */ rtDbgModDeferredDbg_RvaToSegOff, + /*.pfnImageSize = */ rtDbgModDeferredDbg_ImageSize, + + /*.pfnSegmentAdd = */ rtDbgModDeferredDbg_SegmentAdd, + /*.pfnSegmentCount = */ rtDbgModDeferredDbg_SegmentCount, + /*.pfnSegmentByIndex = */ rtDbgModDeferredDbg_SegmentByIndex, + + /*.pfnSymbolAdd = */ rtDbgModDeferredDbg_SymbolAdd, + /*.pfnSymbolCount = */ rtDbgModDeferredDbg_SymbolCount, + /*.pfnSymbolByOrdinal = */ rtDbgModDeferredDbg_SymbolByOrdinal, + /*.pfnSymbolByName = */ rtDbgModDeferredDbg_SymbolByName, + /*.pfnSymbolByAddr = */ rtDbgModDeferredDbg_SymbolByAddr, + + /*.pfnLineAdd = */ rtDbgModDeferredDbg_LineAdd, + /*.pfnLineCount = */ rtDbgModDeferredDbg_LineCount, + /*.pfnLineByOrdinal = */ rtDbgModDeferredDbg_LineByOrdinal, + /*.pfnLineByAddr = */ rtDbgModDeferredDbg_LineByAddr, + + /*.pfnUnwindFrame = */ rtDbgModDeferredDbg_UnwindFrame, + + /*.u32EndMagic = */ RTDBGMODVTDBG_MAGIC +}; + + + + +/* + * + * I m a g e M e t h o d s + * I m a g e M e t h o d s + * I m a g e M e t h o d s + * + */ + +/** @interface_method_impl{RTDBGMODVTIMG,pfnUnwindFrame} */ +static DECLCALLBACK(int) +rtDbgModDeferredImg_UnwindFrame(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState) +{ + Assert(((PRTDBGMODDEFERRED)pMod->pvImgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC); + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + rc = pMod->pImgVt->pfnUnwindFrame(pMod, iSeg, off, pState); + return rc; +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnQueryProp} */ +static DECLCALLBACK(int) +rtDbgModDeferredImg_QueryProp(PRTDBGMODINT pMod, RTLDRPROP enmProp, void *pvBuf, size_t cbBuf, size_t *pcbRet) +{ + Assert(((PRTDBGMODDEFERRED)pMod->pvImgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC); + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + rc = pMod->pImgVt->pfnQueryProp(pMod, enmProp, pvBuf, cbBuf, pcbRet); + return rc; +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnGetArch} */ +static DECLCALLBACK(RTLDRARCH) rtDbgModDeferredImg_GetArch(PRTDBGMODINT pMod) +{ + Assert(((PRTDBGMODDEFERRED)pMod->pvImgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC); + + RTLDRARCH enmArch; + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + enmArch = pMod->pImgVt->pfnGetArch(pMod); + else + enmArch = RTLDRARCH_WHATEVER; + return enmArch; +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnGetFormat} */ +static DECLCALLBACK(RTLDRFMT) rtDbgModDeferredImg_GetFormat(PRTDBGMODINT pMod) +{ + Assert(((PRTDBGMODDEFERRED)pMod->pvImgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC); + + RTLDRFMT enmFmt; + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + enmFmt = pMod->pImgVt->pfnGetFormat(pMod); + else + enmFmt = RTLDRFMT_INVALID; + return enmFmt; +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnReadAt} */ +static DECLCALLBACK(int) rtDbgModDeferredImg_ReadAt(PRTDBGMODINT pMod, uint32_t iDbgInfoHint, RTFOFF off, void *pvBuf, size_t cb) +{ + Assert(((PRTDBGMODDEFERRED)pMod->pvImgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC); + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + rc = pMod->pImgVt->pfnReadAt(pMod, iDbgInfoHint, off, pvBuf, cb); + return rc; +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnUnmapPart} */ +static DECLCALLBACK(int) rtDbgModDeferredImg_UnmapPart(PRTDBGMODINT pMod, size_t cb, void const **ppvMap) +{ + Assert(((PRTDBGMODDEFERRED)pMod->pvImgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC); + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + rc = pMod->pImgVt->pfnUnmapPart(pMod, cb, ppvMap); + return rc; +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnMapPart} */ +static DECLCALLBACK(int) rtDbgModDeferredImg_MapPart(PRTDBGMODINT pMod, uint32_t iDbgInfo, RTFOFF off, size_t cb, void const **ppvMap) +{ + Assert(((PRTDBGMODDEFERRED)pMod->pvImgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC); + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + rc = pMod->pImgVt->pfnMapPart(pMod, iDbgInfo, off, cb, ppvMap); + return rc; +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnImageSize} */ +static DECLCALLBACK(RTUINTPTR) rtDbgModDeferredImg_ImageSize(PRTDBGMODINT pMod) +{ + PRTDBGMODDEFERRED pThis = (PRTDBGMODDEFERRED)pMod->pvImgPriv; + Assert(pThis->u32Magic == RTDBGMODDEFERRED_MAGIC); + return pThis->cbImage; +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnRvaToSegOffset} */ +static DECLCALLBACK(int) rtDbgModDeferredImg_RvaToSegOffset(PRTDBGMODINT pMod, RTLDRADDR Rva, + PRTDBGSEGIDX piSeg, PRTLDRADDR poffSeg) +{ + Assert(((PRTDBGMODDEFERRED)pMod->pvImgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC); + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + rc = pMod->pImgVt->pfnRvaToSegOffset(pMod, Rva, piSeg, poffSeg); + return rc; +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnLinkAddressToSegOffset} */ +static DECLCALLBACK(int) rtDbgModDeferredImg_LinkAddressToSegOffset(PRTDBGMODINT pMod, RTLDRADDR LinkAddress, + PRTDBGSEGIDX piSeg, PRTLDRADDR poffSeg) +{ + Assert(((PRTDBGMODDEFERRED)pMod->pvImgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC); + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + rc = pMod->pImgVt->pfnLinkAddressToSegOffset(pMod, LinkAddress, piSeg, poffSeg); + return rc; +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnEnumSymbols} */ +static DECLCALLBACK(int) rtDbgModDeferredImg_EnumSymbols(PRTDBGMODINT pMod, uint32_t fFlags, RTLDRADDR BaseAddress, + PFNRTLDRENUMSYMS pfnCallback, void *pvUser) +{ + Assert(((PRTDBGMODDEFERRED)pMod->pvImgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC); + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + rc = pMod->pImgVt->pfnEnumSymbols(pMod, fFlags, BaseAddress, pfnCallback, pvUser); + return rc; +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnEnumSegments} */ +static DECLCALLBACK(int) rtDbgModDeferredImg_EnumSegments(PRTDBGMODINT pMod, PFNRTLDRENUMSEGS pfnCallback, void *pvUser) +{ + Assert(((PRTDBGMODDEFERRED)pMod->pvImgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC); + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + rc = pMod->pImgVt->pfnEnumSegments(pMod, pfnCallback, pvUser); + return rc; +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnEnumDbgInfo} */ +static DECLCALLBACK(int) rtDbgModDeferredImg_EnumDbgInfo(PRTDBGMODINT pMod, PFNRTLDRENUMDBG pfnCallback, void *pvUser) +{ + Assert(((PRTDBGMODDEFERRED)pMod->pvImgPriv)->u32Magic == RTDBGMODDEFERRED_MAGIC); + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + rc = pMod->pImgVt->pfnEnumDbgInfo(pMod, pfnCallback, pvUser); + return rc; +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnClose} */ +static DECLCALLBACK(int) rtDbgModDeferredImg_Close(PRTDBGMODINT pMod) +{ + rtDbgModDeferredReleaseInstanceData((PRTDBGMODDEFERRED)pMod->pvImgPriv); + return VINF_SUCCESS; +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnTryOpen} */ +static DECLCALLBACK(int) rtDbgModDeferredImg_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch, uint32_t fLdrFlags) +{ + RT_NOREF(enmArch, fLdrFlags); + return rtDbgModDeferredDoIt(pMod, true /*fForceRetry*/); +} + + +/** Virtual function table for the RTLdr based image reader. */ +DECL_HIDDEN_CONST(RTDBGMODVTIMG) const g_rtDbgModVtImgDeferred = +{ + /*.u32Magic = */ RTDBGMODVTIMG_MAGIC, + /*.fReserved = */ 0, + /*.pszName = */ "deferred", + /*.pfnTryOpen = */ rtDbgModDeferredImg_TryOpen, + /*.pfnClose = */ rtDbgModDeferredImg_Close, + /*.pfnEnumDbgInfo = */ rtDbgModDeferredImg_EnumDbgInfo, + /*.pfnEnumSegments = */ rtDbgModDeferredImg_EnumSegments, + /*.pfnEnumSymbols = */ rtDbgModDeferredImg_EnumSymbols, + /*.pfnGetLoadedSize = */ rtDbgModDeferredImg_ImageSize, + /*.pfnLinkAddressToSegOffset = */ rtDbgModDeferredImg_LinkAddressToSegOffset, + /*.pfnRvaToSegOffset = */ rtDbgModDeferredImg_RvaToSegOffset, + /*.pfnMapPart = */ rtDbgModDeferredImg_MapPart, + /*.pfnUnmapPart = */ rtDbgModDeferredImg_UnmapPart, + /*.pfnReadAt = */ rtDbgModDeferredImg_ReadAt, + /*.pfnGetFormat = */ rtDbgModDeferredImg_GetFormat, + /*.pfnGetArch = */ rtDbgModDeferredImg_GetArch, + /*.pfnQueryProp = */ rtDbgModDeferredImg_QueryProp, + /*.pfnUnwindFrame = */ rtDbgModDeferredImg_UnwindFrame, + + /*.u32EndMagic = */ RTDBGMODVTIMG_MAGIC +}; + + +/** + * Creates a deferred loading stub for both image and debug info. + * + * @returns IPRT status code. + * @param pDbgMod The debug module. + * @param pfnDeferred The callback that will try load the image and + * debug info. + * @param cbImage The size of the image. + * @param hDbgCfg The debug config handle. Can be NIL. A + * reference will be retained. + * @param cbDeferred The size of the deferred instance data, 0 if the + * default structure is good enough. + * @param fFlags RTDBGMOD_F_XXX. + * @param ppDeferred Where to return the instance data. Can be NULL. + */ +DECLHIDDEN(int) rtDbgModDeferredCreate(PRTDBGMODINT pDbgMod, PFNRTDBGMODDEFERRED pfnDeferred, RTUINTPTR cbImage, + RTDBGCFG hDbgCfg, size_t cbDeferred, uint32_t fFlags, PRTDBGMODDEFERRED *ppDeferred) +{ + AssertReturn(!pDbgMod->pDbgVt, VERR_DBG_MOD_IPE); + + if (cbDeferred < sizeof(RTDBGMODDEFERRED)) + cbDeferred = sizeof(RTDBGMODDEFERRED); + PRTDBGMODDEFERRED pDeferred = (PRTDBGMODDEFERRED)RTMemAllocZ(cbDeferred); + if (!pDeferred) + return VERR_NO_MEMORY; + + pDeferred->u32Magic = RTDBGMODDEFERRED_MAGIC; + pDeferred->cRefs = 1 + (pDbgMod->pImgVt == NULL); + pDeferred->cbImage = cbImage; + if (hDbgCfg != NIL_RTDBGCFG) + RTDbgCfgRetain(hDbgCfg); + pDeferred->hDbgCfg = hDbgCfg; + pDeferred->pfnDeferred = pfnDeferred; + pDeferred->fFlags = fFlags; + + pDbgMod->pDbgVt = &g_rtDbgModVtDbgDeferred; + pDbgMod->pvDbgPriv = pDeferred; + if (!pDbgMod->pImgVt) + { + pDbgMod->pImgVt = &g_rtDbgModVtImgDeferred; + pDbgMod->pvImgPriv = pDeferred; + } + pDbgMod->fDeferred = true; + pDbgMod->fDeferredFailed = false; + + if (ppDeferred) + *ppDeferred = pDeferred; + return VINF_SUCCESS; +} + diff --git a/src/VBox/Runtime/common/dbg/dbgmoddwarf.cpp b/src/VBox/Runtime/common/dbg/dbgmoddwarf.cpp new file mode 100644 index 00000000..48645bab --- /dev/null +++ b/src/VBox/Runtime/common/dbg/dbgmoddwarf.cpp @@ -0,0 +1,6287 @@ +/* $Id: dbgmoddwarf.cpp $ */ +/** @file + * IPRT - Debug Info Reader For DWARF. + */ + +/* + * Copyright (C) 2011-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_DBG_DWARF +#include <iprt/dbg.h> +#include "internal/iprt.h" + +#include <iprt/asm.h> +#include <iprt/ctype.h> +#include <iprt/err.h> +#include <iprt/list.h> +#include <iprt/log.h> +#include <iprt/mem.h> +#define RTDBGMODDWARF_WITH_MEM_CACHE +#ifdef RTDBGMODDWARF_WITH_MEM_CACHE +# include <iprt/memcache.h> +#endif +#include <iprt/path.h> +#include <iprt/string.h> +#include <iprt/strcache.h> +#include <iprt/x86.h> +#include <iprt/formats/dwarf.h> +#include "internal/dbgmod.h" + + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** Pointer to a DWARF section reader. */ +typedef struct RTDWARFCURSOR *PRTDWARFCURSOR; +/** Pointer to an attribute descriptor. */ +typedef struct RTDWARFATTRDESC const *PCRTDWARFATTRDESC; +/** Pointer to a DIE. */ +typedef struct RTDWARFDIE *PRTDWARFDIE; +/** Pointer to a const DIE. */ +typedef struct RTDWARFDIE const *PCRTDWARFDIE; + +/** + * DWARF sections. + */ +typedef enum krtDbgModDwarfSect +{ + krtDbgModDwarfSect_abbrev = 0, + krtDbgModDwarfSect_aranges, + krtDbgModDwarfSect_frame, + krtDbgModDwarfSect_info, + krtDbgModDwarfSect_inlined, + krtDbgModDwarfSect_line, + krtDbgModDwarfSect_loc, + krtDbgModDwarfSect_macinfo, + krtDbgModDwarfSect_pubnames, + krtDbgModDwarfSect_pubtypes, + krtDbgModDwarfSect_ranges, + krtDbgModDwarfSect_str, + krtDbgModDwarfSect_types, + /** End of valid parts (exclusive). */ + krtDbgModDwarfSect_End +} krtDbgModDwarfSect; + +/** + * Abbreviation cache entry. + */ +typedef struct RTDWARFABBREV +{ + /** Whether there are children or not. */ + bool fChildren; +#ifdef LOG_ENABLED + uint8_t cbHdr; /**< For calcing ABGOFF matching dwarfdump. */ +#endif + /** The tag. */ + uint16_t uTag; + /** Offset into the abbrev section of the specification pairs. */ + uint32_t offSpec; + /** The abbreviation table offset this is entry is valid for. + * UINT32_MAX if not valid. */ + uint32_t offAbbrev; +} RTDWARFABBREV; +/** Pointer to an abbreviation cache entry. */ +typedef RTDWARFABBREV *PRTDWARFABBREV; +/** Pointer to a const abbreviation cache entry. */ +typedef RTDWARFABBREV const *PCRTDWARFABBREV; + +/** + * Structure for gathering segment info. + */ +typedef struct RTDBGDWARFSEG +{ + /** The highest offset in the segment. */ + uint64_t offHighest; + /** Calculated base address. */ + uint64_t uBaseAddr; + /** Estimated The segment size. */ + uint64_t cbSegment; + /** Segment number (RTLDRSEG::Sel16bit). */ + RTSEL uSegment; +} RTDBGDWARFSEG; +/** Pointer to segment info. */ +typedef RTDBGDWARFSEG *PRTDBGDWARFSEG; + + +/** + * The instance data of the DWARF reader. + */ +typedef struct RTDBGMODDWARF +{ + /** The debug container containing doing the real work. */ + RTDBGMOD hCnt; + /** The image module (no reference). */ + PRTDBGMODINT pImgMod; + /** The debug info module (no reference). */ + PRTDBGMODINT pDbgInfoMod; + /** Nested image module (with reference ofc). */ + PRTDBGMODINT pNestedMod; + + /** DWARF debug info sections. */ + struct + { + /** The file offset of the part. */ + RTFOFF offFile; + /** The size of the part. */ + size_t cb; + /** The memory mapping of the part. */ + void const *pv; + /** Set if present. */ + bool fPresent; + /** The debug info ordinal number in the image file. */ + uint32_t iDbgInfo; + } aSections[krtDbgModDwarfSect_End]; + + /** The offset into the abbreviation section of the current cache. */ + uint32_t offCachedAbbrev; + /** The number of cached abbreviations we've allocated space for. */ + uint32_t cCachedAbbrevsAlloced; + /** Array of cached abbreviations, indexed by code. */ + PRTDWARFABBREV paCachedAbbrevs; + /** Used by rtDwarfAbbrev_Lookup when the result is uncachable. */ + RTDWARFABBREV LookupAbbrev; + + /** The list of compilation units (RTDWARFDIE). */ + RTLISTANCHOR CompileUnitList; + + /** Set if we have to use link addresses because the module does not have + * fixups (mach_kernel). */ + bool fUseLinkAddress; + /** This is set to -1 if we're doing everything in one pass. + * Otherwise it's 1 or 2: + * - In pass 1, we collect segment info. + * - In pass 2, we add debug info to the container. + * The two pass parsing is necessary for watcom generated symbol files as + * these contains no information about the code and data segments in the + * image. So we have to figure out some approximate stuff based on the + * segments and offsets we encounter in the debug info. */ + int8_t iWatcomPass; + /** Segment index hint. */ + uint16_t iSegHint; + /** The number of segments in paSegs. + * (During segment copying, this is abused to count useful segments.) */ + uint32_t cSegs; + /** Pointer to segments if iWatcomPass isn't -1. */ + PRTDBGDWARFSEG paSegs; +#ifdef RTDBGMODDWARF_WITH_MEM_CACHE + /** DIE allocators. */ + struct + { + RTMEMCACHE hMemCache; + uint32_t cbMax; + } aDieAllocators[2]; +#endif +} RTDBGMODDWARF; +/** Pointer to instance data of the DWARF reader. */ +typedef RTDBGMODDWARF *PRTDBGMODDWARF; + +/** + * DWARF cursor for reading byte data. + */ +typedef struct RTDWARFCURSOR +{ + /** The current position. */ + uint8_t const *pb; + /** The number of bytes left to read. */ + size_t cbLeft; + /** The number of bytes left to read in the current unit. */ + size_t cbUnitLeft; + /** The DWARF debug info reader instance. (Can be NULL for eh_frame.) */ + PRTDBGMODDWARF pDwarfMod; + /** Set if this is 64-bit DWARF, clear if 32-bit. */ + bool f64bitDwarf; + /** Set if the format endian is native, clear if endian needs to be + * inverted. */ + bool fNativEndian; + /** The size of a native address. */ + uint8_t cbNativeAddr; + /** The cursor status code. This is VINF_SUCCESS until some error + * occurs. */ + int rc; + /** The start of the area covered by the cursor. + * Used for repositioning the cursor relative to the start of a section. */ + uint8_t const *pbStart; + /** The section. */ + krtDbgModDwarfSect enmSect; +} RTDWARFCURSOR; + + +/** + * DWARF line number program state. + */ +typedef struct RTDWARFLINESTATE +{ + /** @name Virtual Line Number Machine Registers. + * @{ */ + struct + { + uint64_t uAddress; + uint64_t idxOp; + uint32_t iFile; + uint32_t uLine; + uint32_t uColumn; + bool fIsStatement; + bool fBasicBlock; + bool fEndSequence; + bool fPrologueEnd; + bool fEpilogueBegin; + uint32_t uIsa; + uint32_t uDiscriminator; + RTSEL uSegment; + } Regs; + /** @} */ + + /** Header. */ + struct + { + uint32_t uVer; + uint64_t offFirstOpcode; + uint8_t cbMinInstr; + uint8_t cMaxOpsPerInstr; + uint8_t u8DefIsStmt; + int8_t s8LineBase; + uint8_t u8LineRange; + uint8_t u8OpcodeBase; + uint8_t const *pacStdOperands; + } Hdr; + + /** @name Include Path Table (0-based) + * @{ */ + const char **papszIncPaths; + uint32_t cIncPaths; + /** @} */ + + /** @name File Name Table (0-based, dummy zero entry) + * @{ */ + char **papszFileNames; + uint32_t cFileNames; + /** @} */ + + /** The DWARF debug info reader instance. */ + PRTDBGMODDWARF pDwarfMod; +} RTDWARFLINESTATE; +/** Pointer to a DWARF line number program state. */ +typedef RTDWARFLINESTATE *PRTDWARFLINESTATE; + + +/** + * Decodes an attribute and stores it in the specified DIE member field. + * + * @returns IPRT status code. + * @param pDie Pointer to the DIE structure. + * @param pbMember Pointer to the first byte in the member. + * @param pDesc The attribute descriptor. + * @param uForm The data form. + * @param pCursor The cursor to read data from. + */ +typedef DECLCALLBACKTYPE(int, FNRTDWARFATTRDECODER,(PRTDWARFDIE pDie, uint8_t *pbMember, PCRTDWARFATTRDESC pDesc, + uint32_t uForm, PRTDWARFCURSOR pCursor)); +/** Pointer to an attribute decoder callback. */ +typedef FNRTDWARFATTRDECODER *PFNRTDWARFATTRDECODER; + +/** + * Attribute descriptor. + */ +typedef struct RTDWARFATTRDESC +{ + /** The attribute. */ + uint16_t uAttr; + /** The data member offset. */ + uint16_t off; + /** The data member size and initialization method. */ + uint8_t cbInit; + uint8_t bPadding[3]; /**< Alignment padding. */ + /** The decoder function. */ + PFNRTDWARFATTRDECODER pfnDecoder; +} RTDWARFATTRDESC; + +/** Define a attribute entry. */ +#define ATTR_ENTRY(a_uAttr, a_Struct, a_Member, a_Init, a_pfnDecoder) \ + { \ + a_uAttr, \ + (uint16_t)RT_OFFSETOF(a_Struct, a_Member), \ + a_Init | ((uint8_t)RT_SIZEOFMEMB(a_Struct, a_Member) & ATTR_SIZE_MASK), \ + { 0, 0, 0 }, \ + a_pfnDecoder\ + } + +/** @name Attribute size and init methods. + * @{ */ +#define ATTR_INIT_ZERO UINT8_C(0x00) +#define ATTR_INIT_FFFS UINT8_C(0x80) +#define ATTR_INIT_MASK UINT8_C(0x80) +#define ATTR_SIZE_MASK UINT8_C(0x3f) +#define ATTR_GET_SIZE(a_pAttrDesc) ((a_pAttrDesc)->cbInit & ATTR_SIZE_MASK) +/** @} */ + + +/** + * DIE descriptor. + */ +typedef struct RTDWARFDIEDESC +{ + /** The size of the DIE. */ + size_t cbDie; + /** The number of attributes. */ + size_t cAttributes; + /** Pointer to the array of attributes. */ + PCRTDWARFATTRDESC paAttributes; +} RTDWARFDIEDESC; +typedef struct RTDWARFDIEDESC const *PCRTDWARFDIEDESC; +/** DIE descriptor initializer. */ +#define DIE_DESC_INIT(a_Type, a_aAttrs) { sizeof(a_Type), RT_ELEMENTS(a_aAttrs), &a_aAttrs[0] } + + +/** + * DIE core structure, all inherits (starts with) this. + */ +typedef struct RTDWARFDIE +{ + /** Pointer to the parent node. NULL if root unit. */ + struct RTDWARFDIE *pParent; + /** Our node in the sibling list. */ + RTLISTNODE SiblingNode; + /** List of children. */ + RTLISTNODE ChildList; + /** The number of attributes successfully decoded. */ + uint8_t cDecodedAttrs; + /** The number of unknown or otherwise unhandled attributes. */ + uint8_t cUnhandledAttrs; +#ifdef RTDBGMODDWARF_WITH_MEM_CACHE + /** The allocator index. */ + uint8_t iAllocator; +#endif + /** The die tag, indicating which union structure to use. */ + uint16_t uTag; + /** Offset of the abbreviation specification (within debug_abbrev). */ + uint32_t offSpec; +} RTDWARFDIE; + + +/** + * DWARF address structure. + */ +typedef struct RTDWARFADDR +{ + /** The address. */ + uint64_t uAddress; +} RTDWARFADDR; +typedef RTDWARFADDR *PRTDWARFADDR; +typedef RTDWARFADDR const *PCRTDWARFADDR; + + +/** + * DWARF address range. + */ +typedef struct RTDWARFADDRRANGE +{ + uint64_t uLowAddress; + uint64_t uHighAddress; + uint8_t const *pbRanges; /* ?? */ + uint8_t cAttrs : 2; + uint8_t fHaveLowAddress : 1; + uint8_t fHaveHighAddress : 1; + uint8_t fHaveHighIsAddress : 1; + uint8_t fHaveRanges : 1; +} RTDWARFADDRRANGE; +typedef RTDWARFADDRRANGE *PRTDWARFADDRRANGE; +typedef RTDWARFADDRRANGE const *PCRTDWARFADDRRANGE; + +/** What a RTDWARFREF is relative to. */ +typedef enum krtDwarfRef +{ + krtDwarfRef_NotSet, + krtDwarfRef_LineSection, + krtDwarfRef_LocSection, + krtDwarfRef_RangesSection, + krtDwarfRef_InfoSection, + krtDwarfRef_SameUnit, + krtDwarfRef_TypeId64 +} krtDwarfRef; + +/** + * DWARF reference. + */ +typedef struct RTDWARFREF +{ + /** The offset. */ + uint64_t off; + /** What the offset is relative to. */ + krtDwarfRef enmWrt; +} RTDWARFREF; +typedef RTDWARFREF *PRTDWARFREF; +typedef RTDWARFREF const *PCRTDWARFREF; + + +/** + * DWARF Location state. + */ +typedef struct RTDWARFLOCST +{ + /** The input cursor. */ + RTDWARFCURSOR Cursor; + /** Points to the current top of the stack. Initial value -1. */ + int32_t iTop; + /** The value stack. */ + uint64_t auStack[64]; +} RTDWARFLOCST; +/** Pointer to location state. */ +typedef RTDWARFLOCST *PRTDWARFLOCST; + + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static FNRTDWARFATTRDECODER rtDwarfDecode_Address; +static FNRTDWARFATTRDECODER rtDwarfDecode_Bool; +static FNRTDWARFATTRDECODER rtDwarfDecode_LowHighPc; +static FNRTDWARFATTRDECODER rtDwarfDecode_Ranges; +static FNRTDWARFATTRDECODER rtDwarfDecode_Reference; +static FNRTDWARFATTRDECODER rtDwarfDecode_SectOff; +static FNRTDWARFATTRDECODER rtDwarfDecode_String; +static FNRTDWARFATTRDECODER rtDwarfDecode_UnsignedInt; +static FNRTDWARFATTRDECODER rtDwarfDecode_SegmentLoc; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** RTDWARFDIE description. */ +static const RTDWARFDIEDESC g_CoreDieDesc = { sizeof(RTDWARFDIE), 0, NULL }; + + +/** + * DW_TAG_compile_unit & DW_TAG_partial_unit. + */ +typedef struct RTDWARFDIECOMPILEUNIT +{ + /** The DIE core structure. */ + RTDWARFDIE Core; + /** The unit name. */ + const char *pszName; + /** The address range of the code belonging to this unit. */ + RTDWARFADDRRANGE PcRange; + /** The language name. */ + uint16_t uLanguage; + /** The identifier case. */ + uint8_t uIdentifierCase; + /** String are UTF-8 encoded. If not set, the encoding is + * unknown. */ + bool fUseUtf8; + /** The unit contains main() or equivalent. */ + bool fMainFunction; + /** The line numbers for this unit. */ + RTDWARFREF StmtListRef; + /** The macro information for this unit. */ + RTDWARFREF MacroInfoRef; + /** Reference to the base types. */ + RTDWARFREF BaseTypesRef; + /** Working directory for the unit. */ + const char *pszCurDir; + /** The name of the compiler or whatever that produced this unit. */ + const char *pszProducer; + + /** @name From the unit header. + * @{ */ + /** The offset into debug_info of this unit (for references). */ + uint64_t offUnit; + /** The length of this unit. */ + uint64_t cbUnit; + /** The offset into debug_abbrev of the abbreviation for this unit. */ + uint64_t offAbbrev; + /** The native address size. */ + uint8_t cbNativeAddr; + /** The DWARF version. */ + uint8_t uDwarfVer; + /** @} */ +} RTDWARFDIECOMPILEUNIT; +typedef RTDWARFDIECOMPILEUNIT *PRTDWARFDIECOMPILEUNIT; + + +/** RTDWARFDIECOMPILEUNIT attributes. */ +static const RTDWARFATTRDESC g_aCompileUnitAttrs[] = +{ + ATTR_ENTRY(DW_AT_name, RTDWARFDIECOMPILEUNIT, pszName, ATTR_INIT_ZERO, rtDwarfDecode_String), + ATTR_ENTRY(DW_AT_low_pc, RTDWARFDIECOMPILEUNIT, PcRange, ATTR_INIT_ZERO, rtDwarfDecode_LowHighPc), + ATTR_ENTRY(DW_AT_high_pc, RTDWARFDIECOMPILEUNIT, PcRange, ATTR_INIT_ZERO, rtDwarfDecode_LowHighPc), + ATTR_ENTRY(DW_AT_ranges, RTDWARFDIECOMPILEUNIT, PcRange, ATTR_INIT_ZERO, rtDwarfDecode_Ranges), + ATTR_ENTRY(DW_AT_language, RTDWARFDIECOMPILEUNIT, uLanguage, ATTR_INIT_ZERO, rtDwarfDecode_UnsignedInt), + ATTR_ENTRY(DW_AT_macro_info, RTDWARFDIECOMPILEUNIT, MacroInfoRef, ATTR_INIT_ZERO, rtDwarfDecode_SectOff), + ATTR_ENTRY(DW_AT_stmt_list, RTDWARFDIECOMPILEUNIT, StmtListRef, ATTR_INIT_ZERO, rtDwarfDecode_SectOff), + ATTR_ENTRY(DW_AT_comp_dir, RTDWARFDIECOMPILEUNIT, pszCurDir, ATTR_INIT_ZERO, rtDwarfDecode_String), + ATTR_ENTRY(DW_AT_producer, RTDWARFDIECOMPILEUNIT, pszProducer, ATTR_INIT_ZERO, rtDwarfDecode_String), + ATTR_ENTRY(DW_AT_identifier_case, RTDWARFDIECOMPILEUNIT, uIdentifierCase,ATTR_INIT_ZERO, rtDwarfDecode_UnsignedInt), + ATTR_ENTRY(DW_AT_base_types, RTDWARFDIECOMPILEUNIT, BaseTypesRef, ATTR_INIT_ZERO, rtDwarfDecode_Reference), + ATTR_ENTRY(DW_AT_use_UTF8, RTDWARFDIECOMPILEUNIT, fUseUtf8, ATTR_INIT_ZERO, rtDwarfDecode_Bool), + ATTR_ENTRY(DW_AT_main_subprogram, RTDWARFDIECOMPILEUNIT, fMainFunction, ATTR_INIT_ZERO, rtDwarfDecode_Bool) +}; + +/** RTDWARFDIECOMPILEUNIT description. */ +static const RTDWARFDIEDESC g_CompileUnitDesc = DIE_DESC_INIT(RTDWARFDIECOMPILEUNIT, g_aCompileUnitAttrs); + + +/** + * DW_TAG_subprogram. + */ +typedef struct RTDWARFDIESUBPROGRAM +{ + /** The DIE core structure. */ + RTDWARFDIE Core; + /** The name. */ + const char *pszName; + /** The linkage name. */ + const char *pszLinkageName; + /** The address range of the code belonging to this unit. */ + RTDWARFADDRRANGE PcRange; + /** The first instruction in the function. */ + RTDWARFADDR EntryPc; + /** Segment number (watcom). */ + RTSEL uSegment; + /** Reference to the specification. */ + RTDWARFREF SpecRef; +} RTDWARFDIESUBPROGRAM; +/** Pointer to a DW_TAG_subprogram DIE. */ +typedef RTDWARFDIESUBPROGRAM *PRTDWARFDIESUBPROGRAM; +/** Pointer to a const DW_TAG_subprogram DIE. */ +typedef RTDWARFDIESUBPROGRAM const *PCRTDWARFDIESUBPROGRAM; + + +/** RTDWARFDIESUBPROGRAM attributes. */ +static const RTDWARFATTRDESC g_aSubProgramAttrs[] = +{ + ATTR_ENTRY(DW_AT_name, RTDWARFDIESUBPROGRAM, pszName, ATTR_INIT_ZERO, rtDwarfDecode_String), + ATTR_ENTRY(DW_AT_linkage_name, RTDWARFDIESUBPROGRAM, pszLinkageName, ATTR_INIT_ZERO, rtDwarfDecode_String), + ATTR_ENTRY(DW_AT_MIPS_linkage_name, RTDWARFDIESUBPROGRAM, pszLinkageName, ATTR_INIT_ZERO, rtDwarfDecode_String), + ATTR_ENTRY(DW_AT_low_pc, RTDWARFDIESUBPROGRAM, PcRange, ATTR_INIT_ZERO, rtDwarfDecode_LowHighPc), + ATTR_ENTRY(DW_AT_high_pc, RTDWARFDIESUBPROGRAM, PcRange, ATTR_INIT_ZERO, rtDwarfDecode_LowHighPc), + ATTR_ENTRY(DW_AT_ranges, RTDWARFDIESUBPROGRAM, PcRange, ATTR_INIT_ZERO, rtDwarfDecode_Ranges), + ATTR_ENTRY(DW_AT_entry_pc, RTDWARFDIESUBPROGRAM, EntryPc, ATTR_INIT_ZERO, rtDwarfDecode_Address), + ATTR_ENTRY(DW_AT_segment, RTDWARFDIESUBPROGRAM, uSegment, ATTR_INIT_ZERO, rtDwarfDecode_SegmentLoc), + ATTR_ENTRY(DW_AT_specification, RTDWARFDIESUBPROGRAM, SpecRef, ATTR_INIT_ZERO, rtDwarfDecode_Reference) +}; + +/** RTDWARFDIESUBPROGRAM description. */ +static const RTDWARFDIEDESC g_SubProgramDesc = DIE_DESC_INIT(RTDWARFDIESUBPROGRAM, g_aSubProgramAttrs); + + +/** RTDWARFDIESUBPROGRAM attributes for the specification hack. */ +static const RTDWARFATTRDESC g_aSubProgramSpecHackAttrs[] = +{ + ATTR_ENTRY(DW_AT_name, RTDWARFDIESUBPROGRAM, pszName, ATTR_INIT_ZERO, rtDwarfDecode_String), + ATTR_ENTRY(DW_AT_linkage_name, RTDWARFDIESUBPROGRAM, pszLinkageName, ATTR_INIT_ZERO, rtDwarfDecode_String), + ATTR_ENTRY(DW_AT_MIPS_linkage_name, RTDWARFDIESUBPROGRAM, pszLinkageName, ATTR_INIT_ZERO, rtDwarfDecode_String), +}; + +/** RTDWARFDIESUBPROGRAM description for the specification hack. */ +static const RTDWARFDIEDESC g_SubProgramSpecHackDesc = DIE_DESC_INIT(RTDWARFDIESUBPROGRAM, g_aSubProgramSpecHackAttrs); + + +/** + * DW_TAG_label. + */ +typedef struct RTDWARFDIELABEL +{ + /** The DIE core structure. */ + RTDWARFDIE Core; + /** The name. */ + const char *pszName; + /** The address of the first instruction. */ + RTDWARFADDR Address; + /** Segment number (watcom). */ + RTSEL uSegment; + /** Externally visible? */ + bool fExternal; +} RTDWARFDIELABEL; +/** Pointer to a DW_TAG_label DIE. */ +typedef RTDWARFDIELABEL *PRTDWARFDIELABEL; +/** Pointer to a const DW_TAG_label DIE. */ +typedef RTDWARFDIELABEL const *PCRTDWARFDIELABEL; + + +/** RTDWARFDIESUBPROGRAM attributes. */ +static const RTDWARFATTRDESC g_aLabelAttrs[] = +{ + ATTR_ENTRY(DW_AT_name, RTDWARFDIELABEL, pszName, ATTR_INIT_ZERO, rtDwarfDecode_String), + ATTR_ENTRY(DW_AT_low_pc, RTDWARFDIELABEL, Address, ATTR_INIT_ZERO, rtDwarfDecode_Address), + ATTR_ENTRY(DW_AT_segment, RTDWARFDIELABEL, uSegment, ATTR_INIT_ZERO, rtDwarfDecode_SegmentLoc), + ATTR_ENTRY(DW_AT_external, RTDWARFDIELABEL, fExternal, ATTR_INIT_ZERO, rtDwarfDecode_Bool) +}; + +/** RTDWARFDIESUBPROGRAM description. */ +static const RTDWARFDIEDESC g_LabelDesc = DIE_DESC_INIT(RTDWARFDIELABEL, g_aLabelAttrs); + + +/** + * Tag names and descriptors. + */ +static const struct RTDWARFTAGDESC +{ + /** The tag value. */ + uint16_t uTag; + /** The tag name as string. */ + const char *pszName; + /** The DIE descriptor to use. */ + PCRTDWARFDIEDESC pDesc; +} g_aTagDescs[] = +{ +#define TAGDESC(a_Name, a_pDesc) { DW_ ## a_Name, #a_Name, a_pDesc } +#define TAGDESC_EMPTY() { 0, NULL, &g_CoreDieDesc } +#define TAGDESC_CORE(a_Name) TAGDESC(a_Name, &g_CoreDieDesc) + TAGDESC_EMPTY(), /* 0x00 */ + TAGDESC_CORE(TAG_array_type), + TAGDESC_CORE(TAG_class_type), + TAGDESC_CORE(TAG_entry_point), + TAGDESC_CORE(TAG_enumeration_type), /* 0x04 */ + TAGDESC_CORE(TAG_formal_parameter), + TAGDESC_EMPTY(), + TAGDESC_EMPTY(), + TAGDESC_CORE(TAG_imported_declaration), /* 0x08 */ + TAGDESC_EMPTY(), + TAGDESC(TAG_label, &g_LabelDesc), + TAGDESC_CORE(TAG_lexical_block), + TAGDESC_EMPTY(), /* 0x0c */ + TAGDESC_CORE(TAG_member), + TAGDESC_EMPTY(), + TAGDESC_CORE(TAG_pointer_type), + TAGDESC_CORE(TAG_reference_type), /* 0x10 */ + TAGDESC_CORE(TAG_compile_unit), + TAGDESC_CORE(TAG_string_type), + TAGDESC_CORE(TAG_structure_type), + TAGDESC_EMPTY(), /* 0x14 */ + TAGDESC_CORE(TAG_subroutine_type), + TAGDESC_CORE(TAG_typedef), + TAGDESC_CORE(TAG_union_type), + TAGDESC_CORE(TAG_unspecified_parameters), /* 0x18 */ + TAGDESC_CORE(TAG_variant), + TAGDESC_CORE(TAG_common_block), + TAGDESC_CORE(TAG_common_inclusion), + TAGDESC_CORE(TAG_inheritance), /* 0x1c */ + TAGDESC_CORE(TAG_inlined_subroutine), + TAGDESC_CORE(TAG_module), + TAGDESC_CORE(TAG_ptr_to_member_type), + TAGDESC_CORE(TAG_set_type), /* 0x20 */ + TAGDESC_CORE(TAG_subrange_type), + TAGDESC_CORE(TAG_with_stmt), + TAGDESC_CORE(TAG_access_declaration), + TAGDESC_CORE(TAG_base_type), /* 0x24 */ + TAGDESC_CORE(TAG_catch_block), + TAGDESC_CORE(TAG_const_type), + TAGDESC_CORE(TAG_constant), + TAGDESC_CORE(TAG_enumerator), /* 0x28 */ + TAGDESC_CORE(TAG_file_type), + TAGDESC_CORE(TAG_friend), + TAGDESC_CORE(TAG_namelist), + TAGDESC_CORE(TAG_namelist_item), /* 0x2c */ + TAGDESC_CORE(TAG_packed_type), + TAGDESC(TAG_subprogram, &g_SubProgramDesc), + TAGDESC_CORE(TAG_template_type_parameter), + TAGDESC_CORE(TAG_template_value_parameter), /* 0x30 */ + TAGDESC_CORE(TAG_thrown_type), + TAGDESC_CORE(TAG_try_block), + TAGDESC_CORE(TAG_variant_part), + TAGDESC_CORE(TAG_variable), /* 0x34 */ + TAGDESC_CORE(TAG_volatile_type), + TAGDESC_CORE(TAG_dwarf_procedure), + TAGDESC_CORE(TAG_restrict_type), + TAGDESC_CORE(TAG_interface_type), /* 0x38 */ + TAGDESC_CORE(TAG_namespace), + TAGDESC_CORE(TAG_imported_module), + TAGDESC_CORE(TAG_unspecified_type), + TAGDESC_CORE(TAG_partial_unit), /* 0x3c */ + TAGDESC_CORE(TAG_imported_unit), + TAGDESC_EMPTY(), + TAGDESC_CORE(TAG_condition), + TAGDESC_CORE(TAG_shared_type), /* 0x40 */ + TAGDESC_CORE(TAG_type_unit), + TAGDESC_CORE(TAG_rvalue_reference_type), + TAGDESC_CORE(TAG_template_alias) +#undef TAGDESC +#undef TAGDESC_EMPTY +#undef TAGDESC_CORE +}; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static int rtDwarfInfo_ParseDie(PRTDBGMODDWARF pThis, PRTDWARFDIE pDie, PCRTDWARFDIEDESC pDieDesc, + PRTDWARFCURSOR pCursor, PCRTDWARFABBREV pAbbrev, bool fInitDie); + + + +#if defined(LOG_ENABLED) || defined(RT_STRICT) + +# if 0 /* unused */ +/** + * Turns a tag value into a string for logging purposes. + * + * @returns String name. + * @param uTag The tag. + */ +static const char *rtDwarfLog_GetTagName(uint32_t uTag) +{ + if (uTag < RT_ELEMENTS(g_aTagDescs)) + { + const char *pszTag = g_aTagDescs[uTag].pszName; + if (pszTag) + return pszTag; + } + + static char s_szStatic[32]; + RTStrPrintf(s_szStatic, sizeof(s_szStatic),"DW_TAG_%#x", uTag); + return s_szStatic; +} +# endif + + +/** + * Turns an attributevalue into a string for logging purposes. + * + * @returns String name. + * @param uAttr The attribute. + */ +static const char *rtDwarfLog_AttrName(uint32_t uAttr) +{ + switch (uAttr) + { + RT_CASE_RET_STR(DW_AT_sibling); + RT_CASE_RET_STR(DW_AT_location); + RT_CASE_RET_STR(DW_AT_name); + RT_CASE_RET_STR(DW_AT_ordering); + RT_CASE_RET_STR(DW_AT_byte_size); + RT_CASE_RET_STR(DW_AT_bit_offset); + RT_CASE_RET_STR(DW_AT_bit_size); + RT_CASE_RET_STR(DW_AT_stmt_list); + RT_CASE_RET_STR(DW_AT_low_pc); + RT_CASE_RET_STR(DW_AT_high_pc); + RT_CASE_RET_STR(DW_AT_language); + RT_CASE_RET_STR(DW_AT_discr); + RT_CASE_RET_STR(DW_AT_discr_value); + RT_CASE_RET_STR(DW_AT_visibility); + RT_CASE_RET_STR(DW_AT_import); + RT_CASE_RET_STR(DW_AT_string_length); + RT_CASE_RET_STR(DW_AT_common_reference); + RT_CASE_RET_STR(DW_AT_comp_dir); + RT_CASE_RET_STR(DW_AT_const_value); + RT_CASE_RET_STR(DW_AT_containing_type); + RT_CASE_RET_STR(DW_AT_default_value); + RT_CASE_RET_STR(DW_AT_inline); + RT_CASE_RET_STR(DW_AT_is_optional); + RT_CASE_RET_STR(DW_AT_lower_bound); + RT_CASE_RET_STR(DW_AT_producer); + RT_CASE_RET_STR(DW_AT_prototyped); + RT_CASE_RET_STR(DW_AT_return_addr); + RT_CASE_RET_STR(DW_AT_start_scope); + RT_CASE_RET_STR(DW_AT_bit_stride); + RT_CASE_RET_STR(DW_AT_upper_bound); + RT_CASE_RET_STR(DW_AT_abstract_origin); + RT_CASE_RET_STR(DW_AT_accessibility); + RT_CASE_RET_STR(DW_AT_address_class); + RT_CASE_RET_STR(DW_AT_artificial); + RT_CASE_RET_STR(DW_AT_base_types); + RT_CASE_RET_STR(DW_AT_calling_convention); + RT_CASE_RET_STR(DW_AT_count); + RT_CASE_RET_STR(DW_AT_data_member_location); + RT_CASE_RET_STR(DW_AT_decl_column); + RT_CASE_RET_STR(DW_AT_decl_file); + RT_CASE_RET_STR(DW_AT_decl_line); + RT_CASE_RET_STR(DW_AT_declaration); + RT_CASE_RET_STR(DW_AT_discr_list); + RT_CASE_RET_STR(DW_AT_encoding); + RT_CASE_RET_STR(DW_AT_external); + RT_CASE_RET_STR(DW_AT_frame_base); + RT_CASE_RET_STR(DW_AT_friend); + RT_CASE_RET_STR(DW_AT_identifier_case); + RT_CASE_RET_STR(DW_AT_macro_info); + RT_CASE_RET_STR(DW_AT_namelist_item); + RT_CASE_RET_STR(DW_AT_priority); + RT_CASE_RET_STR(DW_AT_segment); + RT_CASE_RET_STR(DW_AT_specification); + RT_CASE_RET_STR(DW_AT_static_link); + RT_CASE_RET_STR(DW_AT_type); + RT_CASE_RET_STR(DW_AT_use_location); + RT_CASE_RET_STR(DW_AT_variable_parameter); + RT_CASE_RET_STR(DW_AT_virtuality); + RT_CASE_RET_STR(DW_AT_vtable_elem_location); + RT_CASE_RET_STR(DW_AT_allocated); + RT_CASE_RET_STR(DW_AT_associated); + RT_CASE_RET_STR(DW_AT_data_location); + RT_CASE_RET_STR(DW_AT_byte_stride); + RT_CASE_RET_STR(DW_AT_entry_pc); + RT_CASE_RET_STR(DW_AT_use_UTF8); + RT_CASE_RET_STR(DW_AT_extension); + RT_CASE_RET_STR(DW_AT_ranges); + RT_CASE_RET_STR(DW_AT_trampoline); + RT_CASE_RET_STR(DW_AT_call_column); + RT_CASE_RET_STR(DW_AT_call_file); + RT_CASE_RET_STR(DW_AT_call_line); + RT_CASE_RET_STR(DW_AT_description); + RT_CASE_RET_STR(DW_AT_binary_scale); + RT_CASE_RET_STR(DW_AT_decimal_scale); + RT_CASE_RET_STR(DW_AT_small); + RT_CASE_RET_STR(DW_AT_decimal_sign); + RT_CASE_RET_STR(DW_AT_digit_count); + RT_CASE_RET_STR(DW_AT_picture_string); + RT_CASE_RET_STR(DW_AT_mutable); + RT_CASE_RET_STR(DW_AT_threads_scaled); + RT_CASE_RET_STR(DW_AT_explicit); + RT_CASE_RET_STR(DW_AT_object_pointer); + RT_CASE_RET_STR(DW_AT_endianity); + RT_CASE_RET_STR(DW_AT_elemental); + RT_CASE_RET_STR(DW_AT_pure); + RT_CASE_RET_STR(DW_AT_recursive); + RT_CASE_RET_STR(DW_AT_signature); + RT_CASE_RET_STR(DW_AT_main_subprogram); + RT_CASE_RET_STR(DW_AT_data_bit_offset); + RT_CASE_RET_STR(DW_AT_const_expr); + RT_CASE_RET_STR(DW_AT_enum_class); + RT_CASE_RET_STR(DW_AT_linkage_name); + RT_CASE_RET_STR(DW_AT_MIPS_linkage_name); + RT_CASE_RET_STR(DW_AT_WATCOM_memory_model); + RT_CASE_RET_STR(DW_AT_WATCOM_references_start); + RT_CASE_RET_STR(DW_AT_WATCOM_parm_entry); + } + static char s_szStatic[32]; + RTStrPrintf(s_szStatic, sizeof(s_szStatic),"DW_AT_%#x", uAttr); + return s_szStatic; +} + + +/** + * Turns a form value into a string for logging purposes. + * + * @returns String name. + * @param uForm The form. + */ +static const char *rtDwarfLog_FormName(uint32_t uForm) +{ + switch (uForm) + { + RT_CASE_RET_STR(DW_FORM_addr); + RT_CASE_RET_STR(DW_FORM_block2); + RT_CASE_RET_STR(DW_FORM_block4); + RT_CASE_RET_STR(DW_FORM_data2); + RT_CASE_RET_STR(DW_FORM_data4); + RT_CASE_RET_STR(DW_FORM_data8); + RT_CASE_RET_STR(DW_FORM_string); + RT_CASE_RET_STR(DW_FORM_block); + RT_CASE_RET_STR(DW_FORM_block1); + RT_CASE_RET_STR(DW_FORM_data1); + RT_CASE_RET_STR(DW_FORM_flag); + RT_CASE_RET_STR(DW_FORM_sdata); + RT_CASE_RET_STR(DW_FORM_strp); + RT_CASE_RET_STR(DW_FORM_udata); + RT_CASE_RET_STR(DW_FORM_ref_addr); + RT_CASE_RET_STR(DW_FORM_ref1); + RT_CASE_RET_STR(DW_FORM_ref2); + RT_CASE_RET_STR(DW_FORM_ref4); + RT_CASE_RET_STR(DW_FORM_ref8); + RT_CASE_RET_STR(DW_FORM_ref_udata); + RT_CASE_RET_STR(DW_FORM_indirect); + RT_CASE_RET_STR(DW_FORM_sec_offset); + RT_CASE_RET_STR(DW_FORM_exprloc); + RT_CASE_RET_STR(DW_FORM_flag_present); + RT_CASE_RET_STR(DW_FORM_ref_sig8); + } + static char s_szStatic[32]; + RTStrPrintf(s_szStatic, sizeof(s_szStatic),"DW_FORM_%#x", uForm); + return s_szStatic; +} + +#endif /* LOG_ENABLED || RT_STRICT */ + + + +/** @callback_method_impl{FNRTLDRENUMSEGS} */ +static DECLCALLBACK(int) rtDbgModDwarfScanSegmentsCallback(RTLDRMOD hLdrMod, PCRTLDRSEG pSeg, void *pvUser) +{ + PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pvUser; + Log(("Segment %.*s: LinkAddress=%#llx RVA=%#llx cb=%#llx\n", + pSeg->cchName, pSeg->pszName, (uint64_t)pSeg->LinkAddress, (uint64_t)pSeg->RVA, pSeg->cb)); + NOREF(hLdrMod); + + /* Count relevant segments. */ + if (pSeg->RVA != NIL_RTLDRADDR) + pThis->cSegs++; + + return VINF_SUCCESS; +} + + +/** @callback_method_impl{FNRTLDRENUMSEGS} */ +static DECLCALLBACK(int) rtDbgModDwarfAddSegmentsCallback(RTLDRMOD hLdrMod, PCRTLDRSEG pSeg, void *pvUser) +{ + PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pvUser; + Log(("Segment %.*s: LinkAddress=%#llx RVA=%#llx cb=%#llx cbMapped=%#llx\n", + pSeg->cchName, pSeg->pszName, (uint64_t)pSeg->LinkAddress, (uint64_t)pSeg->RVA, pSeg->cb, pSeg->cbMapped)); + NOREF(hLdrMod); + Assert(pSeg->cchName > 0); + Assert(!pSeg->pszName[pSeg->cchName]); + + /* If the segment doesn't have a mapping, just add a dummy so the indexing + works out correctly (same as for the image). */ + if (pSeg->RVA == NIL_RTLDRADDR) + return RTDbgModSegmentAdd(pThis->hCnt, 0, 0, pSeg->pszName, 0 /*fFlags*/, NULL); + + /* The link address is 0 for all segments in a relocatable ELF image. */ + RTLDRADDR cb = pSeg->cb; + if ( cb < pSeg->cbMapped + && RTLdrGetFormat(hLdrMod) != RTLDRFMT_LX /* for debugging our drivers; 64KB section align by linker, 4KB by loader. */ + ) + cb = pSeg->cbMapped; + return RTDbgModSegmentAdd(pThis->hCnt, pSeg->RVA, cb, pSeg->pszName, 0 /*fFlags*/, NULL); +} + + +/** + * Calls rtDbgModDwarfAddSegmentsCallback for each segment in the executable + * image. + * + * @returns IPRT status code. + * @param pThis The DWARF instance. + */ +static int rtDbgModDwarfAddSegmentsFromImage(PRTDBGMODDWARF pThis) +{ + AssertReturn(pThis->pImgMod && pThis->pImgMod->pImgVt, VERR_INTERNAL_ERROR_2); + Assert(!pThis->cSegs); + int rc = pThis->pImgMod->pImgVt->pfnEnumSegments(pThis->pImgMod, rtDbgModDwarfScanSegmentsCallback, pThis); + if (RT_SUCCESS(rc)) + { + if (pThis->cSegs == 0) + pThis->iWatcomPass = 1; + else + { + pThis->cSegs = 0; + pThis->iWatcomPass = -1; + rc = pThis->pImgMod->pImgVt->pfnEnumSegments(pThis->pImgMod, rtDbgModDwarfAddSegmentsCallback, pThis); + } + } + + return rc; +} + + +/** + * Looks up a segment. + * + * @returns Pointer to the segment on success, NULL if not found. + * @param pThis The DWARF instance. + * @param uSeg The segment number / selector. + */ +static PRTDBGDWARFSEG rtDbgModDwarfFindSegment(PRTDBGMODDWARF pThis, RTSEL uSeg) +{ + uint32_t cSegs = pThis->cSegs; + uint32_t iSeg = pThis->iSegHint; + PRTDBGDWARFSEG paSegs = pThis->paSegs; + if ( iSeg < cSegs + && paSegs[iSeg].uSegment == uSeg) + return &paSegs[iSeg]; + + for (iSeg = 0; iSeg < cSegs; iSeg++) + if (uSeg == paSegs[iSeg].uSegment) + { + pThis->iSegHint = iSeg; + return &paSegs[iSeg]; + } + + AssertFailed(); + return NULL; +} + + +/** + * Record a segment:offset during pass 1. + * + * @returns IPRT status code. + * @param pThis The DWARF instance. + * @param uSeg The segment number / selector. + * @param offSeg The segment offset. + */ +static int rtDbgModDwarfRecordSegOffset(PRTDBGMODDWARF pThis, RTSEL uSeg, uint64_t offSeg) +{ + /* Look up the segment. */ + uint32_t cSegs = pThis->cSegs; + uint32_t iSeg = pThis->iSegHint; + PRTDBGDWARFSEG paSegs = pThis->paSegs; + if ( iSeg >= cSegs + || paSegs[iSeg].uSegment != uSeg) + { + for (iSeg = 0; iSeg < cSegs; iSeg++) + if (uSeg <= paSegs[iSeg].uSegment) + break; + if ( iSeg >= cSegs + || paSegs[iSeg].uSegment != uSeg) + { + /* Add */ + void *pvNew = RTMemRealloc(paSegs, (pThis->cSegs + 1) * sizeof(paSegs[0])); + if (!pvNew) + return VERR_NO_MEMORY; + pThis->paSegs = paSegs = (PRTDBGDWARFSEG)pvNew; + if (iSeg != cSegs) + memmove(&paSegs[iSeg + 1], &paSegs[iSeg], (cSegs - iSeg) * sizeof(paSegs[0])); + paSegs[iSeg].offHighest = offSeg; + paSegs[iSeg].uBaseAddr = 0; + paSegs[iSeg].cbSegment = 0; + paSegs[iSeg].uSegment = uSeg; + pThis->cSegs++; + } + + pThis->iSegHint = iSeg; + } + + /* Increase it's range? */ + if (paSegs[iSeg].offHighest < offSeg) + { + Log3(("rtDbgModDwarfRecordSegOffset: iSeg=%d uSeg=%#06x offSeg=%#llx\n", iSeg, uSeg, offSeg)); + paSegs[iSeg].offHighest = offSeg; + } + + return VINF_SUCCESS; +} + + +/** + * Calls pfnSegmentAdd for each segment in the executable image. + * + * @returns IPRT status code. + * @param pThis The DWARF instance. + */ +static int rtDbgModDwarfAddSegmentsFromPass1(PRTDBGMODDWARF pThis) +{ + AssertReturn(pThis->cSegs, VERR_DWARF_BAD_INFO); + uint32_t const cSegs = pThis->cSegs; + PRTDBGDWARFSEG paSegs = pThis->paSegs; + + /* + * Are the segments assigned more or less in numerical order? + */ + if ( paSegs[0].uSegment < 16U + && paSegs[cSegs - 1].uSegment - paSegs[0].uSegment + 1U <= cSegs + 16U) + { + /** @todo heuristics, plase. */ + AssertFailedReturn(VERR_DWARF_TODO); + + } + /* + * Assume DOS segmentation. + */ + else + { + for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++) + paSegs[iSeg].uBaseAddr = (uint32_t)paSegs[iSeg].uSegment << 16; + for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++) + paSegs[iSeg].cbSegment = paSegs[iSeg].offHighest; + } + + /* + * Add them. + */ + for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++) + { + Log3(("rtDbgModDwarfAddSegmentsFromPass1: Seg#%u: %#010llx LB %#llx uSegment=%#x\n", + iSeg, paSegs[iSeg].uBaseAddr, paSegs[iSeg].cbSegment, paSegs[iSeg].uSegment)); + char szName[32]; + RTStrPrintf(szName, sizeof(szName), "seg-%#04xh", paSegs[iSeg].uSegment); + int rc = RTDbgModSegmentAdd(pThis->hCnt, paSegs[iSeg].uBaseAddr, paSegs[iSeg].cbSegment, + szName, 0 /*fFlags*/, NULL); + if (RT_FAILURE(rc)) + return rc; + } + + return VINF_SUCCESS; +} + + +/** + * Loads a DWARF section from the image file. + * + * @returns IPRT status code. + * @param pThis The DWARF instance. + * @param enmSect The section to load. + */ +static int rtDbgModDwarfLoadSection(PRTDBGMODDWARF pThis, krtDbgModDwarfSect enmSect) +{ + /* + * Don't load stuff twice. + */ + if (pThis->aSections[enmSect].pv) + return VINF_SUCCESS; + + /* + * Sections that are not present cannot be loaded, treat them like they + * are empty + */ + if (!pThis->aSections[enmSect].fPresent) + { + Assert(pThis->aSections[enmSect].cb); + return VINF_SUCCESS; + } + if (!pThis->aSections[enmSect].cb) + return VINF_SUCCESS; + + /* + * Sections must be readable with the current image interface. + */ + if (pThis->aSections[enmSect].offFile < 0) + return VERR_OUT_OF_RANGE; + + /* + * Do the job. + */ + return pThis->pDbgInfoMod->pImgVt->pfnMapPart(pThis->pDbgInfoMod, + pThis->aSections[enmSect].iDbgInfo, + pThis->aSections[enmSect].offFile, + pThis->aSections[enmSect].cb, + &pThis->aSections[enmSect].pv); +} + + +#ifdef SOME_UNUSED_FUNCTION +/** + * Unloads a DWARF section previously mapped by rtDbgModDwarfLoadSection. + * + * @returns IPRT status code. + * @param pThis The DWARF instance. + * @param enmSect The section to unload. + */ +static int rtDbgModDwarfUnloadSection(PRTDBGMODDWARF pThis, krtDbgModDwarfSect enmSect) +{ + if (!pThis->aSections[enmSect].pv) + return VINF_SUCCESS; + + int rc = pThis->pDbgInfoMod->pImgVt->pfnUnmapPart(pThis->pDbgInfoMod, pThis->aSections[enmSect].cb, &pThis->aSections[enmSect].pv); + AssertRC(rc); + return rc; +} +#endif + + +/** + * Converts to UTF-8 or otherwise makes sure it's valid UTF-8. + * + * @returns IPRT status code. + * @param pThis The DWARF instance. + * @param ppsz Pointer to the string pointer. May be + * reallocated (RTStr*). + */ +static int rtDbgModDwarfStringToUtf8(PRTDBGMODDWARF pThis, char **ppsz) +{ + /** @todo DWARF & UTF-8. */ + NOREF(pThis); + RTStrPurgeEncoding(*ppsz); + return VINF_SUCCESS; +} + + +/** + * Convers a link address into a segment+offset or RVA. + * + * @returns IPRT status code. + * @param pThis The DWARF instance. + * @param uSegment The segment, 0 if not applicable. + * @param LinkAddress The address to convert.. + * @param piSeg The segment index. + * @param poffSeg Where to return the segment offset. + */ +static int rtDbgModDwarfLinkAddressToSegOffset(PRTDBGMODDWARF pThis, RTSEL uSegment, uint64_t LinkAddress, + PRTDBGSEGIDX piSeg, PRTLDRADDR poffSeg) +{ + if (pThis->paSegs) + { + PRTDBGDWARFSEG pSeg = rtDbgModDwarfFindSegment(pThis, uSegment); + if (pSeg) + { + *piSeg = pSeg - pThis->paSegs; + *poffSeg = LinkAddress; + return VINF_SUCCESS; + } + } + + if (pThis->fUseLinkAddress) + return pThis->pImgMod->pImgVt->pfnLinkAddressToSegOffset(pThis->pImgMod, LinkAddress, piSeg, poffSeg); + + /* If we have a non-zero segment number, assume it's correct for now. + This helps loading watcom linked LX drivers. */ + if (uSegment > 0) + { + *piSeg = uSegment - 1; + *poffSeg = LinkAddress; + return VINF_SUCCESS; + } + + return pThis->pImgMod->pImgVt->pfnRvaToSegOffset(pThis->pImgMod, LinkAddress, piSeg, poffSeg); +} + + +/** + * Converts a segment+offset address into an RVA. + * + * @returns IPRT status code. + * @param pThis The DWARF instance. + * @param idxSegment The segment index. + * @param offSegment The segment offset. + * @param puRva Where to return the calculated RVA. + */ +static int rtDbgModDwarfSegOffsetToRva(PRTDBGMODDWARF pThis, RTDBGSEGIDX idxSegment, uint64_t offSegment, PRTUINTPTR puRva) +{ + if (pThis->paSegs) + { + PRTDBGDWARFSEG pSeg = rtDbgModDwarfFindSegment(pThis, idxSegment); + if (pSeg) + { + *puRva = pSeg->uBaseAddr + offSegment; + return VINF_SUCCESS; + } + } + + RTUINTPTR uRva = RTDbgModSegmentRva(pThis->pImgMod, idxSegment); + if (uRva != RTUINTPTR_MAX) + { + *puRva = uRva + offSegment; + return VINF_SUCCESS; + } + return VERR_INVALID_POINTER; +} + +/** + * Converts a segment+offset address into an RVA. + * + * @returns IPRT status code. + * @param pThis The DWARF instance. + * @param uRva The RVA to convert. + * @param pidxSegment Where to return the segment index. + * @param poffSegment Where to return the segment offset. + */ +static int rtDbgModDwarfRvaToSegOffset(PRTDBGMODDWARF pThis, RTUINTPTR uRva, RTDBGSEGIDX *pidxSegment, uint64_t *poffSegment) +{ + RTUINTPTR offSeg = 0; + RTDBGSEGIDX idxSeg = RTDbgModRvaToSegOff(pThis->pImgMod, uRva, &offSeg); + if (idxSeg != NIL_RTDBGSEGIDX) + { + *pidxSegment = idxSeg; + *poffSegment = offSeg; + return VINF_SUCCESS; + } + return VERR_INVALID_POINTER; +} + + + +/* + * + * DWARF Cursor. + * DWARF Cursor. + * DWARF Cursor. + * + */ + + +/** + * Reads a 8-bit unsigned integer and advances the cursor. + * + * @returns 8-bit unsigned integer. On error RTDWARFCURSOR::rc is set and @a + * uErrValue is returned. + * @param pCursor The cursor. + * @param uErrValue What to return on read error. + */ +static uint8_t rtDwarfCursor_GetU8(PRTDWARFCURSOR pCursor, uint8_t uErrValue) +{ + if (pCursor->cbUnitLeft < 1) + { + pCursor->rc = VERR_DWARF_UNEXPECTED_END; + return uErrValue; + } + + uint8_t u8 = pCursor->pb[0]; + pCursor->pb += 1; + pCursor->cbUnitLeft -= 1; + pCursor->cbLeft -= 1; + return u8; +} + + +/** + * Reads a 16-bit unsigned integer and advances the cursor. + * + * @returns 16-bit unsigned integer. On error RTDWARFCURSOR::rc is set and @a + * uErrValue is returned. + * @param pCursor The cursor. + * @param uErrValue What to return on read error. + */ +static uint16_t rtDwarfCursor_GetU16(PRTDWARFCURSOR pCursor, uint16_t uErrValue) +{ + if (pCursor->cbUnitLeft < 2) + { + pCursor->pb += pCursor->cbUnitLeft; + pCursor->cbLeft -= pCursor->cbUnitLeft; + pCursor->cbUnitLeft = 0; + pCursor->rc = VERR_DWARF_UNEXPECTED_END; + return uErrValue; + } + + uint16_t u16 = RT_MAKE_U16(pCursor->pb[0], pCursor->pb[1]); + pCursor->pb += 2; + pCursor->cbUnitLeft -= 2; + pCursor->cbLeft -= 2; + if (!pCursor->fNativEndian) + u16 = RT_BSWAP_U16(u16); + return u16; +} + + +/** + * Reads a 32-bit unsigned integer and advances the cursor. + * + * @returns 32-bit unsigned integer. On error RTDWARFCURSOR::rc is set and @a + * uErrValue is returned. + * @param pCursor The cursor. + * @param uErrValue What to return on read error. + */ +static uint32_t rtDwarfCursor_GetU32(PRTDWARFCURSOR pCursor, uint32_t uErrValue) +{ + if (pCursor->cbUnitLeft < 4) + { + pCursor->pb += pCursor->cbUnitLeft; + pCursor->cbLeft -= pCursor->cbUnitLeft; + pCursor->cbUnitLeft = 0; + pCursor->rc = VERR_DWARF_UNEXPECTED_END; + return uErrValue; + } + + uint32_t u32 = RT_MAKE_U32_FROM_U8(pCursor->pb[0], pCursor->pb[1], pCursor->pb[2], pCursor->pb[3]); + pCursor->pb += 4; + pCursor->cbUnitLeft -= 4; + pCursor->cbLeft -= 4; + if (!pCursor->fNativEndian) + u32 = RT_BSWAP_U32(u32); + return u32; +} + + +/** + * Reads a 64-bit unsigned integer and advances the cursor. + * + * @returns 64-bit unsigned integer. On error RTDWARFCURSOR::rc is set and @a + * uErrValue is returned. + * @param pCursor The cursor. + * @param uErrValue What to return on read error. + */ +static uint64_t rtDwarfCursor_GetU64(PRTDWARFCURSOR pCursor, uint64_t uErrValue) +{ + if (pCursor->cbUnitLeft < 8) + { + pCursor->pb += pCursor->cbUnitLeft; + pCursor->cbLeft -= pCursor->cbUnitLeft; + pCursor->cbUnitLeft = 0; + pCursor->rc = VERR_DWARF_UNEXPECTED_END; + return uErrValue; + } + + uint64_t u64 = RT_MAKE_U64_FROM_U8(pCursor->pb[0], pCursor->pb[1], pCursor->pb[2], pCursor->pb[3], + pCursor->pb[4], pCursor->pb[5], pCursor->pb[6], pCursor->pb[7]); + pCursor->pb += 8; + pCursor->cbUnitLeft -= 8; + pCursor->cbLeft -= 8; + if (!pCursor->fNativEndian) + u64 = RT_BSWAP_U64(u64); + return u64; +} + + +/** + * Reads an unsigned LEB128 encoded number. + * + * @returns unsigned 64-bit number. On error RTDWARFCURSOR::rc is set and @a + * uErrValue is returned. + * @param pCursor The cursor. + * @param uErrValue The value to return on error. + */ +static uint64_t rtDwarfCursor_GetULeb128(PRTDWARFCURSOR pCursor, uint64_t uErrValue) +{ + if (pCursor->cbUnitLeft < 1) + { + pCursor->rc = VERR_DWARF_UNEXPECTED_END; + return uErrValue; + } + + /* + * Special case - single byte. + */ + uint8_t b = pCursor->pb[0]; + if (!(b & 0x80)) + { + pCursor->pb += 1; + pCursor->cbUnitLeft -= 1; + pCursor->cbLeft -= 1; + return b; + } + + /* + * Generic case. + */ + /* Decode. */ + uint32_t off = 1; + uint64_t u64Ret = b & 0x7f; + do + { + if (off == pCursor->cbUnitLeft) + { + pCursor->rc = VERR_DWARF_UNEXPECTED_END; + u64Ret = uErrValue; + break; + } + b = pCursor->pb[off]; + u64Ret |= (b & 0x7f) << off * 7; + off++; + } while (b & 0x80); + + /* Update the cursor. */ + pCursor->pb += off; + pCursor->cbUnitLeft -= off; + pCursor->cbLeft -= off; + + /* Check the range. */ + uint32_t cBits = off * 7; + if (cBits > 64) + { + pCursor->rc = VERR_DWARF_LEB_OVERFLOW; + u64Ret = uErrValue; + } + + return u64Ret; +} + + +/** + * Reads a signed LEB128 encoded number. + * + * @returns signed 64-bit number. On error RTDWARFCURSOR::rc is set and @a + * uErrValue is returned. + * @param pCursor The cursor. + * @param sErrValue The value to return on error. + */ +static int64_t rtDwarfCursor_GetSLeb128(PRTDWARFCURSOR pCursor, int64_t sErrValue) +{ + if (pCursor->cbUnitLeft < 1) + { + pCursor->rc = VERR_DWARF_UNEXPECTED_END; + return sErrValue; + } + + /* + * Special case - single byte. + */ + uint8_t b = pCursor->pb[0]; + if (!(b & 0x80)) + { + pCursor->pb += 1; + pCursor->cbUnitLeft -= 1; + pCursor->cbLeft -= 1; + if (b & 0x40) + b |= 0x80; + return (int8_t)b; + } + + /* + * Generic case. + */ + /* Decode it. */ + uint32_t off = 1; + uint64_t u64Ret = b & 0x7f; + do + { + if (off == pCursor->cbUnitLeft) + { + pCursor->rc = VERR_DWARF_UNEXPECTED_END; + u64Ret = (uint64_t)sErrValue; + break; + } + b = pCursor->pb[off]; + u64Ret |= (b & 0x7f) << off * 7; + off++; + } while (b & 0x80); + + /* Update cursor. */ + pCursor->pb += off; + pCursor->cbUnitLeft -= off; + pCursor->cbLeft -= off; + + /* Check the range. */ + uint32_t cBits = off * 7; + if (cBits > 64) + { + pCursor->rc = VERR_DWARF_LEB_OVERFLOW; + u64Ret = (uint64_t)sErrValue; + } + /* Sign extend the value. */ + else if (u64Ret & RT_BIT_64(cBits - 1)) + u64Ret |= ~(RT_BIT_64(cBits - 1) - 1); + + return (int64_t)u64Ret; +} + + +/** + * Reads an unsigned LEB128 encoded number, max 32-bit width. + * + * @returns unsigned 32-bit number. On error RTDWARFCURSOR::rc is set and @a + * uErrValue is returned. + * @param pCursor The cursor. + * @param uErrValue The value to return on error. + */ +static uint32_t rtDwarfCursor_GetULeb128AsU32(PRTDWARFCURSOR pCursor, uint32_t uErrValue) +{ + uint64_t u64 = rtDwarfCursor_GetULeb128(pCursor, uErrValue); + if (u64 > UINT32_MAX) + { + pCursor->rc = VERR_DWARF_LEB_OVERFLOW; + return uErrValue; + } + return (uint32_t)u64; +} + + +/** + * Reads a signed LEB128 encoded number, max 32-bit width. + * + * @returns signed 32-bit number. On error RTDWARFCURSOR::rc is set and @a + * uErrValue is returned. + * @param pCursor The cursor. + * @param sErrValue The value to return on error. + */ +static int32_t rtDwarfCursor_GetSLeb128AsS32(PRTDWARFCURSOR pCursor, int32_t sErrValue) +{ + int64_t s64 = rtDwarfCursor_GetSLeb128(pCursor, sErrValue); + if (s64 > INT32_MAX || s64 < INT32_MIN) + { + pCursor->rc = VERR_DWARF_LEB_OVERFLOW; + return sErrValue; + } + return (int32_t)s64; +} + + +/** + * Skips a LEB128 encoded number. + * + * @returns IPRT status code. + * @param pCursor The cursor. + */ +static int rtDwarfCursor_SkipLeb128(PRTDWARFCURSOR pCursor) +{ + if (RT_FAILURE(pCursor->rc)) + return pCursor->rc; + + if (pCursor->cbUnitLeft < 1) + return pCursor->rc = VERR_DWARF_UNEXPECTED_END; + + uint32_t offSkip = 1; + if (pCursor->pb[0] & 0x80) + do + { + if (offSkip == pCursor->cbUnitLeft) + { + pCursor->rc = VERR_DWARF_UNEXPECTED_END; + break; + } + } while (pCursor->pb[offSkip++] & 0x80); + + pCursor->pb += offSkip; + pCursor->cbUnitLeft -= offSkip; + pCursor->cbLeft -= offSkip; + return pCursor->rc; +} + + +/** + * Advances the cursor a given number of bytes. + * + * @returns IPRT status code. + * @param pCursor The cursor. + * @param offSkip The number of bytes to advance. + */ +static int rtDwarfCursor_SkipBytes(PRTDWARFCURSOR pCursor, uint64_t offSkip) +{ + if (RT_FAILURE(pCursor->rc)) + return pCursor->rc; + if (pCursor->cbUnitLeft < offSkip) + return pCursor->rc = VERR_DWARF_UNEXPECTED_END; + + size_t const offSkipSizeT = (size_t)offSkip; + pCursor->cbUnitLeft -= offSkipSizeT; + pCursor->cbLeft -= offSkipSizeT; + pCursor->pb += offSkipSizeT; + + return VINF_SUCCESS; +} + + +/** + * Reads a zero terminated string, advancing the cursor beyond the terminator. + * + * @returns Pointer to the string. + * @param pCursor The cursor. + * @param pszErrValue What to return if the string isn't terminated + * before the end of the unit. + */ +static const char *rtDwarfCursor_GetSZ(PRTDWARFCURSOR pCursor, const char *pszErrValue) +{ + const char *pszRet = (const char *)pCursor->pb; + for (;;) + { + if (!pCursor->cbUnitLeft) + { + pCursor->rc = VERR_DWARF_BAD_STRING; + return pszErrValue; + } + pCursor->cbUnitLeft--; + pCursor->cbLeft--; + if (!*pCursor->pb++) + break; + } + return pszRet; +} + + +/** + * Reads a 1, 2, 4 or 8 byte unsigned value. + * + * @returns 64-bit unsigned value. + * @param pCursor The cursor. + * @param cbValue The value size. + * @param uErrValue The error value. + */ +static uint64_t rtDwarfCursor_GetVarSizedU(PRTDWARFCURSOR pCursor, size_t cbValue, uint64_t uErrValue) +{ + uint64_t u64Ret; + switch (cbValue) + { + case 1: u64Ret = rtDwarfCursor_GetU8( pCursor, UINT8_MAX); break; + case 2: u64Ret = rtDwarfCursor_GetU16(pCursor, UINT16_MAX); break; + case 4: u64Ret = rtDwarfCursor_GetU32(pCursor, UINT32_MAX); break; + case 8: u64Ret = rtDwarfCursor_GetU64(pCursor, UINT64_MAX); break; + default: + pCursor->rc = VERR_DWARF_BAD_INFO; + return uErrValue; + } + if (RT_FAILURE(pCursor->rc)) + return uErrValue; + return u64Ret; +} + + +#if 0 /* unused */ +/** + * Gets the pointer to a variable size block and advances the cursor. + * + * @returns Pointer to the block at the current cursor location. On error + * RTDWARFCURSOR::rc is set and NULL returned. + * @param pCursor The cursor. + * @param cbBlock The block size. + */ +static const uint8_t *rtDwarfCursor_GetBlock(PRTDWARFCURSOR pCursor, uint32_t cbBlock) +{ + if (cbBlock > pCursor->cbUnitLeft) + { + pCursor->rc = VERR_DWARF_UNEXPECTED_END; + return NULL; + } + + uint8_t const *pb = &pCursor->pb[0]; + pCursor->pb += cbBlock; + pCursor->cbUnitLeft -= cbBlock; + pCursor->cbLeft -= cbBlock; + return pb; +} +#endif + + +/** + * Reads an unsigned DWARF half number. + * + * @returns The number. On error RTDWARFCURSOR::rc is set and @a + * uErrValue is returned. + * @param pCursor The cursor. + * @param uErrValue What to return on error. + */ +static uint16_t rtDwarfCursor_GetUHalf(PRTDWARFCURSOR pCursor, uint16_t uErrValue) +{ + return rtDwarfCursor_GetU16(pCursor, uErrValue); +} + + +/** + * Reads an unsigned DWARF byte number. + * + * @returns The number. On error RTDWARFCURSOR::rc is set and @a + * uErrValue is returned. + * @param pCursor The cursor. + * @param uErrValue What to return on error. + */ +static uint8_t rtDwarfCursor_GetUByte(PRTDWARFCURSOR pCursor, uint8_t uErrValue) +{ + return rtDwarfCursor_GetU8(pCursor, uErrValue); +} + + +/** + * Reads a signed DWARF byte number. + * + * @returns The number. On error RTDWARFCURSOR::rc is set and @a + * uErrValue is returned. + * @param pCursor The cursor. + * @param iErrValue What to return on error. + */ +static int8_t rtDwarfCursor_GetSByte(PRTDWARFCURSOR pCursor, int8_t iErrValue) +{ + return (int8_t)rtDwarfCursor_GetU8(pCursor, (uint8_t)iErrValue); +} + + +/** + * Reads a unsigned DWARF offset value. + * + * @returns The value. On error RTDWARFCURSOR::rc is set and @a + * uErrValue is returned. + * @param pCursor The cursor. + * @param uErrValue What to return on error. + */ +static uint64_t rtDwarfCursor_GetUOff(PRTDWARFCURSOR pCursor, uint64_t uErrValue) +{ + if (pCursor->f64bitDwarf) + return rtDwarfCursor_GetU64(pCursor, uErrValue); + return rtDwarfCursor_GetU32(pCursor, (uint32_t)uErrValue); +} + + +/** + * Reads a unsigned DWARF native offset value. + * + * @returns The value. On error RTDWARFCURSOR::rc is set and @a + * uErrValue is returned. + * @param pCursor The cursor. + * @param uErrValue What to return on error. + */ +static uint64_t rtDwarfCursor_GetNativeUOff(PRTDWARFCURSOR pCursor, uint64_t uErrValue) +{ + switch (pCursor->cbNativeAddr) + { + case 1: return rtDwarfCursor_GetU8(pCursor, (uint8_t )uErrValue); + case 2: return rtDwarfCursor_GetU16(pCursor, (uint16_t)uErrValue); + case 4: return rtDwarfCursor_GetU32(pCursor, (uint32_t)uErrValue); + case 8: return rtDwarfCursor_GetU64(pCursor, uErrValue); + default: + pCursor->rc = VERR_INTERNAL_ERROR_2; + return uErrValue; + } +} + + +/** + * Reads a 1, 2, 4 or 8 byte unsigned value. + * + * @returns 64-bit unsigned value. + * @param pCursor The cursor. + * @param bPtrEnc The pointer encoding. + * @param uErrValue The error value. + */ +static uint64_t rtDwarfCursor_GetPtrEnc(PRTDWARFCURSOR pCursor, uint8_t bPtrEnc, uint64_t uErrValue) +{ + uint64_t u64Ret; + switch (bPtrEnc & DW_EH_PE_FORMAT_MASK) + { + case DW_EH_PE_ptr: + u64Ret = rtDwarfCursor_GetNativeUOff(pCursor, uErrValue); + break; + case DW_EH_PE_uleb128: + u64Ret = rtDwarfCursor_GetULeb128(pCursor, uErrValue); + break; + case DW_EH_PE_udata2: + u64Ret = rtDwarfCursor_GetU16(pCursor, UINT16_MAX); + break; + case DW_EH_PE_udata4: + u64Ret = rtDwarfCursor_GetU32(pCursor, UINT32_MAX); + break; + case DW_EH_PE_udata8: + u64Ret = rtDwarfCursor_GetU64(pCursor, UINT64_MAX); + break; + case DW_EH_PE_sleb128: + u64Ret = rtDwarfCursor_GetSLeb128(pCursor, uErrValue); + break; + case DW_EH_PE_sdata2: + u64Ret = (int64_t)(int16_t)rtDwarfCursor_GetU16(pCursor, UINT16_MAX); + break; + case DW_EH_PE_sdata4: + u64Ret = (int64_t)(int32_t)rtDwarfCursor_GetU32(pCursor, UINT32_MAX); + break; + case DW_EH_PE_sdata8: + u64Ret = rtDwarfCursor_GetU64(pCursor, UINT64_MAX); + break; + default: + pCursor->rc = VERR_DWARF_BAD_INFO; + return uErrValue; + } + if (RT_FAILURE(pCursor->rc)) + return uErrValue; + return u64Ret; +} + + +/** + * Gets the unit length, updating the unit length member and DWARF bitness + * members of the cursor. + * + * @returns The unit length. + * @param pCursor The cursor. + */ +static uint64_t rtDwarfCursor_GetInitialLength(PRTDWARFCURSOR pCursor) +{ + /* + * Read the initial length. + */ + pCursor->cbUnitLeft = pCursor->cbLeft; + uint64_t cbUnit = rtDwarfCursor_GetU32(pCursor, 0); + if (cbUnit != UINT32_C(0xffffffff)) + pCursor->f64bitDwarf = false; + else + { + pCursor->f64bitDwarf = true; + cbUnit = rtDwarfCursor_GetU64(pCursor, 0); + } + + + /* + * Set the unit length, quitely fixing bad lengths. + */ + pCursor->cbUnitLeft = (size_t)cbUnit; + if ( pCursor->cbUnitLeft > pCursor->cbLeft + || pCursor->cbUnitLeft != cbUnit) + pCursor->cbUnitLeft = pCursor->cbLeft; + + return cbUnit; +} + + +/** + * Calculates the section offset corresponding to the current cursor position. + * + * @returns 32-bit section offset. If out of range, RTDWARFCURSOR::rc will be + * set and UINT32_MAX returned. + * @param pCursor The cursor. + */ +static uint32_t rtDwarfCursor_CalcSectOffsetU32(PRTDWARFCURSOR pCursor) +{ + size_t off = pCursor->pb - pCursor->pbStart; + uint32_t offRet = (uint32_t)off; + if (offRet != off) + { + AssertFailed(); + pCursor->rc = VERR_OUT_OF_RANGE; + offRet = UINT32_MAX; + } + return offRet; +} + + +/** + * Calculates an absolute cursor position from one relative to the current + * cursor position. + * + * @returns The absolute cursor position. + * @param pCursor The cursor. + * @param offRelative The relative position. Must be a positive + * offset. + */ +static uint8_t const *rtDwarfCursor_CalcPos(PRTDWARFCURSOR pCursor, size_t offRelative) +{ + if (offRelative > pCursor->cbUnitLeft) + { + Log(("rtDwarfCursor_CalcPos: bad position %#zx, cbUnitLeft=%#zu\n", offRelative, pCursor->cbUnitLeft)); + pCursor->rc = VERR_DWARF_BAD_POS; + return NULL; + } + return pCursor->pb + offRelative; +} + + +/** + * Advances the cursor to the given position. + * + * @returns IPRT status code. + * @param pCursor The cursor. + * @param pbNewPos The new position - returned by + * rtDwarfCursor_CalcPos(). + */ +static int rtDwarfCursor_AdvanceToPos(PRTDWARFCURSOR pCursor, uint8_t const *pbNewPos) +{ + if (RT_FAILURE(pCursor->rc)) + return pCursor->rc; + AssertPtr(pbNewPos); + if ((uintptr_t)pbNewPos < (uintptr_t)pCursor->pb) + { + Log(("rtDwarfCursor_AdvanceToPos: bad position %p, current %p\n", pbNewPos, pCursor->pb)); + return pCursor->rc = VERR_DWARF_BAD_POS; + } + + uintptr_t cbAdj = (uintptr_t)pbNewPos - (uintptr_t)pCursor->pb; + if (RT_UNLIKELY(cbAdj > pCursor->cbUnitLeft)) + { + AssertFailed(); + pCursor->rc = VERR_DWARF_BAD_POS; + cbAdj = pCursor->cbUnitLeft; + } + + pCursor->cbUnitLeft -= cbAdj; + pCursor->cbLeft -= cbAdj; + pCursor->pb += cbAdj; + return pCursor->rc; +} + + +/** + * Check if the cursor is at the end of the current DWARF unit. + * + * @retval true if at the end or a cursor error is pending. + * @retval false if not. + * @param pCursor The cursor. + */ +static bool rtDwarfCursor_IsAtEndOfUnit(PRTDWARFCURSOR pCursor) +{ + return !pCursor->cbUnitLeft || RT_FAILURE(pCursor->rc); +} + + +/** + * Skips to the end of the current unit. + * + * @returns IPRT status code. + * @param pCursor The cursor. + */ +static int rtDwarfCursor_SkipUnit(PRTDWARFCURSOR pCursor) +{ + pCursor->pb += pCursor->cbUnitLeft; + pCursor->cbLeft -= pCursor->cbUnitLeft; + pCursor->cbUnitLeft = 0; + return pCursor->rc; +} + + +/** + * Check if the cursor is at the end of the section (or whatever the cursor is + * processing). + * + * @retval true if at the end or a cursor error is pending. + * @retval false if not. + * @param pCursor The cursor. + */ +static bool rtDwarfCursor_IsAtEnd(PRTDWARFCURSOR pCursor) +{ + return !pCursor->cbLeft || RT_FAILURE(pCursor->rc); +} + + +/** + * Initialize a section reader cursor. + * + * @returns IPRT status code. + * @param pCursor The cursor. + * @param pThis The dwarf module. + * @param enmSect The name of the section to read. + */ +static int rtDwarfCursor_Init(PRTDWARFCURSOR pCursor, PRTDBGMODDWARF pThis, krtDbgModDwarfSect enmSect) +{ + int rc = rtDbgModDwarfLoadSection(pThis, enmSect); + if (RT_FAILURE(rc)) + return rc; + + pCursor->enmSect = enmSect; + pCursor->pbStart = (uint8_t const *)pThis->aSections[enmSect].pv; + pCursor->pb = pCursor->pbStart; + pCursor->cbLeft = pThis->aSections[enmSect].cb; + pCursor->cbUnitLeft = pCursor->cbLeft; + pCursor->pDwarfMod = pThis; + pCursor->f64bitDwarf = false; + /** @todo ask the image about the endian used as well as the address + * width. */ + pCursor->fNativEndian = true; + pCursor->cbNativeAddr = 4; + pCursor->rc = VINF_SUCCESS; + + return VINF_SUCCESS; +} + + +/** + * Initialize a section reader cursor with a skip offset. + * + * @returns IPRT status code. + * @param pCursor The cursor. + * @param pThis The dwarf module. + * @param enmSect The name of the section to read. + * @param offSect The offset to skip into the section. + */ +static int rtDwarfCursor_InitWithOffset(PRTDWARFCURSOR pCursor, PRTDBGMODDWARF pThis, + krtDbgModDwarfSect enmSect, uint32_t offSect) +{ + if (offSect > pThis->aSections[enmSect].cb) + { + Log(("rtDwarfCursor_InitWithOffset: offSect=%#x cb=%#x enmSect=%d\n", offSect, pThis->aSections[enmSect].cb, enmSect)); + return VERR_DWARF_BAD_POS; + } + + int rc = rtDwarfCursor_Init(pCursor, pThis, enmSect); + if (RT_SUCCESS(rc)) + { + /* pCursor->pbStart += offSect; - we're skipping, offsets are relative to start of section... */ + pCursor->pb += offSect; + pCursor->cbLeft -= offSect; + pCursor->cbUnitLeft -= offSect; + } + + return rc; +} + + +/** + * Initialize a cursor for a block (subsection) retrieved from the given cursor. + * + * The parent cursor will be advanced past the block. + * + * @returns IPRT status code. + * @param pCursor The cursor. + * @param pParent The parent cursor. Will be moved by @a cbBlock. + * @param cbBlock The size of the block the new cursor should + * cover. + */ +static int rtDwarfCursor_InitForBlock(PRTDWARFCURSOR pCursor, PRTDWARFCURSOR pParent, uint32_t cbBlock) +{ + if (RT_FAILURE(pParent->rc)) + return pParent->rc; + if (pParent->cbUnitLeft < cbBlock) + { + Log(("rtDwarfCursor_InitForBlock: cbUnitLeft=%#x < cbBlock=%#x \n", pParent->cbUnitLeft, cbBlock)); + return VERR_DWARF_BAD_POS; + } + + *pCursor = *pParent; + pCursor->cbLeft = cbBlock; + pCursor->cbUnitLeft = cbBlock; + + pParent->pb += cbBlock; + pParent->cbLeft -= cbBlock; + pParent->cbUnitLeft -= cbBlock; + + return VINF_SUCCESS; +} + + +/** + * Initialize a reader cursor for a memory block (eh_frame). + * + * @returns IPRT status code. + * @param pCursor The cursor. + * @param pvMem The memory block. + * @param cbMem The size of the memory block. + */ +static int rtDwarfCursor_InitForMem(PRTDWARFCURSOR pCursor, void const *pvMem, size_t cbMem) +{ + pCursor->enmSect = krtDbgModDwarfSect_End; + pCursor->pbStart = (uint8_t const *)pvMem; + pCursor->pb = (uint8_t const *)pvMem; + pCursor->cbLeft = cbMem; + pCursor->cbUnitLeft = cbMem; + pCursor->pDwarfMod = NULL; + pCursor->f64bitDwarf = false; + /** @todo ask the image about the endian used as well as the address + * width. */ + pCursor->fNativEndian = true; + pCursor->cbNativeAddr = 4; + pCursor->rc = VINF_SUCCESS; + + return VINF_SUCCESS; +} + + +/** + * Deletes a section reader initialized by rtDwarfCursor_Init. + * + * @returns @a rcOther or RTDWARCURSOR::rc. + * @param pCursor The section reader. + * @param rcOther Other error code to be returned if it indicates + * error or if the cursor status is OK. + */ +static int rtDwarfCursor_Delete(PRTDWARFCURSOR pCursor, int rcOther) +{ + /* ... and a drop of poison. */ + pCursor->pb = NULL; + pCursor->cbLeft = ~(size_t)0; + pCursor->cbUnitLeft = ~(size_t)0; + pCursor->pDwarfMod = NULL; + if (RT_FAILURE(pCursor->rc) && RT_SUCCESS(rcOther)) + rcOther = pCursor->rc; + pCursor->rc = VERR_INTERNAL_ERROR_4; + return rcOther; +} + + +/* + * + * DWARF Frame Unwind Information. + * DWARF Frame Unwind Information. + * DWARF Frame Unwind Information. + * + */ + +/** + * Common information entry (CIE) information. + */ +typedef struct RTDWARFCIEINFO +{ + /** The segment location of the CIE. */ + uint64_t offCie; + /** The DWARF version. */ + uint8_t uDwarfVer; + /** The address pointer encoding. */ + uint8_t bAddressPtrEnc; + /** The segment size (v4). */ + uint8_t cbSegment; + /** The return register column. UINT8_MAX if default register. */ + uint8_t bRetReg; + /** The LSDA pointer encoding. */ + uint8_t bLsdaPtrEnc; + + /** Set if the EH data field is present ('eh'). */ + bool fHasEhData : 1; + /** Set if there is an augmentation data size ('z'). */ + bool fHasAugmentationSize : 1; + /** Set if the augmentation data contains a LSDA (pointer size byte in CIE, + * pointer in FDA) ('L'). */ + bool fHasLanguageSpecificDataArea : 1; + /** Set if the augmentation data contains a personality routine + * (pointer size + pointer) ('P'). */ + bool fHasPersonalityRoutine : 1; + /** Set if the augmentation data contains the address encoding . */ + bool fHasAddressEnc : 1; + /** Set if signal frame. */ + bool fIsSignalFrame : 1; + /** Set if we've encountered unknown augmentation data. This + * means the CIE is incomplete and cannot be used. */ + bool fHasUnknowAugmentation : 1; + + /** Copy of the augmentation string. */ + const char *pszAugmentation; + + /** Code alignment factor for the instruction. */ + uint64_t uCodeAlignFactor; + /** Data alignment factor for the instructions. */ + int64_t iDataAlignFactor; + + /** Pointer to the instruction sequence. */ + uint8_t const *pbInstructions; + /** The length of the instruction sequence. */ + size_t cbInstructions; +} RTDWARFCIEINFO; +/** Pointer to CIE info. */ +typedef RTDWARFCIEINFO *PRTDWARFCIEINFO; +/** Pointer to const CIE info. */ +typedef RTDWARFCIEINFO const *PCRTDWARFCIEINFO; + + +/** Number of registers we care about. + * @note We're currently not expecting to be decoding ppc, arm, ia64 or such, + * only x86 and x86_64. We can easily increase the column count. */ +#define RTDWARFCF_MAX_REGISTERS 96 + + +/** + * Call frame state row. + */ +typedef struct RTDWARFCFROW +{ + /** Stack worked by DW_CFA_remember_state and DW_CFA_restore_state. */ + struct RTDWARFCFROW *pNextOnStack; + + /** @name CFA - Canonical frame address expression. + * Since there are partial CFA instructions, we cannot be lazy like with the + * register but keep register+offset around. For DW_CFA_def_cfa_expression + * we just take down the program location, though. + * @{ */ + /** Pointer to DW_CFA_def_cfa_expression instruction, NULL if reg+offset. */ + uint8_t const *pbCfaExprInstr; + /** The CFA register offset. */ + int64_t offCfaReg; + /** The CFA base register number. */ + uint16_t uCfaBaseReg; + /** Set if we've got a valid CFA definition. */ + bool fCfaDefined : 1; + /** @} */ + + /** Set if on the heap and needs freeing. */ + bool fOnHeap : 1; + /** Pointer to the instructions bytes defining registers. + * NULL means */ + uint8_t const *apbRegInstrs[RTDWARFCF_MAX_REGISTERS]; +} RTDWARFCFROW; +typedef RTDWARFCFROW *PRTDWARFCFROW; +typedef RTDWARFCFROW const *PCRTDWARFCFROW; + +/** Row program execution state. */ +typedef struct RTDWARFCFEXEC +{ + PRTDWARFCFROW pRow; + /** Number of PC bytes left to advance before we get a hit. */ + uint64_t cbLeftToAdvance; + /** Number of pushed rows. */ + uint32_t cPushes; + /** Set if little endian, clear if big endian. */ + bool fLittleEndian; + /** The CIE. */ + PCRTDWARFCIEINFO pCie; + /** The program counter value for the FDE. Subjected to segment. + * Needed for DW_CFA_set_loc. */ + uint64_t uPcBegin; + /** The offset relative to uPcBegin for which we're searching for a row. + * Needed for DW_CFA_set_loc. */ + uint64_t offInRange; +} RTDWARFCFEXEC; +typedef RTDWARFCFEXEC *PRTDWARFCFEXEC; + + +/* Set of macros for getting and skipping operands. */ +#define SKIP_ULEB128_OR_LEB128() \ + do \ + { \ + AssertReturn(offInstr < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO); \ + } while (pbInstr[offInstr++] & 0x80) + +#define GET_ULEB128_AS_U14(a_uDst) \ + do \ + { \ + AssertReturn(offInstr < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO); \ + uint8_t b = pbInstr[offInstr++]; \ + (a_uDst) = b & 0x7f; \ + if (b & 0x80) \ + { \ + AssertReturn(offInstr < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO); \ + b = pbInstr[offInstr++]; \ + AssertReturn(!(b & 0x80), VERR_DBG_MALFORMED_UNWIND_INFO); \ + (a_uDst) |= (uint16_t)b << 7; \ + } \ + } while (0) +#define GET_ULEB128_AS_U63(a_uDst) \ + do \ + { \ + AssertReturn(offInstr < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO); \ + uint8_t b = pbInstr[offInstr++]; \ + (a_uDst) = b & 0x7f; \ + if (b & 0x80) \ + { \ + unsigned cShift = 7; \ + do \ + { \ + AssertReturn(offInstr < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO); \ + AssertReturn(cShift < 63, VERR_DWARF_LEB_OVERFLOW); \ + b = pbInstr[offInstr++]; \ + (a_uDst) |= (uint16_t)(b & 0x7f) << cShift; \ + cShift += 7; \ + } while (b & 0x80); \ + } \ + } while (0) +#define GET_LEB128_AS_I63(a_uDst) \ + do \ + { \ + AssertReturn(offInstr < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO); \ + uint8_t b = pbInstr[offInstr++]; \ + if (!(b & 0x80)) \ + (a_uDst) = !(b & 0x40) ? b : (int64_t)(int8_t)(b | 0x80); \ + else \ + { \ + /* Read value into unsigned variable: */ \ + unsigned cShift = 7; \ + uint64_t uTmp = b & 0x7f; \ + do \ + { \ + AssertReturn(offInstr < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO); \ + AssertReturn(cShift < 63, VERR_DWARF_LEB_OVERFLOW); \ + b = pbInstr[offInstr++]; \ + uTmp |= (uint16_t)(b & 0x7f) << cShift; \ + cShift += 7; \ + } while (b & 0x80); \ + /* Sign extend before setting the destination value: */ \ + cShift -= 7 + 1; \ + if (uTmp & RT_BIT_64(cShift)) \ + uTmp |= ~(RT_BIT_64(cShift) - 1); \ + (a_uDst) = (int64_t)uTmp; \ + } \ + } while (0) + +#define SKIP_BLOCK() \ + do \ + { \ + uint16_t cbBlock; \ + GET_ULEB128_AS_U14(cbBlock); \ + AssertReturn(offInstr + cbBlock <= cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO); \ + offInstr += cbBlock; \ + } while (0) + + +static int rtDwarfUnwind_Execute(PRTDWARFCFEXEC pExecState, uint8_t const *pbInstr, uint32_t cbInstr) +{ + PRTDWARFCFROW pRow = pExecState->pRow; + for (uint32_t offInstr = 0; offInstr < cbInstr;) + { + /* + * Instruction switches. + */ + uint8_t const bInstr = pbInstr[offInstr++]; + switch (bInstr & DW_CFA_high_bit_mask) + { + case DW_CFA_advance_loc: + { + uint8_t const cbAdvance = bInstr & ~DW_CFA_high_bit_mask; + if (cbAdvance > pExecState->cbLeftToAdvance) + return VINF_SUCCESS; + pExecState->cbLeftToAdvance -= cbAdvance; + break; + } + + case DW_CFA_offset: + { + uint8_t iReg = bInstr & ~DW_CFA_high_bit_mask; + if (iReg < RT_ELEMENTS(pRow->apbRegInstrs)) + pRow->apbRegInstrs[iReg] = &pbInstr[offInstr - 1]; + SKIP_ULEB128_OR_LEB128(); + break; + } + + case 0: + switch (bInstr) + { + case DW_CFA_nop: + break; + + /* + * Register instructions. + */ + case DW_CFA_register: + case DW_CFA_offset_extended: + case DW_CFA_offset_extended_sf: + case DW_CFA_val_offset: + case DW_CFA_val_offset_sf: + { + uint8_t const * const pbCurInstr = &pbInstr[offInstr - 1]; + uint16_t iReg; + GET_ULEB128_AS_U14(iReg); + if (iReg < RT_ELEMENTS(pRow->apbRegInstrs)) + pRow->apbRegInstrs[iReg] = pbCurInstr; + SKIP_ULEB128_OR_LEB128(); + break; + } + + case DW_CFA_expression: + case DW_CFA_val_expression: + { + uint8_t const * const pbCurInstr = &pbInstr[offInstr - 1]; + uint16_t iReg; + GET_ULEB128_AS_U14(iReg); + if (iReg < RT_ELEMENTS(pRow->apbRegInstrs)) + pRow->apbRegInstrs[iReg] = pbCurInstr; + SKIP_BLOCK(); + break; + } + + case DW_CFA_restore_extended: + { + uint8_t const * const pbCurInstr = &pbInstr[offInstr - 1]; + uint16_t iReg; + GET_ULEB128_AS_U14(iReg); + if (iReg < RT_ELEMENTS(pRow->apbRegInstrs)) + pRow->apbRegInstrs[iReg] = pbCurInstr; + break; + } + + case DW_CFA_undefined: + { + uint16_t iReg; + GET_ULEB128_AS_U14(iReg); + if (iReg < RT_ELEMENTS(pRow->apbRegInstrs)) + pRow->apbRegInstrs[iReg] = NULL; + break; + } + + case DW_CFA_same_value: + { + uint8_t const * const pbCurInstr = &pbInstr[offInstr - 1]; + uint16_t iReg; + GET_ULEB128_AS_U14(iReg); + if (iReg < RT_ELEMENTS(pRow->apbRegInstrs)) + pRow->apbRegInstrs[iReg] = pbCurInstr; + break; + } + + + /* + * CFA instructions. + */ + case DW_CFA_def_cfa: + { + GET_ULEB128_AS_U14(pRow->uCfaBaseReg); + uint64_t offCfaReg; + GET_ULEB128_AS_U63(offCfaReg); + pRow->offCfaReg = offCfaReg; + pRow->pbCfaExprInstr = NULL; + pRow->fCfaDefined = true; + break; + } + + case DW_CFA_def_cfa_register: + { + GET_ULEB128_AS_U14(pRow->uCfaBaseReg); + pRow->pbCfaExprInstr = NULL; + pRow->fCfaDefined = true; + /* Leaves offCfaReg as is. */ + break; + } + + case DW_CFA_def_cfa_offset: + { + uint64_t offCfaReg; + GET_ULEB128_AS_U63(offCfaReg); + pRow->offCfaReg = offCfaReg; + pRow->pbCfaExprInstr = NULL; + pRow->fCfaDefined = true; + /* Leaves uCfaBaseReg as is. */ + break; + } + + case DW_CFA_def_cfa_sf: + GET_ULEB128_AS_U14(pRow->uCfaBaseReg); + GET_LEB128_AS_I63(pRow->offCfaReg); + pRow->pbCfaExprInstr = NULL; + pRow->fCfaDefined = true; + break; + + case DW_CFA_def_cfa_offset_sf: + GET_LEB128_AS_I63(pRow->offCfaReg); + pRow->pbCfaExprInstr = NULL; + pRow->fCfaDefined = true; + /* Leaves uCfaBaseReg as is. */ + break; + + case DW_CFA_def_cfa_expression: + pRow->pbCfaExprInstr = &pbInstr[offInstr - 1]; + pRow->fCfaDefined = true; + SKIP_BLOCK(); + break; + + /* + * Less likely instructions: + */ + case DW_CFA_advance_loc1: + { + AssertReturn(offInstr < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO); + uint8_t const cbAdvance = pbInstr[offInstr++]; + if (cbAdvance > pExecState->cbLeftToAdvance) + return VINF_SUCCESS; + pExecState->cbLeftToAdvance -= cbAdvance; + break; + } + + case DW_CFA_advance_loc2: + { + AssertReturn(offInstr + 1 < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO); + uint16_t const cbAdvance = pExecState->fLittleEndian + ? RT_MAKE_U16(pbInstr[offInstr], pbInstr[offInstr + 1]) + : RT_MAKE_U16(pbInstr[offInstr + 1], pbInstr[offInstr]); + if (cbAdvance > pExecState->cbLeftToAdvance) + return VINF_SUCCESS; + pExecState->cbLeftToAdvance -= cbAdvance; + offInstr += 2; + break; + } + + case DW_CFA_advance_loc4: + { + AssertReturn(offInstr + 3 < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO); + uint32_t const cbAdvance = pExecState->fLittleEndian + ? RT_MAKE_U32_FROM_U8(pbInstr[offInstr + 0], pbInstr[offInstr + 1], + pbInstr[offInstr + 2], pbInstr[offInstr + 3]) + : RT_MAKE_U32_FROM_U8(pbInstr[offInstr + 3], pbInstr[offInstr + 2], + pbInstr[offInstr + 1], pbInstr[offInstr + 0]); + if (cbAdvance > pExecState->cbLeftToAdvance) + return VINF_SUCCESS; + pExecState->cbLeftToAdvance -= cbAdvance; + offInstr += 4; + break; + } + + /* + * This bugger is really annoying and probably never used. + */ + case DW_CFA_set_loc: + { + /* Ignore the segment number. */ + if (pExecState->pCie->cbSegment) + { + offInstr += pExecState->pCie->cbSegment; + AssertReturn(offInstr < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO); + } + + /* Retrieve the address. sigh. */ + uint64_t uAddress; + switch (pExecState->pCie->bAddressPtrEnc & (DW_EH_PE_FORMAT_MASK | DW_EH_PE_indirect)) + { + case DW_EH_PE_udata2: + AssertReturn(offInstr + 1 < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO); + if (pExecState->fLittleEndian) + uAddress = RT_MAKE_U16(pbInstr[offInstr], pbInstr[offInstr + 1]); + else + uAddress = RT_MAKE_U16(pbInstr[offInstr + 1], pbInstr[offInstr]); + offInstr += 2; + break; + case DW_EH_PE_sdata2: + AssertReturn(offInstr + 1 < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO); + if (pExecState->fLittleEndian) + uAddress = (int64_t)(int16_t)RT_MAKE_U16(pbInstr[offInstr], pbInstr[offInstr + 1]); + else + uAddress = (int64_t)(int16_t)RT_MAKE_U16(pbInstr[offInstr + 1], pbInstr[offInstr]); + offInstr += 2; + break; + case DW_EH_PE_udata4: + AssertReturn(offInstr + 3 < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO); + if (pExecState->fLittleEndian) + uAddress = RT_MAKE_U32_FROM_U8(pbInstr[offInstr + 0], pbInstr[offInstr + 1], + pbInstr[offInstr + 2], pbInstr[offInstr + 3]); + else + uAddress = RT_MAKE_U32_FROM_U8(pbInstr[offInstr + 3], pbInstr[offInstr + 2], + pbInstr[offInstr + 1], pbInstr[offInstr + 0]); + + offInstr += 4; + break; + case DW_EH_PE_sdata4: + AssertReturn(offInstr + 3 < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO); + if (pExecState->fLittleEndian) + uAddress = (int64_t)(int32_t)RT_MAKE_U32_FROM_U8(pbInstr[offInstr + 0], pbInstr[offInstr + 1], + pbInstr[offInstr + 2], pbInstr[offInstr + 3]); + else + uAddress = (int64_t)(int32_t)RT_MAKE_U32_FROM_U8(pbInstr[offInstr + 3], pbInstr[offInstr + 2], + pbInstr[offInstr + 1], pbInstr[offInstr + 0]); + offInstr += 4; + break; + case DW_EH_PE_udata8: + case DW_EH_PE_sdata8: + AssertReturn(offInstr + 7 < cbInstr, VERR_DBG_MALFORMED_UNWIND_INFO); + if (pExecState->fLittleEndian) + uAddress = RT_MAKE_U64_FROM_U8(pbInstr[offInstr + 0], pbInstr[offInstr + 1], + pbInstr[offInstr + 2], pbInstr[offInstr + 3], + pbInstr[offInstr + 4], pbInstr[offInstr + 5], + pbInstr[offInstr + 6], pbInstr[offInstr + 7]); + else + uAddress = RT_MAKE_U64_FROM_U8(pbInstr[offInstr + 7], pbInstr[offInstr + 6], + pbInstr[offInstr + 5], pbInstr[offInstr + 4], + pbInstr[offInstr + 3], pbInstr[offInstr + 2], + pbInstr[offInstr + 1], pbInstr[offInstr + 0]); + offInstr += 8; + break; + case DW_EH_PE_sleb128: + case DW_EH_PE_uleb128: + default: + AssertMsgFailedReturn(("%#x\n", pExecState->pCie->bAddressPtrEnc), VERR_DWARF_TODO); + } + AssertReturn(uAddress >= pExecState->uPcBegin, VERR_DBG_MALFORMED_UNWIND_INFO); + + /* Did we advance past the desire address already? */ + if (uAddress > pExecState->uPcBegin + pExecState->offInRange) + return VINF_SUCCESS; + pExecState->cbLeftToAdvance = pExecState->uPcBegin + pExecState->offInRange - uAddress; + break; + + + /* + * Row state push/pop instructions. + */ + + case DW_CFA_remember_state: + { + AssertReturn(pExecState->cPushes < 10, VERR_DBG_MALFORMED_UNWIND_INFO); + PRTDWARFCFROW pNewRow = (PRTDWARFCFROW)RTMemTmpAlloc(sizeof(*pNewRow)); + AssertReturn(pNewRow, VERR_NO_TMP_MEMORY); + memcpy(pNewRow, pRow, sizeof(*pNewRow)); + pNewRow->pNextOnStack = pRow; + pNewRow->fOnHeap = true; + pExecState->pRow = pNewRow; + pExecState->cPushes += 1; + pRow = pNewRow; + break; + } + + case DW_CFA_restore_state: + AssertReturn(pRow->pNextOnStack, VERR_DBG_MALFORMED_UNWIND_INFO); + Assert(pRow->fOnHeap); + Assert(pExecState->cPushes > 0); + pExecState->cPushes -= 1; + pExecState->pRow = pRow->pNextOnStack; + RTMemTmpFree(pRow); + pRow = pExecState->pRow; + break; + } + } + break; + + case DW_CFA_restore: + { + uint8_t const * const pbCurInstr = &pbInstr[offInstr - 1]; + uint8_t const iReg = bInstr & ~DW_CFA_high_bit_mask; + if (iReg < RT_ELEMENTS(pRow->apbRegInstrs)) + pRow->apbRegInstrs[iReg] = pbCurInstr; + break; + } + } + } + return VINF_TRY_AGAIN; +} + + +/** + * Register getter for AMD64. + * + * @returns true if found, false if not. + * @param pState The unwind state to get the register from. + * @param iReg The dwarf register number. + * @param puValue Where to store the register value. + */ +static bool rtDwarfUnwind_Amd64GetRegFromState(PCRTDBGUNWINDSTATE pState, uint16_t iReg, uint64_t *puValue) +{ + switch (iReg) + { + case DWREG_AMD64_RAX: *puValue = pState->u.x86.auRegs[X86_GREG_xAX]; return true; + case DWREG_AMD64_RDX: *puValue = pState->u.x86.auRegs[X86_GREG_xDX]; return true; + case DWREG_AMD64_RCX: *puValue = pState->u.x86.auRegs[X86_GREG_xCX]; return true; + case DWREG_AMD64_RBX: *puValue = pState->u.x86.auRegs[X86_GREG_xBX]; return true; + case DWREG_AMD64_RSI: *puValue = pState->u.x86.auRegs[X86_GREG_xSI]; return true; + case DWREG_AMD64_RDI: *puValue = pState->u.x86.auRegs[X86_GREG_xDI]; return true; + case DWREG_AMD64_RBP: *puValue = pState->u.x86.auRegs[X86_GREG_xBP]; return true; + case DWREG_AMD64_RSP: *puValue = pState->u.x86.auRegs[X86_GREG_xSP]; return true; + case DWREG_AMD64_R8: *puValue = pState->u.x86.auRegs[X86_GREG_x8]; return true; + case DWREG_AMD64_R9: *puValue = pState->u.x86.auRegs[X86_GREG_x9]; return true; + case DWREG_AMD64_R10: *puValue = pState->u.x86.auRegs[X86_GREG_x10]; return true; + case DWREG_AMD64_R11: *puValue = pState->u.x86.auRegs[X86_GREG_x11]; return true; + case DWREG_AMD64_R12: *puValue = pState->u.x86.auRegs[X86_GREG_x12]; return true; + case DWREG_AMD64_R13: *puValue = pState->u.x86.auRegs[X86_GREG_x13]; return true; + case DWREG_AMD64_R14: *puValue = pState->u.x86.auRegs[X86_GREG_x14]; return true; + case DWREG_AMD64_R15: *puValue = pState->u.x86.auRegs[X86_GREG_x15]; return true; + case DWREG_AMD64_RFLAGS: *puValue = pState->u.x86.uRFlags; return true; + case DWREG_AMD64_ES: *puValue = pState->u.x86.auSegs[X86_SREG_ES]; return true; + case DWREG_AMD64_CS: *puValue = pState->u.x86.auSegs[X86_SREG_CS]; return true; + case DWREG_AMD64_SS: *puValue = pState->u.x86.auSegs[X86_SREG_SS]; return true; + case DWREG_AMD64_DS: *puValue = pState->u.x86.auSegs[X86_SREG_DS]; return true; + case DWREG_AMD64_FS: *puValue = pState->u.x86.auSegs[X86_SREG_FS]; return true; + case DWREG_AMD64_GS: *puValue = pState->u.x86.auSegs[X86_SREG_GS]; return true; + } + return false; +} + + +/** + * Register getter for 386+. + * + * @returns true if found, false if not. + * @param pState The unwind state to get the register from. + * @param iReg The dwarf register number. + * @param puValue Where to store the register value. + */ +static bool rtDwarfUnwind_X86GetRegFromState(PCRTDBGUNWINDSTATE pState, uint16_t iReg, uint64_t *puValue) +{ + switch (iReg) + { + case DWREG_X86_EAX: *puValue = pState->u.x86.auRegs[X86_GREG_xAX]; return true; + case DWREG_X86_ECX: *puValue = pState->u.x86.auRegs[X86_GREG_xCX]; return true; + case DWREG_X86_EDX: *puValue = pState->u.x86.auRegs[X86_GREG_xDX]; return true; + case DWREG_X86_EBX: *puValue = pState->u.x86.auRegs[X86_GREG_xBX]; return true; + case DWREG_X86_ESP: *puValue = pState->u.x86.auRegs[X86_GREG_xSP]; return true; + case DWREG_X86_EBP: *puValue = pState->u.x86.auRegs[X86_GREG_xBP]; return true; + case DWREG_X86_ESI: *puValue = pState->u.x86.auRegs[X86_GREG_xSI]; return true; + case DWREG_X86_EDI: *puValue = pState->u.x86.auRegs[X86_GREG_xDI]; return true; + case DWREG_X86_EFLAGS: *puValue = pState->u.x86.uRFlags; return true; + case DWREG_X86_ES: *puValue = pState->u.x86.auSegs[X86_SREG_ES]; return true; + case DWREG_X86_CS: *puValue = pState->u.x86.auSegs[X86_SREG_CS]; return true; + case DWREG_X86_SS: *puValue = pState->u.x86.auSegs[X86_SREG_SS]; return true; + case DWREG_X86_DS: *puValue = pState->u.x86.auSegs[X86_SREG_DS]; return true; + case DWREG_X86_FS: *puValue = pState->u.x86.auSegs[X86_SREG_FS]; return true; + case DWREG_X86_GS: *puValue = pState->u.x86.auSegs[X86_SREG_GS]; return true; + } + return false; +} + +/** Register getter. */ +typedef bool FNDWARFUNWINDGEREGFROMSTATE(PCRTDBGUNWINDSTATE pState, uint16_t iReg, uint64_t *puValue); +/** Pointer to a register getter. */ +typedef FNDWARFUNWINDGEREGFROMSTATE *PFNDWARFUNWINDGEREGFROMSTATE; + + + +/** + * Does the heavy work for figuring out the return value of a register. + * + * @returns IPRT status code. + * @retval VERR_NOT_FOUND if register is undefined. + * + * @param pRow The DWARF unwind table "row" to use. + * @param uReg The DWARF register number. + * @param pCie The corresponding CIE. + * @param uCfa The canonical frame address to use. + * @param pState The unwind to use when reading stack. + * @param pOldState The unwind state to get register values from. + * @param pfnGetReg The register value getter. + * @param puValue Where to store the return value. + * @param cbValue The size this register would have on the stack. + */ +static int rtDwarfUnwind_CalcRegisterValue(PRTDWARFCFROW pRow, unsigned uReg, PCRTDWARFCIEINFO pCie, uint64_t uCfa, + PRTDBGUNWINDSTATE pState, PCRTDBGUNWINDSTATE pOldState, + PFNDWARFUNWINDGEREGFROMSTATE pfnGetReg, uint64_t *puValue, uint8_t cbValue) +{ + Assert(uReg < RT_ELEMENTS(pRow->apbRegInstrs)); + uint8_t const *pbInstr = pRow->apbRegInstrs[uReg]; + if (!pbInstr) + return VERR_NOT_FOUND; + + uint32_t cbInstr = UINT32_MAX / 2; + uint32_t offInstr = 1; + uint8_t const bInstr = *pbInstr; + switch (bInstr) + { + default: + if ((bInstr & DW_CFA_high_bit_mask) == DW_CFA_offset) + { + uint64_t offCfa; + GET_ULEB128_AS_U63(offCfa); + int rc = pState->pfnReadStack(pState, uCfa + (int64_t)offCfa * pCie->iDataAlignFactor, cbValue, puValue); + Log8(("rtDwarfUnwind_CalcRegisterValue(%#x): DW_CFA_offset %#RX64: %Rrc, %#RX64\n", uReg, uCfa + (int64_t)offCfa * pCie->iDataAlignFactor, rc, *puValue)); + return rc; + } + AssertReturn((bInstr & DW_CFA_high_bit_mask) == DW_CFA_restore, VERR_INTERNAL_ERROR); + RT_FALL_THRU(); + case DW_CFA_restore_extended: + /* Need to search the CIE for the rule. */ + Log8(("rtDwarfUnwind_CalcRegisterValue(%#x): DW_CFA_restore/extended:\n", uReg)); + AssertFailedReturn(VERR_DWARF_TODO); + + case DW_CFA_offset_extended: + { + SKIP_ULEB128_OR_LEB128(); + uint64_t offCfa; + GET_ULEB128_AS_U63(offCfa); + int rc = pState->pfnReadStack(pState, uCfa + (int64_t)offCfa * pCie->iDataAlignFactor, cbValue, puValue); + Log8(("rtDwarfUnwind_CalcRegisterValue(%#x): DW_CFA_offset_extended %#RX64: %Rrc, %#RX64\n", uReg, uCfa + (int64_t)offCfa * pCie->iDataAlignFactor, rc, *puValue)); + return rc; + } + + case DW_CFA_offset_extended_sf: + { + SKIP_ULEB128_OR_LEB128(); + int64_t offCfa; + GET_LEB128_AS_I63(offCfa); + int rc = pState->pfnReadStack(pState, uCfa + offCfa * pCie->iDataAlignFactor, cbValue, puValue); + Log8(("rtDwarfUnwind_CalcRegisterValue(%#x): DW_CFA_offset_extended_sf %#RX64: %Rrc, %#RX64\n", uReg, uCfa + offCfa * pCie->iDataAlignFactor, rc, *puValue)); + return rc; + } + + case DW_CFA_val_offset: + { + SKIP_ULEB128_OR_LEB128(); + uint64_t offCfa; + GET_ULEB128_AS_U63(offCfa); + *puValue = uCfa + (int64_t)offCfa * pCie->iDataAlignFactor; + Log8(("rtDwarfUnwind_CalcRegisterValue(%#x): DW_CFA_val_offset: %#RX64\n", uReg, *puValue)); + return VINF_SUCCESS; + } + + case DW_CFA_val_offset_sf: + { + SKIP_ULEB128_OR_LEB128(); + int64_t offCfa; + GET_LEB128_AS_I63(offCfa); + *puValue = uCfa + offCfa * pCie->iDataAlignFactor; + Log8(("rtDwarfUnwind_CalcRegisterValue(%#x): DW_CFA_val_offset_sf: %#RX64\n", uReg, *puValue)); + return VINF_SUCCESS; + } + + case DW_CFA_register: + { + SKIP_ULEB128_OR_LEB128(); + uint16_t iSrcReg; + GET_ULEB128_AS_U14(iSrcReg); + if (pfnGetReg(pOldState, uReg, puValue)) + { + Log8(("rtDwarfUnwind_CalcRegisterValue(%#x): DW_CFA_register: %#RX64\n", uReg, *puValue)); + return VINF_SUCCESS; + } + Log8(("rtDwarfUnwind_CalcRegisterValue(%#x): DW_CFA_register: VERR_NOT_FOUND\n", uReg)); + return VERR_NOT_FOUND; + } + + case DW_CFA_expression: + Log8(("rtDwarfUnwind_CalcRegisterValue(%#x): DW_CFA_expression: TODO\n", uReg)); + AssertFailedReturn(VERR_DWARF_TODO); + + case DW_CFA_val_expression: + Log8(("rtDwarfUnwind_CalcRegisterValue(%#x): DW_CFA_val_expression: TODO\n", uReg)); + AssertFailedReturn(VERR_DWARF_TODO); + + case DW_CFA_undefined: + Log8(("rtDwarfUnwind_CalcRegisterValue(%#x): DW_CFA_undefined\n", uReg)); + return VERR_NOT_FOUND; + + case DW_CFA_same_value: + if (pfnGetReg(pOldState, uReg, puValue)) + { + Log8(("rtDwarfUnwind_CalcRegisterValue(%#x): DW_CFA_same_value: %#RX64\n", uReg, *puValue)); + return VINF_SUCCESS; + } + Log8(("rtDwarfUnwind_CalcRegisterValue(%#x): DW_CFA_same_value: VERR_NOT_FOUND\n", uReg)); + return VERR_NOT_FOUND; + } +} + + +DECLINLINE(void) rtDwarfUnwind_UpdateX86GRegFromRow(PRTDBGUNWINDSTATE pState, PCRTDBGUNWINDSTATE pOldState, unsigned idxGReg, + PRTDWARFCFROW pRow, unsigned idxDwReg, PCRTDWARFCIEINFO pCie, + uint64_t uCfa, PFNDWARFUNWINDGEREGFROMSTATE pfnGetReg, uint8_t cbGReg) +{ + int rc = rtDwarfUnwind_CalcRegisterValue(pRow, idxDwReg, pCie, uCfa, pState, pOldState, pfnGetReg, + &pState->u.x86.auRegs[idxGReg], cbGReg); + if (RT_SUCCESS(rc)) + pState->u.x86.Loaded.s.fRegs |= RT_BIT_32(idxGReg); +} + + +DECLINLINE(void) rtDwarfUnwind_UpdateX86SRegFromRow(PRTDBGUNWINDSTATE pState, PCRTDBGUNWINDSTATE pOldState, unsigned idxSReg, + PRTDWARFCFROW pRow, unsigned idxDwReg, PCRTDWARFCIEINFO pCie, + uint64_t uCfa, PFNDWARFUNWINDGEREGFROMSTATE pfnGetReg) +{ + uint64_t uValue = pState->u.x86.auSegs[idxSReg]; + int rc = rtDwarfUnwind_CalcRegisterValue(pRow, idxDwReg, pCie, uCfa, pState, pOldState, pfnGetReg, &uValue, sizeof(uint16_t)); + if (RT_SUCCESS(rc)) + { + pState->u.x86.auSegs[idxSReg] = (uint16_t)uValue; + pState->u.x86.Loaded.s.fSegs |= RT_BIT_32(idxSReg); + } +} + + +DECLINLINE(void) rtDwarfUnwind_UpdateX86RFlagsFromRow(PRTDBGUNWINDSTATE pState, PCRTDBGUNWINDSTATE pOldState, + PRTDWARFCFROW pRow, unsigned idxDwReg, PCRTDWARFCIEINFO pCie, + uint64_t uCfa, PFNDWARFUNWINDGEREGFROMSTATE pfnGetReg) +{ + int rc = rtDwarfUnwind_CalcRegisterValue(pRow, idxDwReg, pCie, uCfa, pState, pOldState, pfnGetReg, + &pState->u.x86.uRFlags, sizeof(uint32_t)); + if (RT_SUCCESS(rc)) + pState->u.x86.Loaded.s.fRFlags = 1; +} + + +DECLINLINE(void) rtDwarfUnwind_UpdatePCFromRow(PRTDBGUNWINDSTATE pState, PCRTDBGUNWINDSTATE pOldState, + PRTDWARFCFROW pRow, unsigned idxDwReg, PCRTDWARFCIEINFO pCie, + uint64_t uCfa, PFNDWARFUNWINDGEREGFROMSTATE pfnGetReg, uint8_t cbPc) +{ + if (pCie->bRetReg != UINT8_MAX) + idxDwReg = pCie->bRetReg; + int rc = rtDwarfUnwind_CalcRegisterValue(pRow, idxDwReg, pCie, uCfa, pState, pOldState, pfnGetReg, &pState->uPc, cbPc); + if (RT_SUCCESS(rc)) + pState->u.x86.Loaded.s.fPc = 1; + else + { + rc = pState->pfnReadStack(pState, uCfa - cbPc, cbPc, &pState->uPc); + if (RT_SUCCESS(rc)) + pState->u.x86.Loaded.s.fPc = 1; + } +} + + + +/** + * Updates @a pState with the rules found in @a pRow. + * + * @returns IPRT status code. + * @param pState The unwind state to update. + * @param pRow The "row" in the dwarf unwind table. + * @param pCie The CIE structure for the row. + * @param enmImageArch The image architecture. + */ +static int rtDwarfUnwind_UpdateStateFromRow(PRTDBGUNWINDSTATE pState, PRTDWARFCFROW pRow, + PCRTDWARFCIEINFO pCie, RTLDRARCH enmImageArch) +{ + /* + * We need to make a copy of the current state so we can get at the + * current register values while calculating the ones of the next frame. + */ + RTDBGUNWINDSTATE const Old = *pState; + + /* + * Get the register state getter. + */ + PFNDWARFUNWINDGEREGFROMSTATE pfnGetReg; + switch (enmImageArch) + { + case RTLDRARCH_AMD64: + pfnGetReg = rtDwarfUnwind_Amd64GetRegFromState; + break; + case RTLDRARCH_X86_32: + case RTLDRARCH_X86_16: + pfnGetReg = rtDwarfUnwind_X86GetRegFromState; + break; + default: + return VERR_NOT_SUPPORTED; + } + + /* + * Calc the canonical frame address for the current row. + */ + AssertReturn(pRow->fCfaDefined, VERR_DBG_MALFORMED_UNWIND_INFO); + uint64_t uCfa = 0; + if (!pRow->pbCfaExprInstr) + { + pfnGetReg(&Old, pRow->uCfaBaseReg, &uCfa); + uCfa += pRow->offCfaReg; + } + else + { + AssertFailed(); + return VERR_DWARF_TODO; + } + Log8(("rtDwarfUnwind_UpdateStateFromRow: uCfa=%RX64\n", uCfa)); + + /* + * Do the architecture specific register updating. + */ + switch (enmImageArch) + { + case RTLDRARCH_AMD64: + pState->enmRetType = RTDBGRETURNTYPE_NEAR64; + pState->u.x86.FrameAddr.off = uCfa - 8*2; + pState->u.x86.Loaded.fAll = 0; + pState->u.x86.Loaded.s.fFrameAddr = 1; + rtDwarfUnwind_UpdatePCFromRow(pState, &Old, pRow, DWREG_AMD64_RA, pCie, uCfa, pfnGetReg, sizeof(uint64_t)); + rtDwarfUnwind_UpdateX86RFlagsFromRow(pState, &Old, pRow, DWREG_AMD64_RFLAGS, pCie, uCfa, pfnGetReg); + rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xAX, pRow, DWREG_AMD64_RAX, pCie, uCfa, pfnGetReg, sizeof(uint64_t)); + rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xCX, pRow, DWREG_AMD64_RCX, pCie, uCfa, pfnGetReg, sizeof(uint64_t)); + rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xDX, pRow, DWREG_AMD64_RDX, pCie, uCfa, pfnGetReg, sizeof(uint64_t)); + rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xBX, pRow, DWREG_AMD64_RBX, pCie, uCfa, pfnGetReg, sizeof(uint64_t)); + rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xSP, pRow, DWREG_AMD64_RSP, pCie, uCfa, pfnGetReg, sizeof(uint64_t)); + rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xBP, pRow, DWREG_AMD64_RBP, pCie, uCfa, pfnGetReg, sizeof(uint64_t)); + rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xSI, pRow, DWREG_AMD64_RSI, pCie, uCfa, pfnGetReg, sizeof(uint64_t)); + rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xDI, pRow, DWREG_AMD64_RDI, pCie, uCfa, pfnGetReg, sizeof(uint64_t)); + rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_x8, pRow, DWREG_AMD64_R8, pCie, uCfa, pfnGetReg, sizeof(uint64_t)); + rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_x9, pRow, DWREG_AMD64_R9, pCie, uCfa, pfnGetReg, sizeof(uint64_t)); + rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_x10, pRow, DWREG_AMD64_R10, pCie, uCfa, pfnGetReg, sizeof(uint64_t)); + rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_x11, pRow, DWREG_AMD64_R11, pCie, uCfa, pfnGetReg, sizeof(uint64_t)); + rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_x12, pRow, DWREG_AMD64_R12, pCie, uCfa, pfnGetReg, sizeof(uint64_t)); + rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_x13, pRow, DWREG_AMD64_R13, pCie, uCfa, pfnGetReg, sizeof(uint64_t)); + rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_x14, pRow, DWREG_AMD64_R14, pCie, uCfa, pfnGetReg, sizeof(uint64_t)); + rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_x15, pRow, DWREG_AMD64_R15, pCie, uCfa, pfnGetReg, sizeof(uint64_t)); + rtDwarfUnwind_UpdateX86SRegFromRow(pState, &Old, X86_SREG_ES, pRow, DWREG_AMD64_ES, pCie, uCfa, pfnGetReg); + rtDwarfUnwind_UpdateX86SRegFromRow(pState, &Old, X86_SREG_CS, pRow, DWREG_AMD64_CS, pCie, uCfa, pfnGetReg); + rtDwarfUnwind_UpdateX86SRegFromRow(pState, &Old, X86_SREG_SS, pRow, DWREG_AMD64_SS, pCie, uCfa, pfnGetReg); + rtDwarfUnwind_UpdateX86SRegFromRow(pState, &Old, X86_SREG_DS, pRow, DWREG_AMD64_DS, pCie, uCfa, pfnGetReg); + rtDwarfUnwind_UpdateX86SRegFromRow(pState, &Old, X86_SREG_FS, pRow, DWREG_AMD64_FS, pCie, uCfa, pfnGetReg); + rtDwarfUnwind_UpdateX86SRegFromRow(pState, &Old, X86_SREG_GS, pRow, DWREG_AMD64_GS, pCie, uCfa, pfnGetReg); + break; + + case RTLDRARCH_X86_32: + case RTLDRARCH_X86_16: + pState->enmRetType = RTDBGRETURNTYPE_NEAR32; + pState->u.x86.FrameAddr.off = uCfa - 4*2; + pState->u.x86.Loaded.fAll = 0; + pState->u.x86.Loaded.s.fFrameAddr = 1; + rtDwarfUnwind_UpdatePCFromRow(pState, &Old, pRow, DWREG_X86_RA, pCie, uCfa, pfnGetReg, sizeof(uint32_t)); + rtDwarfUnwind_UpdateX86RFlagsFromRow(pState, &Old, pRow, DWREG_X86_EFLAGS, pCie, uCfa, pfnGetReg); + rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xAX, pRow, DWREG_X86_EAX, pCie, uCfa, pfnGetReg, sizeof(uint32_t)); + rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xCX, pRow, DWREG_X86_ECX, pCie, uCfa, pfnGetReg, sizeof(uint32_t)); + rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xDX, pRow, DWREG_X86_EDX, pCie, uCfa, pfnGetReg, sizeof(uint32_t)); + rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xBX, pRow, DWREG_X86_EBX, pCie, uCfa, pfnGetReg, sizeof(uint32_t)); + rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xSP, pRow, DWREG_X86_ESP, pCie, uCfa, pfnGetReg, sizeof(uint32_t)); + rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xBP, pRow, DWREG_X86_EBP, pCie, uCfa, pfnGetReg, sizeof(uint32_t)); + rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xSI, pRow, DWREG_X86_ESI, pCie, uCfa, pfnGetReg, sizeof(uint32_t)); + rtDwarfUnwind_UpdateX86GRegFromRow(pState, &Old, X86_GREG_xDI, pRow, DWREG_X86_EDI, pCie, uCfa, pfnGetReg, sizeof(uint32_t)); + rtDwarfUnwind_UpdateX86SRegFromRow(pState, &Old, X86_SREG_ES, pRow, DWREG_X86_ES, pCie, uCfa, pfnGetReg); + rtDwarfUnwind_UpdateX86SRegFromRow(pState, &Old, X86_SREG_CS, pRow, DWREG_X86_CS, pCie, uCfa, pfnGetReg); + rtDwarfUnwind_UpdateX86SRegFromRow(pState, &Old, X86_SREG_SS, pRow, DWREG_X86_SS, pCie, uCfa, pfnGetReg); + rtDwarfUnwind_UpdateX86SRegFromRow(pState, &Old, X86_SREG_DS, pRow, DWREG_X86_DS, pCie, uCfa, pfnGetReg); + rtDwarfUnwind_UpdateX86SRegFromRow(pState, &Old, X86_SREG_FS, pRow, DWREG_X86_FS, pCie, uCfa, pfnGetReg); + rtDwarfUnwind_UpdateX86SRegFromRow(pState, &Old, X86_SREG_GS, pRow, DWREG_X86_GS, pCie, uCfa, pfnGetReg); + if (pState->u.x86.Loaded.s.fRegs & RT_BIT_32(X86_GREG_xSP)) + pState->u.x86.FrameAddr.off = pState->u.x86.auRegs[X86_GREG_xSP] - 8; + else + pState->u.x86.FrameAddr.off = uCfa - 8; + pState->u.x86.FrameAddr.sel = pState->u.x86.auSegs[X86_SREG_SS]; + if (pState->u.x86.Loaded.s.fSegs & RT_BIT_32(X86_SREG_CS)) + { + if ((pState->uPc >> 16) == pState->u.x86.auSegs[X86_SREG_CS]) + { + pState->enmRetType = RTDBGRETURNTYPE_FAR16; + pState->uPc &= UINT16_MAX; + Log8(("rtDwarfUnwind_UpdateStateFromRow: Detected FAR16 return to %04x:%04RX64\n", pState->u.x86.auSegs[X86_SREG_CS], pState->uPc)); + } + else + { + pState->enmRetType = RTDBGRETURNTYPE_FAR32; + Log8(("rtDwarfUnwind_UpdateStateFromRow: CS loaded, assume far return.\n")); + } + } + break; + + default: + AssertFailedReturn(VERR_NOT_SUPPORTED); + } + + return VINF_SUCCESS; +} + + +/** + * Processes a FDE, taking over after the PC range field. + * + * @returns IPRT status code. + * @param pCursor The cursor. + * @param pCie Information about the corresponding CIE. + * @param uPcBegin The PC begin field value (sans segment). + * @param cbPcRange The PC range from @a uPcBegin. + * @param offInRange The offset into the range corresponding to + * pState->uPc. + * @param enmImageArch The image architecture. + * @param pState The unwind state to work. + */ +static int rtDwarfUnwind_ProcessFde(PRTDWARFCURSOR pCursor, PCRTDWARFCIEINFO pCie, uint64_t uPcBegin, + uint64_t cbPcRange, uint64_t offInRange, RTLDRARCH enmImageArch, PRTDBGUNWINDSTATE pState) +{ + /* + * Deal with augmented data fields. + */ + /* The size. */ + size_t cbInstr = ~(size_t)0; + if (pCie->fHasAugmentationSize) + { + uint64_t cbAugData = rtDwarfCursor_GetULeb128(pCursor, UINT64_MAX); + if (RT_FAILURE(pCursor->rc)) + return pCursor->rc; + if (cbAugData > pCursor->cbUnitLeft) + return VERR_DBG_MALFORMED_UNWIND_INFO; + cbInstr = pCursor->cbUnitLeft - cbAugData; + } + else if (pCie->fHasUnknowAugmentation) + return VERR_DBG_MALFORMED_UNWIND_INFO; + + /* Parse the string and fetch FDE fields. */ + if (!pCie->fHasEhData) + for (const char *pszAug = pCie->pszAugmentation; *pszAug != '\0'; pszAug++) + switch (*pszAug) + { + case 'L': + if (pCie->bLsdaPtrEnc != DW_EH_PE_omit) + rtDwarfCursor_GetPtrEnc(pCursor, pCie->bLsdaPtrEnc, 0); + break; + } + + /* Skip unconsumed bytes. */ + if ( cbInstr != ~(size_t)0 + && pCursor->cbUnitLeft > cbInstr) + rtDwarfCursor_SkipBytes(pCursor, pCursor->cbUnitLeft - cbInstr); + if (RT_FAILURE(pCursor->rc)) + return pCursor->rc; + + /* + * Now "execute" the programs till we've constructed the desired row. + */ + RTDWARFCFROW Row; + RTDWARFCFEXEC ExecState = { &Row, offInRange, 0, true /** @todo byte-order*/, pCie, uPcBegin, offInRange }; + RT_ZERO(Row); + + int rc = rtDwarfUnwind_Execute(&ExecState, pCie->pbInstructions, (uint32_t)pCie->cbInstructions); + if (rc == VINF_TRY_AGAIN) + rc = rtDwarfUnwind_Execute(&ExecState, pCursor->pb, (uint32_t)pCursor->cbUnitLeft); + + /* On success, extract whatever state we've got. */ + if (RT_SUCCESS(rc)) + rc = rtDwarfUnwind_UpdateStateFromRow(pState, &Row, pCie, enmImageArch); + + /* + * Clean up allocations in case of pushes. + */ + if (ExecState.pRow == &Row) + Assert(!ExecState.pRow->fOnHeap); + else + do + { + PRTDWARFCFROW pPopped = ExecState.pRow; + ExecState.pRow = ExecState.pRow->pNextOnStack; + Assert(pPopped->fOnHeap); + RTMemTmpFree(pPopped); + } while (ExecState.pRow && ExecState.pRow != &Row); + + RT_NOREF(pState, uPcBegin, cbPcRange, offInRange); + return rc; +} + + +/** + * Load the information we need from a CIE. + * + * This starts after the initial length and CIE_pointer fields has + * been processed. + * + * @returns IPRT status code. + * @param pCursor The cursor. + * @param pNewCie The structure to populate with parsed CIE info. + * @param offUnit The unit offset. + * @param bDefaultPtrEnc The default pointer encoding. + */ +static int rtDwarfUnwind_LoadCie(PRTDWARFCURSOR pCursor, PRTDWARFCIEINFO pNewCie, uint64_t offUnit, uint8_t bDefaultPtrEnc) +{ + /* + * Initialize the CIE record and get the version. + */ + RT_ZERO(*pNewCie); + pNewCie->offCie = offUnit; + pNewCie->bLsdaPtrEnc = DW_EH_PE_omit; + pNewCie->bAddressPtrEnc = DW_EH_PE_omit; /* set later */ + pNewCie->uDwarfVer = rtDwarfCursor_GetUByte(pCursor, 0); + if ( pNewCie->uDwarfVer >= 1 /* Note! Some GCC versions may emit v1 here. */ + && pNewCie->uDwarfVer <= 5) + { /* likely */ } + else + { + Log(("rtDwarfUnwind_LoadCie(%RX64): uDwarfVer=%u: VERR_VERSION_MISMATCH\n", offUnit, pNewCie->uDwarfVer)); + return VERR_VERSION_MISMATCH; + } + + /* + * The augmentation string. + * + * First deal with special "eh" string from oldish GCC (dwarf2out.c about 1997), specified in LSB: + * https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html + */ + pNewCie->pszAugmentation = rtDwarfCursor_GetSZ(pCursor, ""); + if ( pNewCie->pszAugmentation[0] == 'e' + && pNewCie->pszAugmentation[1] == 'h' + && pNewCie->pszAugmentation[2] == '\0') + { + pNewCie->fHasEhData = true; + rtDwarfCursor_GetPtrEnc(pCursor, bDefaultPtrEnc, 0); + } + else + { + /* Regular augmentation string. */ + for (const char *pszAug = pNewCie->pszAugmentation; *pszAug != '\0'; pszAug++) + switch (*pszAug) + { + case 'z': + pNewCie->fHasAugmentationSize = true; + break; + case 'L': + pNewCie->fHasLanguageSpecificDataArea = true; + break; + case 'P': + pNewCie->fHasPersonalityRoutine = true; + break; + case 'R': + pNewCie->fHasAddressEnc = true; + break; + case 'S': + pNewCie->fIsSignalFrame = true; + break; + default: + pNewCie->fHasUnknowAugmentation = true; + break; + } + } + + /* + * More standard fields + */ + uint8_t cbAddress = 0; + if (pNewCie->uDwarfVer >= 4) + { + cbAddress = rtDwarfCursor_GetU8(pCursor, bDefaultPtrEnc == DW_EH_PE_udata8 ? 8 : 4); + pNewCie->cbSegment = rtDwarfCursor_GetU8(pCursor, 0); + } + pNewCie->uCodeAlignFactor = rtDwarfCursor_GetULeb128(pCursor, 1); + pNewCie->iDataAlignFactor = rtDwarfCursor_GetSLeb128(pCursor, 1); + pNewCie->bRetReg = rtDwarfCursor_GetU8(pCursor, UINT8_MAX); + + /* + * Augmentation data. + */ + if (!pNewCie->fHasEhData) + { + /* The size. */ + size_t cbInstr = ~(size_t)0; + if (pNewCie->fHasAugmentationSize) + { + uint64_t cbAugData = rtDwarfCursor_GetULeb128(pCursor, UINT64_MAX); + if (RT_FAILURE(pCursor->rc)) + { + Log(("rtDwarfUnwind_LoadCie(%#RX64): rtDwarfCursor_GetULeb128 -> %Rrc!\n", offUnit, pCursor->rc)); + return pCursor->rc; + } + if (cbAugData > pCursor->cbUnitLeft) + { + Log(("rtDwarfUnwind_LoadCie(%#RX64): cbAugData=%#x pCursor->cbUnitLeft=%#x -> VERR_DBG_MALFORMED_UNWIND_INFO!\n", offUnit, cbAugData, pCursor->cbUnitLeft)); + return VERR_DBG_MALFORMED_UNWIND_INFO; + } + cbInstr = pCursor->cbUnitLeft - cbAugData; + } + else if (pNewCie->fHasUnknowAugmentation) + { + Log(("rtDwarfUnwind_LoadCie(%#RX64): fHasUnknowAugmentation=1 -> VERR_DBG_MALFORMED_UNWIND_INFO!\n", offUnit)); + return VERR_DBG_MALFORMED_UNWIND_INFO; + } + + /* Parse the string. */ + for (const char *pszAug = pNewCie->pszAugmentation; *pszAug != '\0'; pszAug++) + switch (*pszAug) + { + case 'L': + pNewCie->bLsdaPtrEnc = rtDwarfCursor_GetU8(pCursor, DW_EH_PE_omit); + break; + case 'P': + rtDwarfCursor_GetPtrEnc(pCursor, rtDwarfCursor_GetU8(pCursor, DW_EH_PE_omit), 0); + break; + case 'R': + pNewCie->bAddressPtrEnc = rtDwarfCursor_GetU8(pCursor, DW_EH_PE_omit); + break; + } + + /* Skip unconsumed bytes. */ + if ( cbInstr != ~(size_t)0 + && pCursor->cbUnitLeft > cbInstr) + rtDwarfCursor_SkipBytes(pCursor, pCursor->cbUnitLeft - cbInstr); + } + + /* + * Note down where the instructions are. + */ + pNewCie->pbInstructions = pCursor->pb; + pNewCie->cbInstructions = pCursor->cbUnitLeft; + + /* + * Determine the target address encoding. Make sure we resolve DW_EH_PE_ptr. + */ + if (pNewCie->bAddressPtrEnc == DW_EH_PE_omit) + switch (cbAddress) + { + case 2: pNewCie->bAddressPtrEnc = DW_EH_PE_udata2; break; + case 4: pNewCie->bAddressPtrEnc = DW_EH_PE_udata4; break; + case 8: pNewCie->bAddressPtrEnc = DW_EH_PE_udata8; break; + default: pNewCie->bAddressPtrEnc = bDefaultPtrEnc; break; + } + else if ((pNewCie->bAddressPtrEnc & DW_EH_PE_FORMAT_MASK) == DW_EH_PE_ptr) + pNewCie->bAddressPtrEnc = bDefaultPtrEnc; + + return VINF_SUCCESS; +} + + +/** + * Does a slow unwind of a '.debug_frame' or '.eh_frame' section. + * + * @returns IPRT status code. + * @param pCursor The cursor. + * @param uRvaCursor The RVA corrsponding to the cursor start location. + * @param idxSeg The segment of the PC location. + * @param offSeg The segment offset of the PC location. + * @param uRva The RVA of the PC location. + * @param pState The unwind state to work. + * @param bDefaultPtrEnc The default pointer encoding. + * @param fIsEhFrame Set if this is a '.eh_frame'. GCC generate these + * with different CIE_pointer values. + * @param enmImageArch The image architecture. + */ +DECLHIDDEN(int) rtDwarfUnwind_Slow(PRTDWARFCURSOR pCursor, RTUINTPTR uRvaCursor, + RTDBGSEGIDX idxSeg, RTUINTPTR offSeg, RTUINTPTR uRva, + PRTDBGUNWINDSTATE pState, uint8_t bDefaultPtrEnc, bool fIsEhFrame, RTLDRARCH enmImageArch) +{ + Log8(("rtDwarfUnwind_Slow: idxSeg=%#x offSeg=%RTptr uRva=%RTptr enmArch=%d PC=%#RX64\n", idxSeg, offSeg, uRva, pState->enmArch, pState->uPc)); + + /* + * CIE info we collect. + */ + PRTDWARFCIEINFO paCies = NULL; + uint32_t cCies = 0; + PRTDWARFCIEINFO pCieHint = NULL; + + /* + * Do the scanning. + */ + uint64_t const offCieOffset = pCursor->f64bitDwarf ? UINT64_MAX : UINT32_MAX; + int rc = VERR_DBG_UNWIND_INFO_NOT_FOUND; + while (!rtDwarfCursor_IsAtEnd(pCursor)) + { + uint64_t const offUnit = rtDwarfCursor_CalcSectOffsetU32(pCursor); + if (rtDwarfCursor_GetInitialLength(pCursor) == 0) + break; + + uint64_t const offRelCie = rtDwarfCursor_GetUOff(pCursor, offCieOffset); + if (offRelCie != offCieOffset) + { + /* + * Frame descriptor entry (FDE). + */ + /* Locate the corresponding CIE. The CIE pointer is self relative + in .eh_frame and section relative in .debug_frame. */ + PRTDWARFCIEINFO pCieForFde; + uint64_t offCie = fIsEhFrame ? offUnit + 4 - offRelCie : offRelCie; + if (pCieHint && pCieHint->offCie == offCie) + pCieForFde = pCieHint; + else + { + pCieForFde = NULL; + uint32_t i = cCies; + while (i-- > 0) + if (paCies[i].offCie == offCie) + { + pCieHint = pCieForFde = &paCies[i]; + break; + } + } + if (pCieForFde) + { + /* Read the PC range covered by this FDE (the fields are also known as initial_location). */ + RTDBGSEGIDX idxFdeSeg = RTDBGSEGIDX_RVA; + if (pCieForFde->cbSegment) + idxFdeSeg = rtDwarfCursor_GetVarSizedU(pCursor, pCieForFde->cbSegment, RTDBGSEGIDX_RVA); + uint64_t uPcBegin; + switch (pCieForFde->bAddressPtrEnc & DW_EH_PE_APPL_MASK) + { + default: AssertFailed(); + RT_FALL_THRU(); + case DW_EH_PE_absptr: + uPcBegin = rtDwarfCursor_GetPtrEnc(pCursor, pCieForFde->bAddressPtrEnc, 0); + break; + case DW_EH_PE_pcrel: + { + uPcBegin = rtDwarfCursor_CalcSectOffsetU32(pCursor) + uRvaCursor; + uPcBegin += rtDwarfCursor_GetPtrEnc(pCursor, pCieForFde->bAddressPtrEnc, 0); + break; + } + } + uint64_t cbPcRange = rtDwarfCursor_GetPtrEnc(pCursor, pCieForFde->bAddressPtrEnc, 0); + + /* Match it with what we're looking for. */ + bool fMatch = idxFdeSeg == RTDBGSEGIDX_RVA + ? uRva - uPcBegin < cbPcRange + : idxSeg == idxFdeSeg && offSeg - uPcBegin < cbPcRange; + Log8(("%#08RX64: FDE pCie=%p idxFdeSeg=%#x uPcBegin=%#RX64 cbPcRange=%#x fMatch=%d\n", + offUnit, pCieForFde, idxFdeSeg, uPcBegin, cbPcRange, fMatch)); + if (fMatch) + { + rc = rtDwarfUnwind_ProcessFde(pCursor, pCieForFde, uPcBegin, cbPcRange, + idxFdeSeg == RTDBGSEGIDX_RVA ? uRva - uPcBegin : offSeg - uPcBegin, + enmImageArch, pState); + break; + } + } + else + Log8(("%#08RX64: FDE - pCie=NULL!! offCie=%#RX64 offRelCie=%#RX64 fIsEhFrame=%d\n", offUnit, offCie, offRelCie, fIsEhFrame)); + } + else + { + /* + * Common information entry (CIE). Record the info we need about it. + */ + if ((cCies % 8) == 0) + { + void *pvNew = RTMemRealloc(paCies, sizeof(paCies[0]) * (cCies + 8)); + if (pvNew) + { + paCies = (PRTDWARFCIEINFO)pvNew; + pCieHint = NULL; + } + else + { + rc = VERR_NO_MEMORY; + break; + } + } + Log8(("%#08RX64: CIE\n", offUnit)); + int rc2 = rtDwarfUnwind_LoadCie(pCursor, &paCies[cCies], offUnit, bDefaultPtrEnc); + if (RT_SUCCESS(rc2)) + { + Log8(("%#08RX64: CIE #%u: offCie=%#RX64\n", offUnit, cCies, paCies[cCies].offCie)); + cCies++; + } + } + rtDwarfCursor_SkipUnit(pCursor); + } + + /* + * Cleanup. + */ + if (paCies) + RTMemFree(paCies); + Log8(("rtDwarfUnwind_Slow: returns %Rrc PC=%#RX64\n", rc, pState->uPc)); + return rc; +} + + +/** + * Helper for translating a loader architecture value to a pointe encoding. + * + * @returns Pointer encoding. + * @param enmLdrArch The loader architecture value to convert. + */ +static uint8_t rtDwarfUnwind_ArchToPtrEnc(RTLDRARCH enmLdrArch) +{ + switch (enmLdrArch) + { + case RTLDRARCH_AMD64: + case RTLDRARCH_ARM64: + return DW_EH_PE_udata8; + case RTLDRARCH_X86_16: + case RTLDRARCH_X86_32: + case RTLDRARCH_ARM32: + return DW_EH_PE_udata4; + case RTLDRARCH_HOST: + case RTLDRARCH_WHATEVER: + case RTLDRARCH_INVALID: + case RTLDRARCH_END: + case RTLDRARCH_32BIT_HACK: + break; + } + AssertFailed(); + return DW_EH_PE_udata4; +} + + +/** + * Interface for the loader code. + * + * @returns IPRT status. + * @param pvSection The '.eh_frame' section data. + * @param cbSection The size of the '.eh_frame' section data. + * @param uRvaSection The RVA of the '.eh_frame' section. + * @param idxSeg The segment of the PC location. + * @param offSeg The segment offset of the PC location. + * @param uRva The RVA of the PC location. + * @param pState The unwind state to work. + * @param enmArch The image architecture. + */ +DECLHIDDEN(int) rtDwarfUnwind_EhData(void const *pvSection, size_t cbSection, RTUINTPTR uRvaSection, + RTDBGSEGIDX idxSeg, RTUINTPTR offSeg, RTUINTPTR uRva, + PRTDBGUNWINDSTATE pState, RTLDRARCH enmArch) +{ + RTDWARFCURSOR Cursor; + rtDwarfCursor_InitForMem(&Cursor, pvSection, cbSection); + int rc = rtDwarfUnwind_Slow(&Cursor, uRvaSection, idxSeg, offSeg, uRva, pState, + rtDwarfUnwind_ArchToPtrEnc(enmArch), true /*fIsEhFrame*/, enmArch); + LogFlow(("rtDwarfUnwind_EhData: rtDwarfUnwind_Slow -> %Rrc\n", rc)); + rc = rtDwarfCursor_Delete(&Cursor, rc); + LogFlow(("rtDwarfUnwind_EhData: returns %Rrc\n", rc)); + return rc; +} + + +/* + * + * DWARF Line Numbers. + * DWARF Line Numbers. + * DWARF Line Numbers. + * + */ + + +/** + * Defines a file name. + * + * @returns IPRT status code. + * @param pLnState The line number program state. + * @param pszFilename The name of the file. + * @param idxInc The include path index. + */ +static int rtDwarfLine_DefineFileName(PRTDWARFLINESTATE pLnState, const char *pszFilename, uint64_t idxInc) +{ + /* + * Resize the array if necessary. + */ + uint32_t iFileName = pLnState->cFileNames; + if ((iFileName % 2) == 0) + { + void *pv = RTMemRealloc(pLnState->papszFileNames, sizeof(pLnState->papszFileNames[0]) * (iFileName + 2)); + if (!pv) + return VERR_NO_MEMORY; + pLnState->papszFileNames = (char **)pv; + } + + /* + * Add the file name. + */ + if ( pszFilename[0] == '/' + || pszFilename[0] == '\\' + || (RT_C_IS_ALPHA(pszFilename[0]) && pszFilename[1] == ':') ) + pLnState->papszFileNames[iFileName] = RTStrDup(pszFilename); + else if (idxInc < pLnState->cIncPaths) + pLnState->papszFileNames[iFileName] = RTPathJoinA(pLnState->papszIncPaths[idxInc], pszFilename); + else + return VERR_DWARF_BAD_LINE_NUMBER_HEADER; + if (!pLnState->papszFileNames[iFileName]) + return VERR_NO_STR_MEMORY; + pLnState->cFileNames = iFileName + 1; + + /* + * Sanitize the name. + */ + int rc = rtDbgModDwarfStringToUtf8(pLnState->pDwarfMod, &pLnState->papszFileNames[iFileName]); + Log((" File #%02u = '%s'\n", iFileName, pLnState->papszFileNames[iFileName])); + return rc; +} + + +/** + * Adds a line to the table and resets parts of the state (DW_LNS_copy). + * + * @returns IPRT status code + * @param pLnState The line number program state. + * @param offOpCode The opcode offset (for logging + * purposes). + */ +static int rtDwarfLine_AddLine(PRTDWARFLINESTATE pLnState, uint32_t offOpCode) +{ + PRTDBGMODDWARF pThis = pLnState->pDwarfMod; + int rc; + if (pThis->iWatcomPass == 1) + rc = rtDbgModDwarfRecordSegOffset(pThis, pLnState->Regs.uSegment, pLnState->Regs.uAddress + 1); + else + { + const char *pszFile = pLnState->Regs.iFile < pLnState->cFileNames + ? pLnState->papszFileNames[pLnState->Regs.iFile] + : "<bad file name index>"; + NOREF(offOpCode); + + RTDBGSEGIDX iSeg; + RTUINTPTR offSeg; + rc = rtDbgModDwarfLinkAddressToSegOffset(pLnState->pDwarfMod, pLnState->Regs.uSegment, pLnState->Regs.uAddress, + &iSeg, &offSeg); /*AssertRC(rc);*/ + if (RT_SUCCESS(rc)) + { + Log2(("rtDwarfLine_AddLine: %x:%08llx (%#llx) %s(%d) [offOpCode=%08x]\n", iSeg, offSeg, pLnState->Regs.uAddress, pszFile, pLnState->Regs.uLine, offOpCode)); + rc = RTDbgModLineAdd(pLnState->pDwarfMod->hCnt, pszFile, pLnState->Regs.uLine, iSeg, offSeg, NULL); + + /* Ignore address conflicts for now. */ + if (rc == VERR_DBG_ADDRESS_CONFLICT) + rc = VINF_SUCCESS; + } + else + rc = VINF_SUCCESS; /* ignore failure */ + } + + pLnState->Regs.fBasicBlock = false; + pLnState->Regs.fPrologueEnd = false; + pLnState->Regs.fEpilogueBegin = false; + pLnState->Regs.uDiscriminator = 0; + return rc; +} + + +/** + * Reset the program to the start-of-sequence state. + * + * @param pLnState The line number program state. + */ +static void rtDwarfLine_ResetState(PRTDWARFLINESTATE pLnState) +{ + pLnState->Regs.uAddress = 0; + pLnState->Regs.idxOp = 0; + pLnState->Regs.iFile = 1; + pLnState->Regs.uLine = 1; + pLnState->Regs.uColumn = 0; + pLnState->Regs.fIsStatement = RT_BOOL(pLnState->Hdr.u8DefIsStmt); + pLnState->Regs.fBasicBlock = false; + pLnState->Regs.fEndSequence = false; + pLnState->Regs.fPrologueEnd = false; + pLnState->Regs.fEpilogueBegin = false; + pLnState->Regs.uIsa = 0; + pLnState->Regs.uDiscriminator = 0; + pLnState->Regs.uSegment = 0; +} + + +/** + * Runs the line number program. + * + * @returns IPRT status code. + * @param pLnState The line number program state. + * @param pCursor The cursor. + */ +static int rtDwarfLine_RunProgram(PRTDWARFLINESTATE pLnState, PRTDWARFCURSOR pCursor) +{ + LogFlow(("rtDwarfLine_RunProgram: cbUnitLeft=%zu\n", pCursor->cbUnitLeft)); + + int rc = VINF_SUCCESS; + rtDwarfLine_ResetState(pLnState); + + while (!rtDwarfCursor_IsAtEndOfUnit(pCursor)) + { +#ifdef LOG_ENABLED + uint32_t const offOpCode = rtDwarfCursor_CalcSectOffsetU32(pCursor); +#else + uint32_t const offOpCode = 0; +#endif + uint8_t bOpCode = rtDwarfCursor_GetUByte(pCursor, DW_LNS_extended); + if (bOpCode >= pLnState->Hdr.u8OpcodeBase) + { + /* + * Special opcode. + */ + uint8_t const bLogOpCode = bOpCode; NOREF(bLogOpCode); + bOpCode -= pLnState->Hdr.u8OpcodeBase; + + int32_t const cLineDelta = bOpCode % pLnState->Hdr.u8LineRange + (int32_t)pLnState->Hdr.s8LineBase; + bOpCode /= pLnState->Hdr.u8LineRange; + + uint64_t uTmp = bOpCode + pLnState->Regs.idxOp; + uint64_t const cAddressDelta = uTmp / pLnState->Hdr.cMaxOpsPerInstr * pLnState->Hdr.cbMinInstr; + uint64_t const cOpIndexDelta = uTmp % pLnState->Hdr.cMaxOpsPerInstr; + + pLnState->Regs.uLine += cLineDelta; + pLnState->Regs.uAddress += cAddressDelta; + pLnState->Regs.idxOp += cOpIndexDelta; + Log2(("%08x: DW Special Opcode %#04x: uLine + %d => %u; uAddress + %#llx => %#llx; idxOp + %#llx => %#llx\n", + offOpCode, bLogOpCode, cLineDelta, pLnState->Regs.uLine, cAddressDelta, pLnState->Regs.uAddress, + cOpIndexDelta, pLnState->Regs.idxOp)); + + /* + * LLVM emits debug info for global constructors (_GLOBAL__I_a) which are not part of source + * code but are inserted by the compiler: The resulting line number will be 0 + * because they are not part of the source file obviously (see https://reviews.llvm.org/rL205999), + * so skip adding them when they are encountered. + */ + if (pLnState->Regs.uLine) + rc = rtDwarfLine_AddLine(pLnState, offOpCode); + } + else + { + switch (bOpCode) + { + /* + * Standard opcode. + */ + case DW_LNS_copy: + Log2(("%08x: DW_LNS_copy\n", offOpCode)); + /* See the comment about LLVM above. */ + if (pLnState->Regs.uLine) + rc = rtDwarfLine_AddLine(pLnState, offOpCode); + break; + + case DW_LNS_advance_pc: + { + uint64_t u64Adv = rtDwarfCursor_GetULeb128(pCursor, 0); + pLnState->Regs.uAddress += (pLnState->Regs.idxOp + u64Adv) / pLnState->Hdr.cMaxOpsPerInstr + * pLnState->Hdr.cbMinInstr; + pLnState->Regs.idxOp += (pLnState->Regs.idxOp + u64Adv) % pLnState->Hdr.cMaxOpsPerInstr; + Log2(("%08x: DW_LNS_advance_pc: u64Adv=%#llx (%lld) )\n", offOpCode, u64Adv, u64Adv)); + break; + } + + case DW_LNS_advance_line: + { + int32_t cLineDelta = rtDwarfCursor_GetSLeb128AsS32(pCursor, 0); + pLnState->Regs.uLine += cLineDelta; + Log2(("%08x: DW_LNS_advance_line: uLine + %d => %u\n", offOpCode, cLineDelta, pLnState->Regs.uLine)); + break; + } + + case DW_LNS_set_file: + pLnState->Regs.iFile = rtDwarfCursor_GetULeb128AsU32(pCursor, 0); + Log2(("%08x: DW_LNS_set_file: iFile=%u\n", offOpCode, pLnState->Regs.iFile)); + break; + + case DW_LNS_set_column: + pLnState->Regs.uColumn = rtDwarfCursor_GetULeb128AsU32(pCursor, 0); + Log2(("%08x: DW_LNS_set_column\n", offOpCode)); + break; + + case DW_LNS_negate_stmt: + pLnState->Regs.fIsStatement = !pLnState->Regs.fIsStatement; + Log2(("%08x: DW_LNS_negate_stmt\n", offOpCode)); + break; + + case DW_LNS_set_basic_block: + pLnState->Regs.fBasicBlock = true; + Log2(("%08x: DW_LNS_set_basic_block\n", offOpCode)); + break; + + case DW_LNS_const_add_pc: + { + uint8_t u8Adv = (255 - pLnState->Hdr.u8OpcodeBase) / pLnState->Hdr.u8LineRange; + if (pLnState->Hdr.cMaxOpsPerInstr <= 1) + pLnState->Regs.uAddress += (uint32_t)pLnState->Hdr.cbMinInstr * u8Adv; + else + { + pLnState->Regs.uAddress += (pLnState->Regs.idxOp + u8Adv) / pLnState->Hdr.cMaxOpsPerInstr + * pLnState->Hdr.cbMinInstr; + pLnState->Regs.idxOp = (pLnState->Regs.idxOp + u8Adv) % pLnState->Hdr.cMaxOpsPerInstr; + } + Log2(("%08x: DW_LNS_const_add_pc\n", offOpCode)); + break; + } + case DW_LNS_fixed_advance_pc: + pLnState->Regs.uAddress += rtDwarfCursor_GetUHalf(pCursor, 0); + pLnState->Regs.idxOp = 0; + Log2(("%08x: DW_LNS_fixed_advance_pc\n", offOpCode)); + break; + + case DW_LNS_set_prologue_end: + pLnState->Regs.fPrologueEnd = true; + Log2(("%08x: DW_LNS_set_prologue_end\n", offOpCode)); + break; + + case DW_LNS_set_epilogue_begin: + pLnState->Regs.fEpilogueBegin = true; + Log2(("%08x: DW_LNS_set_epilogue_begin\n", offOpCode)); + break; + + case DW_LNS_set_isa: + pLnState->Regs.uIsa = rtDwarfCursor_GetULeb128AsU32(pCursor, 0); + Log2(("%08x: DW_LNS_set_isa %#x\n", offOpCode, pLnState->Regs.uIsa)); + break; + + default: + { + unsigned cOpsToSkip = pLnState->Hdr.pacStdOperands[bOpCode - 1]; + Log(("rtDwarfLine_RunProgram: Unknown standard opcode %#x, %#x operands, at %08x.\n", bOpCode, cOpsToSkip, offOpCode)); + while (cOpsToSkip-- > 0) + rc = rtDwarfCursor_SkipLeb128(pCursor); + break; + } + + /* + * Extended opcode. + */ + case DW_LNS_extended: + { + /* The instruction has a length prefix. */ + uint64_t cbInstr = rtDwarfCursor_GetULeb128(pCursor, UINT64_MAX); + if (RT_FAILURE(pCursor->rc)) + return pCursor->rc; + if (cbInstr > pCursor->cbUnitLeft) + return VERR_DWARF_BAD_LNE; + uint8_t const * const pbEndOfInstr = rtDwarfCursor_CalcPos(pCursor, cbInstr); + + /* Get the opcode and deal with it if we know it. */ + bOpCode = rtDwarfCursor_GetUByte(pCursor, 0); + switch (bOpCode) + { + case DW_LNE_end_sequence: +#if 0 /* No need for this, I think. */ + pLnState->Regs.fEndSequence = true; + rc = rtDwarfLine_AddLine(pLnState, offOpCode); +#endif + rtDwarfLine_ResetState(pLnState); + Log2(("%08x: DW_LNE_end_sequence\n", offOpCode)); + break; + + case DW_LNE_set_address: + pLnState->Regs.uAddress = rtDwarfCursor_GetVarSizedU(pCursor, cbInstr - 1, UINT64_MAX); + pLnState->Regs.idxOp = 0; + Log2(("%08x: DW_LNE_set_address: %#llx\n", offOpCode, pLnState->Regs.uAddress)); + break; + + case DW_LNE_define_file: + { + const char *pszFilename = rtDwarfCursor_GetSZ(pCursor, NULL); + uint32_t idxInc = rtDwarfCursor_GetULeb128AsU32(pCursor, UINT32_MAX); + rtDwarfCursor_SkipLeb128(pCursor); /* st_mtime */ + rtDwarfCursor_SkipLeb128(pCursor); /* st_size */ + Log2(("%08x: DW_LNE_define_file: {%d}/%s\n", offOpCode, idxInc, pszFilename)); + + rc = rtDwarfCursor_AdvanceToPos(pCursor, pbEndOfInstr); + if (RT_SUCCESS(rc)) + rc = rtDwarfLine_DefineFileName(pLnState, pszFilename, idxInc); + break; + } + + /* + * Note! Was defined in DWARF 4. But... Watcom used it for setting the + * segment in DWARF 2, creating an incompatibility with the newer + * standard. And gcc 10 uses v3 for these. + */ + case DW_LNE_set_descriminator: + if (pLnState->Hdr.uVer != 2) + { + Assert(pLnState->Hdr.uVer >= 3); + pLnState->Regs.uDiscriminator = rtDwarfCursor_GetULeb128AsU32(pCursor, UINT32_MAX); + Log2(("%08x: DW_LNE_set_descriminator: %u\n", offOpCode, pLnState->Regs.uDiscriminator)); + } + else + { + uint64_t uSeg = rtDwarfCursor_GetVarSizedU(pCursor, cbInstr - 1, UINT64_MAX); + Log2(("%08x: DW_LNE_set_segment: %#llx, cbInstr=%#x - Watcom Extension\n", offOpCode, uSeg, cbInstr)); + pLnState->Regs.uSegment = (RTSEL)uSeg; + AssertStmt(pLnState->Regs.uSegment == uSeg, rc = VERR_DWARF_BAD_INFO); + } + break; + + default: + Log(("rtDwarfLine_RunProgram: Unknown extended opcode %#x, length %#x at %08x\n", bOpCode, cbInstr, offOpCode)); + break; + } + + /* Advance the cursor to the end of the instruction . */ + rtDwarfCursor_AdvanceToPos(pCursor, pbEndOfInstr); + break; + } + } + } + + /* + * Check the status before looping. + */ + if (RT_FAILURE(rc)) + return rc; + if (RT_FAILURE(pCursor->rc)) + return pCursor->rc; + } + return rc; +} + + +/** + * Reads the include directories for a line number unit. + * + * @returns IPRT status code + * @param pLnState The line number program state. + * @param pCursor The cursor. + */ +static int rtDwarfLine_ReadFileNames(PRTDWARFLINESTATE pLnState, PRTDWARFCURSOR pCursor) +{ + int rc = rtDwarfLine_DefineFileName(pLnState, "/<bad-zero-file-name-entry>", 0); + if (RT_FAILURE(rc)) + return rc; + + for (;;) + { + const char *psz = rtDwarfCursor_GetSZ(pCursor, NULL); + if (!*psz) + break; + + uint64_t idxInc = rtDwarfCursor_GetULeb128(pCursor, UINT64_MAX); + rtDwarfCursor_SkipLeb128(pCursor); /* st_mtime */ + rtDwarfCursor_SkipLeb128(pCursor); /* st_size */ + + rc = rtDwarfLine_DefineFileName(pLnState, psz, idxInc); + if (RT_FAILURE(rc)) + return rc; + } + return pCursor->rc; +} + + +/** + * Reads the include directories for a line number unit. + * + * @returns IPRT status code + * @param pLnState The line number program state. + * @param pCursor The cursor. + */ +static int rtDwarfLine_ReadIncludePaths(PRTDWARFLINESTATE pLnState, PRTDWARFCURSOR pCursor) +{ + const char *psz = ""; /* The zeroth is the unit dir. */ + for (;;) + { + if ((pLnState->cIncPaths % 2) == 0) + { + void *pv = RTMemRealloc(pLnState->papszIncPaths, sizeof(pLnState->papszIncPaths[0]) * (pLnState->cIncPaths + 2)); + if (!pv) + return VERR_NO_MEMORY; + pLnState->papszIncPaths = (const char **)pv; + } + Log((" Path #%02u = '%s'\n", pLnState->cIncPaths, psz)); + pLnState->papszIncPaths[pLnState->cIncPaths] = psz; + pLnState->cIncPaths++; + + psz = rtDwarfCursor_GetSZ(pCursor, NULL); + if (!*psz) + break; + } + + return pCursor->rc; +} + + +/** + * Explodes the line number table for a compilation unit. + * + * @returns IPRT status code + * @param pThis The DWARF instance. + * @param pCursor The cursor to read the line number information + * via. + */ +static int rtDwarfLine_ExplodeUnit(PRTDBGMODDWARF pThis, PRTDWARFCURSOR pCursor) +{ + RTDWARFLINESTATE LnState; + RT_ZERO(LnState); + LnState.pDwarfMod = pThis; + + /* + * Parse the header. + */ + rtDwarfCursor_GetInitialLength(pCursor); + LnState.Hdr.uVer = rtDwarfCursor_GetUHalf(pCursor, 0); + if ( LnState.Hdr.uVer < 2 + || LnState.Hdr.uVer > 4) + return rtDwarfCursor_SkipUnit(pCursor); + + LnState.Hdr.offFirstOpcode = rtDwarfCursor_GetUOff(pCursor, 0); + uint8_t const * const pbFirstOpcode = rtDwarfCursor_CalcPos(pCursor, LnState.Hdr.offFirstOpcode); + + LnState.Hdr.cbMinInstr = rtDwarfCursor_GetUByte(pCursor, 0); + if (LnState.Hdr.uVer >= 4) + LnState.Hdr.cMaxOpsPerInstr = rtDwarfCursor_GetUByte(pCursor, 0); + else + LnState.Hdr.cMaxOpsPerInstr = 1; + LnState.Hdr.u8DefIsStmt = rtDwarfCursor_GetUByte(pCursor, 0); + LnState.Hdr.s8LineBase = rtDwarfCursor_GetSByte(pCursor, 0); + LnState.Hdr.u8LineRange = rtDwarfCursor_GetUByte(pCursor, 0); + LnState.Hdr.u8OpcodeBase = rtDwarfCursor_GetUByte(pCursor, 0); + + if ( !LnState.Hdr.u8OpcodeBase + || !LnState.Hdr.cMaxOpsPerInstr + || !LnState.Hdr.u8LineRange + || LnState.Hdr.u8DefIsStmt > 1) + return VERR_DWARF_BAD_LINE_NUMBER_HEADER; + Log2(("DWARF Line number header:\n" + " uVer %d\n" + " offFirstOpcode %#llx\n" + " cbMinInstr %u\n" + " cMaxOpsPerInstr %u\n" + " u8DefIsStmt %u\n" + " s8LineBase %d\n" + " u8LineRange %u\n" + " u8OpcodeBase %u\n", + LnState.Hdr.uVer, LnState.Hdr.offFirstOpcode, LnState.Hdr.cbMinInstr, LnState.Hdr.cMaxOpsPerInstr, + LnState.Hdr.u8DefIsStmt, LnState.Hdr.s8LineBase, LnState.Hdr.u8LineRange, LnState.Hdr.u8OpcodeBase)); + + LnState.Hdr.pacStdOperands = pCursor->pb; + for (uint8_t iStdOpcode = 1; iStdOpcode < LnState.Hdr.u8OpcodeBase; iStdOpcode++) + rtDwarfCursor_GetUByte(pCursor, 0); + + int rc = pCursor->rc; + if (RT_SUCCESS(rc)) + rc = rtDwarfLine_ReadIncludePaths(&LnState, pCursor); + if (RT_SUCCESS(rc)) + rc = rtDwarfLine_ReadFileNames(&LnState, pCursor); + + /* + * Run the program.... + */ + if (RT_SUCCESS(rc)) + rc = rtDwarfCursor_AdvanceToPos(pCursor, pbFirstOpcode); + if (RT_SUCCESS(rc)) + rc = rtDwarfLine_RunProgram(&LnState, pCursor); + + /* + * Clean up. + */ + size_t i = LnState.cFileNames; + while (i-- > 0) + RTStrFree(LnState.papszFileNames[i]); + RTMemFree(LnState.papszFileNames); + RTMemFree(LnState.papszIncPaths); + + Assert(rtDwarfCursor_IsAtEndOfUnit(pCursor) || RT_FAILURE(rc)); + return rc; +} + + +/** + * Explodes the line number table. + * + * The line numbers are insered into the debug info container. + * + * @returns IPRT status code + * @param pThis The DWARF instance. + */ +static int rtDwarfLine_ExplodeAll(PRTDBGMODDWARF pThis) +{ + if (!pThis->aSections[krtDbgModDwarfSect_line].fPresent) + return VINF_SUCCESS; + + RTDWARFCURSOR Cursor; + int rc = rtDwarfCursor_Init(&Cursor, pThis, krtDbgModDwarfSect_line); + if (RT_FAILURE(rc)) + return rc; + + while ( !rtDwarfCursor_IsAtEnd(&Cursor) + && RT_SUCCESS(rc)) + rc = rtDwarfLine_ExplodeUnit(pThis, &Cursor); + + return rtDwarfCursor_Delete(&Cursor, rc); +} + + +/* + * + * DWARF Abbreviations. + * DWARF Abbreviations. + * DWARF Abbreviations. + * + */ + +/** + * Deals with a cache miss in rtDwarfAbbrev_Lookup. + * + * @returns Pointer to abbreviation cache entry (read only). May be rendered + * invalid by subsequent calls to this function. + * @param pThis The DWARF instance. + * @param uCode The abbreviation code to lookup. + */ +static PCRTDWARFABBREV rtDwarfAbbrev_LookupMiss(PRTDBGMODDWARF pThis, uint32_t uCode) +{ + /* + * There is no entry with code zero. + */ + if (!uCode) + return NULL; + + /* + * Resize the cache array if the code is considered cachable. + */ + bool fFillCache = true; + if (pThis->cCachedAbbrevsAlloced < uCode) + { + if (uCode >= _64K) + fFillCache = false; + else + { + uint32_t cNew = RT_ALIGN(uCode, 64); + void *pv = RTMemRealloc(pThis->paCachedAbbrevs, sizeof(pThis->paCachedAbbrevs[0]) * cNew); + if (!pv) + fFillCache = false; + else + { + Log(("rtDwarfAbbrev_LookupMiss: Growing from %u to %u...\n", pThis->cCachedAbbrevsAlloced, cNew)); + pThis->paCachedAbbrevs = (PRTDWARFABBREV)pv; + for (uint32_t i = pThis->cCachedAbbrevsAlloced; i < cNew; i++) + pThis->paCachedAbbrevs[i].offAbbrev = UINT32_MAX; + pThis->cCachedAbbrevsAlloced = cNew; + } + } + } + + /* + * Walk the abbreviations till we find the desired code. + */ + RTDWARFCURSOR Cursor; + int rc = rtDwarfCursor_InitWithOffset(&Cursor, pThis, krtDbgModDwarfSect_abbrev, pThis->offCachedAbbrev); + if (RT_FAILURE(rc)) + return NULL; + + PRTDWARFABBREV pRet = NULL; + if (fFillCache) + { + /* + * Search for the entry and fill the cache while doing so. + * We assume that abbreviation codes for a unit will stop when we see + * zero code or when the code value drops. + */ + uint32_t uPrevCode = 0; + for (;;) + { + /* Read the 'header'. Skipping zero code bytes. */ +#ifdef LOG_ENABLED + uint32_t const offStart = rtDwarfCursor_CalcSectOffsetU32(&Cursor); +#endif + uint32_t const uCurCode = rtDwarfCursor_GetULeb128AsU32(&Cursor, 0); + if (pRet && (uCurCode == 0 || uCurCode < uPrevCode)) + break; /* probably end of unit. */ + if (uCurCode != 0) + { + uint32_t const uCurTag = rtDwarfCursor_GetULeb128AsU32(&Cursor, 0); + uint8_t const uChildren = rtDwarfCursor_GetU8(&Cursor, 0); + if (RT_FAILURE(Cursor.rc)) + break; + if ( uCurTag > 0xffff + || uChildren > 1) + { + Cursor.rc = VERR_DWARF_BAD_ABBREV; + break; + } + + /* Cache it? */ + if (uCurCode <= pThis->cCachedAbbrevsAlloced) + { + PRTDWARFABBREV pEntry = &pThis->paCachedAbbrevs[uCurCode - 1]; + if (pEntry->offAbbrev != pThis->offCachedAbbrev) + { + pEntry->offAbbrev = pThis->offCachedAbbrev; + pEntry->fChildren = RT_BOOL(uChildren); + pEntry->uTag = uCurTag; + pEntry->offSpec = rtDwarfCursor_CalcSectOffsetU32(&Cursor); +#ifdef LOG_ENABLED + pEntry->cbHdr = (uint8_t)(pEntry->offSpec - offStart); + Log7(("rtDwarfAbbrev_LookupMiss(%#x): fill: %#x: uTag=%#x offAbbrev=%#x%s\n", + uCode, offStart, pEntry->uTag, pEntry->offAbbrev, pEntry->fChildren ? " has-children" : "")); +#endif + if (uCurCode == uCode) + { + Assert(!pRet); + pRet = pEntry; + if (uCurCode == pThis->cCachedAbbrevsAlloced) + break; + } + } + else if (pRet) + break; /* Next unit, don't cache more. */ + /* else: We're growing the cache and re-reading old data. */ + } + + /* Skip the specification. */ + uint32_t uAttr, uForm; + do + { + uAttr = rtDwarfCursor_GetULeb128AsU32(&Cursor, 0); + uForm = rtDwarfCursor_GetULeb128AsU32(&Cursor, 0); + } while (uAttr != 0); + } + if (RT_FAILURE(Cursor.rc)) + break; + + /* Done? (Maximize cache filling.) */ + if ( pRet != NULL + && uCurCode >= pThis->cCachedAbbrevsAlloced) + break; + uPrevCode = uCurCode; + } + if (pRet) + Log6(("rtDwarfAbbrev_LookupMiss(%#x): uTag=%#x offSpec=%#x offAbbrev=%#x [fill]\n", + uCode, pRet->uTag, pRet->offSpec, pRet->offAbbrev)); + else + Log6(("rtDwarfAbbrev_LookupMiss(%#x): failed [fill]\n", uCode)); + } + else + { + /* + * Search for the entry with the desired code, no cache filling. + */ + for (;;) + { + /* Read the 'header'. */ +#ifdef LOG_ENABLED + uint32_t const offStart = rtDwarfCursor_CalcSectOffsetU32(&Cursor); +#endif + uint32_t const uCurCode = rtDwarfCursor_GetULeb128AsU32(&Cursor, 0); + uint32_t const uCurTag = rtDwarfCursor_GetULeb128AsU32(&Cursor, 0); + uint8_t const uChildren = rtDwarfCursor_GetU8(&Cursor, 0); + if (RT_FAILURE(Cursor.rc)) + break; + if ( uCurTag > 0xffff + || uChildren > 1) + { + Cursor.rc = VERR_DWARF_BAD_ABBREV; + break; + } + + /* Do we have a match? */ + if (uCurCode == uCode) + { + pRet = &pThis->LookupAbbrev; + pRet->fChildren = RT_BOOL(uChildren); + pRet->uTag = uCurTag; + pRet->offSpec = rtDwarfCursor_CalcSectOffsetU32(&Cursor); + pRet->offAbbrev = pThis->offCachedAbbrev; +#ifdef LOG_ENABLED + pRet->cbHdr = (uint8_t)(pRet->offSpec - offStart); +#endif + break; + } + + /* Skip the specification. */ + uint32_t uAttr, uForm; + do + { + uAttr = rtDwarfCursor_GetULeb128AsU32(&Cursor, 0); + uForm = rtDwarfCursor_GetULeb128AsU32(&Cursor, 0); + } while (uAttr != 0); + if (RT_FAILURE(Cursor.rc)) + break; + } + if (pRet) + Log6(("rtDwarfAbbrev_LookupMiss(%#x): uTag=%#x offSpec=%#x offAbbrev=%#x [no-fill]\n", + uCode, pRet->uTag, pRet->offSpec, pRet->offAbbrev)); + else + Log6(("rtDwarfAbbrev_LookupMiss(%#x): failed [no-fill]\n", uCode)); + } + + rtDwarfCursor_Delete(&Cursor, VINF_SUCCESS); + return pRet; +} + + +/** + * Looks up an abbreviation. + * + * @returns Pointer to abbreviation cache entry (read only). May be rendered + * invalid by subsequent calls to this function. + * @param pThis The DWARF instance. + * @param uCode The abbreviation code to lookup. + */ +static PCRTDWARFABBREV rtDwarfAbbrev_Lookup(PRTDBGMODDWARF pThis, uint32_t uCode) +{ + uCode -= 1; + if (uCode < pThis->cCachedAbbrevsAlloced) + { + if (pThis->paCachedAbbrevs[uCode].offAbbrev == pThis->offCachedAbbrev) + return &pThis->paCachedAbbrevs[uCode]; + } + return rtDwarfAbbrev_LookupMiss(pThis, uCode + 1); +} + + +/** + * Sets the abbreviation offset of the current unit. + * + * @param pThis The DWARF instance. + * @param offAbbrev The offset into the abbreviation section. + */ +static void rtDwarfAbbrev_SetUnitOffset(PRTDBGMODDWARF pThis, uint32_t offAbbrev) +{ + pThis->offCachedAbbrev = offAbbrev; +} + + + +/* + * + * DIE Attribute Parsers. + * DIE Attribute Parsers. + * DIE Attribute Parsers. + * + */ + +/** + * Gets the compilation unit a DIE belongs to. + * + * @returns The compilation unit DIE. + * @param pDie Some DIE in the unit. + */ +static PRTDWARFDIECOMPILEUNIT rtDwarfDie_GetCompileUnit(PRTDWARFDIE pDie) +{ + while (pDie->pParent) + pDie = pDie->pParent; + AssertReturn( pDie->uTag == DW_TAG_compile_unit + || pDie->uTag == DW_TAG_partial_unit, + NULL); + return (PRTDWARFDIECOMPILEUNIT)pDie; +} + + +/** + * Resolves a string section (debug_str) reference. + * + * @returns Pointer to the string (inside the string section). + * @param pThis The DWARF instance. + * @param pCursor The cursor. + * @param pszErrValue What to return on failure (@a + * pCursor->rc is set). + */ +static const char *rtDwarfDecodeHlp_GetStrp(PRTDBGMODDWARF pThis, PRTDWARFCURSOR pCursor, const char *pszErrValue) +{ + uint64_t offDebugStr = rtDwarfCursor_GetUOff(pCursor, UINT64_MAX); + if (RT_FAILURE(pCursor->rc)) + return pszErrValue; + + if (offDebugStr >= pThis->aSections[krtDbgModDwarfSect_str].cb) + { + /* Ugly: Exploit the cursor status field for reporting errors. */ + pCursor->rc = VERR_DWARF_BAD_INFO; + return pszErrValue; + } + + if (!pThis->aSections[krtDbgModDwarfSect_str].pv) + { + int rc = rtDbgModDwarfLoadSection(pThis, krtDbgModDwarfSect_str); + if (RT_FAILURE(rc)) + { + /* Ugly: Exploit the cursor status field for reporting errors. */ + pCursor->rc = rc; + return pszErrValue; + } + } + + return (const char *)pThis->aSections[krtDbgModDwarfSect_str].pv + (size_t)offDebugStr; +} + + +/** @callback_method_impl{FNRTDWARFATTRDECODER} */ +static DECLCALLBACK(int) rtDwarfDecode_Address(PRTDWARFDIE pDie, uint8_t *pbMember, PCRTDWARFATTRDESC pDesc, + uint32_t uForm, PRTDWARFCURSOR pCursor) +{ + AssertReturn(ATTR_GET_SIZE(pDesc) == sizeof(RTDWARFADDR), VERR_INTERNAL_ERROR_3); + NOREF(pDie); + + uint64_t uAddr; + switch (uForm) + { + case DW_FORM_addr: uAddr = rtDwarfCursor_GetNativeUOff(pCursor, 0); break; + case DW_FORM_data1: uAddr = rtDwarfCursor_GetU8(pCursor, 0); break; + case DW_FORM_data2: uAddr = rtDwarfCursor_GetU16(pCursor, 0); break; + case DW_FORM_data4: uAddr = rtDwarfCursor_GetU32(pCursor, 0); break; + case DW_FORM_data8: uAddr = rtDwarfCursor_GetU64(pCursor, 0); break; + case DW_FORM_udata: uAddr = rtDwarfCursor_GetULeb128(pCursor, 0); break; + default: + AssertMsgFailedReturn(("%#x (%s)\n", uForm, rtDwarfLog_FormName(uForm)), VERR_DWARF_UNEXPECTED_FORM); + } + if (RT_FAILURE(pCursor->rc)) + return pCursor->rc; + + PRTDWARFADDR pAddr = (PRTDWARFADDR)pbMember; + pAddr->uAddress = uAddr; + + Log4((" %-20s %#010llx [%s]\n", rtDwarfLog_AttrName(pDesc->uAttr), uAddr, rtDwarfLog_FormName(uForm))); + return VINF_SUCCESS; +} + + +/** @callback_method_impl{FNRTDWARFATTRDECODER} */ +static DECLCALLBACK(int) rtDwarfDecode_Bool(PRTDWARFDIE pDie, uint8_t *pbMember, PCRTDWARFATTRDESC pDesc, + uint32_t uForm, PRTDWARFCURSOR pCursor) +{ + AssertReturn(ATTR_GET_SIZE(pDesc) == sizeof(bool), VERR_INTERNAL_ERROR_3); + NOREF(pDie); + + bool *pfMember = (bool *)pbMember; + switch (uForm) + { + case DW_FORM_flag: + { + uint8_t b = rtDwarfCursor_GetU8(pCursor, UINT8_MAX); + if (b > 1) + { + Log(("Unexpected boolean value %#x\n", b)); + return RT_FAILURE(pCursor->rc) ? pCursor->rc : pCursor->rc = VERR_DWARF_BAD_INFO; + } + *pfMember = RT_BOOL(b); + break; + } + + case DW_FORM_flag_present: + *pfMember = true; + break; + + default: + AssertMsgFailedReturn(("%#x\n", uForm), VERR_DWARF_UNEXPECTED_FORM); + } + + Log4((" %-20s %RTbool [%s]\n", rtDwarfLog_AttrName(pDesc->uAttr), *pfMember, rtDwarfLog_FormName(uForm))); + return VINF_SUCCESS; +} + + +/** @callback_method_impl{FNRTDWARFATTRDECODER} */ +static DECLCALLBACK(int) rtDwarfDecode_LowHighPc(PRTDWARFDIE pDie, uint8_t *pbMember, PCRTDWARFATTRDESC pDesc, + uint32_t uForm, PRTDWARFCURSOR pCursor) +{ + AssertReturn(ATTR_GET_SIZE(pDesc) == sizeof(RTDWARFADDRRANGE), VERR_INTERNAL_ERROR_3); + AssertReturn(pDesc->uAttr == DW_AT_low_pc || pDesc->uAttr == DW_AT_high_pc, VERR_INTERNAL_ERROR_3); + NOREF(pDie); + + uint64_t uAddr; + switch (uForm) + { + case DW_FORM_addr: uAddr = rtDwarfCursor_GetNativeUOff(pCursor, 0); break; + case DW_FORM_data1: uAddr = rtDwarfCursor_GetU8(pCursor, 0); break; + case DW_FORM_data2: uAddr = rtDwarfCursor_GetU16(pCursor, 0); break; + case DW_FORM_data4: uAddr = rtDwarfCursor_GetU32(pCursor, 0); break; + case DW_FORM_data8: uAddr = rtDwarfCursor_GetU64(pCursor, 0); break; + case DW_FORM_udata: uAddr = rtDwarfCursor_GetULeb128(pCursor, 0); break; + default: + AssertMsgFailedReturn(("%#x\n", uForm), VERR_DWARF_UNEXPECTED_FORM); + } + if (RT_FAILURE(pCursor->rc)) + return pCursor->rc; + + PRTDWARFADDRRANGE pRange = (PRTDWARFADDRRANGE)pbMember; + if (pDesc->uAttr == DW_AT_low_pc) + { + if (pRange->fHaveLowAddress) + { + Log(("rtDwarfDecode_LowHighPc: Duplicate DW_AT_low_pc\n")); + return pCursor->rc = VERR_DWARF_BAD_INFO; + } + pRange->fHaveLowAddress = true; + pRange->uLowAddress = uAddr; + } + else + { + if (pRange->fHaveHighAddress) + { + Log(("rtDwarfDecode_LowHighPc: Duplicate DW_AT_high_pc\n")); + return pCursor->rc = VERR_DWARF_BAD_INFO; + } + pRange->fHaveHighAddress = true; + pRange->fHaveHighIsAddress = uForm == DW_FORM_addr; + if (!pRange->fHaveHighIsAddress && pRange->fHaveLowAddress) + { + pRange->fHaveHighIsAddress = true; + pRange->uHighAddress = uAddr + pRange->uLowAddress; + } + else + pRange->uHighAddress = uAddr; + + } + pRange->cAttrs++; + + Log4((" %-20s %#010llx [%s]\n", rtDwarfLog_AttrName(pDesc->uAttr), uAddr, rtDwarfLog_FormName(uForm))); + return VINF_SUCCESS; +} + + +/** @callback_method_impl{FNRTDWARFATTRDECODER} */ +static DECLCALLBACK(int) rtDwarfDecode_Ranges(PRTDWARFDIE pDie, uint8_t *pbMember, PCRTDWARFATTRDESC pDesc, + uint32_t uForm, PRTDWARFCURSOR pCursor) +{ + AssertReturn(ATTR_GET_SIZE(pDesc) == sizeof(RTDWARFADDRRANGE), VERR_INTERNAL_ERROR_3); + AssertReturn(pDesc->uAttr == DW_AT_ranges, VERR_INTERNAL_ERROR_3); + NOREF(pDie); + + /* Decode it. */ + uint64_t off; + switch (uForm) + { + case DW_FORM_addr: off = rtDwarfCursor_GetNativeUOff(pCursor, 0); break; + case DW_FORM_data4: off = rtDwarfCursor_GetU32(pCursor, 0); break; + case DW_FORM_data8: off = rtDwarfCursor_GetU64(pCursor, 0); break; + case DW_FORM_sec_offset: off = rtDwarfCursor_GetUOff(pCursor, 0); break; + default: + AssertMsgFailedReturn(("%#x\n", uForm), VERR_DWARF_UNEXPECTED_FORM); + } + if (RT_FAILURE(pCursor->rc)) + return pCursor->rc; + + /* Validate the offset and load the ranges. */ + PRTDBGMODDWARF pThis = pCursor->pDwarfMod; + if (off >= pThis->aSections[krtDbgModDwarfSect_ranges].cb) + { + Log(("rtDwarfDecode_Ranges: bad ranges off=%#llx\n", off)); + return pCursor->rc = VERR_DWARF_BAD_POS; + } + + if (!pThis->aSections[krtDbgModDwarfSect_ranges].pv) + { + int rc = rtDbgModDwarfLoadSection(pThis, krtDbgModDwarfSect_ranges); + if (RT_FAILURE(rc)) + return pCursor->rc = rc; + } + + /* Store the result. */ + PRTDWARFADDRRANGE pRange = (PRTDWARFADDRRANGE)pbMember; + if (pRange->fHaveRanges) + { + Log(("rtDwarfDecode_Ranges: Duplicate DW_AT_ranges\n")); + return pCursor->rc = VERR_DWARF_BAD_INFO; + } + pRange->fHaveRanges = true; + pRange->cAttrs++; + pRange->pbRanges = (uint8_t const *)pThis->aSections[krtDbgModDwarfSect_ranges].pv + (size_t)off; + + Log4((" %-20s TODO [%s]\n", rtDwarfLog_AttrName(pDesc->uAttr), rtDwarfLog_FormName(uForm))); + return VINF_SUCCESS; +} + + +/** @callback_method_impl{FNRTDWARFATTRDECODER} */ +static DECLCALLBACK(int) rtDwarfDecode_Reference(PRTDWARFDIE pDie, uint8_t *pbMember, PCRTDWARFATTRDESC pDesc, + uint32_t uForm, PRTDWARFCURSOR pCursor) +{ + AssertReturn(ATTR_GET_SIZE(pDesc) == sizeof(RTDWARFREF), VERR_INTERNAL_ERROR_3); + + /* Decode it. */ + uint64_t off; + krtDwarfRef enmWrt = krtDwarfRef_SameUnit; + switch (uForm) + { + case DW_FORM_ref1: off = rtDwarfCursor_GetU8(pCursor, 0); break; + case DW_FORM_ref2: off = rtDwarfCursor_GetU16(pCursor, 0); break; + case DW_FORM_ref4: off = rtDwarfCursor_GetU32(pCursor, 0); break; + case DW_FORM_ref8: off = rtDwarfCursor_GetU64(pCursor, 0); break; + case DW_FORM_ref_udata: off = rtDwarfCursor_GetULeb128(pCursor, 0); break; + + case DW_FORM_ref_addr: + enmWrt = krtDwarfRef_InfoSection; + off = rtDwarfCursor_GetUOff(pCursor, 0); + break; + + case DW_FORM_ref_sig8: + enmWrt = krtDwarfRef_TypeId64; + off = rtDwarfCursor_GetU64(pCursor, 0); + break; + + default: + AssertMsgFailedReturn(("%#x\n", uForm), VERR_DWARF_UNEXPECTED_FORM); + } + if (RT_FAILURE(pCursor->rc)) + return pCursor->rc; + + /* Validate the offset and convert to debug_info relative offsets. */ + if (enmWrt == krtDwarfRef_InfoSection) + { + if (off >= pCursor->pDwarfMod->aSections[krtDbgModDwarfSect_info].cb) + { + Log(("rtDwarfDecode_Reference: bad info off=%#llx\n", off)); + return pCursor->rc = VERR_DWARF_BAD_POS; + } + } + else if (enmWrt == krtDwarfRef_SameUnit) + { + PRTDWARFDIECOMPILEUNIT pUnit = rtDwarfDie_GetCompileUnit(pDie); + if (off >= pUnit->cbUnit) + { + Log(("rtDwarfDecode_Reference: bad unit off=%#llx\n", off)); + return pCursor->rc = VERR_DWARF_BAD_POS; + } + off += pUnit->offUnit; + enmWrt = krtDwarfRef_InfoSection; + } + /* else: not bother verifying/resolving the indirect type reference yet. */ + + /* Store it */ + PRTDWARFREF pRef = (PRTDWARFREF)pbMember; + pRef->enmWrt = enmWrt; + pRef->off = off; + + Log4((" %-20s %d:%#010llx [%s]\n", rtDwarfLog_AttrName(pDesc->uAttr), enmWrt, off, rtDwarfLog_FormName(uForm))); + return VINF_SUCCESS; +} + + +/** @callback_method_impl{FNRTDWARFATTRDECODER} */ +static DECLCALLBACK(int) rtDwarfDecode_SectOff(PRTDWARFDIE pDie, uint8_t *pbMember, PCRTDWARFATTRDESC pDesc, + uint32_t uForm, PRTDWARFCURSOR pCursor) +{ + AssertReturn(ATTR_GET_SIZE(pDesc) == sizeof(RTDWARFREF), VERR_INTERNAL_ERROR_3); + NOREF(pDie); + + uint64_t off; + switch (uForm) + { + case DW_FORM_data4: off = rtDwarfCursor_GetU32(pCursor, 0); break; + case DW_FORM_data8: off = rtDwarfCursor_GetU64(pCursor, 0); break; + case DW_FORM_sec_offset: off = rtDwarfCursor_GetUOff(pCursor, 0); break; + default: + AssertMsgFailedReturn(("%#x (%s)\n", uForm, rtDwarfLog_FormName(uForm)), VERR_DWARF_UNEXPECTED_FORM); + } + if (RT_FAILURE(pCursor->rc)) + return pCursor->rc; + + krtDbgModDwarfSect enmSect; + krtDwarfRef enmWrt; + switch (pDesc->uAttr) + { + case DW_AT_stmt_list: enmSect = krtDbgModDwarfSect_line; enmWrt = krtDwarfRef_LineSection; break; + case DW_AT_macro_info: enmSect = krtDbgModDwarfSect_loc; enmWrt = krtDwarfRef_LocSection; break; + case DW_AT_ranges: enmSect = krtDbgModDwarfSect_ranges; enmWrt = krtDwarfRef_RangesSection; break; + default: + AssertMsgFailedReturn(("%u (%s)\n", pDesc->uAttr, rtDwarfLog_AttrName(pDesc->uAttr)), VERR_INTERNAL_ERROR_4); + } + size_t cbSect = pCursor->pDwarfMod->aSections[enmSect].cb; + if (off >= cbSect) + { + /* Watcom generates offset past the end of the section, increasing the + offset by one for each compile unit. So, just fudge it. */ + Log(("rtDwarfDecode_SectOff: bad off=%#llx, attr %#x (%s), enmSect=%d cb=%#llx; Assuming watcom/gcc.\n", off, + pDesc->uAttr, rtDwarfLog_AttrName(pDesc->uAttr), enmSect, cbSect)); + off = cbSect; + } + + PRTDWARFREF pRef = (PRTDWARFREF)pbMember; + pRef->enmWrt = enmWrt; + pRef->off = off; + + Log4((" %-20s %d:%#010llx [%s]\n", rtDwarfLog_AttrName(pDesc->uAttr), enmWrt, off, rtDwarfLog_FormName(uForm))); + return VINF_SUCCESS; +} + + +/** @callback_method_impl{FNRTDWARFATTRDECODER} */ +static DECLCALLBACK(int) rtDwarfDecode_String(PRTDWARFDIE pDie, uint8_t *pbMember, PCRTDWARFATTRDESC pDesc, + uint32_t uForm, PRTDWARFCURSOR pCursor) +{ + AssertReturn(ATTR_GET_SIZE(pDesc) == sizeof(const char *), VERR_INTERNAL_ERROR_3); + NOREF(pDie); + + const char *psz; + switch (uForm) + { + case DW_FORM_string: + psz = rtDwarfCursor_GetSZ(pCursor, NULL); + break; + + case DW_FORM_strp: + psz = rtDwarfDecodeHlp_GetStrp(pCursor->pDwarfMod, pCursor, NULL); + break; + + default: + AssertMsgFailedReturn(("%#x\n", uForm), VERR_DWARF_UNEXPECTED_FORM); + } + + *(const char **)pbMember = psz; + Log4((" %-20s '%s' [%s]\n", rtDwarfLog_AttrName(pDesc->uAttr), psz, rtDwarfLog_FormName(uForm))); + return pCursor->rc; +} + + +/** @callback_method_impl{FNRTDWARFATTRDECODER} */ +static DECLCALLBACK(int) rtDwarfDecode_UnsignedInt(PRTDWARFDIE pDie, uint8_t *pbMember, PCRTDWARFATTRDESC pDesc, + uint32_t uForm, PRTDWARFCURSOR pCursor) +{ + NOREF(pDie); + uint64_t u64Val; + switch (uForm) + { + case DW_FORM_udata: u64Val = rtDwarfCursor_GetULeb128(pCursor, 0); break; + case DW_FORM_data1: u64Val = rtDwarfCursor_GetU8(pCursor, 0); break; + case DW_FORM_data2: u64Val = rtDwarfCursor_GetU16(pCursor, 0); break; + case DW_FORM_data4: u64Val = rtDwarfCursor_GetU32(pCursor, 0); break; + case DW_FORM_data8: u64Val = rtDwarfCursor_GetU64(pCursor, 0); break; + default: + AssertMsgFailedReturn(("%#x\n", uForm), VERR_DWARF_UNEXPECTED_FORM); + } + if (RT_FAILURE(pCursor->rc)) + return pCursor->rc; + + switch (ATTR_GET_SIZE(pDesc)) + { + case 1: + *pbMember = (uint8_t)u64Val; + if (*pbMember != u64Val) + { + AssertFailed(); + return VERR_OUT_OF_RANGE; + } + break; + + case 2: + *(uint16_t *)pbMember = (uint16_t)u64Val; + if (*(uint16_t *)pbMember != u64Val) + { + AssertFailed(); + return VERR_OUT_OF_RANGE; + } + break; + + case 4: + *(uint32_t *)pbMember = (uint32_t)u64Val; + if (*(uint32_t *)pbMember != u64Val) + { + AssertFailed(); + return VERR_OUT_OF_RANGE; + } + break; + + case 8: + *(uint64_t *)pbMember = (uint64_t)u64Val; + if (*(uint64_t *)pbMember != u64Val) + { + AssertFailed(); + return VERR_OUT_OF_RANGE; + } + break; + + default: + AssertMsgFailedReturn(("%#x\n", ATTR_GET_SIZE(pDesc)), VERR_INTERNAL_ERROR_2); + } + return VINF_SUCCESS; +} + + +/** + * Initialize location interpreter state from cursor & form. + * + * @returns IPRT status code. + * @retval VERR_NOT_FOUND if no location information (i.e. there is source but + * it resulted in no byte code). + * @param pLoc The location state structure to initialize. + * @param pCursor The cursor to read from. + * @param uForm The attribute form. + */ +static int rtDwarfLoc_Init(PRTDWARFLOCST pLoc, PRTDWARFCURSOR pCursor, uint32_t uForm) +{ + uint32_t cbBlock; + switch (uForm) + { + case DW_FORM_block1: + cbBlock = rtDwarfCursor_GetU8(pCursor, 0); + break; + + case DW_FORM_block2: + cbBlock = rtDwarfCursor_GetU16(pCursor, 0); + break; + + case DW_FORM_block4: + cbBlock = rtDwarfCursor_GetU32(pCursor, 0); + break; + + case DW_FORM_block: + cbBlock = rtDwarfCursor_GetULeb128(pCursor, 0); + break; + + default: + AssertMsgFailedReturn(("uForm=%#x\n", uForm), VERR_DWARF_UNEXPECTED_FORM); + } + if (!cbBlock) + return VERR_NOT_FOUND; + + int rc = rtDwarfCursor_InitForBlock(&pLoc->Cursor, pCursor, cbBlock); + if (RT_FAILURE(rc)) + return rc; + pLoc->iTop = -1; + return VINF_SUCCESS; +} + + +/** + * Pushes a value onto the stack. + * + * @returns VINF_SUCCESS or VERR_DWARF_STACK_OVERFLOW. + * @param pLoc The state. + * @param uValue The value to push. + */ +static int rtDwarfLoc_Push(PRTDWARFLOCST pLoc, uint64_t uValue) +{ + int iTop = pLoc->iTop + 1; + AssertReturn((unsigned)iTop < RT_ELEMENTS(pLoc->auStack), VERR_DWARF_STACK_OVERFLOW); + pLoc->auStack[iTop] = uValue; + pLoc->iTop = iTop; + return VINF_SUCCESS; +} + + +static int rtDwarfLoc_Evaluate(PRTDWARFLOCST pLoc, void *pvLater, void *pvUser) +{ + RT_NOREF_PV(pvLater); RT_NOREF_PV(pvUser); + + while (!rtDwarfCursor_IsAtEndOfUnit(&pLoc->Cursor)) + { + /* Read the next opcode.*/ + uint8_t const bOpcode = rtDwarfCursor_GetU8(&pLoc->Cursor, 0); + + /* Get its operands. */ + uint64_t uOperand1 = 0; + uint64_t uOperand2 = 0; + switch (bOpcode) + { + case DW_OP_addr: + uOperand1 = rtDwarfCursor_GetNativeUOff(&pLoc->Cursor, 0); + break; + case DW_OP_pick: + case DW_OP_const1u: + case DW_OP_deref_size: + case DW_OP_xderef_size: + uOperand1 = rtDwarfCursor_GetU8(&pLoc->Cursor, 0); + break; + case DW_OP_const1s: + uOperand1 = (int8_t)rtDwarfCursor_GetU8(&pLoc->Cursor, 0); + break; + case DW_OP_const2u: + uOperand1 = rtDwarfCursor_GetU16(&pLoc->Cursor, 0); + break; + case DW_OP_skip: + case DW_OP_bra: + case DW_OP_const2s: + uOperand1 = (int16_t)rtDwarfCursor_GetU16(&pLoc->Cursor, 0); + break; + case DW_OP_const4u: + uOperand1 = rtDwarfCursor_GetU32(&pLoc->Cursor, 0); + break; + case DW_OP_const4s: + uOperand1 = (int32_t)rtDwarfCursor_GetU32(&pLoc->Cursor, 0); + break; + case DW_OP_const8u: + uOperand1 = rtDwarfCursor_GetU64(&pLoc->Cursor, 0); + break; + case DW_OP_const8s: + uOperand1 = rtDwarfCursor_GetU64(&pLoc->Cursor, 0); + break; + case DW_OP_regx: + case DW_OP_piece: + case DW_OP_plus_uconst: + case DW_OP_constu: + uOperand1 = rtDwarfCursor_GetULeb128(&pLoc->Cursor, 0); + break; + case DW_OP_consts: + case DW_OP_fbreg: + case DW_OP_breg0+0: case DW_OP_breg0+1: case DW_OP_breg0+2: case DW_OP_breg0+3: + case DW_OP_breg0+4: case DW_OP_breg0+5: case DW_OP_breg0+6: case DW_OP_breg0+7: + case DW_OP_breg0+8: case DW_OP_breg0+9: case DW_OP_breg0+10: case DW_OP_breg0+11: + case DW_OP_breg0+12: case DW_OP_breg0+13: case DW_OP_breg0+14: case DW_OP_breg0+15: + case DW_OP_breg0+16: case DW_OP_breg0+17: case DW_OP_breg0+18: case DW_OP_breg0+19: + case DW_OP_breg0+20: case DW_OP_breg0+21: case DW_OP_breg0+22: case DW_OP_breg0+23: + case DW_OP_breg0+24: case DW_OP_breg0+25: case DW_OP_breg0+26: case DW_OP_breg0+27: + case DW_OP_breg0+28: case DW_OP_breg0+29: case DW_OP_breg0+30: case DW_OP_breg0+31: + uOperand1 = rtDwarfCursor_GetSLeb128(&pLoc->Cursor, 0); + break; + case DW_OP_bregx: + uOperand1 = rtDwarfCursor_GetULeb128(&pLoc->Cursor, 0); + uOperand2 = rtDwarfCursor_GetSLeb128(&pLoc->Cursor, 0); + break; + } + if (RT_FAILURE(pLoc->Cursor.rc)) + break; + + /* Interpret the opcode. */ + int rc; + switch (bOpcode) + { + case DW_OP_const1u: + case DW_OP_const1s: + case DW_OP_const2u: + case DW_OP_const2s: + case DW_OP_const4u: + case DW_OP_const4s: + case DW_OP_const8u: + case DW_OP_const8s: + case DW_OP_constu: + case DW_OP_consts: + case DW_OP_addr: + rc = rtDwarfLoc_Push(pLoc, uOperand1); + break; + case DW_OP_lit0 + 0: case DW_OP_lit0 + 1: case DW_OP_lit0 + 2: case DW_OP_lit0 + 3: + case DW_OP_lit0 + 4: case DW_OP_lit0 + 5: case DW_OP_lit0 + 6: case DW_OP_lit0 + 7: + case DW_OP_lit0 + 8: case DW_OP_lit0 + 9: case DW_OP_lit0 + 10: case DW_OP_lit0 + 11: + case DW_OP_lit0 + 12: case DW_OP_lit0 + 13: case DW_OP_lit0 + 14: case DW_OP_lit0 + 15: + case DW_OP_lit0 + 16: case DW_OP_lit0 + 17: case DW_OP_lit0 + 18: case DW_OP_lit0 + 19: + case DW_OP_lit0 + 20: case DW_OP_lit0 + 21: case DW_OP_lit0 + 22: case DW_OP_lit0 + 23: + case DW_OP_lit0 + 24: case DW_OP_lit0 + 25: case DW_OP_lit0 + 26: case DW_OP_lit0 + 27: + case DW_OP_lit0 + 28: case DW_OP_lit0 + 29: case DW_OP_lit0 + 30: case DW_OP_lit0 + 31: + rc = rtDwarfLoc_Push(pLoc, bOpcode - DW_OP_lit0); + break; + case DW_OP_nop: + break; + case DW_OP_dup: /** @todo 0 operands. */ + case DW_OP_drop: /** @todo 0 operands. */ + case DW_OP_over: /** @todo 0 operands. */ + case DW_OP_pick: /** @todo 1 operands, a 1-byte stack index. */ + case DW_OP_swap: /** @todo 0 operands. */ + case DW_OP_rot: /** @todo 0 operands. */ + case DW_OP_abs: /** @todo 0 operands. */ + case DW_OP_and: /** @todo 0 operands. */ + case DW_OP_div: /** @todo 0 operands. */ + case DW_OP_minus: /** @todo 0 operands. */ + case DW_OP_mod: /** @todo 0 operands. */ + case DW_OP_mul: /** @todo 0 operands. */ + case DW_OP_neg: /** @todo 0 operands. */ + case DW_OP_not: /** @todo 0 operands. */ + case DW_OP_or: /** @todo 0 operands. */ + case DW_OP_plus: /** @todo 0 operands. */ + case DW_OP_plus_uconst: /** @todo 1 operands, a ULEB128 addend. */ + case DW_OP_shl: /** @todo 0 operands. */ + case DW_OP_shr: /** @todo 0 operands. */ + case DW_OP_shra: /** @todo 0 operands. */ + case DW_OP_xor: /** @todo 0 operands. */ + case DW_OP_skip: /** @todo 1 signed 2-byte constant. */ + case DW_OP_bra: /** @todo 1 signed 2-byte constant. */ + case DW_OP_eq: /** @todo 0 operands. */ + case DW_OP_ge: /** @todo 0 operands. */ + case DW_OP_gt: /** @todo 0 operands. */ + case DW_OP_le: /** @todo 0 operands. */ + case DW_OP_lt: /** @todo 0 operands. */ + case DW_OP_ne: /** @todo 0 operands. */ + case DW_OP_reg0 + 0: case DW_OP_reg0 + 1: case DW_OP_reg0 + 2: case DW_OP_reg0 + 3: /** @todo 0 operands - reg 0..31. */ + case DW_OP_reg0 + 4: case DW_OP_reg0 + 5: case DW_OP_reg0 + 6: case DW_OP_reg0 + 7: + case DW_OP_reg0 + 8: case DW_OP_reg0 + 9: case DW_OP_reg0 + 10: case DW_OP_reg0 + 11: + case DW_OP_reg0 + 12: case DW_OP_reg0 + 13: case DW_OP_reg0 + 14: case DW_OP_reg0 + 15: + case DW_OP_reg0 + 16: case DW_OP_reg0 + 17: case DW_OP_reg0 + 18: case DW_OP_reg0 + 19: + case DW_OP_reg0 + 20: case DW_OP_reg0 + 21: case DW_OP_reg0 + 22: case DW_OP_reg0 + 23: + case DW_OP_reg0 + 24: case DW_OP_reg0 + 25: case DW_OP_reg0 + 26: case DW_OP_reg0 + 27: + case DW_OP_reg0 + 28: case DW_OP_reg0 + 29: case DW_OP_reg0 + 30: case DW_OP_reg0 + 31: + case DW_OP_breg0+ 0: case DW_OP_breg0+ 1: case DW_OP_breg0+ 2: case DW_OP_breg0+ 3: /** @todo 1 operand, a SLEB128 offset. */ + case DW_OP_breg0+ 4: case DW_OP_breg0+ 5: case DW_OP_breg0+ 6: case DW_OP_breg0+ 7: + case DW_OP_breg0+ 8: case DW_OP_breg0+ 9: case DW_OP_breg0+ 10: case DW_OP_breg0+ 11: + case DW_OP_breg0+ 12: case DW_OP_breg0+ 13: case DW_OP_breg0+ 14: case DW_OP_breg0+ 15: + case DW_OP_breg0+ 16: case DW_OP_breg0+ 17: case DW_OP_breg0+ 18: case DW_OP_breg0+ 19: + case DW_OP_breg0+ 20: case DW_OP_breg0+ 21: case DW_OP_breg0+ 22: case DW_OP_breg0+ 23: + case DW_OP_breg0+ 24: case DW_OP_breg0+ 25: case DW_OP_breg0+ 26: case DW_OP_breg0+ 27: + case DW_OP_breg0+ 28: case DW_OP_breg0+ 29: case DW_OP_breg0+ 30: case DW_OP_breg0+ 31: + case DW_OP_piece: /** @todo 1 operand, a ULEB128 size of piece addressed. */ + case DW_OP_regx: /** @todo 1 operand, a ULEB128 register. */ + case DW_OP_fbreg: /** @todo 1 operand, a SLEB128 offset. */ + case DW_OP_bregx: /** @todo 2 operands, a ULEB128 register followed by a SLEB128 offset. */ + case DW_OP_deref: /** @todo 0 operands. */ + case DW_OP_deref_size: /** @todo 1 operand, a 1-byte size of data retrieved. */ + case DW_OP_xderef: /** @todo 0 operands. */ + case DW_OP_xderef_size: /** @todo 1 operand, a 1-byte size of data retrieved. */ + AssertMsgFailedReturn(("bOpcode=%#x\n", bOpcode), VERR_DWARF_TODO); + default: + AssertMsgFailedReturn(("bOpcode=%#x\n", bOpcode), VERR_DWARF_UNKNOWN_LOC_OPCODE); + } + } + + return pLoc->Cursor.rc; +} + + +/** @callback_method_impl{FNRTDWARFATTRDECODER} */ +static DECLCALLBACK(int) rtDwarfDecode_SegmentLoc(PRTDWARFDIE pDie, uint8_t *pbMember, PCRTDWARFATTRDESC pDesc, + uint32_t uForm, PRTDWARFCURSOR pCursor) +{ + NOREF(pDie); + AssertReturn(ATTR_GET_SIZE(pDesc) == 2, VERR_DWARF_IPE); + + int rc; + if ( uForm == DW_FORM_block + || uForm == DW_FORM_block1 + || uForm == DW_FORM_block2 + || uForm == DW_FORM_block4) + { + RTDWARFLOCST LocSt; + rc = rtDwarfLoc_Init(&LocSt, pCursor, uForm); + if (RT_SUCCESS(rc)) + { + rc = rtDwarfLoc_Evaluate(&LocSt, NULL, NULL); + if (RT_SUCCESS(rc)) + { + if (LocSt.iTop >= 0) + { + *(uint16_t *)pbMember = LocSt.auStack[LocSt.iTop]; + Log4((" %-20s %#06llx [%s]\n", rtDwarfLog_AttrName(pDesc->uAttr), + LocSt.auStack[LocSt.iTop], rtDwarfLog_FormName(uForm))); + return VINF_SUCCESS; + } + rc = VERR_DWARF_STACK_UNDERFLOW; + } + } + } + else + rc = rtDwarfDecode_UnsignedInt(pDie, pbMember, pDesc, uForm, pCursor); + return rc; +} + +/* + * + * DWARF debug_info parser + * DWARF debug_info parser + * DWARF debug_info parser + * + */ + + +/** + * Special hack to get the name and/or linkage name for a subprogram via a + * specification reference. + * + * Since this is a hack, we ignore failure. + * + * If we want to really make use of DWARF info, we'll have to create some kind + * of lookup tree for handling this. But currently we don't, so a hack will + * suffice. + * + * @param pThis The DWARF instance. + * @param pSubProgram The subprogram which is short on names. + */ +static void rtDwarfInfo_TryGetSubProgramNameFromSpecRef(PRTDBGMODDWARF pThis, PRTDWARFDIESUBPROGRAM pSubProgram) +{ + /* + * Must have a spec ref, and it must be in the info section. + */ + if (pSubProgram->SpecRef.enmWrt != krtDwarfRef_InfoSection) + return; + + /* + * Create a cursor for reading the info and then the abbrivation code + * starting the off the DIE. + */ + RTDWARFCURSOR InfoCursor; + int rc = rtDwarfCursor_InitWithOffset(&InfoCursor, pThis, krtDbgModDwarfSect_info, pSubProgram->SpecRef.off); + if (RT_FAILURE(rc)) + return; + + uint32_t uAbbrCode = rtDwarfCursor_GetULeb128AsU32(&InfoCursor, UINT32_MAX); + if (uAbbrCode) + { + /* Only references to subprogram tags are interesting here. */ + PCRTDWARFABBREV pAbbrev = rtDwarfAbbrev_Lookup(pThis, uAbbrCode); + if ( pAbbrev + && pAbbrev->uTag == DW_TAG_subprogram) + { + /* + * Use rtDwarfInfo_ParseDie to do the parsing, but with a different + * attribute spec than usual. + */ + rtDwarfInfo_ParseDie(pThis, &pSubProgram->Core, &g_SubProgramSpecHackDesc, &InfoCursor, + pAbbrev, false /*fInitDie*/); + } + } + + rtDwarfCursor_Delete(&InfoCursor, VINF_SUCCESS); +} + + +/** + * Select which name to use. + * + * @returns One of the names. + * @param pszName The DWARF name, may exclude namespace and class. + * Can also be NULL. + * @param pszLinkageName The linkage name. Can be NULL. + */ +static const char *rtDwarfInfo_SelectName(const char *pszName, const char *pszLinkageName) +{ + if (!pszName || !pszLinkageName) + return pszName ? pszName : pszLinkageName; + + /* + * Some heuristics for selecting the link name if the normal name is missing + * namespace or class prefixes. + */ + size_t cchName = strlen(pszName); + size_t cchLinkageName = strlen(pszLinkageName); + if (cchLinkageName <= cchName + 1) + return pszName; + + const char *psz = strstr(pszLinkageName, pszName); + if (!psz || psz - pszLinkageName < 4) + return pszName; + + return pszLinkageName; +} + + +/** + * Parse the attributes of a DIE. + * + * @returns IPRT status code. + * @param pThis The DWARF instance. + * @param pDie The internal DIE structure to fill. + */ +static int rtDwarfInfo_SnoopSymbols(PRTDBGMODDWARF pThis, PRTDWARFDIE pDie) +{ + int rc = VINF_SUCCESS; + switch (pDie->uTag) + { + case DW_TAG_subprogram: + { + PRTDWARFDIESUBPROGRAM pSubProgram = (PRTDWARFDIESUBPROGRAM)pDie; + + /* Obtain referenced specification there is only partial info. */ + if ( pSubProgram->PcRange.cAttrs + && !pSubProgram->pszName) + rtDwarfInfo_TryGetSubProgramNameFromSpecRef(pThis, pSubProgram); + + if (pSubProgram->PcRange.cAttrs) + { + if (pSubProgram->PcRange.fHaveRanges) + Log5(("subprogram %s (%s) <implement ranges>\n", pSubProgram->pszName, pSubProgram->pszLinkageName)); + else + { + Log5(("subprogram %s (%s) %#llx-%#llx%s\n", pSubProgram->pszName, pSubProgram->pszLinkageName, + pSubProgram->PcRange.uLowAddress, pSubProgram->PcRange.uHighAddress, + pSubProgram->PcRange.cAttrs == 2 ? "" : " !bad!")); + if ( ( pSubProgram->pszName || pSubProgram->pszLinkageName) + && pSubProgram->PcRange.cAttrs == 2) + { + if (pThis->iWatcomPass == 1) + rc = rtDbgModDwarfRecordSegOffset(pThis, pSubProgram->uSegment, pSubProgram->PcRange.uHighAddress); + else + { + RTDBGSEGIDX iSeg; + RTUINTPTR offSeg; + rc = rtDbgModDwarfLinkAddressToSegOffset(pThis, pSubProgram->uSegment, + pSubProgram->PcRange.uLowAddress, + &iSeg, &offSeg); + if (RT_SUCCESS(rc)) + { + uint64_t cb; + if (pSubProgram->PcRange.uHighAddress >= pSubProgram->PcRange.uLowAddress) + cb = pSubProgram->PcRange.uHighAddress - pSubProgram->PcRange.uLowAddress; + else + cb = 1; + rc = RTDbgModSymbolAdd(pThis->hCnt, + rtDwarfInfo_SelectName(pSubProgram->pszName, pSubProgram->pszLinkageName), + iSeg, offSeg, cb, 0 /*fFlags*/, NULL /*piOrdinal*/); + if (RT_FAILURE(rc)) + { + if ( rc == VERR_DBG_DUPLICATE_SYMBOL + || rc == VERR_DBG_ADDRESS_CONFLICT /** @todo figure why this happens with 10.6.8 mach_kernel, 32-bit. */ + ) + rc = VINF_SUCCESS; + else + AssertMsgFailed(("%Rrc\n", rc)); + } + } + else if ( pSubProgram->PcRange.uLowAddress == 0 /* see with vmlinux */ + && pSubProgram->PcRange.uHighAddress == 0) + { + Log5(("rtDbgModDwarfLinkAddressToSegOffset: Ignoring empty range.\n")); + rc = VINF_SUCCESS; /* ignore */ + } + else + { + AssertRC(rc); + Log5(("rtDbgModDwarfLinkAddressToSegOffset failed: %Rrc\n", rc)); + } + } + } + } + } + else + Log5(("subprogram %s (%s) external\n", pSubProgram->pszName, pSubProgram->pszLinkageName)); + break; + } + + case DW_TAG_label: + { + PCRTDWARFDIELABEL pLabel = (PCRTDWARFDIELABEL)pDie; + //if (pLabel->fExternal) + { + Log5(("label %s %#x:%#llx\n", pLabel->pszName, pLabel->uSegment, pLabel->Address.uAddress)); + if (pThis->iWatcomPass == 1) + rc = rtDbgModDwarfRecordSegOffset(pThis, pLabel->uSegment, pLabel->Address.uAddress); + else if (pLabel->pszName && *pLabel->pszName != '\0') /* Seen empty labels with isolinux. */ + { + RTDBGSEGIDX iSeg; + RTUINTPTR offSeg; + rc = rtDbgModDwarfLinkAddressToSegOffset(pThis, pLabel->uSegment, pLabel->Address.uAddress, + &iSeg, &offSeg); + AssertRC(rc); + if (RT_SUCCESS(rc)) + { + rc = RTDbgModSymbolAdd(pThis->hCnt, pLabel->pszName, iSeg, offSeg, 0 /*cb*/, + 0 /*fFlags*/, NULL /*piOrdinal*/); + AssertMsg(RT_SUCCESS(rc) || rc == VERR_DBG_ADDRESS_CONFLICT, + ("%Rrc %s %x:%x\n", rc, pLabel->pszName, iSeg, offSeg)); + } + else + Log5(("rtDbgModDwarfLinkAddressToSegOffset failed: %Rrc\n", rc)); + + /* Ignore errors regarding local labels. */ + if (RT_FAILURE(rc) && !pLabel->fExternal) + rc = -rc; + } + + } + break; + } + + } + return rc; +} + + +/** + * Initializes the non-core fields of an internal DIE structure. + * + * @param pDie The DIE structure. + * @param pDieDesc The DIE descriptor. + */ +static void rtDwarfInfo_InitDie(PRTDWARFDIE pDie, PCRTDWARFDIEDESC pDieDesc) +{ + size_t i = pDieDesc->cAttributes; + while (i-- > 0) + { + switch (pDieDesc->paAttributes[i].cbInit & ATTR_INIT_MASK) + { + case ATTR_INIT_ZERO: + /* Nothing to do (RTMemAllocZ). */ + break; + + case ATTR_INIT_FFFS: + switch (pDieDesc->paAttributes[i].cbInit & ATTR_SIZE_MASK) + { + case 1: + *(uint8_t *)((uintptr_t)pDie + pDieDesc->paAttributes[i].off) = UINT8_MAX; + break; + case 2: + *(uint16_t *)((uintptr_t)pDie + pDieDesc->paAttributes[i].off) = UINT16_MAX; + break; + case 4: + *(uint32_t *)((uintptr_t)pDie + pDieDesc->paAttributes[i].off) = UINT32_MAX; + break; + case 8: + *(uint64_t *)((uintptr_t)pDie + pDieDesc->paAttributes[i].off) = UINT64_MAX; + break; + default: + AssertFailed(); + memset((uint8_t *)pDie + pDieDesc->paAttributes[i].off, 0xff, + pDieDesc->paAttributes[i].cbInit & ATTR_SIZE_MASK); + break; + } + break; + + default: + AssertFailed(); + } + } +} + + +/** + * Creates a new internal DIE structure and links it up. + * + * @returns Pointer to the new DIE structure. + * @param pThis The DWARF instance. + * @param pDieDesc The DIE descriptor (for size and init). + * @param pAbbrev The abbreviation cache entry. + * @param pParent The parent DIE (NULL if unit). + */ +static PRTDWARFDIE rtDwarfInfo_NewDie(PRTDBGMODDWARF pThis, PCRTDWARFDIEDESC pDieDesc, + PCRTDWARFABBREV pAbbrev, PRTDWARFDIE pParent) +{ + NOREF(pThis); + Assert(pDieDesc->cbDie >= sizeof(RTDWARFDIE)); +#ifdef RTDBGMODDWARF_WITH_MEM_CACHE + uint32_t iAllocator = pDieDesc->cbDie > pThis->aDieAllocators[0].cbMax; + Assert(pDieDesc->cbDie <= pThis->aDieAllocators[iAllocator].cbMax); + PRTDWARFDIE pDie = (PRTDWARFDIE)RTMemCacheAlloc(pThis->aDieAllocators[iAllocator].hMemCache); +#else + PRTDWARFDIE pDie = (PRTDWARFDIE)RTMemAllocZ(pDieDesc->cbDie); +#endif + if (pDie) + { +#ifdef RTDBGMODDWARF_WITH_MEM_CACHE + RT_BZERO(pDie, pDieDesc->cbDie); + pDie->iAllocator = iAllocator; +#endif + rtDwarfInfo_InitDie(pDie, pDieDesc); + + pDie->uTag = pAbbrev->uTag; + pDie->offSpec = pAbbrev->offSpec; + pDie->pParent = pParent; + if (pParent) + RTListAppend(&pParent->ChildList, &pDie->SiblingNode); + else + RTListInit(&pDie->SiblingNode); + RTListInit(&pDie->ChildList); + + } + return pDie; +} + + +/** + * Free all children of a DIE. + * + * @param pThis The DWARF instance. + * @param pParentDie The parent DIE. + */ +static void rtDwarfInfo_FreeChildren(PRTDBGMODDWARF pThis, PRTDWARFDIE pParentDie) +{ + PRTDWARFDIE pChild, pNextChild; + RTListForEachSafe(&pParentDie->ChildList, pChild, pNextChild, RTDWARFDIE, SiblingNode) + { + if (!RTListIsEmpty(&pChild->ChildList)) + rtDwarfInfo_FreeChildren(pThis, pChild); + RTListNodeRemove(&pChild->SiblingNode); +#ifdef RTDBGMODDWARF_WITH_MEM_CACHE + RTMemCacheFree(pThis->aDieAllocators[pChild->iAllocator].hMemCache, pChild); +#else + RTMemFree(pChild); +#endif + } +} + + +/** + * Free a DIE an all its children. + * + * @param pThis The DWARF instance. + * @param pDie The DIE to free. + */ +static void rtDwarfInfo_FreeDie(PRTDBGMODDWARF pThis, PRTDWARFDIE pDie) +{ + rtDwarfInfo_FreeChildren(pThis, pDie); + RTListNodeRemove(&pDie->SiblingNode); +#ifdef RTDBGMODDWARF_WITH_MEM_CACHE + RTMemCacheFree(pThis->aDieAllocators[pDie->iAllocator].hMemCache, pDie); +#else + RTMemFree(pChild); +#endif +} + + +/** + * Skips a form. + * @returns IPRT status code + * @param pCursor The cursor. + * @param uForm The form to skip. + */ +static int rtDwarfInfo_SkipForm(PRTDWARFCURSOR pCursor, uint32_t uForm) +{ + switch (uForm) + { + case DW_FORM_addr: + return rtDwarfCursor_SkipBytes(pCursor, pCursor->cbNativeAddr); + + case DW_FORM_block: + case DW_FORM_exprloc: + return rtDwarfCursor_SkipBytes(pCursor, rtDwarfCursor_GetULeb128(pCursor, 0)); + + case DW_FORM_block1: + return rtDwarfCursor_SkipBytes(pCursor, rtDwarfCursor_GetU8(pCursor, 0)); + + case DW_FORM_block2: + return rtDwarfCursor_SkipBytes(pCursor, rtDwarfCursor_GetU16(pCursor, 0)); + + case DW_FORM_block4: + return rtDwarfCursor_SkipBytes(pCursor, rtDwarfCursor_GetU32(pCursor, 0)); + + case DW_FORM_data1: + case DW_FORM_ref1: + case DW_FORM_flag: + return rtDwarfCursor_SkipBytes(pCursor, 1); + + case DW_FORM_data2: + case DW_FORM_ref2: + return rtDwarfCursor_SkipBytes(pCursor, 2); + + case DW_FORM_data4: + case DW_FORM_ref4: + return rtDwarfCursor_SkipBytes(pCursor, 4); + + case DW_FORM_data8: + case DW_FORM_ref8: + case DW_FORM_ref_sig8: + return rtDwarfCursor_SkipBytes(pCursor, 8); + + case DW_FORM_udata: + case DW_FORM_sdata: + case DW_FORM_ref_udata: + return rtDwarfCursor_SkipLeb128(pCursor); + + case DW_FORM_string: + rtDwarfCursor_GetSZ(pCursor, NULL); + return pCursor->rc; + + case DW_FORM_indirect: + return rtDwarfInfo_SkipForm(pCursor, rtDwarfCursor_GetULeb128AsU32(pCursor, UINT32_MAX)); + + case DW_FORM_strp: + case DW_FORM_ref_addr: + case DW_FORM_sec_offset: + return rtDwarfCursor_SkipBytes(pCursor, pCursor->f64bitDwarf ? 8 : 4); + + case DW_FORM_flag_present: + return pCursor->rc; /* no data */ + + default: + Log(("rtDwarfInfo_SkipForm: Unknown form %#x\n", uForm)); + return VERR_DWARF_UNKNOWN_FORM; + } +} + + + +#ifdef SOME_UNUSED_FUNCTION +/** + * Skips a DIE. + * + * @returns IPRT status code. + * @param pCursor The cursor. + * @param pAbbrevCursor The abbreviation cursor. + */ +static int rtDwarfInfo_SkipDie(PRTDWARFCURSOR pCursor, PRTDWARFCURSOR pAbbrevCursor) +{ + for (;;) + { + uint32_t uAttr = rtDwarfCursor_GetULeb128AsU32(pAbbrevCursor, 0); + uint32_t uForm = rtDwarfCursor_GetULeb128AsU32(pAbbrevCursor, 0); + if (uAttr == 0 && uForm == 0) + break; + + int rc = rtDwarfInfo_SkipForm(pCursor, uForm); + if (RT_FAILURE(rc)) + return rc; + } + return RT_FAILURE(pCursor->rc) ? pCursor->rc : pAbbrevCursor->rc; +} +#endif + + +/** + * Parse the attributes of a DIE. + * + * @returns IPRT status code. + * @param pThis The DWARF instance. + * @param pDie The internal DIE structure to fill. + * @param pDieDesc The DIE descriptor. + * @param pCursor The debug_info cursor. + * @param pAbbrev The abbreviation cache entry. + * @param fInitDie Whether to initialize the DIE first. If not (@c + * false) it's safe to assume we're following a + * DW_AT_specification or DW_AT_abstract_origin, + * and that we shouldn't be snooping any symbols. + */ +static int rtDwarfInfo_ParseDie(PRTDBGMODDWARF pThis, PRTDWARFDIE pDie, PCRTDWARFDIEDESC pDieDesc, + PRTDWARFCURSOR pCursor, PCRTDWARFABBREV pAbbrev, bool fInitDie) +{ + RTDWARFCURSOR AbbrevCursor; + int rc = rtDwarfCursor_InitWithOffset(&AbbrevCursor, pThis, krtDbgModDwarfSect_abbrev, pAbbrev->offSpec); + if (RT_FAILURE(rc)) + return rc; + + if (fInitDie) + rtDwarfInfo_InitDie(pDie, pDieDesc); + for (;;) + { +#ifdef LOG_ENABLED + uint32_t const off = (uint32_t)(AbbrevCursor.pb - AbbrevCursor.pbStart); +#endif + uint32_t uAttr = rtDwarfCursor_GetULeb128AsU32(&AbbrevCursor, 0); + uint32_t uForm = rtDwarfCursor_GetULeb128AsU32(&AbbrevCursor, 0); + Log4((" %04x: %-23s [%s]\n", off, rtDwarfLog_AttrName(uAttr), rtDwarfLog_FormName(uForm))); + if (uAttr == 0) + break; + if (uForm == DW_FORM_indirect) + uForm = rtDwarfCursor_GetULeb128AsU32(pCursor, 0); + + /* Look up the attribute in the descriptor and invoke the decoder. */ + PCRTDWARFATTRDESC pAttr = NULL; + size_t i = pDieDesc->cAttributes; + while (i-- > 0) + if (pDieDesc->paAttributes[i].uAttr == uAttr) + { + pAttr = &pDieDesc->paAttributes[i]; + rc = pAttr->pfnDecoder(pDie, (uint8_t *)pDie + pAttr->off, pAttr, uForm, pCursor); + break; + } + + /* Some house keeping. */ + if (pAttr) + pDie->cDecodedAttrs++; + else + { + pDie->cUnhandledAttrs++; + rc = rtDwarfInfo_SkipForm(pCursor, uForm); + } + if (RT_FAILURE(rc)) + break; + } + + rc = rtDwarfCursor_Delete(&AbbrevCursor, rc); + if (RT_SUCCESS(rc)) + rc = pCursor->rc; + + /* + * Snoop up symbols on the way out. + */ + if (RT_SUCCESS(rc) && fInitDie) + { + rc = rtDwarfInfo_SnoopSymbols(pThis, pDie); + /* Ignore duplicates, get work done instead. */ + /** @todo clean up global/static symbol mess. */ + if (rc == VERR_DBG_DUPLICATE_SYMBOL || rc == VERR_DBG_ADDRESS_CONFLICT) + rc = VINF_SUCCESS; + } + + return rc; +} + + +/** + * Load the debug information of a unit. + * + * @returns IPRT status code. + * @param pThis The DWARF instance. + * @param pCursor The debug_info cursor. + * @param fKeepDies Whether to keep the DIEs or discard them as soon + * as possible. + */ +static int rtDwarfInfo_LoadUnit(PRTDBGMODDWARF pThis, PRTDWARFCURSOR pCursor, bool fKeepDies) +{ + Log(("rtDwarfInfo_LoadUnit: %#x\n", rtDwarfCursor_CalcSectOffsetU32(pCursor))); + + /* + * Read the compilation unit header. + */ + uint64_t offUnit = rtDwarfCursor_CalcSectOffsetU32(pCursor); + uint64_t cbUnit = rtDwarfCursor_GetInitialLength(pCursor); + cbUnit += rtDwarfCursor_CalcSectOffsetU32(pCursor) - offUnit; + uint16_t const uVer = rtDwarfCursor_GetUHalf(pCursor, 0); + if ( uVer < 2 + || uVer > 4) + return rtDwarfCursor_SkipUnit(pCursor); + uint64_t const offAbbrev = rtDwarfCursor_GetUOff(pCursor, UINT64_MAX); + uint8_t const cbNativeAddr = rtDwarfCursor_GetU8(pCursor, UINT8_MAX); + if (RT_FAILURE(pCursor->rc)) + return pCursor->rc; + Log((" uVer=%d offAbbrev=%#llx cbNativeAddr=%d\n", uVer, offAbbrev, cbNativeAddr)); + + /* + * Set up the abbreviation cache and store the native address size in the cursor. + */ + if (offAbbrev > UINT32_MAX) + { + Log(("Unexpected abbrviation code offset of %#llx\n", offAbbrev)); + return VERR_DWARF_BAD_INFO; + } + rtDwarfAbbrev_SetUnitOffset(pThis, (uint32_t)offAbbrev); + pCursor->cbNativeAddr = cbNativeAddr; + + /* + * The first DIE is a compile or partial unit, parse it here. + */ + uint32_t uAbbrCode = rtDwarfCursor_GetULeb128AsU32(pCursor, UINT32_MAX); + if (!uAbbrCode) + { + Log(("Unexpected abbrviation code of zero\n")); + return VERR_DWARF_BAD_INFO; + } + PCRTDWARFABBREV pAbbrev = rtDwarfAbbrev_Lookup(pThis, uAbbrCode); + if (!pAbbrev) + return VERR_DWARF_ABBREV_NOT_FOUND; + if ( pAbbrev->uTag != DW_TAG_compile_unit + && pAbbrev->uTag != DW_TAG_partial_unit) + { + Log(("Unexpected compile/partial unit tag %#x\n", pAbbrev->uTag)); + return VERR_DWARF_BAD_INFO; + } + + PRTDWARFDIECOMPILEUNIT pUnit; + pUnit = (PRTDWARFDIECOMPILEUNIT)rtDwarfInfo_NewDie(pThis, &g_CompileUnitDesc, pAbbrev, NULL /*pParent*/); + if (!pUnit) + return VERR_NO_MEMORY; + pUnit->offUnit = offUnit; + pUnit->cbUnit = cbUnit; + pUnit->offAbbrev = offAbbrev; + pUnit->cbNativeAddr = cbNativeAddr; + pUnit->uDwarfVer = (uint8_t)uVer; + RTListAppend(&pThis->CompileUnitList, &pUnit->Core.SiblingNode); + + int rc = rtDwarfInfo_ParseDie(pThis, &pUnit->Core, &g_CompileUnitDesc, pCursor, pAbbrev, true /*fInitDie*/); + if (RT_FAILURE(rc)) + return rc; + + /* + * Parse DIEs. + */ + uint32_t cDepth = 0; + PRTDWARFDIE pParentDie = &pUnit->Core; + while (!rtDwarfCursor_IsAtEndOfUnit(pCursor)) + { +#ifdef LOG_ENABLED + uint32_t offLog = rtDwarfCursor_CalcSectOffsetU32(pCursor); +#endif + uAbbrCode = rtDwarfCursor_GetULeb128AsU32(pCursor, UINT32_MAX); + if (!uAbbrCode) + { + /* End of siblings, up one level. (Is this correct?) */ + if (pParentDie->pParent) + { + pParentDie = pParentDie->pParent; + cDepth--; + if (!fKeepDies && pParentDie->pParent) + rtDwarfInfo_FreeChildren(pThis, pParentDie); + } + } + else + { + /* + * Look up the abbreviation and match the tag up with a descriptor. + */ + pAbbrev = rtDwarfAbbrev_Lookup(pThis, uAbbrCode); + if (!pAbbrev) + return VERR_DWARF_ABBREV_NOT_FOUND; + + PCRTDWARFDIEDESC pDieDesc; + const char *pszName; + if (pAbbrev->uTag < RT_ELEMENTS(g_aTagDescs)) + { + Assert(g_aTagDescs[pAbbrev->uTag].uTag == pAbbrev->uTag || g_aTagDescs[pAbbrev->uTag].uTag == 0); + pszName = g_aTagDescs[pAbbrev->uTag].pszName; + pDieDesc = g_aTagDescs[pAbbrev->uTag].pDesc; + } + else + { + pszName = "<unknown>"; + pDieDesc = &g_CoreDieDesc; + } + Log4(("%08x: %*stag=%s (%#x, abbrev %u @ %#x)%s\n", offLog, cDepth * 2, "", pszName, + pAbbrev->uTag, uAbbrCode, pAbbrev->offSpec - pAbbrev->cbHdr, pAbbrev->fChildren ? " has children" : "")); + + /* + * Create a new internal DIE structure and parse the + * attributes. + */ + PRTDWARFDIE pNewDie = rtDwarfInfo_NewDie(pThis, pDieDesc, pAbbrev, pParentDie); + if (!pNewDie) + return VERR_NO_MEMORY; + + if (pAbbrev->fChildren) + { + pParentDie = pNewDie; + cDepth++; + } + + rc = rtDwarfInfo_ParseDie(pThis, pNewDie, pDieDesc, pCursor, pAbbrev, true /*fInitDie*/); + if (RT_FAILURE(rc)) + return rc; + + if (!fKeepDies && !pAbbrev->fChildren) + rtDwarfInfo_FreeDie(pThis, pNewDie); + } + } /* while more DIEs */ + + + /* Unlink and free child DIEs if told to do so. */ + if (!fKeepDies) + rtDwarfInfo_FreeChildren(pThis, &pUnit->Core); + + return RT_SUCCESS(rc) ? pCursor->rc : rc; +} + + +/** + * Extracts the symbols. + * + * The symbols are insered into the debug info container. + * + * @returns IPRT status code + * @param pThis The DWARF instance. + */ +static int rtDwarfInfo_LoadAll(PRTDBGMODDWARF pThis) +{ + RTDWARFCURSOR Cursor; + int rc = rtDwarfCursor_Init(&Cursor, pThis, krtDbgModDwarfSect_info); + if (RT_SUCCESS(rc)) + { + while ( !rtDwarfCursor_IsAtEnd(&Cursor) + && RT_SUCCESS(rc)) + rc = rtDwarfInfo_LoadUnit(pThis, &Cursor, false /* fKeepDies */); + + rc = rtDwarfCursor_Delete(&Cursor, rc); + } + return rc; +} + + + +/* + * + * Public and image level symbol handling. + * Public and image level symbol handling. + * Public and image level symbol handling. + * Public and image level symbol handling. + * + * + */ + +#define RTDBGDWARF_SYM_ENUM_BASE_ADDRESS UINT32_C(0x200000) + +/** @callback_method_impl{FNRTLDRENUMSYMS, + * Adds missing symbols from the image symbol table.} */ +static DECLCALLBACK(int) rtDwarfSyms_EnumSymbolsCallback(RTLDRMOD hLdrMod, const char *pszSymbol, unsigned uSymbol, + RTLDRADDR Value, void *pvUser) +{ + PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pvUser; + RT_NOREF_PV(hLdrMod); RT_NOREF_PV(uSymbol); + Assert(pThis->iWatcomPass != 1); + + RTLDRADDR uRva = Value - RTDBGDWARF_SYM_ENUM_BASE_ADDRESS; + if ( Value >= RTDBGDWARF_SYM_ENUM_BASE_ADDRESS + && uRva < _1G) + { + RTDBGSYMBOL SymInfo; + RTINTPTR offDisp; + int rc = RTDbgModSymbolByAddr(pThis->hCnt, RTDBGSEGIDX_RVA, uRva, RTDBGSYMADDR_FLAGS_LESS_OR_EQUAL, &offDisp, &SymInfo); + if ( RT_FAILURE(rc) + || offDisp != 0) + { + rc = RTDbgModSymbolAdd(pThis->hCnt, pszSymbol, RTDBGSEGIDX_RVA, uRva, 1, 0 /*fFlags*/, NULL /*piOrdinal*/); + Log(("Dwarf: Symbol #%05u %#018RTptr %s [%Rrc]\n", uSymbol, Value, pszSymbol, rc)); NOREF(rc); + } + } + else + Log(("Dwarf: Symbol #%05u %#018RTptr '%s' [SKIPPED - INVALID ADDRESS]\n", uSymbol, Value, pszSymbol)); + return VINF_SUCCESS; +} + + + +/** + * Loads additional symbols from the pubnames section and the executable image. + * + * The symbols are insered into the debug info container. + * + * @returns IPRT status code + * @param pThis The DWARF instance. + */ +static int rtDwarfSyms_LoadAll(PRTDBGMODDWARF pThis) +{ + /* + * pubnames. + */ + int rc = VINF_SUCCESS; + if (pThis->aSections[krtDbgModDwarfSect_pubnames].fPresent) + { +// RTDWARFCURSOR Cursor; +// int rc = rtDwarfCursor_Init(&Cursor, pThis, krtDbgModDwarfSect_info); +// if (RT_SUCCESS(rc)) +// { +// while ( !rtDwarfCursor_IsAtEnd(&Cursor) +// && RT_SUCCESS(rc)) +// rc = rtDwarfInfo_LoadUnit(pThis, &Cursor, false /* fKeepDies */); +// +// rc = rtDwarfCursor_Delete(&Cursor, rc); +// } +// return rc; + } + + /* + * The executable image. + */ + if ( pThis->pImgMod + && pThis->pImgMod->pImgVt->pfnEnumSymbols + && pThis->iWatcomPass != 1 + && RT_SUCCESS(rc)) + { + rc = pThis->pImgMod->pImgVt->pfnEnumSymbols(pThis->pImgMod, + RTLDR_ENUM_SYMBOL_FLAGS_ALL | RTLDR_ENUM_SYMBOL_FLAGS_NO_FWD, + RTDBGDWARF_SYM_ENUM_BASE_ADDRESS, + rtDwarfSyms_EnumSymbolsCallback, + pThis); + } + + return rc; +} + + + + +/* + * + * DWARF Debug module implementation. + * DWARF Debug module implementation. + * DWARF Debug module implementation. + * + */ + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnUnwindFrame} */ +static DECLCALLBACK(int) rtDbgModDwarf_UnwindFrame(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState) +{ + PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv; + + /* + * Unwinding info is stored in the '.debug_frame' section, or altertively + * in the '.eh_frame' one in the image. In the latter case the dbgmodldr.cpp + * part of the operation will take care of it. Since the sections contain the + * same data, we just create a cursor and call a common function to do the job. + */ + if (pThis->aSections[krtDbgModDwarfSect_frame].fPresent) + { + RTDWARFCURSOR Cursor; + int rc = rtDwarfCursor_Init(&Cursor, pThis, krtDbgModDwarfSect_frame); + if (RT_SUCCESS(rc)) + { + /* Figure default pointer encoding from image arch. */ + uint8_t bPtrEnc = rtDwarfUnwind_ArchToPtrEnc(pMod->pImgVt->pfnGetArch(pMod)); + + /* Make sure we've got both seg:off and rva for the input address. */ + RTUINTPTR uRva = off; + if (iSeg == RTDBGSEGIDX_RVA) + rtDbgModDwarfRvaToSegOffset(pThis, uRva, &iSeg, &off); + else + rtDbgModDwarfSegOffsetToRva(pThis, iSeg, off, &uRva); + + /* Do the work */ + rc = rtDwarfUnwind_Slow(&Cursor, 0 /** @todo .debug_frame RVA*/, iSeg, off, uRva, + pState, bPtrEnc, false /*fIsEhFrame*/, pMod->pImgVt->pfnGetArch(pMod)); + + rc = rtDwarfCursor_Delete(&Cursor, rc); + } + return rc; + } + return VERR_DBG_NO_UNWIND_INFO; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByAddr} */ +static DECLCALLBACK(int) rtDbgModDwarf_LineByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, + PRTINTPTR poffDisp, PRTDBGLINE pLineInfo) +{ + PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv; + return RTDbgModLineByAddr(pThis->hCnt, iSeg, off, poffDisp, pLineInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByOrdinal} */ +static DECLCALLBACK(int) rtDbgModDwarf_LineByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo) +{ + PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv; + return RTDbgModLineByOrdinal(pThis->hCnt, iOrdinal, pLineInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineCount} */ +static DECLCALLBACK(uint32_t) rtDbgModDwarf_LineCount(PRTDBGMODINT pMod) +{ + PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv; + return RTDbgModLineCount(pThis->hCnt); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineAdd} */ +static DECLCALLBACK(int) rtDbgModDwarf_LineAdd(PRTDBGMODINT pMod, const char *pszFile, size_t cchFile, uint32_t uLineNo, + uint32_t iSeg, RTUINTPTR off, uint32_t *piOrdinal) +{ + PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv; + Assert(!pszFile[cchFile]); NOREF(cchFile); + return RTDbgModLineAdd(pThis->hCnt, pszFile, uLineNo, iSeg, off, piOrdinal); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByAddr} */ +static DECLCALLBACK(int) rtDbgModDwarf_SymbolByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags, + PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo) +{ + PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv; + return RTDbgModSymbolByAddr(pThis->hCnt, iSeg, off, fFlags, poffDisp, pSymInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByName} */ +static DECLCALLBACK(int) rtDbgModDwarf_SymbolByName(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol, + PRTDBGSYMBOL pSymInfo) +{ + PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv; + Assert(!pszSymbol[cchSymbol]); RT_NOREF_PV(cchSymbol); + return RTDbgModSymbolByName(pThis->hCnt, pszSymbol/*, cchSymbol*/, pSymInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByOrdinal} */ +static DECLCALLBACK(int) rtDbgModDwarf_SymbolByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo) +{ + PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv; + return RTDbgModSymbolByOrdinal(pThis->hCnt, iOrdinal, pSymInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolCount} */ +static DECLCALLBACK(uint32_t) rtDbgModDwarf_SymbolCount(PRTDBGMODINT pMod) +{ + PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv; + return RTDbgModSymbolCount(pThis->hCnt); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolAdd} */ +static DECLCALLBACK(int) rtDbgModDwarf_SymbolAdd(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol, + RTDBGSEGIDX iSeg, RTUINTPTR off, RTUINTPTR cb, uint32_t fFlags, + uint32_t *piOrdinal) +{ + PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv; + Assert(!pszSymbol[cchSymbol]); NOREF(cchSymbol); + return RTDbgModSymbolAdd(pThis->hCnt, pszSymbol, iSeg, off, cb, fFlags, piOrdinal); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentByIndex} */ +static DECLCALLBACK(int) rtDbgModDwarf_SegmentByIndex(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo) +{ + PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv; + return RTDbgModSegmentByIndex(pThis->hCnt, iSeg, pSegInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentCount} */ +static DECLCALLBACK(RTDBGSEGIDX) rtDbgModDwarf_SegmentCount(PRTDBGMODINT pMod) +{ + PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv; + return RTDbgModSegmentCount(pThis->hCnt); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentAdd} */ +static DECLCALLBACK(int) rtDbgModDwarf_SegmentAdd(PRTDBGMODINT pMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName, size_t cchName, + uint32_t fFlags, PRTDBGSEGIDX piSeg) +{ + PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv; + Assert(!pszName[cchName]); NOREF(cchName); + return RTDbgModSegmentAdd(pThis->hCnt, uRva, cb, pszName, fFlags, piSeg); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnImageSize} */ +static DECLCALLBACK(RTUINTPTR) rtDbgModDwarf_ImageSize(PRTDBGMODINT pMod) +{ + PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv; + RTUINTPTR cb1 = RTDbgModImageSize(pThis->hCnt); + RTUINTPTR cb2 = pThis->pImgMod->pImgVt->pfnImageSize(pMod); + return RT_MAX(cb1, cb2); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnRvaToSegOff} */ +static DECLCALLBACK(RTDBGSEGIDX) rtDbgModDwarf_RvaToSegOff(PRTDBGMODINT pMod, RTUINTPTR uRva, PRTUINTPTR poffSeg) +{ + PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv; + return RTDbgModRvaToSegOff(pThis->hCnt, uRva, poffSeg); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnClose} */ +static DECLCALLBACK(int) rtDbgModDwarf_Close(PRTDBGMODINT pMod) +{ + PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv; + + for (unsigned iSect = 0; iSect < RT_ELEMENTS(pThis->aSections); iSect++) + if (pThis->aSections[iSect].pv) + pThis->pDbgInfoMod->pImgVt->pfnUnmapPart(pThis->pDbgInfoMod, pThis->aSections[iSect].cb, &pThis->aSections[iSect].pv); + + RTDbgModRelease(pThis->hCnt); + RTMemFree(pThis->paCachedAbbrevs); + if (pThis->pNestedMod) + { + pThis->pNestedMod->pImgVt->pfnClose(pThis->pNestedMod); + RTStrCacheRelease(g_hDbgModStrCache, pThis->pNestedMod->pszName); + RTStrCacheRelease(g_hDbgModStrCache, pThis->pNestedMod->pszDbgFile); + RTMemFree(pThis->pNestedMod); + pThis->pNestedMod = NULL; + } + +#ifdef RTDBGMODDWARF_WITH_MEM_CACHE + uint32_t i = RT_ELEMENTS(pThis->aDieAllocators); + while (i-- > 0) + { + RTMemCacheDestroy(pThis->aDieAllocators[i].hMemCache); + pThis->aDieAllocators[i].hMemCache = NIL_RTMEMCACHE; + } +#endif + + RTMemFree(pThis); + + return VINF_SUCCESS; +} + + +/** @callback_method_impl{FNRTLDRENUMDBG} */ +static DECLCALLBACK(int) rtDbgModDwarfEnumCallback(RTLDRMOD hLdrMod, PCRTLDRDBGINFO pDbgInfo, void *pvUser) +{ + RT_NOREF_PV(hLdrMod); + + /* + * Skip stuff we can't handle. + */ + if (pDbgInfo->enmType != RTLDRDBGINFOTYPE_DWARF) + return VINF_SUCCESS; + const char *pszSection = pDbgInfo->u.Dwarf.pszSection; + if (!pszSection || !*pszSection) + return VINF_SUCCESS; + Assert(!pDbgInfo->pszExtFile); + + /* + * Must have a part name starting with debug_ and possibly prefixed by dots + * or underscores. + */ + if (!strncmp(pszSection, RT_STR_TUPLE(".debug_"))) /* ELF */ + pszSection += sizeof(".debug_") - 1; + else if (!strncmp(pszSection, RT_STR_TUPLE("__debug_"))) /* Mach-O */ + pszSection += sizeof("__debug_") - 1; + else if (!strcmp(pszSection, ".WATCOM_references")) + return VINF_SUCCESS; /* Ignore special watcom section for now.*/ + else if ( !strcmp(pszSection, "__apple_types") + || !strcmp(pszSection, "__apple_namespac") + || !strcmp(pszSection, "__apple_objc") + || !strcmp(pszSection, "__apple_names")) + return VINF_SUCCESS; /* Ignore special apple sections for now. */ + else + AssertMsgFailedReturn(("%s\n", pszSection), VINF_SUCCESS /*ignore*/); + + /* + * Figure out which part we're talking about. + */ + krtDbgModDwarfSect enmSect; + if (0) { /* dummy */ } +#define ELSE_IF_STRCMP_SET(a_Name) else if (!strcmp(pszSection, #a_Name)) enmSect = krtDbgModDwarfSect_ ## a_Name + ELSE_IF_STRCMP_SET(abbrev); + ELSE_IF_STRCMP_SET(aranges); + ELSE_IF_STRCMP_SET(frame); + ELSE_IF_STRCMP_SET(info); + ELSE_IF_STRCMP_SET(inlined); + ELSE_IF_STRCMP_SET(line); + ELSE_IF_STRCMP_SET(loc); + ELSE_IF_STRCMP_SET(macinfo); + ELSE_IF_STRCMP_SET(pubnames); + ELSE_IF_STRCMP_SET(pubtypes); + ELSE_IF_STRCMP_SET(ranges); + ELSE_IF_STRCMP_SET(str); + ELSE_IF_STRCMP_SET(types); +#undef ELSE_IF_STRCMP_SET + else + { + AssertMsgFailed(("%s\n", pszSection)); + return VINF_SUCCESS; + } + + /* + * Record the section. + */ + PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pvUser; + AssertMsgReturn(!pThis->aSections[enmSect].fPresent, ("duplicate %s\n", pszSection), VINF_SUCCESS /*ignore*/); + + pThis->aSections[enmSect].fPresent = true; + pThis->aSections[enmSect].offFile = pDbgInfo->offFile; + pThis->aSections[enmSect].pv = NULL; + pThis->aSections[enmSect].cb = (size_t)pDbgInfo->cb; + pThis->aSections[enmSect].iDbgInfo = pDbgInfo->iDbgInfo; + if (pThis->aSections[enmSect].cb != pDbgInfo->cb) + pThis->aSections[enmSect].cb = ~(size_t)0; + + return VINF_SUCCESS; +} + + +static int rtDbgModDwarfTryOpenDbgFile(PRTDBGMODINT pDbgMod, PRTDBGMODDWARF pThis, RTLDRARCH enmArch) +{ + if ( !pDbgMod->pszDbgFile + || RTPathIsSame(pDbgMod->pszDbgFile, pDbgMod->pszImgFile) == (int)true /* returns VERR too */) + return VERR_DBG_NO_MATCHING_INTERPRETER; + + /* + * Only open the image. + */ + PRTDBGMODINT pDbgInfoMod = (PRTDBGMODINT)RTMemAllocZ(sizeof(*pDbgInfoMod)); + if (!pDbgInfoMod) + return VERR_NO_MEMORY; + + int rc; + pDbgInfoMod->u32Magic = RTDBGMOD_MAGIC; + pDbgInfoMod->cRefs = 1; + if (RTStrCacheRetain(pDbgMod->pszDbgFile) != UINT32_MAX) + { + pDbgInfoMod->pszImgFile = pDbgMod->pszDbgFile; + if (RTStrCacheRetain(pDbgMod->pszName) != UINT32_MAX) + { + pDbgInfoMod->pszName = pDbgMod->pszName; + pDbgInfoMod->pImgVt = &g_rtDbgModVtImgLdr; + rc = pDbgInfoMod->pImgVt->pfnTryOpen(pDbgInfoMod, enmArch, 0 /*fLdrFlags*/); + if (RT_SUCCESS(rc)) + { + pThis->pDbgInfoMod = pDbgInfoMod; + pThis->pNestedMod = pDbgInfoMod; + return VINF_SUCCESS; + } + + RTStrCacheRelease(g_hDbgModStrCache, pDbgInfoMod->pszName); + } + else + rc = VERR_NO_STR_MEMORY; + RTStrCacheRelease(g_hDbgModStrCache, pDbgInfoMod->pszImgFile); + } + else + rc = VERR_NO_STR_MEMORY; + RTMemFree(pDbgInfoMod); + return rc; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnTryOpen} */ +static DECLCALLBACK(int) rtDbgModDwarf_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch) +{ + /* + * DWARF is only supported when part of an image. + */ + if (!pMod->pImgVt) + return VERR_DBG_NO_MATCHING_INTERPRETER; + + /* + * Create the module instance data. + */ + PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)RTMemAllocZ(sizeof(*pThis)); + if (!pThis) + return VERR_NO_MEMORY; + pThis->pDbgInfoMod = pMod; + pThis->pImgMod = pMod; + RTListInit(&pThis->CompileUnitList); + + /** @todo better fUseLinkAddress heuristics! */ + /* mach_kernel: */ + if ( (pMod->pszDbgFile && strstr(pMod->pszDbgFile, "mach_kernel")) + || (pMod->pszImgFile && strstr(pMod->pszImgFile, "mach_kernel")) + || (pMod->pszImgFileSpecified && strstr(pMod->pszImgFileSpecified, "mach_kernel")) ) + pThis->fUseLinkAddress = true; + +#ifdef RTDBGMODDWARF_WITH_MEM_CACHE + AssertCompile(RT_ELEMENTS(pThis->aDieAllocators) == 2); + pThis->aDieAllocators[0].cbMax = sizeof(RTDWARFDIE); + pThis->aDieAllocators[1].cbMax = sizeof(RTDWARFDIECOMPILEUNIT); + for (uint32_t i = 0; i < RT_ELEMENTS(g_aTagDescs); i++) + if (g_aTagDescs[i].pDesc && g_aTagDescs[i].pDesc->cbDie > pThis->aDieAllocators[1].cbMax) + pThis->aDieAllocators[1].cbMax = (uint32_t)g_aTagDescs[i].pDesc->cbDie; + pThis->aDieAllocators[1].cbMax = RT_ALIGN_32(pThis->aDieAllocators[1].cbMax, sizeof(uint64_t)); + + for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aDieAllocators); i++) + { + int rc = RTMemCacheCreate(&pThis->aDieAllocators[i].hMemCache, pThis->aDieAllocators[i].cbMax, sizeof(uint64_t), + UINT32_MAX, NULL /*pfnCtor*/, NULL /*pfnDtor*/, NULL /*pvUser*/, 0 /*fFlags*/); + if (RT_FAILURE(rc)) + { + while (i-- > 0) + RTMemCacheDestroy(pThis->aDieAllocators[i].hMemCache); + RTMemFree(pThis); + return rc; + } + } +#endif + + /* + * If the debug file name is set, let's see if it's an ELF image with DWARF + * inside it. In that case we'll have to deal with two image modules, one + * for segments and address translation and one for the debug information. + */ + if (pMod->pszDbgFile != NULL) + rtDbgModDwarfTryOpenDbgFile(pMod, pThis, enmArch); + + /* + * Enumerate the debug info in the module, looking for DWARF bits. + */ + int rc = pThis->pDbgInfoMod->pImgVt->pfnEnumDbgInfo(pThis->pDbgInfoMod, rtDbgModDwarfEnumCallback, pThis); + if (RT_SUCCESS(rc)) + { + if (pThis->aSections[krtDbgModDwarfSect_info].fPresent) + { + /* + * Extract / explode the data we want (symbols and line numbers) + * storing them in a container module. + */ + rc = RTDbgModCreate(&pThis->hCnt, pMod->pszName, 0 /*cbSeg*/, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + pMod->pvDbgPriv = pThis; + + rc = rtDbgModDwarfAddSegmentsFromImage(pThis); + if (RT_SUCCESS(rc)) + rc = rtDwarfInfo_LoadAll(pThis); + if (RT_SUCCESS(rc)) + rc = rtDwarfSyms_LoadAll(pThis); + if (RT_SUCCESS(rc)) + rc = rtDwarfLine_ExplodeAll(pThis); + if (RT_SUCCESS(rc) && pThis->iWatcomPass == 1) + { + rc = rtDbgModDwarfAddSegmentsFromPass1(pThis); + pThis->iWatcomPass = 2; + if (RT_SUCCESS(rc)) + rc = rtDwarfInfo_LoadAll(pThis); + if (RT_SUCCESS(rc)) + rc = rtDwarfSyms_LoadAll(pThis); + if (RT_SUCCESS(rc)) + rc = rtDwarfLine_ExplodeAll(pThis); + } + + /* + * Free the cached abbreviations and unload all sections. + */ + pThis->cCachedAbbrevsAlloced = 0; + RTMemFree(pThis->paCachedAbbrevs); + pThis->paCachedAbbrevs = NULL; + + for (unsigned iSect = 0; iSect < RT_ELEMENTS(pThis->aSections); iSect++) + if (pThis->aSections[iSect].pv) + pThis->pDbgInfoMod->pImgVt->pfnUnmapPart(pThis->pDbgInfoMod, pThis->aSections[iSect].cb, + &pThis->aSections[iSect].pv); + + if (RT_SUCCESS(rc)) + { + /** @todo Kill pThis->CompileUnitList and the alloc caches. */ + return VINF_SUCCESS; + } + + /* bail out. */ + RTDbgModRelease(pThis->hCnt); + pMod->pvDbgPriv = NULL; + } + } + else + rc = VERR_DBG_NO_MATCHING_INTERPRETER; + } + + if (pThis->paCachedAbbrevs) + RTMemFree(pThis->paCachedAbbrevs); + pThis->paCachedAbbrevs = NULL; + + for (unsigned iSect = 0; iSect < RT_ELEMENTS(pThis->aSections); iSect++) + if (pThis->aSections[iSect].pv) + pThis->pDbgInfoMod->pImgVt->pfnUnmapPart(pThis->pDbgInfoMod, pThis->aSections[iSect].cb, + &pThis->aSections[iSect].pv); + +#ifdef RTDBGMODDWARF_WITH_MEM_CACHE + uint32_t i = RT_ELEMENTS(pThis->aDieAllocators); + while (i-- > 0) + { + RTMemCacheDestroy(pThis->aDieAllocators[i].hMemCache); + pThis->aDieAllocators[i].hMemCache = NIL_RTMEMCACHE; + } +#endif + + RTMemFree(pThis); + + return rc; +} + + + +/** Virtual function table for the DWARF debug info reader. */ +DECL_HIDDEN_CONST(RTDBGMODVTDBG) const g_rtDbgModVtDbgDwarf = +{ + /*.u32Magic = */ RTDBGMODVTDBG_MAGIC, + /*.fSupports = */ RT_DBGTYPE_DWARF, + /*.pszName = */ "dwarf", + /*.pfnTryOpen = */ rtDbgModDwarf_TryOpen, + /*.pfnClose = */ rtDbgModDwarf_Close, + + /*.pfnRvaToSegOff = */ rtDbgModDwarf_RvaToSegOff, + /*.pfnImageSize = */ rtDbgModDwarf_ImageSize, + + /*.pfnSegmentAdd = */ rtDbgModDwarf_SegmentAdd, + /*.pfnSegmentCount = */ rtDbgModDwarf_SegmentCount, + /*.pfnSegmentByIndex = */ rtDbgModDwarf_SegmentByIndex, + + /*.pfnSymbolAdd = */ rtDbgModDwarf_SymbolAdd, + /*.pfnSymbolCount = */ rtDbgModDwarf_SymbolCount, + /*.pfnSymbolByOrdinal = */ rtDbgModDwarf_SymbolByOrdinal, + /*.pfnSymbolByName = */ rtDbgModDwarf_SymbolByName, + /*.pfnSymbolByAddr = */ rtDbgModDwarf_SymbolByAddr, + + /*.pfnLineAdd = */ rtDbgModDwarf_LineAdd, + /*.pfnLineCount = */ rtDbgModDwarf_LineCount, + /*.pfnLineByOrdinal = */ rtDbgModDwarf_LineByOrdinal, + /*.pfnLineByAddr = */ rtDbgModDwarf_LineByAddr, + + /*.pfnUnwindFrame = */ rtDbgModDwarf_UnwindFrame, + + /*.u32EndMagic = */ RTDBGMODVTDBG_MAGIC +}; + diff --git a/src/VBox/Runtime/common/dbg/dbgmodexports.cpp b/src/VBox/Runtime/common/dbg/dbgmodexports.cpp new file mode 100644 index 00000000..716c43e1 --- /dev/null +++ b/src/VBox/Runtime/common/dbg/dbgmodexports.cpp @@ -0,0 +1,183 @@ +/* $Id: dbgmodexports.cpp $ */ +/** @file + * IPRT - Debug Module Using Image Exports. + */ + +/* + * Copyright (C) 2013-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_DBG +#include <iprt/dbg.h> +#include "internal/iprt.h" + +#include <iprt/alloca.h> +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/log.h> +#include <iprt/string.h> +#include "internal/dbgmod.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef struct RTDBGMODEXPORTARGS +{ + PRTDBGMODINT pDbgMod; + RTLDRADDR uImageBase; + RTLDRADDR uRvaNext; + uint32_t cSegs; +} RTDBGMODEXPORTARGS; +/** Pointer to an argument package. */ +typedef RTDBGMODEXPORTARGS *PRTDBGMODEXPORTARGS; + + +/** + * @callback_method_impl{FNRTLDRENUMSYMS, + * Copies the symbols over into the container} */ +static DECLCALLBACK(int) rtDbgModExportsAddSymbolCallback(RTLDRMOD hLdrMod, const char *pszSymbol, unsigned uSymbol, + RTLDRADDR Value, void *pvUser) +{ + PRTDBGMODEXPORTARGS pArgs = (PRTDBGMODEXPORTARGS)pvUser; + NOREF(hLdrMod); + + if (Value >= pArgs->uImageBase) + { + char szOrdinalNm[48]; + if (!pszSymbol || *pszSymbol == '\0') + { + RTStrPrintf(szOrdinalNm, sizeof(szOrdinalNm), "Ordinal%u", uSymbol); + pszSymbol = szOrdinalNm; + } + + int rc = RTDbgModSymbolAdd(pArgs->pDbgMod, pszSymbol, RTDBGSEGIDX_RVA, Value - pArgs->uImageBase, + 0 /*cb*/, 0 /* fFlags */, NULL /*piOrdinal*/); + Log(("Symbol #%05u %#018RTptr %s [%Rrc]\n", uSymbol, Value, pszSymbol, rc)); NOREF(rc); + } + else + Log(("Symbol #%05u %#018RTptr %s [SKIPPED - INVALID ADDRESS]\n", uSymbol, Value, pszSymbol)); + return VINF_SUCCESS; +} + + +/** @callback_method_impl{FNRTLDRENUMSEGS, + * Copies the segments over into the container} */ +static DECLCALLBACK(int) rtDbgModExportsAddSegmentsCallback(RTLDRMOD hLdrMod, PCRTLDRSEG pSeg, void *pvUser) +{ + PRTDBGMODEXPORTARGS pArgs = (PRTDBGMODEXPORTARGS)pvUser; + Log(("Segment %.*s: LinkAddress=%#llx RVA=%#llx cb=%#llx\n", + pSeg->cchName, pSeg->pszName, (uint64_t)pSeg->LinkAddress, (uint64_t)pSeg->RVA, pSeg->cb)); + NOREF(hLdrMod); + + pArgs->cSegs++; + + /* Add dummy segments for segments that doesn't get mapped. */ + if ( pSeg->LinkAddress == NIL_RTLDRADDR + || pSeg->RVA == NIL_RTLDRADDR) + return RTDbgModSegmentAdd(pArgs->pDbgMod, 0, 0, pSeg->pszName, 0 /*fFlags*/, NULL); + + RTLDRADDR uRva = pSeg->RVA; +#if 0 /* Kluge for the .data..percpu segment in 64-bit linux kernels and similar. (Moved to ELF.) */ + if (uRva < pArgs->uRvaNext) + uRva = RT_ALIGN_T(pArgs->uRvaNext, pSeg->Alignment, RTLDRADDR); +#endif + + /* Find the best base address for the module. */ + if ( ( !pArgs->uImageBase + || pArgs->uImageBase > pSeg->LinkAddress) + && ( pSeg->LinkAddress != 0 /* .data..percpu again. */ + || pArgs->cSegs == 1)) + pArgs->uImageBase = pSeg->LinkAddress; + + /* Add it. */ + RTLDRADDR cb = RT_MAX(pSeg->cb, pSeg->cbMapped); + pArgs->uRvaNext = uRva + cb; + return RTDbgModSegmentAdd(pArgs->pDbgMod, uRva, cb, pSeg->pszName, 0 /*fFlags*/, NULL); +} + + +/** + * Creates the debug info side of affairs based on exports and segments found in + * the image part. + * + * The image part must be successfully initialized prior to the call, while the + * debug bits must not be present of course. + * + * @returns IPRT status code + * @param pDbgMod The debug module structure. + */ +DECLHIDDEN(int) rtDbgModCreateForExports(PRTDBGMODINT pDbgMod) +{ + AssertReturn(!pDbgMod->pDbgVt, VERR_DBG_MOD_IPE); + AssertReturn(pDbgMod->pImgVt, VERR_DBG_MOD_IPE); + RTUINTPTR cbImage = pDbgMod->pImgVt->pfnImageSize(pDbgMod); + AssertReturn(cbImage > 0, VERR_DBG_MOD_IPE); + + /* + * We simply use a container type for this work. + */ + int rc = rtDbgModContainerCreate(pDbgMod, 0); + if (RT_FAILURE(rc)) + return rc; + pDbgMod->fExports = true; + + /* + * Copy the segments and symbols. + */ + RTDBGMODEXPORTARGS Args; + Args.pDbgMod = pDbgMod; + Args.uImageBase = 0; + Args.uRvaNext = 0; + Args.cSegs = 0; + rc = pDbgMod->pImgVt->pfnEnumSegments(pDbgMod, rtDbgModExportsAddSegmentsCallback, &Args); + if (RT_SUCCESS(rc)) + { + rc = pDbgMod->pImgVt->pfnEnumSymbols(pDbgMod, RTLDR_ENUM_SYMBOL_FLAGS_ALL | RTLDR_ENUM_SYMBOL_FLAGS_NO_FWD, + Args.uImageBase ? Args.uImageBase : 0x10000, + rtDbgModExportsAddSymbolCallback, &Args); + if (RT_FAILURE(rc)) + Log(("rtDbgModCreateForExports: Error during symbol enum: %Rrc\n", rc)); + } + else + Log(("rtDbgModCreateForExports: Error during segment enum: %Rrc\n", rc)); + + /* This won't fail. */ + if (RT_SUCCESS(rc)) + rc = VINF_SUCCESS; + else + rc = -rc; /* Change it into a warning. */ + return rc; +} + diff --git a/src/VBox/Runtime/common/dbg/dbgmodghidra.cpp b/src/VBox/Runtime/common/dbg/dbgmodghidra.cpp new file mode 100644 index 00000000..d69f4640 --- /dev/null +++ b/src/VBox/Runtime/common/dbg/dbgmodghidra.cpp @@ -0,0 +1,526 @@ +/* $Id: dbgmodghidra.cpp $ */ +/** @file + * IPRT - Debug Info Reader for Ghidra XML files created with createPdbXmlFiles.bat/pdb.exe. + */ + +/* + * Copyright (C) 2021-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/dbg.h> +#include "internal/iprt.h" + +#include <iprt/err.h> +#include <iprt/ctype.h> +#include <iprt/mem.h> +#include <iprt/sort.h> +#include <iprt/stream.h> +#include <iprt/string.h> +#include <iprt/cpp/xml.h> +#include "internal/dbgmod.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Temporary segment data. + */ +typedef struct RTDBGMODGHIDRASEG +{ + const char *pszNumber; + RTUINTPTR uRva; +} RTDBGMODGHIDRASEG; +typedef RTDBGMODGHIDRASEG *PRTDBGMODGHIDRASEG; +typedef const RTDBGMODGHIDRASEG *PCRTDBGMODGHIDRASEG; + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnUnwindFrame} */ +static DECLCALLBACK(int) rtDbgModGhidra_UnwindFrame(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState) +{ + RT_NOREF(pMod, iSeg, off, pState); + return VERR_DBG_NO_UNWIND_INFO; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByAddr} */ +static DECLCALLBACK(int) rtDbgModGhidra_LineByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, + PRTINTPTR poffDisp, PRTDBGLINE pLineInfo) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModLineByAddr(hCnt, iSeg, off, poffDisp, pLineInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByOrdinal} */ +static DECLCALLBACK(int) rtDbgModGhidra_LineByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModLineByOrdinal(hCnt, iOrdinal, pLineInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineCount} */ +static DECLCALLBACK(uint32_t) rtDbgModGhidra_LineCount(PRTDBGMODINT pMod) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModLineCount(hCnt); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineAdd} */ +static DECLCALLBACK(int) rtDbgModGhidra_LineAdd(PRTDBGMODINT pMod, const char *pszFile, size_t cchFile, uint32_t uLineNo, + uint32_t iSeg, RTUINTPTR off, uint32_t *piOrdinal) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + Assert(!pszFile[cchFile]); NOREF(cchFile); + return RTDbgModLineAdd(hCnt, pszFile, uLineNo, iSeg, off, piOrdinal); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByAddr} */ +static DECLCALLBACK(int) rtDbgModGhidra_SymbolByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags, + PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModSymbolByAddr(hCnt, iSeg, off, fFlags, poffDisp, pSymInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByName} */ +static DECLCALLBACK(int) rtDbgModGhidra_SymbolByName(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol, + PRTDBGSYMBOL pSymInfo) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + Assert(!pszSymbol[cchSymbol]); NOREF(cchSymbol); + return RTDbgModSymbolByName(hCnt, pszSymbol, pSymInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByOrdinal} */ +static DECLCALLBACK(int) rtDbgModGhidra_SymbolByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModSymbolByOrdinal(hCnt, iOrdinal, pSymInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolCount} */ +static DECLCALLBACK(uint32_t) rtDbgModGhidra_SymbolCount(PRTDBGMODINT pMod) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModSymbolCount(hCnt); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolAdd} */ +static DECLCALLBACK(int) rtDbgModGhidra_SymbolAdd(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol, + RTDBGSEGIDX iSeg, RTUINTPTR off, RTUINTPTR cb, uint32_t fFlags, + uint32_t *piOrdinal) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + Assert(!pszSymbol[cchSymbol]); NOREF(cchSymbol); + return RTDbgModSymbolAdd(hCnt, pszSymbol, iSeg, off, cb, fFlags, piOrdinal); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentByIndex} */ +static DECLCALLBACK(int) rtDbgModGhidra_SegmentByIndex(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModSegmentByIndex(hCnt, iSeg, pSegInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentCount} */ +static DECLCALLBACK(RTDBGSEGIDX) rtDbgModGhidra_SegmentCount(PRTDBGMODINT pMod) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModSegmentCount(hCnt); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentAdd} */ +static DECLCALLBACK(int) rtDbgModGhidra_SegmentAdd(PRTDBGMODINT pMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName, + size_t cchName, uint32_t fFlags, PRTDBGSEGIDX piSeg) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + Assert(!pszName[cchName]); NOREF(cchName); + return RTDbgModSegmentAdd(hCnt, uRva, cb, pszName, fFlags, piSeg); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnImageSize} */ +static DECLCALLBACK(RTUINTPTR) rtDbgModGhidra_ImageSize(PRTDBGMODINT pMod) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModImageSize(hCnt); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnRvaToSegOff} */ +static DECLCALLBACK(RTDBGSEGIDX) rtDbgModGhidra_RvaToSegOff(PRTDBGMODINT pMod, RTUINTPTR uRva, PRTUINTPTR poffSeg) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModRvaToSegOff(hCnt, uRva, poffSeg); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnClose} */ +static DECLCALLBACK(int) rtDbgModGhidra_Close(PRTDBGMODINT pMod) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + RTDbgModRelease(hCnt); + pMod->pvDbgPriv = NULL; + return VINF_SUCCESS; +} + + +/** + * Returns the table with the given name from the given table list. + * + * @returns Pointer to the XML element node containing the given table or NULL if not found. + * @param pelmTables Pointer to the node containing the tables. + * @param pszName The table name to look for. + */ +static const xml::ElementNode *rtDbgModGhidraGetTableByName(const xml::ElementNode *pelmTables, const char *pszName) +{ + xml::NodesLoop nl1(*pelmTables, "table"); + const xml::ElementNode *pelmTbl; + while ((pelmTbl = nl1.forAllNodes())) + { + const char *pszTblName = NULL; + + if ( pelmTbl->getAttributeValue("name", &pszTblName) + && !strcmp(pszTblName, pszName)) + return pelmTbl; + } + + return NULL; +} + + +/** + * Adds the symbols from the given \"Symbols\" table. + * + * @returns IPRT status code. + * @param hCnt Debug module container handle. + * @param elmTbl Reference to the XML node containing the symbols. + */ +static int rtDbgModGhidraXmlParseSymbols(RTDBGMOD hCnt, const xml::ElementNode &elmTbl) +{ + xml::NodesLoop nlSym(elmTbl, "symbol"); + const xml::ElementNode *pelmSym; + while ((pelmSym = nlSym.forAllNodes())) + { + /* Only parse Function and PublicSymbol tags. */ + const char *pszTag = NULL; + if ( pelmSym->getAttributeValue("tag", &pszTag) + && ( !strcmp(pszTag, "PublicSymbol") + || !strcmp(pszTag, "Function"))) + { + const char *pszSymName = NULL; + if ( !pelmSym->getAttributeValue("undecorated", &pszSymName) + || *pszSymName == '\0') + pelmSym->getAttributeValue("name", &pszSymName); + + if ( pszSymName + && strlen(pszSymName) < RTDBG_SYMBOL_NAME_LENGTH) + { + uint64_t u64Addr = 0; + uint64_t u64Length = 0; + if ( pelmSym->getAttributeValue("address", &u64Addr) + && pelmSym->getAttributeValue("length", &u64Length)) + { + int rc = RTDbgModSymbolAdd(hCnt, pszSymName, RTDBGSEGIDX_RVA, u64Addr, u64Length, 0 /*fFlags*/, NULL); + if ( RT_FAILURE(rc) + && rc != VERR_DBG_DUPLICATE_SYMBOL + && rc != VERR_DBG_ADDRESS_CONFLICT + && rc != VERR_DBG_INVALID_RVA) /* (don't be too strict) */ + return rc; + } + } + } + } + + return VINF_SUCCESS; +} + + +/** + * Adds the symbols from the given \"functions\" table. + * + * @returns IPRT status code. + * @param hCnt Debug module container handle. + * @param elmTbl Reference to the XML node containing the symbols. + */ +static int rtDbgModGhidraXmlParseFunctions(RTDBGMOD hCnt, const xml::ElementNode &elmTbl) +{ + xml::NodesLoop nlFun(elmTbl, "function"); + const xml::ElementNode *pelmFun; + while ((pelmFun = nlFun.forAllNodes())) + { + xml::NodesLoop nlLn(*pelmFun, "line_number"); + const xml::ElementNode *pelmLn; + while ((pelmLn = nlLn.forAllNodes())) + { + const char *pszFile = NULL; + uint32_t uLineNo = 0; + uint64_t off = 0; + if ( pelmLn->getAttributeValue("source_file", &pszFile) + && pelmLn->getAttributeValue("start", &uLineNo) + && pelmLn->getAttributeValue("addr", &off)) + { + int rc = RTDbgModLineAdd(hCnt, pszFile, uLineNo, RTDBGSEGIDX_RVA, off, NULL /*piOrdinal*/); + if ( RT_FAILURE(rc) + && rc != VERR_DBG_DUPLICATE_SYMBOL + && rc != VERR_DBG_ADDRESS_CONFLICT + && rc != VERR_DBG_INVALID_RVA) /* (don't be too strict) */ + return rc; + } + } + } + + return VINF_SUCCESS; +} + + +/** + * @copydoc FNRTSORTCMP + */ +static DECLCALLBACK(int) rtDbgModGhidraSegmentsSortCmp(void const *pvElement1, void const *pvElement2, void *pvUser) +{ + RT_NOREF(pvUser); + PCRTDBGMODGHIDRASEG pSeg1 = (PCRTDBGMODGHIDRASEG)pvElement1; + PCRTDBGMODGHIDRASEG pSeg2 = (PCRTDBGMODGHIDRASEG)pvElement2; + + if (pSeg1->uRva > pSeg2->uRva) + return 1; + if (pSeg1->uRva < pSeg2->uRva) + return -1; + + return 0; +} + + +/** + * Adds the segments in the given \"SegmentMap\" table. + * + * @returns IPRT status code. + * @param hCnt Debug module container handle. + * @param elmTblSeg Reference to the XML node containing the segments. + */ +static int rtDbgModGhidraSegmentsAdd(RTDBGMOD hCnt, const xml::ElementNode &elmTblSeg) +{ + RTDBGMODGHIDRASEG aSegments[32]; + uint32_t idxSeg = 0; + + xml::NodesLoop nl1(elmTblSeg, "segment"); + const xml::ElementNode *pelmSeg; + while ( (pelmSeg = nl1.forAllNodes()) + && idxSeg < RT_ELEMENTS(aSegments)) + { + const char *pszNumber = NULL; + RTUINTPTR uRva = 0; + + if ( pelmSeg->getAttributeValue("number", &pszNumber) + && pelmSeg->getAttributeValue("address", &uRva)) + { + aSegments[idxSeg].pszNumber = pszNumber; + aSegments[idxSeg].uRva = uRva; + idxSeg++; + } + } + + /* Sort the segments by RVA so it is possible to deduce segment sizes. */ + RTSortShell(&aSegments[0], idxSeg, sizeof(aSegments[0]), rtDbgModGhidraSegmentsSortCmp, NULL); + + for (uint32_t i = 0; i < idxSeg - 1; i++) + { + int rc = RTDbgModSegmentAdd(hCnt, aSegments[i].uRva, aSegments[i + 1].uRva - aSegments[i].uRva, + aSegments[i].pszNumber, 0 /*fFlags*/, NULL); + if (RT_FAILURE(rc)) + return rc; + } + + /* Last segment for which we assume a size of 0 right now. */ + int rc = RTDbgModSegmentAdd(hCnt, aSegments[idxSeg - 1].uRva, 0, + aSegments[idxSeg - 1].pszNumber, 0 /*fFlags*/, NULL); + if (RT_FAILURE(rc)) + return rc; + + return rc; +} + + +/** + * Load the symbols from an XML document. + * + * @returns IPRT status code. + * @param hCnt Debug module container handle. + * @param a_pDoc Pointer to the XML document. + */ +static int rtDbgModGhidraXmlParse(RTDBGMOD hCnt, xml::Document *a_pDoc) +{ + /* + * Get the root element and check whether it looks like a valid Ghidra XML. + */ + const xml::ElementNode *pelmRoot = a_pDoc->getRootElement(); + if ( !pelmRoot + || strcmp(pelmRoot->getName(), "pdb") != 0) + return VERR_DBG_NO_MATCHING_INTERPRETER; + + const xml::ElementNode *pelmTables = pelmRoot->findChildElement("tables"); + if (!pelmTables) + return VERR_DBG_NO_MATCHING_INTERPRETER; + + const xml::ElementNode *pelmTbl = rtDbgModGhidraGetTableByName(pelmTables, "SegmentMap"); + if (pelmTbl) + { + int rc = rtDbgModGhidraSegmentsAdd(hCnt, *pelmTbl); + if (RT_SUCCESS(rc)) + { + pelmTbl = rtDbgModGhidraGetTableByName(pelmTables, "Symbols"); + if (pelmTbl) + { + rc = rtDbgModGhidraXmlParseSymbols(hCnt, *pelmTbl); + if (RT_SUCCESS(rc)) + { + pelmTbl = pelmRoot->findChildElement("functions"); /* Might not be there. */ + if (pelmTbl) + rc = rtDbgModGhidraXmlParseFunctions(hCnt, *pelmTbl); + return rc; + } + } + } + } + + return VERR_DBG_NO_MATCHING_INTERPRETER; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnTryOpen} */ +static DECLCALLBACK(int) rtDbgModGhidra_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch) +{ + RT_NOREF(enmArch); + + /* + * Fend off images. + */ + if (!pMod->pszDbgFile) + return VERR_DBG_NO_MATCHING_INTERPRETER; + pMod->pvDbgPriv = NULL; + + /* + * Try open the file and create an instance. + */ + xml::Document Doc; + { + xml::XmlFileParser Parser; + try + { + Parser.read(pMod->pszDbgFile, Doc); + } + catch (xml::XmlError &rErr) + { + RT_NOREF(rErr); + return VERR_DBG_NO_MATCHING_INTERPRETER; + } + catch (xml::EIPRTFailure &rErr) + { + return rErr.rc(); + } + } + + RTDBGMOD hCnt; + int rc = RTDbgModCreate(&hCnt, pMod->pszName, 0 /*cbSeg*/, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + /* + * Hand the xml doc over to the common code. + */ + try + { + rc = rtDbgModGhidraXmlParse(hCnt, &Doc); + if (RT_SUCCESS(rc)) + { + pMod->pvDbgPriv = hCnt; + return VINF_SUCCESS; + } + } + catch (RTCError &rXcpt) // includes all XML exceptions + { + RT_NOREF(rXcpt); + rc = VERR_DBG_NO_MATCHING_INTERPRETER; + } + RTDbgModRelease(hCnt); + } + + return rc; +} + + + +/** Virtual function table for the Ghidra XML file reader. */ +DECL_HIDDEN_CONST(RTDBGMODVTDBG) const g_rtDbgModVtDbgGhidra = +{ + /*.u32Magic = */ RTDBGMODVTDBG_MAGIC, + /*.fSupports = */ RT_DBGTYPE_OTHER | RT_DBGTYPE_MAP, + /*.pszName = */ "ghidra", + /*.pfnTryOpen = */ rtDbgModGhidra_TryOpen, + /*.pfnClose = */ rtDbgModGhidra_Close, + + /*.pfnRvaToSegOff = */ rtDbgModGhidra_RvaToSegOff, + /*.pfnImageSize = */ rtDbgModGhidra_ImageSize, + + /*.pfnSegmentAdd = */ rtDbgModGhidra_SegmentAdd, + /*.pfnSegmentCount = */ rtDbgModGhidra_SegmentCount, + /*.pfnSegmentByIndex = */ rtDbgModGhidra_SegmentByIndex, + + /*.pfnSymbolAdd = */ rtDbgModGhidra_SymbolAdd, + /*.pfnSymbolCount = */ rtDbgModGhidra_SymbolCount, + /*.pfnSymbolByOrdinal = */ rtDbgModGhidra_SymbolByOrdinal, + /*.pfnSymbolByName = */ rtDbgModGhidra_SymbolByName, + /*.pfnSymbolByAddr = */ rtDbgModGhidra_SymbolByAddr, + + /*.pfnLineAdd = */ rtDbgModGhidra_LineAdd, + /*.pfnLineCount = */ rtDbgModGhidra_LineCount, + /*.pfnLineByOrdinal = */ rtDbgModGhidra_LineByOrdinal, + /*.pfnLineByAddr = */ rtDbgModGhidra_LineByAddr, + + /*.pfnUnwindFrame = */ rtDbgModGhidra_UnwindFrame, + + /*.u32EndMagic = */ RTDBGMODVTDBG_MAGIC +}; + diff --git a/src/VBox/Runtime/common/dbg/dbgmodldr.cpp b/src/VBox/Runtime/common/dbg/dbgmodldr.cpp new file mode 100644 index 00000000..b81f6d89 --- /dev/null +++ b/src/VBox/Runtime/common/dbg/dbgmodldr.cpp @@ -0,0 +1,286 @@ +/* $Id: dbgmodldr.cpp $ */ +/** @file + * IPRT - Debug Module Image Interpretation by RTLdr. + */ + +/* + * Copyright (C) 2011-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/dbg.h> +#include "internal/iprt.h" + +#include <iprt/assert.h> +#include <iprt/errcore.h> +#include <iprt/file.h> +#include <iprt/ldr.h> +#include <iprt/mem.h> +#include <iprt/param.h> +#include <iprt/path.h> +#include <iprt/string.h> +#include "internal/dbgmod.h" +#include "internal/ldr.h" +#include "internal/magics.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * The instance data of the RTLdr based image reader. + */ +typedef struct RTDBGMODLDR +{ + /** Magic value (RTDBGMODLDR_MAGIC). */ + uint32_t u32Magic; + /** The loader handle. */ + RTLDRMOD hLdrMod; +} RTDBGMODLDR; +/** Pointer to instance data NM map reader. */ +typedef RTDBGMODLDR *PRTDBGMODLDR; + + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnUnwindFrame} */ +static DECLCALLBACK(int) rtDbgModLdr_UnwindFrame(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState) +{ + PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv; + Assert(pThis->u32Magic == RTDBGMODLDR_MAGIC); + return RTLdrUnwindFrame(pThis->hLdrMod, NULL, iSeg, off, pState); +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnQueryProp} */ +static DECLCALLBACK(int) rtDbgModLdr_QueryProp(PRTDBGMODINT pMod, RTLDRPROP enmProp, void *pvBuf, size_t cbBuf, size_t *pcbRet) +{ + PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv; + Assert(pThis->u32Magic == RTDBGMODLDR_MAGIC); + return RTLdrQueryPropEx(pThis->hLdrMod, enmProp, NULL /*pvBits*/, pvBuf, cbBuf, pcbRet); +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnGetArch} */ +static DECLCALLBACK(RTLDRARCH) rtDbgModLdr_GetArch(PRTDBGMODINT pMod) +{ + PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv; + Assert(pThis->u32Magic == RTDBGMODLDR_MAGIC); + return RTLdrGetArch(pThis->hLdrMod); +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnGetFormat} */ +static DECLCALLBACK(RTLDRFMT) rtDbgModLdr_GetFormat(PRTDBGMODINT pMod) +{ + PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv; + Assert(pThis->u32Magic == RTDBGMODLDR_MAGIC); + return RTLdrGetFormat(pThis->hLdrMod); +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnReadAt} */ +static DECLCALLBACK(int) rtDbgModLdr_ReadAt(PRTDBGMODINT pMod, uint32_t iDbgInfoHint, RTFOFF off, void *pvBuf, size_t cb) +{ + PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv; + Assert(pThis->u32Magic == RTDBGMODLDR_MAGIC); + RT_NOREF_PV(iDbgInfoHint); + return rtLdrReadAt(pThis->hLdrMod, pvBuf, UINT32_MAX /** @todo iDbgInfo*/, off, cb); +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnUnmapPart} */ +static DECLCALLBACK(int) rtDbgModLdr_UnmapPart(PRTDBGMODINT pMod, size_t cb, void const **ppvMap) +{ + Assert(((PRTDBGMODLDR)pMod->pvImgPriv)->u32Magic == RTDBGMODLDR_MAGIC); + NOREF(pMod); NOREF(cb); + RTMemFree((void *)*ppvMap); + *ppvMap = NULL; + return VINF_SUCCESS; +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnMapPart} */ +static DECLCALLBACK(int) rtDbgModLdr_MapPart(PRTDBGMODINT pMod, uint32_t iDbgInfo, RTFOFF off, size_t cb, void const **ppvMap) +{ + PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv; + Assert(pThis->u32Magic == RTDBGMODLDR_MAGIC); + + void *pvMap = RTMemAlloc(cb); + if (!pvMap) + return VERR_NO_MEMORY; + + int rc = rtLdrReadAt(pThis->hLdrMod, pvMap, iDbgInfo, off, cb); + if (RT_SUCCESS(rc)) + *ppvMap = pvMap; + else + { + RTMemFree(pvMap); + *ppvMap = NULL; + } + return rc; +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnImageSize} */ +static DECLCALLBACK(RTUINTPTR) rtDbgModLdr_ImageSize(PRTDBGMODINT pMod) +{ + PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv; + Assert(pThis->u32Magic == RTDBGMODLDR_MAGIC); + return RTLdrSize(pThis->hLdrMod); +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnRvaToSegOffset} */ +static DECLCALLBACK(int) rtDbgModLdr_RvaToSegOffset(PRTDBGMODINT pMod, RTLDRADDR Rva, PRTDBGSEGIDX piSeg, PRTLDRADDR poffSeg) +{ + PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv; + Assert(pThis->u32Magic == RTDBGMODLDR_MAGIC); + return RTLdrRvaToSegOffset(pThis->hLdrMod, Rva, piSeg, poffSeg); +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnLinkAddressToSegOffset} */ +static DECLCALLBACK(int) rtDbgModLdr_LinkAddressToSegOffset(PRTDBGMODINT pMod, RTLDRADDR LinkAddress, + PRTDBGSEGIDX piSeg, PRTLDRADDR poffSeg) +{ + PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv; + Assert(pThis->u32Magic == RTDBGMODLDR_MAGIC); + return RTLdrLinkAddressToSegOffset(pThis->hLdrMod, LinkAddress, piSeg, poffSeg); +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnEnumSymbols} */ +static DECLCALLBACK(int) rtDbgModLdr_EnumSymbols(PRTDBGMODINT pMod, uint32_t fFlags, RTLDRADDR BaseAddress, + PFNRTLDRENUMSYMS pfnCallback, void *pvUser) +{ + PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv; + Assert(pThis->u32Magic == RTDBGMODLDR_MAGIC); + return RTLdrEnumSymbols(pThis->hLdrMod, fFlags, NULL /*pvBits*/, BaseAddress, pfnCallback, pvUser); +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnEnumSegments} */ +static DECLCALLBACK(int) rtDbgModLdr_EnumSegments(PRTDBGMODINT pMod, PFNRTLDRENUMSEGS pfnCallback, void *pvUser) +{ + PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv; + Assert(pThis->u32Magic == RTDBGMODLDR_MAGIC); + return RTLdrEnumSegments(pThis->hLdrMod, pfnCallback, pvUser); +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnEnumDbgInfo} */ +static DECLCALLBACK(int) rtDbgModLdr_EnumDbgInfo(PRTDBGMODINT pMod, PFNRTLDRENUMDBG pfnCallback, void *pvUser) +{ + PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv; + Assert(pThis->u32Magic == RTDBGMODLDR_MAGIC); + return RTLdrEnumDbgInfo(pThis->hLdrMod, NULL, pfnCallback, pvUser); +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnClose} */ +static DECLCALLBACK(int) rtDbgModLdr_Close(PRTDBGMODINT pMod) +{ + PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv; + AssertPtr(pThis); + Assert(pThis->u32Magic == RTDBGMODLDR_MAGIC); + + int rc = RTLdrClose(pThis->hLdrMod); AssertRC(rc); + pThis->hLdrMod = NIL_RTLDRMOD; + pThis->u32Magic = RTDBGMODLDR_MAGIC_DEAD; + + RTMemFree(pThis); + + return VINF_SUCCESS; +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnTryOpen} */ +static DECLCALLBACK(int) rtDbgModLdr_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch, uint32_t fLdrFlags) +{ + RTLDRMOD hLdrMod; + int rc = RTLdrOpen(pMod->pszImgFile, RTLDR_O_FOR_DEBUG | fLdrFlags, enmArch, &hLdrMod); + if (RT_SUCCESS(rc)) + { + rc = rtDbgModLdrOpenFromHandle(pMod, hLdrMod); + if (RT_FAILURE(rc)) + RTLdrClose(hLdrMod); + } + return rc; +} + + +/** Virtual function table for the RTLdr based image reader. */ +DECL_HIDDEN_CONST(RTDBGMODVTIMG) const g_rtDbgModVtImgLdr = +{ + /*.u32Magic = */ RTDBGMODVTIMG_MAGIC, + /*.fReserved = */ 0, + /*.pszName = */ "RTLdr", + /*.pfnTryOpen = */ rtDbgModLdr_TryOpen, + /*.pfnClose = */ rtDbgModLdr_Close, + /*.pfnEnumDbgInfo = */ rtDbgModLdr_EnumDbgInfo, + /*.pfnEnumSegments = */ rtDbgModLdr_EnumSegments, + /*.pfnEnumSymbols = */ rtDbgModLdr_EnumSymbols, + /*.pfnImageSize = */ rtDbgModLdr_ImageSize, + /*.pfnLinkAddressToSegOffset = */ rtDbgModLdr_LinkAddressToSegOffset, + /*.pfnRvaToSegOffset= */ rtDbgModLdr_RvaToSegOffset, + /*.pfnMapPart = */ rtDbgModLdr_MapPart, + /*.pfnUnmapPart = */ rtDbgModLdr_UnmapPart, + /*.pfnReadAt = */ rtDbgModLdr_ReadAt, + /*.pfnGetFormat = */ rtDbgModLdr_GetFormat, + /*.pfnGetArch = */ rtDbgModLdr_GetArch, + /*.pfnQueryProp = */ rtDbgModLdr_QueryProp, + /*.pfnUnwindFrame = */ rtDbgModLdr_UnwindFrame, + + /*.u32EndMagic = */ RTDBGMODVTIMG_MAGIC +}; + + +/** + * Open PE-image trick. + * + * @returns IPRT status code + * @param pDbgMod The debug module instance. + * @param hLdrMod The module to open a image debug backend for. + */ +DECLHIDDEN(int) rtDbgModLdrOpenFromHandle(PRTDBGMODINT pDbgMod, RTLDRMOD hLdrMod) +{ + PRTDBGMODLDR pThis = (PRTDBGMODLDR)RTMemAllocZ(sizeof(RTDBGMODLDR)); + if (!pThis) + return VERR_NO_MEMORY; + + pThis->u32Magic = RTDBGMODLDR_MAGIC; + pThis->hLdrMod = hLdrMod; + pDbgMod->pvImgPriv = pThis; + return VINF_SUCCESS; +} + diff --git a/src/VBox/Runtime/common/dbg/dbgmodmapsym.cpp b/src/VBox/Runtime/common/dbg/dbgmodmapsym.cpp new file mode 100644 index 00000000..8cbc0447 --- /dev/null +++ b/src/VBox/Runtime/common/dbg/dbgmodmapsym.cpp @@ -0,0 +1,622 @@ +/* $Id: dbgmodmapsym.cpp $ */ +/** @file + * IPRT - Debug Map Reader for MAPSYM files (used by SYMDBG from old MASM). + * + * MAPSYM is was the tool producing these files from linker map files for + * use with SYMDBG (which shipped with MASM 3.0 (possibly earlier)), the OS/2 + * kernel debugger, and other tools. The format is very limited and they had + * to strip down the os2krnl.map file in later years to keep MAPSYM happy. + */ + +/* + * Copyright (C) 2009-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_DBG +#include <iprt/dbg.h> +#include "internal/iprt.h" + +#include <iprt/err.h> +#include <iprt/ctype.h> +#include <iprt/file.h> +#include <iprt/log.h> +#include <iprt/mem.h> +#include <iprt/stream.h> +#include <iprt/string.h> +#include "internal/dbgmod.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** @name MAPSYM structures and constants. + * @{ */ + +/** MAPSYM: Header structure. */ +typedef struct MAPSYMHDR +{ + uint16_t off16NextMap; /**< 0x00: Offset of the next map divided by 16. */ + uint8_t bFlags; /**< 0x02: Who really knows... */ + uint8_t bReserved; /**< 0x03: Reserved / unknown. */ + uint16_t uSegEntry; /**< 0x04: Some entrypoint/segment thing we don't care about. */ + uint16_t cConsts; /**< 0x06: Constants referenced by offConstDef. */ + uint16_t offConstDef; /**< 0x08: Offset to head of constant chain. Not div 16? */ + uint16_t cSegs; /**< 0x0a: Number of segments in the map. */ + uint16_t off16SegDef; /**< 0x0c: Offset of the segment defintions divided by 16. */ + uint8_t cchMaxSym; /**< 0x0e: Maximum symbol-name length. */ + uint8_t cchModule; /**< 0x0f: Length of the module name. */ + RT_FLEXIBLE_ARRAY_EXTENSION + char achModule[RT_FLEXIBLE_ARRAY]; /**< 0x10: Module name, length given by cchModule. */ +} MAPSYMHDR; + +/** MAPSYM: Tail structure. */ +typedef struct MAPSYMTAIL +{ + uint16_t offNextMap; /**< 0x00: Always zero (it's the tail, see). */ + uint8_t bRelease; /**< 0x02: Minor version number. */ + uint8_t bVersion; /**< 0x03: Major version number. */ +} MAPSYMTAIL; + +/** MAPSYM: Segment defintion. */ +typedef struct MAPSYMSEGDEF +{ + uint16_t off16NextSeg; /**< 0x00: Offset of the next segment divided by 16. */ + uint16_t cSymbols; /**< 0x02: Number of symbol offsets . */ + uint16_t offSymbolOffsets; /**< 0x04: Offset of the symbol offset table. Each entry is a 16-bit value giving + * the offset symbol relative to this structure. */ + uint16_t au16Reserved0[4]; /**< 0x06: Reserved / unknown. + * First byte/word seems to be 1-based segment number. */ + uint8_t bFlags; /**< 0x0e: MAPSYMSEGDEF_F_32BIT or zero. */ + uint8_t bReserved1; /**< 0x0f: Reserved / unknown. */ + uint16_t offLineDef; /**< 0x10: Offset to the line defintions. */ + uint16_t u16Reserved2; /**< 0x12: Reserved / unknown. Often seen holding 0xff00. */ + uint8_t cchSegName; /**< 0x14: Segment name length. */ + RT_FLEXIBLE_ARRAY_EXTENSION + char achSegName[RT_FLEXIBLE_ARRAY]; /**< 0x15: Segment name, length given by cchSegName. */ +} MAPSYMSEGDEF; + +#define MAPSYMSEGDEF_F_32BIT UINT8_C(0x01) /**< Indicates 32-bit segment rather than 16-bit, relevant for symbols. */ +#define MAPSYMSEGDEF_F_UNKNOWN UINT8_C(0x02) /**< Set on all segments in os2krnlr.sym from ACP2. */ + +/** MAPSYM: 16-bit symbol */ +typedef struct MAPSYMSYMDEF16 +{ + uint16_t uValue; /**< 0x00: The symbol value (address). */ + uint8_t cchName; /**< 0x02: Symbol name length. */ + char achName[1]; /**< 0x03: The symbol name, length give by cchName. */ +} MAPSYMSYMDEF16; + +/** MAPSYM: 16-bit symbol */ +typedef struct MAPSYMSYMDEF32 +{ + uint32_t uValue; /**< 0x00: The symbol value (address). */ + uint8_t cchName; /**< 0x04: Symbol name length. */ + char achName[1]; /**< 0x05: The symbol name, length give by cchName. */ +} MAPSYMSYMDEF32; + +/** MAPSYM: Line number defintions. */ +typedef struct MAPSYMLINEDEF +{ + uint16_t off16NextLine; /**< 0x00: Offset to the next line defintion divided by 16. */ + uint16_t uSegment; /**< 0x02: Guessing this must be segment number. */ + uint16_t offLines; /**< 0x04: Offset to the line number array, relative to this structure. */ + uint16_t cLines; /**< 0x08: Number of line numbers in the array. */ + uint8_t cchSrcFile; /**< 0x0a: Length of source filename. */ + RT_FLEXIBLE_ARRAY_EXTENSION + char achSrcFile[RT_FLEXIBLE_ARRAY]; /**< 0x0b: Source filename, length given by cchSrcFile. */ +} MAPSYMLINEDEF; + +/** MAPSYM: 16-bit line numbers. */ +typedef struct MAPSYMLINENO16 +{ + uint16_t offSeg; + uint16_t uLineNo; +} MAPSYMLINENO16; + +/** @} */ + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** Maximum number of segments we expect in a MAPSYM file. */ +#define RTDBGMODMAPSYM_MAX_SEGMENTS 256 + + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnUnwindFrame} */ +static DECLCALLBACK(int) rtDbgModMapSym_UnwindFrame(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState) +{ + RT_NOREF(pMod, iSeg, off, pState); + return VERR_DBG_NO_UNWIND_INFO; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByAddr} */ +static DECLCALLBACK(int) rtDbgModMapSym_LineByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, + PRTINTPTR poffDisp, PRTDBGLINE pLineInfo) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModLineByAddr(hCnt, iSeg, off, poffDisp, pLineInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByOrdinal} */ +static DECLCALLBACK(int) rtDbgModMapSym_LineByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModLineByOrdinal(hCnt, iOrdinal, pLineInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineCount} */ +static DECLCALLBACK(uint32_t) rtDbgModMapSym_LineCount(PRTDBGMODINT pMod) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModLineCount(hCnt); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineAdd} */ +static DECLCALLBACK(int) rtDbgModMapSym_LineAdd(PRTDBGMODINT pMod, const char *pszFile, size_t cchFile, uint32_t uLineNo, + uint32_t iSeg, RTUINTPTR off, uint32_t *piOrdinal) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + Assert(!pszFile[cchFile]); NOREF(cchFile); + return RTDbgModLineAdd(hCnt, pszFile, uLineNo, iSeg, off, piOrdinal); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByAddr} */ +static DECLCALLBACK(int) rtDbgModMapSym_SymbolByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags, + PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModSymbolByAddr(hCnt, iSeg, off, fFlags, poffDisp, pSymInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByName} */ +static DECLCALLBACK(int) rtDbgModMapSym_SymbolByName(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol, + PRTDBGSYMBOL pSymInfo) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + Assert(!pszSymbol[cchSymbol]); NOREF(cchSymbol); + return RTDbgModSymbolByName(hCnt, pszSymbol, pSymInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByOrdinal} */ +static DECLCALLBACK(int) rtDbgModMapSym_SymbolByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModSymbolByOrdinal(hCnt, iOrdinal, pSymInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolCount} */ +static DECLCALLBACK(uint32_t) rtDbgModMapSym_SymbolCount(PRTDBGMODINT pMod) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModSymbolCount(hCnt); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolAdd} */ +static DECLCALLBACK(int) rtDbgModMapSym_SymbolAdd(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol, + RTDBGSEGIDX iSeg, RTUINTPTR off, RTUINTPTR cb, uint32_t fFlags, + uint32_t *piOrdinal) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + Assert(!pszSymbol[cchSymbol]); NOREF(cchSymbol); + return RTDbgModSymbolAdd(hCnt, pszSymbol, iSeg, off, cb, fFlags, piOrdinal); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentByIndex} */ +static DECLCALLBACK(int) rtDbgModMapSym_SegmentByIndex(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModSegmentByIndex(hCnt, iSeg, pSegInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentCount} */ +static DECLCALLBACK(RTDBGSEGIDX) rtDbgModMapSym_SegmentCount(PRTDBGMODINT pMod) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModSegmentCount(hCnt); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentAdd} */ +static DECLCALLBACK(int) rtDbgModMapSym_SegmentAdd(PRTDBGMODINT pMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName, + size_t cchName, uint32_t fFlags, PRTDBGSEGIDX piSeg) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + Assert(!pszName[cchName]); NOREF(cchName); + return RTDbgModSegmentAdd(hCnt, uRva, cb, pszName, fFlags, piSeg); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnImageSize} */ +static DECLCALLBACK(RTUINTPTR) rtDbgModMapSym_ImageSize(PRTDBGMODINT pMod) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModImageSize(hCnt); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnRvaToSegOff} */ +static DECLCALLBACK(RTDBGSEGIDX) rtDbgModMapSym_RvaToSegOff(PRTDBGMODINT pMod, RTUINTPTR uRva, PRTUINTPTR poffSeg) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + return RTDbgModRvaToSegOff(hCnt, uRva, poffSeg); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnClose} */ +static DECLCALLBACK(int) rtDbgModMapSym_Close(PRTDBGMODINT pMod) +{ + RTDBGMOD hCnt = (RTDBGMOD)pMod->pvDbgPriv; + RTDbgModRelease(hCnt); + pMod->pvDbgPriv = NULL; + return VINF_SUCCESS; +} + + +/** + * Validate the module header. + * + * @returns true if valid, false if not. + * @param pHdr The header. + * @param cbAvail How much we've actually read. + * @param cbFile The file size (relative to module header). + */ +static bool rtDbgModMapSymIsValidHeader(MAPSYMHDR const *pHdr, size_t cbAvail, uint64_t cbFile) +{ + if (cbAvail <= RT_UOFFSETOF(MAPSYMHDR, achModule)) + return false; + + if (pHdr->cSegs == 0) + return false; + if (pHdr->cSegs > RTDBGMODMAPSYM_MAX_SEGMENTS) + return false; + + if (pHdr->off16SegDef == 0) + return false; + if (pHdr->off16SegDef * (uint32_t)16 >= cbFile) + return false; + + if (pHdr->cchModule == 0) + return false; + if (pHdr->cchModule > 128) /* Note must be smaller than abPadding below in caller */ + return false; + + size_t cchMaxName = cbAvail - RT_UOFFSETOF(MAPSYMHDR, achModule); + if (pHdr->cchModule > cchMaxName) + return false; + + for (uint32_t i = 0; i < pHdr->cchModule; i++) + { + unsigned char const uch = pHdr->achModule[i]; + if ( uch < 0x20 + || uch >= 0x7f) + return false; + } + + return true; +} + + +/** + * Validate the given segment definition. + * + * @returns true if valid, false if not. + * @param pSegDef The segment definition structure. + * @param cbMax Host many bytes are available starting with pSegDef. + */ +static bool rtDbgModMapSymIsValidSegDef(MAPSYMSEGDEF const *pSegDef, size_t cbMax) +{ + if (RT_UOFFSETOF(MAPSYMSEGDEF, achSegName) > cbMax) + return false; + if (pSegDef->cSymbols) + { + if (pSegDef->cSymbols > _32K) + { + Log(("rtDbgModMapSymIsValidSegDef: Too many symbols: %#x\n", pSegDef->cSymbols)); + return false; + } + + if (pSegDef->offSymbolOffsets + (uint32_t)2 * pSegDef->cSymbols > cbMax) + { + Log(("rtDbgModMapSymIsValidSegDef: Bad symbol offset/count: %#x/%#x\n", pSegDef->offSymbolOffsets, pSegDef->cSymbols)); + return false; + } + } + + size_t cchMaxName = cbMax - RT_UOFFSETOF(MAPSYMHDR, achModule); + if (pSegDef->cchSegName > cchMaxName) + { + Log(("rtDbgModMapSymIsValidSegDef: Bad segment name length\n")); + return false; + } + + for (uint32_t i = 0; i < pSegDef->cchSegName; i++) + { + unsigned char uch = pSegDef->achSegName[i]; + if ( uch < 0x20 + || uch >= 0x7f) + { + Log(("rtDbgModMapSymIsValidSegDef: Bad segment name: %.*Rhxs\n", pSegDef->cchSegName, pSegDef->achSegName)); + return false; + } + } + + return true; +} + + +/** + * Fills @a hCnt with segments and symbols from the MAPSYM file. + * + * @note We only support reading the first module, right now. + */ +static int rtDbgModMapSymReadIt(RTDBGMOD hCnt, uint8_t const *pbFile, size_t cbFile) +{ + /* + * Revalidate the header. + */ + MAPSYMHDR const *pHdr = (MAPSYMHDR const *)pbFile; + if (!rtDbgModMapSymIsValidHeader(pHdr, cbFile, cbFile)) + return VERR_DBG_NO_MATCHING_INTERPRETER; + Log(("rtDbgModMapSymReadIt: szModule='%.*s' cSegs=%u off16NextMap=%#x\n", + pHdr->cchModule, pHdr->achModule, pHdr->cSegs, pHdr->off16NextMap)); + + /* + * Load each segment. + */ + uint32_t uRva = 0; + uint32_t const cSegs = pHdr->cSegs; + uint32_t offSegment = pHdr->off16SegDef * (uint32_t)16; + for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++) + { + if (offSegment >= cbFile) + return VERR_DBG_NO_MATCHING_INTERPRETER; + + size_t const cbMax = cbFile - offSegment; + MAPSYMSEGDEF const *pSegDef = (MAPSYMSEGDEF const *)&pbFile[offSegment]; + if (!rtDbgModMapSymIsValidSegDef(pSegDef, cbMax)) + return VERR_DBG_NO_MATCHING_INTERPRETER; + + Log(("rtDbgModMapSymReadIt: Segment #%u: flags=%#x name='%.*s' symbols=%#x @ %#x next=%#x lines=@%#x (reserved: %#x %#x %#x %#x %#x %#x)\n", + iSeg, pSegDef->bFlags, pSegDef->cchSegName, pSegDef->achSegName, pSegDef->cSymbols, pSegDef->offSymbolOffsets, + pSegDef->off16NextSeg, pSegDef->offLineDef, pSegDef->au16Reserved0[0], pSegDef->au16Reserved0[1], + pSegDef->au16Reserved0[2], pSegDef->au16Reserved0[3], pSegDef->bReserved1, pSegDef->u16Reserved2)); + + /* + * First symbol pass finds the largest symbol and uses that as the segment size. + */ + uint32_t cbSegmentEst = 0; + uint32_t const cSymbols = pSegDef->cSymbols; + uint16_t const * const paoffSymbols = (uint16_t const *)&pbFile[offSegment + pSegDef->offSymbolOffsets]; + bool const fIs32Bit = RT_BOOL(pSegDef->bFlags & MAPSYMSEGDEF_F_32BIT); + uint32_t const cbSymDef = fIs32Bit ? 4 + 1 : 2 + 1; + for (uint32_t iSymbol = 0; iSymbol < cSymbols; iSymbol++) + { + uint32_t off = paoffSymbols[iSymbol] + offSegment; + if (off + cbSymDef <= cbFile) + { + uint32_t uValue = fIs32Bit ? *(uint32_t const *)&pbFile[off] : (uint32_t)*(uint16_t const *)&pbFile[off]; + if (uValue > cbSegmentEst) + cbSegmentEst = uValue; + } + else + Log(("rtDbgModMapSymReadIt: Bad symbol offset %#x\n", off)); + } + + /* + * Add the segment. + */ + char szName[256]; + memcpy(szName, pSegDef->achSegName, pSegDef->cchSegName); + szName[pSegDef->cchSegName] = '\0'; + if (!pSegDef->cchSegName) + RTStrPrintf(szName, sizeof(szName), "seg%02u", iSeg); + + RTDBGSEGIDX idxDbgSeg = iSeg; + int rc = RTDbgModSegmentAdd(hCnt, uRva, cbSegmentEst, szName, 0 /*fFlags*/, &idxDbgSeg); + if (RT_FAILURE(rc)) + return rc; + + uRva += cbSegmentEst; + + /* + * The second symbol pass loads the symbol values and names. + */ + for (uint32_t iSymbol = 0; iSymbol < cSymbols; iSymbol++) + { + uint32_t off = paoffSymbols[iSymbol] + offSegment; + if (off + cbSymDef <= cbFile) + { + /* Get value: */ + uint32_t uValue = RT_MAKE_U16(pbFile[off], pbFile[off + 1]); + off += 2; + if (fIs32Bit) + { + uValue |= RT_MAKE_U32_FROM_U8(0, 0, pbFile[off], pbFile[off + 1]); + off += 2; + } + + /* Get name: */ + uint8_t cchName = pbFile[off++]; + if (off + cchName <= cbFile) + { + memcpy(szName, &pbFile[off], cchName); + szName[cchName] = '\0'; + RTStrPurgeEncoding(szName); + } + else + cchName = 0; + if (cchName == 0) + RTStrPrintf(szName, sizeof(szName), "unknown_%u_%u", iSeg, iSymbol); + + /* Try add it: */ + rc = RTDbgModSymbolAdd(hCnt, szName, idxDbgSeg, uValue, 0 /*cb*/, 0 /*fFlags*/, NULL /*piOrdinal*/); + if (RT_SUCCESS(rc)) + Log7(("rtDbgModMapSymReadIt: %02x:%06x %s\n", idxDbgSeg, uValue, szName)); + else if ( rc == VERR_DBG_DUPLICATE_SYMBOL + || rc == VERR_DBG_ADDRESS_CONFLICT + || rc == VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE) + Log(("rtDbgModMapSymReadIt: %02x:%06x %s\n", idxDbgSeg, uValue, szName)); + else + { + Log(("rtDbgModMapSymReadIt: Unexpected RTDbgModSymbolAdd failure: %Rrc - %02x:%06x %s\n", + rc, idxDbgSeg, uValue, szName)); + return rc; + } + } + } + + /* Next segment */ + offSegment = pSegDef->off16NextSeg * (uint32_t)16; + } + return VINF_SUCCESS; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnTryOpen} */ +static DECLCALLBACK(int) rtDbgModMapSym_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch) +{ + NOREF(enmArch); + + /* + * Fend off images. + */ + if ( !pMod->pszDbgFile + || pMod->pImgVt) + return VERR_DBG_NO_MATCHING_INTERPRETER; + pMod->pvDbgPriv = NULL; + + /* + * Try open the file and check out the first header. + */ + RTFILE hFile; + int rc = RTFileOpen(&hFile, pMod->pszDbgFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE); + if (RT_SUCCESS(rc)) + { + uint64_t cbFile = 0; + rc = RTFileQuerySize(hFile, &cbFile); + if ( RT_SUCCESS(rc) + && cbFile < _2M) + { + union + { + MAPSYMHDR Hdr; + char abPadding[sizeof(MAPSYMHDR) + 257]; /* rtDbgModMapSymIsValidHeader makes size assumptions. */ + } uBuf; + size_t cbToRead = (size_t)RT_MIN(cbFile, sizeof(uBuf)); + rc = RTFileReadAt(hFile, 0, &uBuf, RT_MIN(cbFile, sizeof(uBuf)), NULL); + if (RT_SUCCESS(rc)) + { + if (rtDbgModMapSymIsValidHeader(&uBuf.Hdr, cbToRead, cbFile)) + { + uBuf.Hdr.achModule[uBuf.Hdr.cchModule] = '\0'; + + /* + * Read the whole thing into memory, create an + * instance/container and load it with symbols. + */ + void *pvFile = NULL; + size_t cbFile2 = 0; + rc = RTFileReadAllByHandle(hFile, &pvFile, &cbFile2); + if (RT_SUCCESS(rc)) + { + RTDBGMOD hCnt; + rc = RTDbgModCreate(&hCnt, uBuf.Hdr.achModule, 0 /*cbSeg*/, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + rc = rtDbgModMapSymReadIt(hCnt, (uint8_t const *)pvFile, cbFile2); + if (RT_SUCCESS(rc)) + pMod->pvDbgPriv = hCnt; + else + RTDbgModRelease(hCnt); + } + RTFileReadAllFree(pvFile, cbFile2); + } + } + else + rc = VERR_DBG_NO_MATCHING_INTERPRETER; + } + } + RTFileClose(hFile); + } + Log(("rtDbgModMapSym_TryOpen: %s -> %Rrc, %p\n", pMod->pszDbgFile, rc, pMod->pvDbgPriv)); + return rc; +} + + + +/** Virtual function table for the MAPSYM file reader. */ +DECL_HIDDEN_CONST(RTDBGMODVTDBG) const g_rtDbgModVtDbgMapSym = +{ + /*.u32Magic = */ RTDBGMODVTDBG_MAGIC, + /*.fSupports = */ RT_DBGTYPE_SYM, + /*.pszName = */ "mapsym", + /*.pfnTryOpen = */ rtDbgModMapSym_TryOpen, + /*.pfnClose = */ rtDbgModMapSym_Close, + + /*.pfnRvaToSegOff = */ rtDbgModMapSym_RvaToSegOff, + /*.pfnImageSize = */ rtDbgModMapSym_ImageSize, + + /*.pfnSegmentAdd = */ rtDbgModMapSym_SegmentAdd, + /*.pfnSegmentCount = */ rtDbgModMapSym_SegmentCount, + /*.pfnSegmentByIndex = */ rtDbgModMapSym_SegmentByIndex, + + /*.pfnSymbolAdd = */ rtDbgModMapSym_SymbolAdd, + /*.pfnSymbolCount = */ rtDbgModMapSym_SymbolCount, + /*.pfnSymbolByOrdinal = */ rtDbgModMapSym_SymbolByOrdinal, + /*.pfnSymbolByName = */ rtDbgModMapSym_SymbolByName, + /*.pfnSymbolByAddr = */ rtDbgModMapSym_SymbolByAddr, + + /*.pfnLineAdd = */ rtDbgModMapSym_LineAdd, + /*.pfnLineCount = */ rtDbgModMapSym_LineCount, + /*.pfnLineByOrdinal = */ rtDbgModMapSym_LineByOrdinal, + /*.pfnLineByAddr = */ rtDbgModMapSym_LineByAddr, + + /*.pfnUnwindFrame = */ rtDbgModMapSym_UnwindFrame, + + /*.u32EndMagic = */ RTDBGMODVTDBG_MAGIC +}; + diff --git a/src/VBox/Runtime/common/dbg/dbgmodnm.cpp b/src/VBox/Runtime/common/dbg/dbgmodnm.cpp new file mode 100644 index 00000000..04174c7b --- /dev/null +++ b/src/VBox/Runtime/common/dbg/dbgmodnm.cpp @@ -0,0 +1,581 @@ +/* $Id: dbgmodnm.cpp $ */ +/** @file + * IPRT - Debug Map Reader For NM Like Mapfiles. + */ + +/* + * Copyright (C) 2009-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/dbg.h> +#include "internal/iprt.h" + +#include <iprt/err.h> +#include <iprt/ctype.h> +#include <iprt/mem.h> +#include <iprt/stream.h> +#include <iprt/string.h> +#include "internal/dbgmod.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Instance data. + */ +typedef struct RTDBGMODNM +{ + /** The debug container containing doing the real work. */ + RTDBGMOD hCnt; +} RTDBGMODNM; +/** Pointer to instance data NM map reader. */ +typedef RTDBGMODNM *PRTDBGMODNM; + + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnUnwindFrame} */ +static DECLCALLBACK(int) rtDbgModNm_UnwindFrame(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState) +{ + RT_NOREF(pMod, iSeg, off, pState); + return VERR_DBG_NO_UNWIND_INFO; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByAddr} */ +static DECLCALLBACK(int) rtDbgModNm_LineByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, + PRTINTPTR poffDisp, PRTDBGLINE pLineInfo) +{ + PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv; + return RTDbgModLineByAddr(pThis->hCnt, iSeg, off, poffDisp, pLineInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByOrdinal} */ +static DECLCALLBACK(int) rtDbgModNm_LineByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo) +{ + PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv; + return RTDbgModLineByOrdinal(pThis->hCnt, iOrdinal, pLineInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineCount} */ +static DECLCALLBACK(uint32_t) rtDbgModNm_LineCount(PRTDBGMODINT pMod) +{ + PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv; + return RTDbgModLineCount(pThis->hCnt); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineAdd} */ +static DECLCALLBACK(int) rtDbgModNm_LineAdd(PRTDBGMODINT pMod, const char *pszFile, size_t cchFile, uint32_t uLineNo, + uint32_t iSeg, RTUINTPTR off, uint32_t *piOrdinal) +{ + PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv; + Assert(!pszFile[cchFile]); NOREF(cchFile); + return RTDbgModLineAdd(pThis->hCnt, pszFile, uLineNo, iSeg, off, piOrdinal); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByAddr} */ +static DECLCALLBACK(int) rtDbgModNm_SymbolByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags, + PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo) +{ + PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv; + return RTDbgModSymbolByAddr(pThis->hCnt, iSeg, off, fFlags, poffDisp, pSymInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByName} */ +static DECLCALLBACK(int) rtDbgModNm_SymbolByName(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol, + PRTDBGSYMBOL pSymInfo) +{ + PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv; + Assert(!pszSymbol[cchSymbol]); NOREF(cchSymbol); + return RTDbgModSymbolByName(pThis->hCnt, pszSymbol, pSymInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByOrdinal} */ +static DECLCALLBACK(int) rtDbgModNm_SymbolByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo) +{ + PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv; + return RTDbgModSymbolByOrdinal(pThis->hCnt, iOrdinal, pSymInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolCount} */ +static DECLCALLBACK(uint32_t) rtDbgModNm_SymbolCount(PRTDBGMODINT pMod) +{ + PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv; + return RTDbgModSymbolCount(pThis->hCnt); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolAdd} */ +static DECLCALLBACK(int) rtDbgModNm_SymbolAdd(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol, + RTDBGSEGIDX iSeg, RTUINTPTR off, RTUINTPTR cb, uint32_t fFlags, + uint32_t *piOrdinal) +{ + PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv; + Assert(!pszSymbol[cchSymbol]); NOREF(cchSymbol); + return RTDbgModSymbolAdd(pThis->hCnt, pszSymbol, iSeg, off, cb, fFlags, piOrdinal); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentByIndex} */ +static DECLCALLBACK(int) rtDbgModNm_SegmentByIndex(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo) +{ + PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv; + return RTDbgModSegmentByIndex(pThis->hCnt, iSeg, pSegInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentCount} */ +static DECLCALLBACK(RTDBGSEGIDX) rtDbgModNm_SegmentCount(PRTDBGMODINT pMod) +{ + PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv; + return RTDbgModSegmentCount(pThis->hCnt); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentAdd} */ +static DECLCALLBACK(int) rtDbgModNm_SegmentAdd(PRTDBGMODINT pMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName, + size_t cchName, uint32_t fFlags, PRTDBGSEGIDX piSeg) +{ + PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv; + Assert(!pszName[cchName]); NOREF(cchName); + return RTDbgModSegmentAdd(pThis->hCnt, uRva, cb, pszName, fFlags, piSeg); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnImageSize} */ +static DECLCALLBACK(RTUINTPTR) rtDbgModNm_ImageSize(PRTDBGMODINT pMod) +{ + PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv; + return RTDbgModImageSize(pThis->hCnt); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnRvaToSegOff} */ +static DECLCALLBACK(RTDBGSEGIDX) rtDbgModNm_RvaToSegOff(PRTDBGMODINT pMod, RTUINTPTR uRva, PRTUINTPTR poffSeg) +{ + PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv; + return RTDbgModRvaToSegOff(pThis->hCnt, uRva, poffSeg); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnClose} */ +static DECLCALLBACK(int) rtDbgModNm_Close(PRTDBGMODINT pMod) +{ + PRTDBGMODNM pThis = (PRTDBGMODNM)pMod->pvDbgPriv; + RTDbgModRelease(pThis->hCnt); + pThis->hCnt = NIL_RTDBGMOD; + RTMemFree(pThis); + return VINF_SUCCESS; +} + + +/** + * Scans a NM-like map file. + * + * This implements both passes to avoid code duplication. + * + * @returns IPRT status code. + * @param pThis The instance data. + * @param pStrm The stream. + * @param fAddSymbols false in the first pass, true in the second. + */ +static int rtDbgModNmScanFile(PRTDBGMODNM pThis, PRTSTREAM pStrm, bool fAddSymbols) +{ + /* + * Try parse the stream. + */ + RTUINTPTR SegZeroRva = fAddSymbols ? RTDbgModSegmentRva(pThis->hCnt, 0/*iSeg*/) : 0; + char szSym[RTDBG_SYMBOL_NAME_LENGTH] = ""; + size_t cchMod = 0; + size_t offSym = 0; + unsigned cchAddr = 0; + uint64_t u64Low = UINT64_MAX; + uint64_t u64High = 0; + int fWithType = -1; + char szLine[512]; + int rc; + while (RT_SUCCESS(rc = RTStrmGetLine(pStrm, szLine, sizeof(szLine)))) + { + char chType; + if (RT_C_IS_XDIGIT(szLine[0])) + { + /* + * This is really what C was made for, string parsing. + */ + /* The symbol value (address). */ + uint64_t u64Addr; + char *psz; + rc = RTStrToUInt64Ex(szLine, &psz, 16, &u64Addr); + if (rc != VWRN_TRAILING_CHARS) + return VERR_DBG_NOT_NM_MAP_FILE; + + /* Check the address width. */ + if (cchAddr == 0) + cchAddr = psz == &szLine[8] ? 8 : 16; + if (psz != &szLine[cchAddr]) + return VERR_DBG_NOT_NM_MAP_FILE; + + /* Get the type and check for single space before symbol. */ + char *pszName; + if (fWithType < 0) + fWithType = RT_C_IS_BLANK(szLine[cchAddr + 2]) ? 1 : 0; /* have type? Linux 2.4 /proc/ksyms doesn't. */ + if (fWithType) + { + chType = szLine[cchAddr + 1]; + pszName = &szLine[cchAddr + 3]; + if ( RT_C_IS_BLANK(chType) + || !RT_C_IS_BLANK(szLine[cchAddr + 2]) + || RT_C_IS_BLANK(szLine[cchAddr + 3])) + return VERR_DBG_NOT_NM_MAP_FILE; + } + else + { + chType = 'T'; + pszName = &szLine[cchAddr + 1]; + } + + /* Find the end of the symbol name. */ + char *pszNameEnd = pszName; + char ch; + while ((ch = *pszNameEnd) != '\0' && !RT_C_IS_SPACE(ch)) + pszNameEnd++; + + /* Any module name (linux /proc/kallsyms) following in brackets? */ + char *pszModName = pszNameEnd; + char *pszModNameEnd = pszModName; + if (*pszModName) + { + *pszModName++ = '\0'; + pszModNameEnd = pszModName = RTStrStripL(pszModName); + if (*pszModName != '\0') + { + if (*pszModName != '[') + return VERR_DBG_NOT_LINUX_KALLSYMS; + pszModNameEnd = ++pszModName; + while ((ch = *pszModNameEnd) != '\0' && ch != ']') + pszModNameEnd++; + if (ch != ']') + return VERR_DBG_NOT_LINUX_KALLSYMS; + char *pszEnd = pszModNameEnd + 1; + if ((size_t)(pszModNameEnd - pszModName) >= 128) /* lazy bird */ + return VERR_DBG_NOT_LINUX_KALLSYMS; + *pszModNameEnd = '\0'; + if (*pszEnd) + pszEnd = RTStrStripL(pszEnd); + if (*pszEnd) + return VERR_DBG_NOT_LINUX_KALLSYMS; + } + } + + /* + * Did the module change? Then update the symbol prefix. + */ + if ( cchMod != (size_t)(pszModNameEnd - pszModName) + || memcmp(pszModName, szSym, cchMod)) + { + cchMod = pszModNameEnd - pszModName; + if (cchMod == 0) + offSym = 0; + else + { + memcpy(szSym, pszModName, cchMod); + szSym[cchMod] = '.'; + offSym = cchMod + 1; + } + szSym[offSym] = '\0'; + } + + /* + * Validate the type and add the symbol if it's a type we care for. + */ + uint32_t fFlags = 0; + RTDBGSEGIDX iSegSym = 0; + switch (chType) + { + /* absolute */ + case 'a': + case '?': /* /proc/kallsyms */ + iSegSym = RTDBGSEGIDX_ABS; + /// @todo fFlags |= RTDBG_SYM_FLAGS_LOCAL; + break; + case 'A': + iSegSym = RTDBGSEGIDX_ABS; + /// @todo fFlags |= RTDBG_SYM_FLAGS_PUBLIC; + break; + + case 'b': + /// @todo fFlags |= RTDBG_SYM_FLAGS_LOCAL; + break; + case 'B': + /// @todo fFlags |= RTDBG_SYM_FLAGS_PUBLIC; + break; + + case 'c': + /// @todo fFlags |= RTDBG_SYM_FLAGS_LOCAL | RTDBG_SYM_FLAGS_COMMON; + break; + case 'C': + /// @todo fFlags |= RTDBG_SYM_FLAGS_PUBLIC | RTDBG_SYM_FLAGS_COMMON; + break; + + case 'd': + /// @todo fFlags |= RTDBG_SYM_FLAGS_LOCAL; + break; + case 'D': + /// @todo fFlags |= RTDBG_SYM_FLAGS_PUBLIC; + break; + + case 'g': + /// @todo fFlags |= RTDBG_SYM_FLAGS_LOCAL; + break; + case 'G': + /// @todo fFlags |= RTDBG_SYM_FLAGS_PUBLIC; + break; + + case 'i': + /// @todo fFlags |= RTDBG_SYM_FLAGS_LOCAL; + break; + case 'I': + /// @todo fFlags |= RTDBG_SYM_FLAGS_PUBLIC; + break; + + case 'r': + /// @todo fFlags |= RTDBG_SYM_FLAGS_LOCAL | RTDBG_SYM_FLAGS_CONST; + break; + case 'R': + /// @todo fFlags |= RTDBG_SYM_FLAGS_PUBLIC | RTDBG_SYM_FLAGS_CONST; + break; + + case 's': + /// @todo fFlags |= RTDBG_SYM_FLAGS_LOCAL; + break; + case 'S': + /// @todo fFlags |= RTDBG_SYM_FLAGS_PUBLIC; + break; + + case 't': + /// @todo fFlags |= RTDBG_SYM_FLAGS_LOCAL | RTDBG_SYM_FLAGS_TEXT; + break; + case 'T': + /// @todo fFlags |= RTDBG_SYM_FLAGS_PUBLIC | RTDBG_SYM_FLAGS_TEXT; + break; + + case 'w': + /// @todo fFlags |= RTDBG_SYM_FLAGS_WEAK | RTDBG_SYM_FLAGS_LOCAL; //??? + break; + case 'W': + /// @todo fFlags |= RTDBG_SYM_FLAGS_WEAK | RTDBG_SYM_FLAGS_PUBLIC; + break; + + case 'N': /* debug */ + case 'n': + case '-': /* stabs */ + case 'u': /* undefined (/proc/kallsyms) */ + case 'U': + case 'v': /* weakext */ + case 'V': + iSegSym = NIL_RTDBGSEGIDX; + break; + + default: + return VERR_DBG_NOT_NM_MAP_FILE; + } + + if (iSegSym != NIL_RTDBGSEGIDX) + { + if (fAddSymbols) + { + size_t cchName = pszNameEnd - pszName; + if (cchName >= sizeof(szSym) - offSym) + cchName = sizeof(szSym) - offSym - 1; + memcpy(&szSym[offSym], pszName, cchName + 1); + if (iSegSym == 0) + rc = RTDbgModSymbolAdd(pThis->hCnt, szSym, iSegSym, u64Addr - SegZeroRva, 0/*cb*/, fFlags, NULL); + else + rc = RTDbgModSymbolAdd(pThis->hCnt, szSym, iSegSym, u64Addr, 0/*cb*/, fFlags, NULL); + if ( RT_FAILURE(rc) + && rc != VERR_DBG_DUPLICATE_SYMBOL + && rc != VERR_DBG_ADDRESS_CONFLICT) /* (don't be too strict) */ + return rc; + } + + /* Track segment span. */ + if (iSegSym == 0) + { + if (u64Low > u64Addr) + u64Low = u64Addr; + if (u64High < u64Addr) + u64High = u64Addr; + } + } + } + else + { + /* + * This is either a blank line or a symbol without an address. + */ + RTStrStripR(szLine); + if (szLine[0]) + { + size_t cch = strlen(szLine); + if (cchAddr == 0) + cchAddr = cch < 16+3 || szLine[8+1] != ' ' ? 8 : 16; + if (cch < cchAddr+3+1) + return VERR_DBG_NOT_NM_MAP_FILE; + chType = szLine[cchAddr + 1]; + if ( chType != 'U' + && chType != 'w') + return VERR_DBG_NOT_NM_MAP_FILE; + char *pszType = RTStrStripL(szLine); + if (pszType != &szLine[cchAddr + 1]) + return VERR_DBG_NOT_NM_MAP_FILE; + if (!RT_C_IS_BLANK(szLine[cchAddr + 2])) + return VERR_DBG_NOT_NM_MAP_FILE; + } + /* else: blank - ignored */ + } + } + + /* + * The final segment. + */ + if (rc == VERR_EOF) + { + if (fAddSymbols) + rc = VINF_SUCCESS; + else + { + if ( u64Low != UINT64_MAX + || u64High != 0) + rc = RTDbgModSegmentAdd(pThis->hCnt, u64Low, u64High - u64Low + 1, "main", 0, NULL); + else /* No sensible symbols... throw an error instead? */ + rc = RTDbgModSegmentAdd(pThis->hCnt, 0, 0, "main", 0, NULL); + } + } + + return rc; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnTryOpen} */ +static DECLCALLBACK(int) rtDbgModNm_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch) +{ + NOREF(enmArch); + + /* + * Fend off images. + */ + if ( !pMod->pszDbgFile + || pMod->pImgVt) + return VERR_DBG_NO_MATCHING_INTERPRETER; + + /* + * Try open the file and create an instance. + */ + PRTSTREAM pStrm; + int rc = RTStrmOpen(pMod->pszDbgFile, "r", &pStrm); + if (RT_SUCCESS(rc)) + { + PRTDBGMODNM pThis = (PRTDBGMODNM)RTMemAlloc(sizeof(*pThis)); + if (pThis) + { + rc = RTDbgModCreate(&pThis->hCnt, pMod->pszName, 0 /*cbSeg*/, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + /* + * Scan the file twice, first to figure the segment + * sizes, then to add the symbol. + */ + rc = rtDbgModNmScanFile(pThis, pStrm, false /*fAddSymbols*/); + if (RT_SUCCESS(rc)) + rc = RTStrmRewind(pStrm); + if (RT_SUCCESS(rc)) + rc = rtDbgModNmScanFile(pThis, pStrm, true /*fAddSymbols*/); + if (RT_SUCCESS(rc)) + { + RTStrmClose(pStrm); + pMod->pvDbgPriv = pThis; + return rc; + } + } + RTDbgModRelease(pThis->hCnt); + RTMemFree(pThis); + } + else + rc = VERR_NO_MEMORY; + RTStrmClose(pStrm); + } + return rc; +} + + + +/** Virtual function table for the NM-like map file reader. */ +DECL_HIDDEN_CONST(RTDBGMODVTDBG) const g_rtDbgModVtDbgNm = +{ + /*.u32Magic = */ RTDBGMODVTDBG_MAGIC, + /*.fSupports = */ RT_DBGTYPE_MAP, + /*.pszName = */ "nm", + /*.pfnTryOpen = */ rtDbgModNm_TryOpen, + /*.pfnClose = */ rtDbgModNm_Close, + + /*.pfnRvaToSegOff = */ rtDbgModNm_RvaToSegOff, + /*.pfnImageSize = */ rtDbgModNm_ImageSize, + + /*.pfnSegmentAdd = */ rtDbgModNm_SegmentAdd, + /*.pfnSegmentCount = */ rtDbgModNm_SegmentCount, + /*.pfnSegmentByIndex = */ rtDbgModNm_SegmentByIndex, + + /*.pfnSymbolAdd = */ rtDbgModNm_SymbolAdd, + /*.pfnSymbolCount = */ rtDbgModNm_SymbolCount, + /*.pfnSymbolByOrdinal = */ rtDbgModNm_SymbolByOrdinal, + /*.pfnSymbolByName = */ rtDbgModNm_SymbolByName, + /*.pfnSymbolByAddr = */ rtDbgModNm_SymbolByAddr, + + /*.pfnLineAdd = */ rtDbgModNm_LineAdd, + /*.pfnLineCount = */ rtDbgModNm_LineCount, + /*.pfnLineByOrdinal = */ rtDbgModNm_LineByOrdinal, + /*.pfnLineByAddr = */ rtDbgModNm_LineByAddr, + + /*.pfnUnwindFrame = */ rtDbgModNm_UnwindFrame, + + /*.u32EndMagic = */ RTDBGMODVTDBG_MAGIC +}; + diff --git a/src/VBox/Runtime/common/dbg/dbgstackdumpself-amd64-x86.asm b/src/VBox/Runtime/common/dbg/dbgstackdumpself-amd64-x86.asm new file mode 100644 index 00000000..9c21438d --- /dev/null +++ b/src/VBox/Runtime/common/dbg/dbgstackdumpself-amd64-x86.asm @@ -0,0 +1,157 @@ +; $Id: dbgstackdumpself-amd64-x86.asm $ +;; @file +; IPRT - RTDbgStackDumpSelf assembly wrapper calling rtDbgStackDumpSelfWorker. +; + +; +; Copyright (C) 2006-2023 Oracle and/or its affiliates. +; +; This file is part of VirtualBox base platform packages, as +; available from https://www.virtualbox.org. +; +; This program is free software; you can redistribute it and/or +; modify it under the terms of the GNU General Public License +; as published by the Free Software Foundation, in version 3 of the +; License. +; +; This program is distributed in the hope that it will be useful, but +; WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +; General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program; if not, see <https://www.gnu.org/licenses>. +; +; The contents of this file may alternatively be used under the terms +; of the Common Development and Distribution License Version 1.0 +; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +; in the VirtualBox distribution, in which case the provisions of the +; CDDL are applicable instead of those of the GPL. +; +; You may elect to license modified versions of this file under the +; terms and conditions of either the GPL or the CDDL or both. +; +; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +; + + +;********************************************************************************************************************************* +;* Header Files * +;********************************************************************************************************************************* +%define RT_ASM_WITH_SEH64 +%include "iprt/asmdefs.mac" + + +;********************************************************************************************************************************* +;* Extern Symbols * +;********************************************************************************************************************************* +extern NAME(rtDbgStackDumpSelfWorker) + + +BEGINCODE + +;; +; Collects register state and calls C worker. +; +RT_BEGINPROC RTDbgStackDumpSelf + push xBP + SEH64_PUSH_xBP + mov xBP, xSP + SEH64_SET_FRAME_xBP 0 + + ; + ; Push all GPRS in reserve order... + ; +%ifdef RT_ARCH_AMD64 + push r15 + SEH64_PUSH_GREG r15 + push r14 + SEH64_PUSH_GREG r14 + push r13 + SEH64_PUSH_GREG r13 + push r12 + SEH64_PUSH_GREG r12 + push r13 + SEH64_PUSH_GREG r13 + push r12 + SEH64_PUSH_GREG r12 + push r11 + SEH64_PUSH_GREG r11 + push r10 + SEH64_PUSH_GREG r10 + push r9 + SEH64_PUSH_GREG r9 + push r8 + SEH64_PUSH_GREG r8 +%endif + push xDI + SEH64_PUSH_GREG xDI + push xSI + SEH64_PUSH_GREG xSI +%ifdef RT_ARCH_AMD64 + mov r10, [xBP] ; Caller RBP. + push r10 + lea r10, [xBP + xCB * 2] ; Caller RSP. + push r10 +%else + push dword [xBP] ; Caller EBP + push xAX + lea xAX, [xBP + xCB * 2] ; Caller ESP. + xchg xAX, [xSP] +%endif + push xBX + SEH64_PUSH_GREG xBX + push xDX + SEH64_PUSH_GREG xDX + push xCX + SEH64_PUSH_GREG xCX + push xAX + SEH64_PUSH_GREG xAX + + ; + ; ... preceeded by the EIP/RIP. + ; +%ifdef RT_ARCH_AMD64 + mov r10, [xBP + xCB] + push r10 +%else + push dword [xBP + xCB] +%endif + + ; + ; Call the worker function passing the register array as the fourth argument. + ; +%ifdef ASM_CALL64_GCC + mov rcx, rsp ; 4th parameter = register array. + sub rsp, 8h ; Align stack. + SEH64_ALLOCATE_STACK 28h +%elifdef ASM_CALL64_MSC + mov r9, rsp ; 4th parameter = register array. + sub rsp, 28h ; Aling stack and allocate spill area. + SEH64_ALLOCATE_STACK 28h +%else + mov eax, esp ; Save register array location + and xSP, ~15 ; Align the stack. +%endif +SEH64_END_PROLOGUE + +%ifndef RT_ARCH_AMD64 + push eax + push dword [xBP + xCB * 4] + push dword [xBP + xCB * 3] + push dword [xBP + xCB * 2] +%endif +%ifdef ASM_FORMAT_ELF + %ifdef PIC + call NAME(rtDbgStackDumpSelfWorker) wrt ..plt + %else + call NAME(rtDbgStackDumpSelfWorker) + %endif +%else + call NAME(rtDbgStackDumpSelfWorker) +%endif + + leave + ret +ENDPROC RTDbgStackDumpSelf + diff --git a/src/VBox/Runtime/common/dbg/dbgstackdumpself.cpp b/src/VBox/Runtime/common/dbg/dbgstackdumpself.cpp new file mode 100644 index 00000000..32ef86b3 --- /dev/null +++ b/src/VBox/Runtime/common/dbg/dbgstackdumpself.cpp @@ -0,0 +1,543 @@ +/* $Id: dbgstackdumpself.cpp $ */ +/** @file + * IPRT - Dump current thread stack to buffer. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "internal/iprt.h" +#include <iprt/dbg.h> + +#include <iprt/errcore.h> +#include <iprt/string.h> +#include <iprt/ldr.h> +#include <iprt/list.h> +#include <iprt/mem.h> +#include <iprt/path.h> +#include <iprt/string.h> + +#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) +# include <iprt/x86.h> +#else +# error "PORTME" +#endif + +#ifdef RT_OS_WINDOWS +# include <iprt/param.h> +# include <iprt/utf16.h> +# include <iprt/win/windows.h> +#elif defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) +# include <dlfcn.h> +#endif + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Cached module. + */ +typedef struct RTDBGSTACKSELFMOD +{ + /** List entry. */ + RTLISTNODE ListEntry; + /** The mapping address. */ + uintptr_t uMapping; + /** The size of the mapping. */ + size_t cbMapping; + /** The loader module handle. */ + RTLDRMOD hLdrMod; + /** The debug module handle, if available. */ + RTDBGMOD hDbgMod; + /** Offset into szFilename of the name part. */ + size_t offName; + /** the module filename. */ + char szFilename[RTPATH_MAX]; +} RTDBGSTACKSELFMOD; +/** Pointer to a cached module. */ +typedef RTDBGSTACKSELFMOD *PRTDBGSTACKSELFMOD; + + +/** + * Symbol search state. + */ +typedef struct RTDBGSTACKSELFSYMSEARCH +{ + /** The address (not RVA) we're searching for a symbol for. */ + uintptr_t uSearch; + /** The distance of the current hit. This is ~(uintptr_t)0 if no hit. */ + uintptr_t offBestDist; + /** Where to return symbol information. */ + PRTDBGSYMBOL pSymInfo; +} RTDBGSTACKSELFSYMSEARCH; +typedef RTDBGSTACKSELFSYMSEARCH *PRTDBGSTACKSELFSYMSEARCH; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +/* Wanted to use DECLHIDDEN(DECLASM(size_t)) here, but early solaris 11 doesn't like it. */ +RT_C_DECLS_BEGIN +DECL_HIDDEN_CALLBACK(size_t) rtDbgStackDumpSelfWorker(char *pszStack, size_t cbStack, uint32_t fFlags, PCRTCCUINTREG pauRegs); +RT_C_DECLS_END + + +/** + * Worker for stack and module reader callback. + * + * @returns IPRT status code + * @param pvDst Where to put the request memory. + * @param cbToRead How much to read. + * @param uSrcAddr Where to read the memory from. + */ +static int rtDbgStackDumpSelfSafeMemoryReader(void *pvDst, size_t cbToRead, uintptr_t uSrcAddr) +{ +# ifdef RT_OS_WINDOWS +# if 1 + __try + { + memcpy(pvDst, (void const *)uSrcAddr, cbToRead); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + return VERR_ACCESS_DENIED; + } +# else + SIZE_T cbActual = 0; + if (ReadProcessMemory(GetCurrentProcess(), (void const *)uSrcAddr, pvDst, cbToRead, &cbActual)) + { + if (cbActual >= cbToRead) + return VINF_SUCCESS; + memset((uint8_t *)pvDst + cbActual, 0, cbToRead - cbActual); + return VINF_SUCCESS; + } +# endif +# else + /** @todo secure this from SIGSEGV. */ + memcpy(pvDst, (void const *)uSrcAddr, cbToRead); +# endif + return VINF_SUCCESS; +} + + +#if defined(RT_OS_WINDOWS) && 0 +/** + * @callback_method_impl{FNRTLDRRDRMEMREAD} + */ +static DECLCALLBACK(int) rtDbgStackDumpSelfModReader(void *pvBuf, size_t cb, size_t off, void *pvUser) +{ + PRTDBGSTACKSELFMOD pMod = (PRTDBGSTACKSELFMOD)pvUser; + return rtDbgStackDumpSelfSafeMemoryReader(pvBuf, cb, pMod->uMapping + off); +} +#endif + + +/** + * @interface_method_impl{RTDBGUNWINDSTATE,pfnReadStack} + */ +static DECLCALLBACK(int) rtDbgStackDumpSelfReader(PRTDBGUNWINDSTATE pThis, RTUINTPTR uSp, size_t cbToRead, void *pvDst) +{ + RT_NOREF(pThis); + return rtDbgStackDumpSelfSafeMemoryReader(pvDst, cbToRead, uSp); +} + + +#ifdef RT_OS_WINDOWS +/** + * Figure the size of a loaded PE image. + * @returns The size. + * @param uBase The image base address. + */ +static size_t rtDbgStackDumpSelfGetPeImageSize(uintptr_t uBase) +{ + union + { + IMAGE_DOS_HEADER DosHdr; + IMAGE_NT_HEADERS NtHdrs; + } uBuf; + int rc = rtDbgStackDumpSelfSafeMemoryReader(&uBuf, sizeof(uBuf.DosHdr), uBase); + if (RT_SUCCESS(rc)) + { + if ( uBuf.DosHdr.e_magic == IMAGE_DOS_SIGNATURE + && uBuf.DosHdr.e_lfanew < _2M) + { + rc = rtDbgStackDumpSelfSafeMemoryReader(&uBuf, sizeof(uBuf.NtHdrs), uBase + uBuf.DosHdr.e_lfanew); + if (RT_SUCCESS(rc)) + { + if (uBuf.NtHdrs.Signature == IMAGE_NT_SIGNATURE) + return uBuf.NtHdrs.OptionalHeader.SizeOfImage; + } + } + } + return _64K; +} +#endif + + +/** + * Works the module cache. + * + * @returns Pointer to module cache entry on success, NULL otherwise. + * @param uPc The PC to locate a module for. + * @param pCachedModules The module cache. + */ +static PRTDBGSTACKSELFMOD rtDbgStackDumpSelfQueryModForPC(uintptr_t uPc, PRTLISTANCHOR pCachedModules) +{ + /* + * Search the list first. + */ + PRTDBGSTACKSELFMOD pMod; + RTListForEach(pCachedModules, pMod, RTDBGSTACKSELFMOD, ListEntry) + { + if (uPc - pMod->uMapping < pMod->cbMapping) + return pMod; + } + + /* + * Try figure out the module using the native loader or similar. + */ +#ifdef RT_OS_WINDOWS + HMODULE hmod = NULL; + static bool volatile s_fResolvedSymbols = false; + static decltype(GetModuleHandleExW) *g_pfnGetModuleHandleExW = NULL; + decltype(GetModuleHandleExW) *pfnGetModuleHandleExW; + if (s_fResolvedSymbols) + pfnGetModuleHandleExW = g_pfnGetModuleHandleExW; + else + { + pfnGetModuleHandleExW = (decltype(GetModuleHandleExW) *)GetProcAddress(GetModuleHandleW(L"kernel32.dll"), + "GetModuleHandleExW"); + g_pfnGetModuleHandleExW = pfnGetModuleHandleExW; + s_fResolvedSymbols = true; + } + if ( pfnGetModuleHandleExW + && pfnGetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (LPCWSTR)uPc, &hmod)) + { + WCHAR wszFilename[RTPATH_MAX]; + DWORD cwcFilename = GetModuleFileNameW(hmod, wszFilename, RT_ELEMENTS(wszFilename)); + if (cwcFilename > 0) + { + pMod = (PRTDBGSTACKSELFMOD)RTMemAllocZ(sizeof(*pMod)); + if (pMod) + { + char *pszDst = pMod->szFilename; + int rc = RTUtf16ToUtf8Ex(wszFilename, cwcFilename, &pszDst, sizeof(pMod->szFilename), NULL); + if (RT_SUCCESS(rc)) + { + const char *pszFilename = RTPathFilename(pMod->szFilename); + pMod->offName = pszFilename ? pszFilename - &pMod->szFilename[0] : 0; + pMod->uMapping = (uintptr_t)hmod & ~(uintptr_t)(PAGE_SIZE - 1); + pMod->cbMapping = rtDbgStackDumpSelfGetPeImageSize(pMod->uMapping); + pMod->hLdrMod = NIL_RTLDRMOD; + pMod->hDbgMod = NIL_RTDBGMOD; + +# if 0 /* this ain't reliable, trouble enumerate symbols in VBoxRT. But why bother when we can load it off the disk. */ + rc = RTLdrOpenInMemory(&pMod->szFilename[pMod->offName], RTLDR_O_FOR_DEBUG, RTLdrGetHostArch(), + pMod->cbMapping, rtDbgStackDumpSelfModReader, NULL /*pfnDtor*/, pMod /*pvUser*/, + &pMod->hLdrMod, NULL /*pErrInfo*/); +# else + rc = RTLdrOpen(pMod->szFilename, RTLDR_O_FOR_DEBUG, RTLdrGetHostArch(), &pMod->hLdrMod); +# endif + if (RT_SUCCESS(rc)) + { + pMod->cbMapping = RTLdrSize(pMod->hLdrMod); + + /* Try open debug info for the module. */ + uint32_t uTimeDateStamp = 0; + RTLdrQueryProp(pMod->hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &uTimeDateStamp, sizeof(uTimeDateStamp)); + rc = RTDbgModCreateFromPeImage(&pMod->hDbgMod, pMod->szFilename, &pMod->szFilename[pMod->offName], + &pMod->hLdrMod, (uint32_t)pMod->cbMapping, uTimeDateStamp, NIL_RTDBGCFG); + RTListPrepend(pCachedModules, &pMod->ListEntry); + return pMod; + } + } + RTMemFree(pMod); + } + } + } +#elif defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) + Dl_info Info = { NULL, NULL, NULL, NULL }; + int rc = dladdr((const void *)uPc, &Info); + if (rc != 0 && Info.dli_fname) + { + pMod = (PRTDBGSTACKSELFMOD)RTMemAllocZ(sizeof(*pMod)); + if (pMod) + { + /** @todo better filename translation... */ + rc = RTStrCopy(pMod->szFilename, sizeof(pMod->szFilename), Info.dli_fname); + if (RT_SUCCESS(rc)) + { + RTStrPurgeEncoding(pMod->szFilename); + + const char *pszFilename = RTPathFilename(pMod->szFilename); + pMod->offName = pszFilename ? pszFilename - &pMod->szFilename[0] : 0; + pMod->uMapping = (uintptr_t)Info.dli_fbase; + pMod->cbMapping = 0; + pMod->hLdrMod = NIL_RTLDRMOD; + pMod->hDbgMod = NIL_RTDBGMOD; + + rc = RTLdrOpen(pMod->szFilename, RTLDR_O_FOR_DEBUG, RTLdrGetHostArch(), &pMod->hLdrMod); + if (RT_SUCCESS(rc)) + { + pMod->cbMapping = RTLdrSize(pMod->hLdrMod); + + /* Try open debug info for the module. */ + //uint32_t uTimeDateStamp = 0; + //RTLdrQueryProp(pMod->hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &uTimeDateStamp, sizeof(uTimeDateStamp)); + //RTDbgModCreateFromImage()?? + //rc = RTDbgModCreateFromPeImage(&pMod->hDbgMod, pMod->szFilename, &pMod->szFilename[pMod->offName], + // &pMod->hLdrMod, (uint32_t)pMod->cbMapping, uTimeDateStamp, NIL_RTDBGCFG); + + RTListPrepend(pCachedModules, &pMod->ListEntry); + return pMod; + } + } + RTMemFree(pMod); + } + } +#endif + return NULL; +} + + +/** + * @callback_method_impl{FNRTLDRENUMSYMS} + */ +static DECLCALLBACK(int) rtDbgStackdumpSelfSymbolSearchCallback(RTLDRMOD hLdrMod, const char *pszSymbol, + unsigned uSymbol, RTLDRADDR Value, void *pvUser) +{ + PRTDBGSTACKSELFSYMSEARCH pSearch = (PRTDBGSTACKSELFSYMSEARCH)pvUser; + if (Value >= pSearch->uSearch) + { + uintptr_t const offDist = (uintptr_t)Value - pSearch->uSearch; + if (offDist < pSearch->offBestDist) + { + pSearch->offBestDist = offDist; + + PRTDBGSYMBOL pSymInfo = pSearch->pSymInfo; + pSymInfo->Value = Value; + pSymInfo->offSeg = Value; + pSymInfo->iSeg = RTDBGSEGIDX_ABS; + pSymInfo->iOrdinal = uSymbol; + pSymInfo->fFlags = 0; + if (pszSymbol) + RTStrCopy(pSymInfo->szName, sizeof(pSymInfo->szName), pszSymbol); + else + RTStrPrintf(pSymInfo->szName, sizeof(pSymInfo->szName), "Ordinal#%u", uSymbol); + + if (offDist < 8) + return VINF_CALLBACK_RETURN; + } + } + RT_NOREF(hLdrMod); + return VINF_SUCCESS; +} + + +/** + * Searches for a symbol close to @a uRva. + * + * @returns true if found, false if not. + * @param pMod The module cache entry to search in. + * @param uRva The RVA to locate a symbol for. + * @param poffDisp Where to return the distance between uRva and the returned symbol. + * @param pSymInfo Where to return the symbol information. + */ +static bool rtDbgStackDumpSelfQuerySymbol(PRTDBGSTACKSELFMOD pMod, uintptr_t uRva, PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo) +{ + if (pMod->hDbgMod != NIL_RTDBGMOD) + { + int rc = RTDbgModSymbolByAddr(pMod->hDbgMod, RTDBGSEGIDX_RVA, uRva, RTDBGSYMADDR_FLAGS_LESS_OR_EQUAL, + poffDisp, pSymInfo); + if (RT_SUCCESS(rc)) + return true; + } + + if (pMod->hLdrMod != NIL_RTLDRMOD) + { + RTDBGSTACKSELFSYMSEARCH SearchInfo = { pMod->uMapping + uRva, ~(uintptr_t)0, pSymInfo }; + int rc = RTLdrEnumSymbols(pMod->hLdrMod, 0, (const void *)pMod->uMapping, pMod->uMapping, + rtDbgStackdumpSelfSymbolSearchCallback, &SearchInfo); + if (RT_SUCCESS(rc) && SearchInfo.offBestDist != ~(uintptr_t)0) + { + *poffDisp = SearchInfo.offBestDist; + return true; + } + } + + return false; +} + + +/** + * Does the grunt work for RTDbgStackDumpSelf. + * + * Called thru an assembly wrapper that collects the necessary register state. + * + * @returns Length of the string returned in pszStack. + * @param pszStack Where to output the stack dump. + * @param cbStack The size of the @a pszStack buffer. + * @param fFlags Flags, MBZ. + * @param pauRegs Register state. For AMD64 and x86 this starts with the + * PC and us followed by the general purpose registers. + */ +DECL_HIDDEN_CALLBACK(size_t) rtDbgStackDumpSelfWorker(char *pszStack, size_t cbStack, uint32_t fFlags, PCRTCCUINTREG pauRegs) +{ + RT_NOREF(fFlags); + + /* + * Initialize the unwind state. + */ + RTDBGUNWINDSTATE UnwindState; + RT_ZERO(UnwindState); + + UnwindState.u32Magic = RTDBGUNWINDSTATE_MAGIC; + UnwindState.pfnReadStack = rtDbgStackDumpSelfReader; +#ifdef RT_ARCH_AMD64 + UnwindState.enmArch = RTLDRARCH_AMD64; + UnwindState.uPc = pauRegs[0]; + UnwindState.enmRetType = RTDBGRETURNTYPE_NEAR64; + for (unsigned i = 0; i < 16; i++) + UnwindState.u.x86.auRegs[i] = pauRegs[i + 1]; +#elif defined(RT_ARCH_X86) + UnwindState.enmArch = RTLDRARCH_X86_32; + UnwindState.uPc = pauRegs[0]; + UnwindState.enmRetType = RTDBGRETURNTYPE_NEAR32; + for (unsigned i = 0; i < 8; i++) + UnwindState.u.x86.auRegs[i] = pauRegs[i + 1]; +#else +# error "PORTME" +#endif + + /* + * We cache modules. + */ + PRTDBGSTACKSELFMOD pCurModule = NULL; + RTLISTANCHOR CachedModules; + RTListInit(&CachedModules); + + /* + * Work the stack. + */ + size_t offDst = 0; + while (offDst + 64 < cbStack) + { + /* Try locate the module containing the current PC. */ + if ( !pCurModule + || UnwindState.uPc - pCurModule->uMapping >= pCurModule->cbMapping) + pCurModule = rtDbgStackDumpSelfQueryModForPC(UnwindState.uPc, &CachedModules); + bool fManualUnwind = true; + if (!pCurModule) + offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, "%p\n", UnwindState.uPc); + else + { + uintptr_t const uRva = UnwindState.uPc - pCurModule->uMapping; + + /* + * Add a call stack entry with the symbol if we can. + */ + union + { + RTDBGSYMBOL SymbolInfo; + RTDBGLINE LineInfo; + } uBuf; + RTINTPTR offDisp = 0; + if (!rtDbgStackDumpSelfQuerySymbol(pCurModule, uRva, &offDisp, &uBuf.SymbolInfo)) + offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, "%p %s + %#zx\n", + UnwindState.uPc, &pCurModule->szFilename[pCurModule->offName], (size_t)uRva); + else if (offDisp == 0) + offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, "%p %s!%s (rva:%#zx)\n", UnwindState.uPc, + &pCurModule->szFilename[pCurModule->offName], uBuf.SymbolInfo.szName, (size_t)uRva); + else + offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, "%p %s!%s%c%#zx (rva:%#zx)\n", + UnwindState.uPc, &pCurModule->szFilename[pCurModule->offName], uBuf.SymbolInfo.szName, + offDisp >= 0 ? '+' : '-', (size_t)RT_ABS(offDisp), (size_t)uRva); + + /* + * Try supply the line number. + */ + if (pCurModule->hDbgMod != NIL_RTDBGMOD) + { + offDisp = 0; + int rc = RTDbgModLineByAddr(pCurModule->hDbgMod, RTDBGSEGIDX_RVA, uRva, &offDisp, &uBuf.LineInfo); + if (RT_SUCCESS(rc) && offDisp) + offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, " [%s:%u]\n", + uBuf.LineInfo.szFilename, uBuf.LineInfo.uLineNo); + else if (RT_SUCCESS(rc)) + offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, " [%s:%u (%c%#zx)]\n", uBuf.LineInfo.szFilename, + uBuf.LineInfo.uLineNo, offDisp >= 0 ? '+' : '-', (size_t)RT_ABS(offDisp)); + } + + /* + * Try unwind using the module info. + */ + int rc; + if (pCurModule->hDbgMod != NIL_RTDBGMOD) + rc = RTDbgModUnwindFrame(pCurModule->hDbgMod, RTDBGSEGIDX_RVA, uRva, &UnwindState); + else + rc = RTLdrUnwindFrame(pCurModule->hLdrMod, (void const *)pCurModule->uMapping, UINT32_MAX, uRva, &UnwindState); + if (RT_SUCCESS(rc)) + fManualUnwind = false; + } + if (fManualUnwind) + { + break; + } + } + + /* + * Destroy the cache. + */ + PRTDBGSTACKSELFMOD pNextModule; + RTListForEachSafe(&CachedModules, pCurModule, pNextModule, RTDBGSTACKSELFMOD, ListEntry) + { + if (pCurModule->hDbgMod != NIL_RTDBGMOD) + { + RTDbgModRelease(pCurModule->hDbgMod); + pCurModule->hDbgMod = NIL_RTDBGMOD; + } + if (pCurModule->hLdrMod != NIL_RTLDRMOD) + { + RTLdrClose(pCurModule->hLdrMod); + pCurModule->hLdrMod = NIL_RTLDRMOD; + } + RTMemFree(pCurModule); + } + + return offDst; +} + |