diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2014-03-26 19:21:20 +0000 |
---|---|---|
committer | <> | 2014-05-08 15:03:54 +0000 |
commit | fb123f93f9f5ce42c8e5785d2f8e0edaf951740e (patch) | |
tree | c2103d76aec5f1f10892cd1d3a38e24f665ae5db /src/VBox/Runtime/common/dbg | |
parent | 58ed4748338f9466599adfc8a9171280ed99e23f (diff) | |
download | VirtualBox-master.tar.gz |
Imported from /home/lorry/working-area/delta_VirtualBox/VirtualBox-4.3.10.tar.bz2.HEADVirtualBox-4.3.10master
Diffstat (limited to 'src/VBox/Runtime/common/dbg')
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbg.cpp | 2 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgas.cpp | 180 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgcfg.cpp | 2172 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgmod.cpp | 1276 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgmodcodeview.cpp | 2786 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgmodcontainer.cpp | 186 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgmoddbghelp.cpp | 502 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgmoddeferred.cpp | 642 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgmoddwarf.cpp | 1647 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgmodexports.cpp | 164 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgmodldr.cpp | 102 | ||||
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgmodnm.cpp | 6 |
12 files changed, 8832 insertions, 833 deletions
diff --git a/src/VBox/Runtime/common/dbg/dbg.cpp b/src/VBox/Runtime/common/dbg/dbg.cpp index 4198ddaf..b5ae1d9f 100644 --- a/src/VBox/Runtime/common/dbg/dbg.cpp +++ b/src/VBox/Runtime/common/dbg/dbg.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/dbg/dbgas.cpp b/src/VBox/Runtime/common/dbg/dbgas.cpp index 947fffaa..445cc492 100644 --- a/src/VBox/Runtime/common/dbg/dbgas.cpp +++ b/src/VBox/Runtime/common/dbg/dbgas.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -396,6 +396,26 @@ RTDECL(uint32_t) RTDbgAsRelease(RTDBGAS hDbgAs) RT_EXPORT_SYMBOL(RTDbgAsRelease); +RTDECL(int) RTDbgAsLockExcl(RTDBGAS hDbgAs) +{ + PRTDBGASINT pDbgAs = hDbgAs; + RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE); + RTDBGAS_LOCK_WRITE(pDbgAs); + return VINF_SUCCESS; +} +RT_EXPORT_SYMBOL(RTDbgAsLockExcl); + + +RTDECL(int) RTDbgAsUnlockExcl(RTDBGAS hDbgAs) +{ + PRTDBGASINT pDbgAs = hDbgAs; + RTDBGAS_VALID_RETURN_RC(pDbgAs, VERR_INVALID_HANDLE); + RTDBGAS_UNLOCK_WRITE(pDbgAs); + return VINF_SUCCESS; +} +RT_EXPORT_SYMBOL(RTDbgAsUnlockExcl); + + /** * Gets the name of an address space. * @@ -900,7 +920,7 @@ RTDECL(int) RTDbgAsModuleUnlinkByAddr(RTDBGAS hDbgAs, RTUINTPTR Addr) RTDBGAS_LOCK_WRITE(pDbgAs); PRTDBGASMAP pMap = (PRTDBGASMAP)RTAvlrUIntPtrRangeGet(&pDbgAs->MapTree, Addr); - if (pMap) + if (!pMap) { RTDBGAS_UNLOCK_WRITE(pDbgAs); return VERR_NOT_FOUND; @@ -1283,6 +1303,37 @@ RT_EXPORT_SYMBOL(RTDbgAsSymbolAdd); /** + * Creates a snapshot of the module table on the temporary heap. + * + * The caller must release all the module handles before freeing the table + * using RTMemTmpFree. + * + * @returns Module table snaphot. + * @param pDbgAs The address space instance data. + * @param pcModules Where to return the number of modules. + */ +static PRTDBGMOD rtDbgAsSnapshotModuleTable(PRTDBGASINT pDbgAs, uint32_t *pcModules) +{ + RTDBGAS_LOCK_READ(pDbgAs); + + uint32_t iMod = *pcModules = pDbgAs->cModules; + PRTDBGMOD pahModules = (PRTDBGMOD)RTMemTmpAlloc(sizeof(pahModules[0]) * RT_MAX(iMod, 1)); + if (pahModules) + { + while (iMod-- > 0) + { + RTDBGMOD hMod = (RTDBGMOD)pDbgAs->papModules[iMod]->Core.Key; + pahModules[iMod] = hMod; + RTDbgModRetain(hMod); + } + } + + RTDBGAS_UNLOCK_READ(pDbgAs); + return pahModules; +} + + +/** * Query a symbol by address. * * @returns IPRT status code. See RTDbgModSymbolAddr for more specific ones. @@ -1306,6 +1357,8 @@ RTDECL(int) RTDbgAsSymbolByAddr(RTDBGAS hDbgAs, RTUINTPTR Addr, uint32_t fFlags, */ 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; @@ -1313,9 +1366,46 @@ RTDECL(int) RTDbgAsSymbolByAddr(RTDBGAS hDbgAs, RTUINTPTR Addr, uint32_t fFlags, RTDBGMOD hMod = rtDbgAsModuleByAddr(pDbgAs, Addr, &iSeg, &offSeg, &MapAddr); if (hMod == NIL_RTDBGMOD) { - if (phMod) - *phMod = NIL_RTDBGMOD; - return VERR_NOT_FOUND; + /* + * Check for absolute symbols. Requires iterating all modules. + */ + 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; } /* @@ -1386,37 +1476,6 @@ RT_EXPORT_SYMBOL(RTDbgAsSymbolByAddrA); /** - * Creates a snapshot of the module table on the temporary heap. - * - * The caller must release all the module handles before freeing the table - * using RTMemTmpFree. - * - * @returns Module table snaphot. - * @param pDbgAs The address space instance data. - * @param pcModules Where to return the number of modules. - */ -DECLINLINE(PRTDBGMOD) rtDbgAsSnapshotModuleTable(PRTDBGASINT pDbgAs, uint32_t *pcModules) -{ - RTDBGAS_LOCK_READ(pDbgAs); - - uint32_t iMod = *pcModules = pDbgAs->cModules; - PRTDBGMOD pahModules = (PRTDBGMOD)RTMemTmpAlloc(sizeof(pahModules[0]) * RT_MAX(iMod, 1)); - if (pahModules) - { - while (iMod-- > 0) - { - RTDBGMOD hMod = (RTDBGMOD)pDbgAs->papModules[iMod]->Core.Key; - pahModules[iMod] = hMod; - RTDbgModRetain(hMod); - } - } - - RTDBGAS_UNLOCK_READ(pDbgAs); - return pahModules; -} - - -/** * Attempts to find a mapping of the specified symbol/module and * adjust it's Value field accordingly. * @@ -1675,20 +1734,7 @@ RTDECL(int) RTDbgAsLineAdd(RTDBGAS hDbgAs, const char *pszFile, uint32_t uLineNo RT_EXPORT_SYMBOL(RTDbgAsLineAdd); -/** - * Query a line number by address. - * - * @returns IPRT status code. See RTDbgModSymbolAddrA for more specific ones. - * @retval VERR_INVALID_HANDLE if hDbgAs is invalid. - * @retval VERR_NOT_FOUND if the address couldn't be mapped to a module. - * - * @param hDbgAs The address space handle. - * @param Addr The address which closest symbol is requested. - * @param poffDisp Where to return the distance between the line - * number and address. - * @param pLine Where to return the line number information. - */ -RTDECL(int) RTDbgAsLineByAddr(RTDBGAS hDbgAs, RTUINTPTR Addr, PRTINTPTR poffDisp, PRTDBGLINE pLine) +RTDECL(int) RTDbgAsLineByAddr(RTDBGAS hDbgAs, RTUINTPTR Addr, PRTINTPTR poffDisp, PRTDBGLINE pLine, PRTDBGMOD phMod) { /* * Validate input and resolve the address. @@ -1708,28 +1754,21 @@ RTDECL(int) RTDbgAsLineByAddr(RTDBGAS hDbgAs, RTUINTPTR Addr, PRTINTPTR poffDisp */ int rc = RTDbgModLineByAddr(hMod, iSeg, offSeg, poffDisp, pLine); if (RT_SUCCESS(rc)) + { rtDbgAsAdjustLineAddress(pLine, hMod, MapAddr, iSeg); - RTDbgModRelease(hMod); + if (phMod) + *phMod = hMod; + else + RTDbgModRelease(hMod); + } + else + RTDbgModRelease(hMod); return rc; } RT_EXPORT_SYMBOL(RTDbgAsLineByAddr); -/** - * Query a line number by address. - * - * @returns IPRT status code. See RTDbgModSymbolAddrA for more specific ones. - * @retval VERR_INVALID_HANDLE if hDbgAs is invalid. - * @retval VERR_NOT_FOUND if the address couldn't be mapped to a module. - * - * @param hDbgAs The address space handle. - * @param Addr The address which closest symbol is requested. - * @param poffDisp Where to return the distance between the line - * number and address. - * @param ppLine Where to return the pointer to the allocated line - * number info. Always set. Free with RTDbgLineFree. - */ -RTDECL(int) RTDbgAsLineByAddrA(RTDBGAS hDbgAs, RTUINTPTR Addr, PRTINTPTR poffDisp, PRTDBGLINE *ppLine) +RTDECL(int) RTDbgAsLineByAddrA(RTDBGAS hDbgAs, RTUINTPTR Addr, PRTINTPTR poffDisp, PRTDBGLINE *ppLine, PRTDBGMOD phMod) { /* * Validate input and resolve the address. @@ -1749,8 +1788,15 @@ RTDECL(int) RTDbgAsLineByAddrA(RTDBGAS hDbgAs, RTUINTPTR Addr, PRTINTPTR poffDis */ int rc = RTDbgModLineByAddrA(hMod, iSeg, offSeg, poffDisp, ppLine); if (RT_SUCCESS(rc)) + { rtDbgAsAdjustLineAddress(*ppLine, hMod, MapAddr, iSeg); - RTDbgModRelease(hMod); + 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..77555180 --- /dev/null +++ b/src/VBox/Runtime/common/dbg/dbgcfg.cpp @@ -0,0 +1,2172 @@ +/* $Id: dbgcfg.cpp $ */ +/** @file + * IPRT - Debugging Configuration. + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#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; + + +/** @name Open flags. + * @{ */ +/** The operative system mask. The values are RT_OPSYS_XXX. */ +#define RTDBGCFG_O_OPSYS_MASK UINT32_C(0x000000ff) +/** The files may be compressed MS styled. */ +#define RTDBGCFG_O_MAYBE_COMPRESSED_MS RT_BIT_32(26) +/** Whether to make a recursive search. */ +#define RTDBGCFG_O_RECURSIVE RT_BIT_32(27) +/** We're looking for a separate debug file. */ +#define RTDBGCFG_O_EXT_DEBUG_FILE RT_BIT_32(28) +/** We're looking for an executable image. */ +#define RTDBGCFG_O_EXECUTABLE_IMAGE RT_BIT_32(29) +/** The file search should be done in an case insensitive fashion. */ +#define RTDBGCFG_O_CASE_INSENSITIVE RT_BIT_32(30) +/** Use Windbg style symbol servers when encountered in the path. */ +#define RTDBGCFG_O_SYMSRV RT_BIT_32(31) +/** @} */ + + +/******************************************************************************* +* 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 } +}; + + + +/** + * 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. + * RTPATH_MAX in size. On success, this will contain + * the combined path with @a pszName case correct. + * @param offLastComp The offset of the last component (for chopping it + * off). + * @param pszName What we're looking for. + * @param enmType What kind of thing we're looking for. + */ +static bool rtDbgCfgIsXxxxAndFixCaseWorker(char *pszPath, size_t offLastComp, const char *pszName, + RTDIRENTRYTYPE enmType) +{ + /** @todo IPRT should generalize this so we can use host specific tricks to + * speed it up. */ + + /* Return straight away if the name isn't case foldable. */ + if (!RTStrIsCaseFoldable(pszName)) + return false; + + /* + * Try some simple case folding games. + */ + RTStrToLower(&pszPath[offLastComp]); + if (RTFileExists(pszPath)) + return true; + + RTStrToUpper(&pszPath[offLastComp]); + if (RTFileExists(pszPath)) + return true; + + /* + * Open the directory and check each entry in it. + */ + pszPath[offLastComp] = '\0'; + PRTDIR pDir; + int rc = RTDirOpen(&pDir, pszPath); + if (RT_FAILURE(rc)) + return false; + + for (;;) + { + /* Read the next entry. */ + union + { + RTDIRENTRY Entry; + uint8_t ab[_4K]; + } u; + size_t cbBuf = sizeof(u); + rc = RTDirRead(pDir, &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) ) + { + pszPath[offLastComp] = '\0'; + rc = RTPathAppend(pszPath, RTPATH_MAX, u.Entry.szName); + if ( u.Entry.enmType != enmType + && RT_SUCCESS(rc)) + RTDirQueryUnknownType(pszPath, true /*fFollowSymlinks*/, &u.Entry.enmType); + + if ( u.Entry.enmType == enmType + || RT_FAILURE(rc)) + { + RTDirClose(pDir); + if (RT_FAILURE(rc)) + { + pszPath[offLastComp] = '\0'; + return false; + } + return true; + } + } + } + + RTDirClose(pDir); + pszPath[offLastComp] = '\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, pszSubDir, 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 file name 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, 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 check if we got a hit. + */ + int rc = RTPathAppend(pszPath, RTPATH_MAX, pszFilename); + if (RT_FAILURE(rc)) + return false; + + if (RTFileExists(pszPath)) + return true; + + /* + * Do case insensitive file lookup if requested. + */ + if (fCaseInsensitive) + { + if (rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, pszFilename, 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)) + return true; + + if (fCaseInsensitive) + { + /* Note! Ugly hack here, the pszName parameter points into pszPath! */ + if (rtDbgCfgIsXxxxAndFixCaseWorker(pszPath, cchPath, RTPathFilename(pszPath), 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, + PFNDBGCFGOPEN 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], 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->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 + 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, PRTPATHSPLIT pSplitFn, + uint32_t fFlags, PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ +#ifdef IPRT_WITH_HTTP + if (pThis->fFlags & RTDBGCFG_FLAGS_NO_SYM_SRV) + return VWRN_NOT_FOUND; + if (!pszCacheSubDir || !*pszCacheSubDir) + 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 the file. + */ + RTHTTP hHttp; + rc = RTHttpCreate(&hHttp); + if (RT_FAILURE(rc)) + return rc; + RTHttpUseSystemProxySettings(hHttp); + + static const char * const s_apszHeaders[] = + { + "User-Agent: Microsoft-Symbol-Server/6.6.0999.9", + "Pragma: no-cache", + }; + + rc = RTHttpSetHeaders(hHttp, RT_ELEMENTS(s_apszHeaders), s_apszHeaders); + if (RT_SUCCESS(rc)) + { + char szUrl[_2K]; + RTStrPrintf(szUrl, sizeof(szUrl), "%s/%s/%s/%s", pszServer, pszFilename, pszCacheSubDir, pszFilename); + + /** @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, pszPath); + } + if (rc == VERR_HTTP_NOT_FOUND) + { + /* 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); + + /* + * If we succeeded, give it a try. + */ + if (RT_SUCCESS(rc)) + { + 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; + +#else /* !IPRT_WITH_HTTP */ + return VWRN_NOT_FOUND; +#endif /* !IPRT_WITH_HTTP */ +} + + +static int rtDbgCfgCopyFileToCache(PRTDBGCFGINT pThis, char const *pszSrc, const char *pchCache, size_t cchCache, + const char *pszCacheSubDir, PRTPATHSPLIT pSplitFn) +{ + if (!pszCacheSubDir || !*pszCacheSubDir) + return VINF_SUCCESS; + + /** @todo copy to cache */ + return VINF_SUCCESS; +} + + +static int rtDbgCfgTryOpenCache(PRTDBGCFGINT pThis, char *pszPath, const char *pszCacheSubDir, PRTPATHSPLIT pSplitFn, + uint32_t fFlags, PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + /* + * 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; + } + + size_t cchPath = strlen(pszPath); + + /* + * 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, 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, + uint32_t fFlags, char *pszPath, PFNDBGCFGOPEN 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. + */ + if (!strncmp(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; + { + 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, pszCacheSubDir, pSplitFn, 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, pSplitFn, fFlags, + pfnCallback, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN) + return rc2; + } + } + else if (!strncmp(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, pszCacheSubDir, pSplitFn, 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 (!strncmp(pszDir, RT_STR_TUPLE("rec*"))) + { + pszDir += sizeof("rec*") - 1; + cchDir -= sizeof("rec*") - 1; + fFlagsDir |= RTDBGCFG_O_RECURSIVE; + } + else if (!strncmp(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, 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 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, + uint32_t fFlags, PFNDBGCFGOPEN 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); + + /* + * Do some guessing as to the way we should parse the filename and whether + * it's case exact or not. + */ + bool fDosPath = strchr(pszFilename, ':') != NULL + || strchr(pszFilename, '\\') != 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' 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, 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) + && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_SYSTEM_PATHS) ) + { + rc2 = rtDbgCfgTryOpenList(pThis, &pThis->NtExecutablePathList, pSplitFn, pszCacheSubDir, fFlags, szPath, + pfnCallback, pvUser1, pvUser2); + if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet)) + rcRet = rc2; + } + + if ( rc2 != VINF_CALLBACK_RETURN + && rc2 != VERR_CALLBACK_RETURN + && !(pThis->fFlags & RTDBGCFG_FLAGS_NO_SYSTEM_PATHS) ) + { + rc2 = rtDbgCfgTryOpenList(pThis, &pThis->NtSymbolPathList, pSplitFn, pszCacheSubDir, 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) RTDbgCfgOpenPeImage(RTDBGCFG hDbgCfg, const char *pszFilename, uint32_t cbImage, uint32_t uTimestamp, + PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + char szSubDir[32]; + RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, cbImage); + return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, + 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, + PFNDBGCFGOPEN 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, + 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, + PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + /** @todo test this! */ + char szSubDir[32]; + RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, uAge); + return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, + 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, + PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + char szSubDir[32]; + RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, cbImage); + return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, + 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, + PFNDBGCFGOPEN pfnCallback, void *pvUser1, void *pvUser2) +{ + char szSubDir[32]; + RTStrPrintf(szSubDir, sizeof(szSubDir), "%08x", uCrc32); + return rtDbgCfgOpenWithSubDir(hDbgCfg, pszFilename, szSubDir, + RT_OPSYS_UNKNOWN | RTDBGCFG_O_EXT_DEBUG_FILE, + pfnCallback, pvUser1, pvUser2); +} + + + +/* + * + * 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 *pszDsymName, + uint32_t fFlags, PFNDBGCFGOPEN 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) + && !rtDbgCfgIsDirAndFixCase(pszPath, pszDsymName, fCaseInsensitive) + && !rtDbgCfgIsDirAndFixCase(pszPath, "Contents", fCaseInsensitive) + && !rtDbgCfgIsDirAndFixCase(pszPath, "Resources", fCaseInsensitive) + && !rtDbgCfgIsDirAndFixCase(pszPath, "DWARF", fCaseInsensitive)) + { + if (rtDbgCfgIsFileAndFixCase(pszPath, pSplitFn->apszComps[pSplitFn->cComps - 1], 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 rtDbgCfgTryOpenDsumBundleInList(PRTDBGCFGINT pThis, PRTLISTANCHOR pList, PRTPATHSPLIT pSplitFn, + const char *pszDsymName, const char *pszCacheSubDir, uint32_t fFlags, char *pszPath, + PFNDBGCFGOPEN 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. + */ + if (!strncmp(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; + { + 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, pszCacheSubDir, pSplitFn, 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, pSplitFn, fFlags, + pfnCallback, pvUser1, pvUser2); + if (rc2 == VINF_CALLBACK_RETURN || rc2 == VERR_CALLBACK_RETURN) + return rc2; + } + } + else if (!strncmp(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, pszCacheSubDir, pSplitFn, 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 (!strncmp(pszDir, RT_STR_TUPLE("rec*"))) + { + pszDir += sizeof("rec*") - 1; + cchDir -= sizeof("rec*") - 1; + fFlagsDir |= RTDBGCFG_O_RECURSIVE; + } + else if (!strncmp(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, pszDsymName, 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, pSplitFn); + return rc2; + } + } + + /* Propagate errors. */ + if (RT_FAILURE(rc2) && RT_SUCCESS_NP(rcRet)) + rcRet = rc2; + } + + return rcRet; +} + + +RTDECL(int) RTDbgCfgOpenDsymBundle(RTDBGCFG hDbgCfg, const char *pszImage, PCRTUUID pUuid, + PFNDBGCFGOPEN 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 related adjustments, so, a bug + * found here or there probably means the other version needs updating. + */ + int rcRet = VINF_SUCCESS; + int rc2; + + //RTStrPrintf(szFile, sizeof(szFile), "%s.dSYM/Contents/Resources/DWARF/%s", pszFilename, pszFilename); + + /* + * 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 parameters. + */ + uint32_t fFlags = RTDBGCFG_O_EXT_DEBUG_FILE | RT_OPSYS_DARWIN; + const char *pszCacheSubDir = NULL; + char szCacheSubDir[RTUUID_STR_LENGTH]; + if (pUuid) + { + RTUuidToStr(pUuid, szCacheSubDir, sizeof(szCacheSubDir)); + pszCacheSubDir = szCacheSubDir; + } + + /* + * 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 (RT_SUCCESS(rc2)) + rc2 = RTStrCat(szPath, sizeof(szPath), + ".dSYM" RTPATH_SLASH_STR "Contents" RTPATH_SLASH_STR "Resources" RTPATH_SLASH_STR "DWARF"); + if (RT_SUCCESS(rc2)) + rc2 = RTPathAppend(szPath, sizeof(szPath), pSplitFn->apszComps[pSplitFn->cComps - 1]); + if (RT_SUCCESS(rc2)) + { + 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) + { + char *pszDsymName = (char *)alloca(strlen(pSplitFn->apszComps[pSplitFn->cComps - 1]) + sizeof(".dSYM")); + strcat(strcpy(pszDsymName, pSplitFn->apszComps[pSplitFn->cComps - 1]), ".dSYM"); + + /* + * 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, pszDsymName, 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 = rtDbgCfgTryOpenDsumBundleInList(pThis, &pThis->PathList, pSplitFn, pszDsymName, + pszCacheSubDir, 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) 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) +{ + if (enmOp == RTDBGCFGOP_SET) + rtDbgCfgFreeStrList(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_OFFSETOF(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) + RTListPrepend(pList, &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 pszMnemonics 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) +{ + 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 pThis The config instance. + * @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) +{ + /* + * 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 pThis The config instance. + * @param uValue The value to query. + * @param pszMnemonics 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) +{ + /* + * 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 index 13c677fa..7e627fd9 100644 --- a/src/VBox/Runtime/common/dbg/dbgmod.cpp +++ b/src/VBox/Runtime/common/dbg/dbgmod.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -28,14 +28,17 @@ /******************************************************************************* * 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> @@ -119,6 +122,8 @@ DECLHIDDEN(RTSTRCACHE) g_hDbgModStrCache = NIL_RTSTRCACHE; + + /** * Cleanup debug info interpreter globals. * @@ -253,12 +258,11 @@ static int rtDbgModImageInterpreterRegister(PCRTDBGMODVTIMG pVt) * the built-in interpreters. * * @returns IPRT status code. - * @param pvUser1 NULL. - * @param pvUser2 NULL. + * @param pvUser NULL. */ -static DECLCALLBACK(int) rtDbgModInitOnce(void *pvUser1, void *pvUser2) +static DECLCALLBACK(int) rtDbgModInitOnce(void *pvUser) { - NOREF(pvUser1); NOREF(pvUser2); + NOREF(pvUser); /* * Create the semaphore and string cache. @@ -276,6 +280,12 @@ static DECLCALLBACK(int) rtDbgModInitOnce(void *pvUser1, void *pvUser2) if (RT_SUCCESS(rc)) rc = rtDbgModDebugInterpreterRegister(&g_rtDbgModVtDbgDwarf); if (RT_SUCCESS(rc)) + rc = rtDbgModDebugInterpreterRegister(&g_rtDbgModVtDbgCodeView); +#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)) { @@ -296,28 +306,16 @@ static DECLCALLBACK(int) rtDbgModInitOnce(void *pvUser1, void *pvUser2) } +/** + * Performs lazy init of our global variables. + * @returns IPRT status code. + */ DECLINLINE(int) rtDbgModLazyInit(void) { - return RTOnce(&g_rtDbgModOnce, rtDbgModInitOnce, NULL, NULL); + return RTOnce(&g_rtDbgModOnce, rtDbgModInitOnce, NULL); } -/** - * Creates a module based on the default debug info container. - * - * This can be used to manually load a module and its symbol. The primary user - * group is the debug info interpreters, which use this API to create an - * efficient debug info container behind the scenes and forward all queries to - * it once the info has been loaded. - * - * @returns IPRT status code. - * - * @param phDbgMod Where to return the module handle. - * @param pszName The name of the module (mandatory). - * @param cbSeg The size of initial segment. If zero, segments will - * have to be added manually using RTDbgModSegmentAdd. - * @param fFlags Flags reserved for future extensions, MBZ for now. - */ RTDECL(int) RTDbgModCreate(PRTDBGMOD phDbgMod, const char *pszName, RTUINTPTR cbSeg, uint32_t fFlags) { /* @@ -344,7 +342,8 @@ RTDECL(int) RTDbgModCreate(PRTDBGMOD phDbgMod, const char *pszName, RTUINTPTR cb rc = RTCritSectInit(&pDbgMod->CritSect); if (RT_SUCCESS(rc)) { - pDbgMod->pszName = RTStrCacheEnter(g_hDbgModStrCache, pszName); + 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); @@ -353,6 +352,7 @@ RTDECL(int) RTDbgModCreate(PRTDBGMOD phDbgMod, const char *pszName, RTUINTPTR cb *phDbgMod = pDbgMod; return rc; } + RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFile); RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszName); } RTCritSectDelete(&pDbgMod->CritSect); @@ -364,16 +364,419 @@ RTDECL(int) RTDbgModCreate(PRTDBGMOD phDbgMod, const char *pszName, RTUINTPTR cb RT_EXPORT_SYMBOL(RTDbgModCreate); -RTDECL(int) RTDbgModCreateDeferred(PRTDBGMOD phDbgMod, const char *pszFilename, const char *pszName, - RTUINTPTR cb, uint32_t fFlags) +RTDECL(int) RTDbgModCreateFromMap(PRTDBGMOD phDbgMod, const char *pszFilename, const char *pszName, + RTUINTPTR uSubtrahend, 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(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)) + { + rc = VERR_DBG_NO_MATCHING_INTERPRETER; + 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 */ + 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; + NOREF(pDbgInfo); /** @todo consider a more direct search for a interpreter. */ + + 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; + + 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); + RTPathStripExt(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) { - NOREF(phDbgMod); NOREF(pszFilename); NOREF(pszName); NOREF(cb); NOREF(fFlags); - return VERR_NOT_IMPLEMENTED; + 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; } -RT_EXPORT_SYMBOL(RTDbgModCreateDeferred); -RTDECL(int) RTDbgModCreateFromImage(PRTDBGMOD phDbgMod, const char *pszFilename, const char *pszName, uint32_t fFlags) +/** @callback_method_impl{FNRTDBGCFGOPEN} */ +static DECLCALLBACK(int) rtDbgModExtDbgInfoOpenCallback2(RTDBGCFG hDbgCfg, const char *pszFilename, void *pvUser1, void *pvUser2) +{ + PRTDBGMODINT pDbgMod = (PRTDBGMODINT)pvUser1; + NOREF(pvUser2); /** @todo image matching string or smth. */ + + 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: + { + rc = RTDbgCfgOpenDsymBundle(hDbgCfg, pDbgMod->pszImgFile, NULL /**@todo 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. @@ -383,14 +786,14 @@ RTDECL(int) RTDbgModCreateFromImage(PRTDBGMOD phDbgMod, const char *pszFilename, AssertPtrReturn(pszFilename, VERR_INVALID_POINTER); AssertReturn(*pszFilename, VERR_INVALID_PARAMETER); AssertPtrNullReturn(pszName, VERR_INVALID_POINTER); - AssertReturn(fFlags == 0, VERR_INVALID_PARAMETER); + AssertReturn(enmArch > RTLDRARCH_INVALID && enmArch < RTLDRARCH_END, VERR_INVALID_PARAMETER); int rc = rtDbgModLazyInit(); if (RT_FAILURE(rc)) return rc; if (!pszName) - pszName = RTPathFilename(pszFilename); + pszName = RTPathFilenameEx(pszFilename, RTPATH_STR_F_STYLE_DOS); /* * Allocate a new module instance. @@ -403,12 +806,15 @@ RTDECL(int) RTDbgModCreateFromImage(PRTDBGMOD phDbgMod, const char *pszFilename, rc = RTCritSectInit(&pDbgMod->CritSect); if (RT_SUCCESS(rc)) { - pDbgMod->pszName = RTStrCacheEnter(g_hDbgModStrCache, pszName); + 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. */ @@ -421,38 +827,40 @@ RTDECL(int) RTDbgModCreateFromImage(PRTDBGMOD phDbgMod, const char *pszFilename, { pDbgMod->pImgVt = pImg->pVt; pDbgMod->pvImgPriv = NULL; - rc = pImg->pVt->pfnTryOpen(pDbgMod); + /** @todo need to specify some arch stuff here. */ + rc = pImg->pVt->pfnTryOpen(pDbgMod, enmArch); if (RT_SUCCESS(rc)) { /* - * Find a debug info interpreter. + * Image detected, but found no debug info we were + * able to understand. */ - rc = VERR_DBG_NO_MATCHING_INTERPRETER; - for (PRTDBGMODREGDBG pDbg = g_pDbgHead; pDbg; pDbg = pDbg->pNext) + /** @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)) { - pDbgMod->pDbgVt = pDbg->pVt; - pDbgMod->pvDbgPriv = NULL; - rc = pDbg->pVt->pfnTryOpen(pDbgMod); - if (RT_SUCCESS(rc)) - { - /* - * That's it! - */ - ASMAtomicIncU32(&pDbg->cUsers); - ASMAtomicIncU32(&pImg->cUsers); - RTSemRWReleaseRead(g_hDbgModRWSem); - - *phDbgMod = pDbgMod; - return rc; - } + /* + * We're done! + */ + ASMAtomicIncU32(&pImg->cUsers); + RTSemRWReleaseRead(g_hDbgModRWSem); + + *phDbgMod = pDbgMod; + return VINF_SUCCESS; } - /* - * Image detected, but found no debug info we were - * able to understand. - */ - /** @todo Fall back on exported symbols! */ + /* Failed, close up the shop. */ pDbgMod->pImgVt->pfnClose(pDbgMod); + pDbgMod->pImgVt = NULL; + pDbgMod->pvImgPriv = NULL; break; } } @@ -471,7 +879,7 @@ RTDECL(int) RTDbgModCreateFromImage(PRTDBGMOD phDbgMod, const char *pszFilename, { pDbgMod->pDbgVt = pDbg->pVt; pDbgMod->pvDbgPriv = NULL; - rc = pDbg->pVt->pfnTryOpen(pDbgMod); + rc = pDbg->pVt->pfnTryOpen(pDbgMod, enmArch); if (RT_SUCCESS(rc)) { /* @@ -492,10 +900,15 @@ RTDECL(int) RTDbgModCreateFromImage(PRTDBGMOD phDbgMod, const char *pszFilename, /* bail out */ RTSemRWReleaseRead(g_hDbgModRWSem); } - RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszName); + RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFileSpecified); + RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszImgFile); } - 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); } @@ -505,8 +918,150 @@ RTDECL(int) RTDbgModCreateFromImage(PRTDBGMOD phDbgMod, const char *pszFilename, RT_EXPORT_SYMBOL(RTDbgModCreateFromImage); -RTDECL(int) RTDbgModCreateFromMap(PRTDBGMOD phDbgMod, const char *pszFilename, const char *pszName, - RTUINTPTR uSubtrahend, uint32_t fFlags) + + + +/* + * + * 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)); + + 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; + rc = pImg->pVt->pfnTryOpen(pDbgMod, RTLDRARCH_WHATEVER); + if (RT_SUCCESS(rc)) + 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, RTLDRMOD hLdrMod, + uint32_t cbImage, uint32_t uTimestamp, RTDBGCFG hDbgCfg) { /* * Input validation and lazy initialization. @@ -515,16 +1070,21 @@ RTDECL(int) RTDbgModCreateFromMap(PRTDBGMOD phDbgMod, const char *pszFilename, c *phDbgMod = NIL_RTDBGMOD; AssertPtrReturn(pszFilename, VERR_INVALID_POINTER); AssertReturn(*pszFilename, VERR_INVALID_PARAMETER); - AssertPtrNullReturn(pszName, VERR_INVALID_POINTER); - AssertReturn(fFlags == 0, VERR_INVALID_PARAMETER); - AssertReturn(uSubtrahend == 0, VERR_NOT_IMPLEMENTED); /** @todo implement uSubtrahend. */ + if (!pszName) + pszName = RTPathFilenameEx(pszFilename, RTPATH_STR_F_STYLE_DOS); + AssertPtrReturn(pszName, VERR_INVALID_POINTER); + AssertReturn(hLdrMod == NIL_RTLDRMOD || RTLdrSize(hLdrMod) != ~(size_t)0, VERR_INVALID_HANDLE); int rc = rtDbgModLazyInit(); if (RT_FAILURE(rc)) return rc; - if (!pszName) - pszName = RTPathFilename(pszFilename); + uint64_t fDbgCfg = 0; + if (hDbgCfg) + { + rc = RTDbgCfgQueryUInt(hDbgCfg, RTDBGCFGPROP_FLAGS, &fDbgCfg); + AssertRCReturn(rc, rc); + } /* * Allocate a new module instance. @@ -537,51 +1097,76 @@ RTDECL(int) RTDbgModCreateFromMap(PRTDBGMOD phDbgMod, const char *pszFilename, c rc = RTCritSectInit(&pDbgMod->CritSect); if (RT_SUCCESS(rc)) { - pDbgMod->pszName = RTStrCacheEnter(g_hDbgModStrCache, pszName); + pDbgMod->pszName = RTStrCacheEnterLower(g_hDbgModStrCache, pszName); if (pDbgMod->pszName) { - pDbgMod->pszDbgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename); - if (pDbgMod->pszDbgFile) + pDbgMod->pszImgFile = RTStrCacheEnter(g_hDbgModStrCache, pszFilename); + if (pDbgMod->pszImgFile) { + RTStrCacheRetain(pDbgMod->pszImgFile); + pDbgMod->pszImgFileSpecified = pDbgMod->pszImgFile; + /* - * Try the map file readers. + * If we have a loader module, we must instantiate the loader + * side of things regardless of the deferred setting. */ - rc = RTSemRWRequestRead(g_hDbgModRWSem, RT_INDEFINITE_WAIT); + if (hLdrMod != NIL_RTLDRMOD) + { + if (!cbImage) + cbImage = (uint32_t)RTLdrSize(hLdrMod); + pDbgMod->pImgVt = &g_rtDbgModVtImgLdr; + + rc = rtDbgModLdrOpenFromHandle(pDbgMod, hLdrMod); + } if (RT_SUCCESS(rc)) { - rc = VERR_DBG_NO_MATCHING_INTERPRETER; - for (PRTDBGMODREGDBG pCur = g_pDbgHead; pCur; pCur = pCur->pNext) + /* + * Do it now or procrastinate? + */ + if (!(fDbgCfg & RTDBGCFG_FLAGS_DEFERRED) || !cbImage) { - if (pCur->pVt->fSupports & RT_DBGTYPE_MAP) - { - pDbgMod->pDbgVt = pCur->pVt; - pDbgMod->pvDbgPriv = NULL; - rc = pCur->pVt->pfnTryOpen(pDbgMod); - if (RT_SUCCESS(rc)) - { - ASMAtomicIncU32(&pCur->cUsers); - RTSemRWReleaseRead(g_hDbgModRWSem); - - *phDbgMod = pDbgMod; - return rc; - } - } + 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, &pDeferred); + if (RT_SUCCESS(rc)) + pDeferred->u.PeImage.uTimestamp = uTimestamp; + } + if (RT_SUCCESS(rc)) + { + *phDbgMod = pDbgMod; + return VINF_SUCCESS; } - /* bail out */ - RTSemRWReleaseRead(g_hDbgModRWSem); + /* Failed, bail out. */ + if (hLdrMod != NIL_RTLDRMOD) + { + Assert(pDbgMod->pImgVt); + pDbgMod->pImgVt->pfnClose(pDbgMod); + } } RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszName); } - RTStrCacheRelease(g_hDbgModStrCache, pDbgMod->pszDbgFile); + 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(RTDbgModCreateFromMap); +RT_EXPORT_SYMBOL(RTDbgModCreateFromPeImage); /** @@ -616,6 +1201,7 @@ static void rtDbgModDestroy(PRTDBGMODINT pDbgMod) 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); @@ -623,15 +1209,6 @@ static void rtDbgModDestroy(PRTDBGMODINT pDbgMod) } -/** - * Retains another reference to the module. - * - * @returns New reference count, UINT32_MAX on invalid handle (asserted). - * - * @param hDbgMod The module handle. - * - * @remarks Will not take any locks. - */ RTDECL(uint32_t) RTDbgModRetain(RTDBGMOD hDbgMod) { PRTDBGMODINT pDbgMod = hDbgMod; @@ -641,18 +1218,6 @@ RTDECL(uint32_t) RTDbgModRetain(RTDBGMOD hDbgMod) RT_EXPORT_SYMBOL(RTDbgModRetain); -/** - * Release a reference to the module. - * - * When the reference count reaches zero, the module is destroyed. - * - * @returns New reference count, UINT32_MAX on invalid handle (asserted). - * - * @param hDbgMod The module handle. The NIL handle is quietly ignored - * and 0 is returned. - * - * @remarks Will not take any locks. - */ RTDECL(uint32_t) RTDbgModRelease(RTDBGMOD hDbgMod) { if (hDbgMod == NIL_RTDBGMOD) @@ -668,13 +1233,6 @@ RTDECL(uint32_t) RTDbgModRelease(RTDBGMOD hDbgMod) RT_EXPORT_SYMBOL(RTDbgModRelease); -/** - * Gets the module name. - * - * @returns Pointer to a read only string containing the name. - * - * @param hDbgMod The module handle. - */ RTDECL(const char *) RTDbgModName(RTDBGMOD hDbgMod) { PRTDBGMODINT pDbgMod = hDbgMod; @@ -684,17 +1242,79 @@ RTDECL(const char *) RTDbgModName(RTDBGMOD hDbgMod) RT_EXPORT_SYMBOL(RTDbgModName); -/** - * Converts an image relative address to a segment:offset address. - * - * @returns Segment index on success. - * NIL_RTDBGSEGIDX is returned if the module handle or the RVA are - * invalid. - * - * @param hDbgMod The module handle. - * @param uRva The image relative address to convert. - * @param poffSeg Where to return the segment offset. Optional. - */ +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; @@ -709,17 +1329,6 @@ RTDECL(RTDBGSEGIDX) RTDbgModRvaToSegOff(RTDBGMOD hDbgMod, RTUINTPTR uRva, PRTUIN RT_EXPORT_SYMBOL(RTDbgModRvaToSegOff); -/** - * Image size when mapped if segments are mapped adjacently. - * - * For ELF, PE, and Mach-O images this is (usually) a natural query, for LX and - * NE and such it's a bit odder and the answer may not make much sense for them. - * - * @returns Image mapped size. - * RTUINTPTR_MAX is returned if the handle is invalid. - * - * @param hDbgMod The module handle. - */ RTDECL(RTUINTPTR) RTDbgModImageSize(RTDBGMOD hDbgMod) { PRTDBGMODINT pDbgMod = hDbgMod; @@ -734,13 +1343,6 @@ RTDECL(RTUINTPTR) RTDbgModImageSize(RTDBGMOD hDbgMod) RT_EXPORT_SYMBOL(RTDbgModImageSize); -/** - * Gets the module tag value if any. - * - * @returns The tag. 0 if hDbgMod is invalid. - * - * @param hDbgMod The module handle. - */ RTDECL(uint64_t) RTDbgModGetTag(RTDBGMOD hDbgMod) { PRTDBGMODINT pDbgMod = hDbgMod; @@ -750,19 +1352,6 @@ RTDECL(uint64_t) RTDbgModGetTag(RTDBGMOD hDbgMod) RT_EXPORT_SYMBOL(RTDbgModGetTag); -/** - * Tags or untags the module. - * - * @returns IPRT status code. - * @retval VERR_INVALID_HANDLE if hDbgMod is invalid. - * - * @param hDbgMod The module handle. - * @param uTag The tag value. The convention is that 0 is no tag - * and any other value means it's tagged. It's adviced - * to use some kind of unique number like an address - * (global or string cache for instance) to avoid - * collisions with other users - */ RTDECL(int) RTDbgModSetTag(RTDBGMOD hDbgMod, uint64_t uTag) { PRTDBGMODINT pDbgMod = hDbgMod; @@ -777,35 +1366,6 @@ RTDECL(int) RTDbgModSetTag(RTDBGMOD hDbgMod, uint64_t uTag) RT_EXPORT_SYMBOL(RTDbgModSetTag); -/** - * Adds a segment to the module. Optional feature. - * - * This method is intended used for manually constructing debug info for a - * module. The main usage is from other debug info interpreters that want to - * avoid writing a debug info database and instead uses the standard container - * behind the scenes. - * - * @returns IPRT status code. - * @retval VERR_NOT_SUPPORTED if this feature isn't support by the debug info - * interpreter. This is a common return code. - * @retval VERR_INVALID_HANDLE if hDbgMod is invalid. - * @retval VERR_DBG_ADDRESS_WRAP if uRva+cb wraps around. - * @retval VERR_DBG_SEGMENT_NAME_OUT_OF_RANGE if pszName is too short or long. - * @retval VERR_INVALID_PARAMETER if fFlags contains undefined flags. - * @retval VERR_DBG_SPECIAL_SEGMENT if *piSeg is a special segment. - * @retval VERR_DBG_INVALID_SEGMENT_INDEX if *piSeg doesn't meet expectations. - * - * @param hDbgMod The module handle. - * @param uRva The image relative address of the segment. - * @param cb The size of the segment. - * @param pszName The segment name. Does not normally need to be - * unique, although this is somewhat up to the - * debug interpreter to decide. - * @param fFlags Segment flags. Reserved for future used, MBZ. - * @param piSeg The segment index or NIL_RTDBGSEGIDX on input. - * The assigned segment index on successful return. - * Optional. - */ RTDECL(int) RTDbgModSegmentAdd(RTDBGMOD hDbgMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName, uint32_t fFlags, PRTDBGSEGIDX piSeg) { @@ -836,17 +1396,6 @@ RTDECL(int) RTDbgModSegmentAdd(RTDBGMOD hDbgMod, RTUINTPTR uRva, RTUINTPTR cb, c RT_EXPORT_SYMBOL(RTDbgModSegmentAdd); -/** - * Gets the number of segments in the module. - * - * This is can be used to determine the range which can be passed to - * RTDbgModSegmentByIndex and derivatives. - * - * @returns The segment relative address. - * NIL_RTDBGSEGIDX if the handle is invalid. - * - * @param hDbgMod The module handle. - */ RTDECL(RTDBGSEGIDX) RTDbgModSegmentCount(RTDBGMOD hDbgMod) { PRTDBGMODINT pDbgMod = hDbgMod; @@ -861,23 +1410,6 @@ RTDECL(RTDBGSEGIDX) RTDbgModSegmentCount(RTDBGMOD hDbgMod) RT_EXPORT_SYMBOL(RTDbgModSegmentCount); -/** - * Query information about a segment. - * - * This can be used together with RTDbgModSegmentCount to enumerate segments. - * The index starts a 0 and stops one below RTDbgModSegmentCount. - * - * @returns IPRT status code. - * @retval VERR_DBG_INVALID_SEGMENT_INDEX if iSeg is too high. - * @retval VERR_DBG_SPECIAL_SEGMENT if iSeg indicates a special segment. - * @retval VERR_INVALID_HANDLE if hDbgMod is invalid. - * - * @param hDbgMod The module handle. - * @param iSeg The segment index. No special segments. - * @param pSegInfo Where to return the segment info. The - * RTDBGSEGMENT::Address member will be set to - * RTUINTPTR_MAX or the load address used at link time. - */ RTDECL(int) RTDbgModSegmentByIndex(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo) { AssertMsgReturn(iSeg <= RTDBGSEGIDX_LAST, ("%#x\n", iSeg), VERR_DBG_SPECIAL_SEGMENT); @@ -893,20 +1425,6 @@ RTDECL(int) RTDbgModSegmentByIndex(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, PRTDBGSEG RT_EXPORT_SYMBOL(RTDbgModSegmentByIndex); -/** - * Gets the size of a segment. - * - * This is a just a wrapper around RTDbgModSegmentByIndex. - * - * @returns The segment size. - * RTUINTPTR_MAX is returned if either the handle and segment index are - * invalid. - * - * @param hDbgMod The module handle. - * @param iSeg The segment index. RTDBGSEGIDX_ABS is not allowed. - * If RTDBGSEGIDX_RVA is used, the functions returns - * the same value as RTDbgModImageSize. - */ RTDECL(RTUINTPTR) RTDbgModSegmentSize(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg) { if (iSeg == RTDBGSEGIDX_RVA) @@ -918,19 +1436,6 @@ RTDECL(RTUINTPTR) RTDbgModSegmentSize(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg) RT_EXPORT_SYMBOL(RTDbgModSegmentSize); -/** - * Gets the image relative address of a segment. - * - * This is a just a wrapper around RTDbgModSegmentByIndex. - * - * @returns The segment relative address. - * RTUINTPTR_MAX is returned if either the handle and segment index are - * invalid. - * - * @param hDbgMod The module handle. - * @param iSeg The segment index. No special segment indexes - * allowed (asserted). - */ RTDECL(RTUINTPTR) RTDbgModSegmentRva(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg) { RTDBGSEGMENT SegInfo; @@ -940,34 +1445,6 @@ RTDECL(RTUINTPTR) RTDbgModSegmentRva(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg) RT_EXPORT_SYMBOL(RTDbgModSegmentRva); -/** - * Adds a line number to the module. - * - * @returns IPRT status code. - * @retval VERR_NOT_SUPPORTED if the module interpret doesn't support adding - * custom symbols. This is a common place occurrence. - * @retval VERR_INVALID_HANDLE if hDbgMod is invalid. - * @retval VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE if the symbol name is too long or - * short. - * @retval VERR_DBG_INVALID_RVA if an image relative address is specified and - * it's not inside any of the segments defined by the module. - * @retval VERR_DBG_INVALID_SEGMENT_INDEX if the segment index isn't valid. - * @retval VERR_DBG_INVALID_SEGMENT_OFFSET if the segment offset is beyond the - * end of the segment. - * @retval VERR_DBG_ADDRESS_WRAP if off+cb wraps around. - * @retval VERR_INVALID_PARAMETER if the symbol flags sets undefined bits. - * - * @param hDbgMod The module handle. - * @param pszSymbol The symbol name. - * @param iSeg The segment index. - * @param off The segment offset. - * @param cb The size of the symbol. Can be zero, although this - * may depend somewhat on the debug interpreter. - * @param fFlags Symbol flags. Reserved for the future, MBZ. - * @param piOrdinal Where to return the symbol ordinal on success. If - * the interpreter doesn't do ordinals, this will be set to - * UINT32_MAX. Optional. - */ RTDECL(int) RTDbgModSymbolAdd(RTDBGMOD hDbgMod, const char *pszSymbol, RTDBGSEGIDX iSeg, RTUINTPTR off, RTUINTPTR cb, uint32_t fFlags, uint32_t *piOrdinal) { @@ -1014,18 +1491,6 @@ RTDECL(int) RTDbgModSymbolAdd(RTDBGMOD hDbgMod, const char *pszSymbol, RTDBGSEGI RT_EXPORT_SYMBOL(RTDbgModSymbolAdd); -/** - * Gets the symbol count. - * - * This can be used together wtih RTDbgModSymbolByOrdinal or - * RTDbgModSymbolByOrdinalA to enumerate all the symbols. - * - * @returns The number of symbols in the module. - * UINT32_MAX is returned if the module handle is invalid or some other - * error occurs. - * - * @param hDbgMod The module handle. - */ RTDECL(uint32_t) RTDbgModSymbolCount(RTDBGMOD hDbgMod) { PRTDBGMODINT pDbgMod = hDbgMod; @@ -1040,20 +1505,6 @@ RTDECL(uint32_t) RTDbgModSymbolCount(RTDBGMOD hDbgMod) RT_EXPORT_SYMBOL(RTDbgModSymbolCount); -/** - * Queries symbol information by ordinal number. - * - * @returns IPRT status code. - * @retval VERR_SYMBOL_NOT_FOUND if there is no symbol at the given number. - * @retval VERR_DBG_NO_SYMBOLS if there aren't any symbols. - * @retval VERR_INVALID_HANDLE if hDbgMod is invalid. - * @retval VERR_NOT_SUPPORTED if lookup by ordinal is not supported. - * - * @param hDbgMod The module handle. - * @param iOrdinal The symbol ordinal number. 0-based. The highest - * number is RTDbgModSymbolCount() - 1. - * @param pSymInfo Where to store the symbol information. - */ RTDECL(int) RTDbgModSymbolByOrdinal(RTDBGMOD hDbgMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo) { PRTDBGMODINT pDbgMod = hDbgMod; @@ -1068,22 +1519,6 @@ RTDECL(int) RTDbgModSymbolByOrdinal(RTDBGMOD hDbgMod, uint32_t iOrdinal, PRTDBGS RT_EXPORT_SYMBOL(RTDbgModSymbolByOrdinal); -/** - * Queries symbol information by ordinal number. - * - * @returns IPRT status code. - * @retval VERR_DBG_NO_SYMBOLS if there aren't any symbols. - * @retval VERR_NOT_SUPPORTED if lookup by ordinal is not supported. - * @retval VERR_SYMBOL_NOT_FOUND if there is no symbol at the given number. - * @retval VERR_NO_MEMORY if RTDbgSymbolAlloc fails. - * - * @param hDbgMod The module handle. - * @param iOrdinal The symbol ordinal number. 0-based. The highest - * number is RTDbgModSymbolCount() - 1. - * @param ppSymInfo Where to store the pointer to the returned - * symbol information. Always set. Free with - * RTDbgSymbolFree. - */ RTDECL(int) RTDbgModSymbolByOrdinalA(RTDBGMOD hDbgMod, uint32_t iOrdinal, PRTDBGSYMBOL *ppSymInfo) { AssertPtr(ppSymInfo); @@ -1104,33 +1539,6 @@ RTDECL(int) RTDbgModSymbolByOrdinalA(RTDBGMOD hDbgMod, uint32_t iOrdinal, PRTDBG RT_EXPORT_SYMBOL(RTDbgModSymbolByOrdinalA); -/** - * Queries symbol information by address. - * - * The returned symbol is what the debug info interpreter considers the symbol - * most applicable to the specified address. This usually means a symbol with an - * address equal or lower than the requested. - * - * @returns IPRT status code. - * @retval VERR_SYMBOL_NOT_FOUND if no suitable symbol was found. - * @retval VERR_DBG_NO_SYMBOLS if there aren't any symbols. - * @retval VERR_INVALID_HANDLE if hDbgMod is invalid. - * @retval VERR_DBG_INVALID_RVA if an image relative address is specified and - * it's not inside any of the segments defined by the module. - * @retval VERR_DBG_INVALID_SEGMENT_INDEX if the segment index isn't valid. - * @retval VERR_DBG_INVALID_SEGMENT_OFFSET if the segment offset is beyond the - * end of the segment. - * @retval VERR_INVALID_PARAMETER if incorrect flags. - * - * @param hDbgMod The module handle. - * @param iSeg The segment number. - * @param off The offset into the segment. - * @param fFlags Symbol search flags, see RTDBGSYMADDR_FLAGS_XXX. - * @param poffDisp Where to store the distance between the - * specified address and the returned symbol. - * Optional. - * @param pSymInfo Where to store the symbol information. - */ RTDECL(int) RTDbgModSymbolByAddr(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags, PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo) { @@ -1169,35 +1577,6 @@ RTDECL(int) RTDbgModSymbolByAddr(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR o RT_EXPORT_SYMBOL(RTDbgModSymbolByAddr); -/** - * Queries symbol information by address. - * - * The returned symbol is what the debug info interpreter considers the symbol - * most applicable to the specified address. This usually means a symbol with an - * address equal or lower than the requested. - * - * @returns IPRT status code. - * @retval VERR_SYMBOL_NOT_FOUND if no suitable symbol was found. - * @retval VERR_DBG_NO_SYMBOLS if there aren't any symbols. - * @retval VERR_INVALID_HANDLE if hDbgMod is invalid. - * @retval VERR_DBG_INVALID_RVA if an image relative address is specified and - * it's not inside any of the segments defined by the module. - * @retval VERR_DBG_INVALID_SEGMENT_INDEX if the segment index isn't valid. - * @retval VERR_DBG_INVALID_SEGMENT_OFFSET if the segment offset is beyond the - * end of the segment. - * @retval VERR_NO_MEMORY if RTDbgSymbolAlloc fails. - * @retval VERR_INVALID_PARAMETER if incorrect flags. - * - * @param hDbgMod The module handle. - * @param iSeg The segment index. - * @param off The offset into the segment. - * @param fFlags Symbol search flags, see RTDBGSYMADDR_FLAGS_XXX. - * @param poffDisp Where to store the distance between the - * specified address and the returned symbol. Optional. - * @param ppSymInfo Where to store the pointer to the returned - * symbol information. Always set. Free with - * RTDbgSymbolFree. - */ RTDECL(int) RTDbgModSymbolByAddrA(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags, PRTINTPTR poffDisp, PRTDBGSYMBOL *ppSymInfo) { @@ -1219,19 +1598,6 @@ RTDECL(int) RTDbgModSymbolByAddrA(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR RT_EXPORT_SYMBOL(RTDbgModSymbolByAddrA); -/** - * Queries symbol information by symbol name. - * - * @returns IPRT status code. - * @retval VERR_DBG_NO_SYMBOLS if there aren't any symbols. - * @retval VERR_SYMBOL_NOT_FOUND if no suitable symbol was found. - * @retval VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE if the symbol name is too long or - * short. - * - * @param hDbgMod The module handle. - * @param pszSymbol The symbol name. - * @param pSymInfo Where to store the symbol information. - */ RTDECL(int) RTDbgModSymbolByName(RTDBGMOD hDbgMod, const char *pszSymbol, PRTDBGSYMBOL pSymInfo) { /* @@ -1257,22 +1623,6 @@ RTDECL(int) RTDbgModSymbolByName(RTDBGMOD hDbgMod, const char *pszSymbol, PRTDBG RT_EXPORT_SYMBOL(RTDbgModSymbolByName); -/** - * Queries symbol information by symbol name. - * - * @returns IPRT status code. - * @retval VERR_DBG_NO_SYMBOLS if there aren't any symbols. - * @retval VERR_SYMBOL_NOT_FOUND if no suitable symbol was found. - * @retval VERR_DBG_SYMBOL_NAME_OUT_OF_RANGE if the symbol name is too long or - * short. - * @retval VERR_NO_MEMORY if RTDbgSymbolAlloc fails. - * - * @param hDbgMod The module handle. - * @param pszSymbol The symbol name. - * @param ppSymInfo Where to store the pointer to the returned - * symbol information. Always set. Free with - * RTDbgSymbolFree. - */ RTDECL(int) RTDbgModSymbolByNameA(RTDBGMOD hDbgMod, const char *pszSymbol, PRTDBGSYMBOL *ppSymInfo) { AssertPtr(ppSymInfo); @@ -1293,31 +1643,6 @@ RTDECL(int) RTDbgModSymbolByNameA(RTDBGMOD hDbgMod, const char *pszSymbol, PRTDB RT_EXPORT_SYMBOL(RTDbgModSymbolByNameA); -/** - * Adds a line number to the module. - * - * @returns IPRT status code. - * @retval VERR_NOT_SUPPORTED if the module interpret doesn't support adding - * custom symbols. This should be consider a normal response. - * @retval VERR_INVALID_HANDLE if hDbgMod is invalid. - * @retval VERR_DBG_FILE_NAME_OUT_OF_RANGE if the file name is too longer or - * empty. - * @retval VERR_DBG_INVALID_RVA if an image relative address is specified and - * it's not inside any of the segments defined by the module. - * @retval VERR_DBG_INVALID_SEGMENT_INDEX if the segment index isn't valid. - * @retval VERR_DBG_INVALID_SEGMENT_OFFSET if the segment offset is beyond the - * end of the segment. - * @retval VERR_INVALID_PARAMETER if the line number flags sets undefined bits. - * - * @param hDbgMod The module handle. - * @param pszFile The file name. - * @param uLineNo The line number. - * @param iSeg The segment index. - * @param off The segment offset. - * @param piOrdinal Where to return the line number ordinal on - * success. If the interpreter doesn't do ordinals, - * this will be set to UINT32_MAX. Optional. - */ RTDECL(int) RTDbgModLineAdd(RTDBGMOD hDbgMod, const char *pszFile, uint32_t uLineNo, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t *piOrdinal) { @@ -1362,18 +1687,6 @@ RTDECL(int) RTDbgModLineAdd(RTDBGMOD hDbgMod, const char *pszFile, uint32_t uLin RT_EXPORT_SYMBOL(RTDbgModLineAdd); -/** - * Gets the line number count. - * - * This can be used together wtih RTDbgModLineByOrdinal or RTDbgModSymbolByLineA - * to enumerate all the line number information. - * - * @returns The number of line numbers in the module. - * UINT32_MAX is returned if the module handle is invalid or some other - * error occurs. - * - * @param hDbgMod The module handle. - */ RTDECL(uint32_t) RTDbgModLineCount(RTDBGMOD hDbgMod) { PRTDBGMODINT pDbgMod = hDbgMod; @@ -1388,23 +1701,6 @@ RTDECL(uint32_t) RTDbgModLineCount(RTDBGMOD hDbgMod) RT_EXPORT_SYMBOL(RTDbgModLineCount); -/** - * Queries line number information by ordinal number. - * - * This can be used to enumerate the line numbers for the module. Use - * RTDbgModLineCount() to figure the end of the ordinals. - * - * @returns IPRT status code. - * @retval VERR_DBG_NO_LINE_NUMBERS if there aren't any line numbers. - * @retval VERR_DBG_LINE_NOT_FOUND if there is no line number with that - * ordinal. - * @retval VERR_INVALID_HANDLE if hDbgMod is invalid. - - * @param hDbgMod The module handle. - * @param iOrdinal The line number ordinal number. - * @param pLineInfo Where to store the information about the line - * number. - */ RTDECL(int) RTDbgModLineByOrdinal(RTDBGMOD hDbgMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo) { PRTDBGMODINT pDbgMod = hDbgMod; @@ -1419,25 +1715,6 @@ RTDECL(int) RTDbgModLineByOrdinal(RTDBGMOD hDbgMod, uint32_t iOrdinal, PRTDBGLIN RT_EXPORT_SYMBOL(RTDbgModLineByOrdinal); -/** - * Queries line number information by ordinal number. - * - * This can be used to enumerate the line numbers for the module. Use - * RTDbgModLineCount() to figure the end of the ordinals. - * - * @returns IPRT status code. - * @retval VERR_DBG_NO_LINE_NUMBERS if there aren't any line numbers. - * @retval VERR_DBG_LINE_NOT_FOUND if there is no line number with that - * ordinal. - * @retval VERR_INVALID_HANDLE if hDbgMod is invalid. - * @retval VERR_NO_MEMORY if RTDbgLineAlloc fails. - * - * @param hDbgMod The module handle. - * @param iOrdinal The line number ordinal number. - * @param ppLineInfo Where to store the pointer to the returned line - * number information. Always set. Free with - * RTDbgLineFree. - */ RTDECL(int) RTDbgModLineByOrdinalA(RTDBGMOD hDbgMod, uint32_t iOrdinal, PRTDBGLINE *ppLineInfo) { AssertPtr(ppLineInfo); @@ -1458,31 +1735,6 @@ RTDECL(int) RTDbgModLineByOrdinalA(RTDBGMOD hDbgMod, uint32_t iOrdinal, PRTDBGLI RT_EXPORT_SYMBOL(RTDbgModLineByOrdinalA); -/** - * Queries line number information by address. - * - * The returned line number is what the debug info interpreter considers the - * one most applicable to the specified address. This usually means a line - * number with an address equal or lower than the requested. - * - * @returns IPRT status code. - * @retval VERR_DBG_NO_LINE_NUMBERS if there aren't any line numbers. - * @retval VERR_DBG_LINE_NOT_FOUND if no suitable line number was found. - * @retval VERR_INVALID_HANDLE if hDbgMod is invalid. - * @retval VERR_DBG_INVALID_RVA if an image relative address is specified and - * it's not inside any of the segments defined by the module. - * @retval VERR_DBG_INVALID_SEGMENT_INDEX if the segment index isn't valid. - * @retval VERR_DBG_INVALID_SEGMENT_OFFSET if the segment offset is beyond the - * end of the segment. - * - * @param hDbgMod The module handle. - * @param iSeg The segment number. - * @param off The offset into the segment. - * @param poffDisp Where to store the distance between the - * specified address and the returned symbol. - * Optional. - * @param pLineInfo Where to store the line number information. - */ RTDECL(int) RTDbgModLineByAddr(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTINTPTR poffDisp, PRTDBGLINE pLineInfo) { /* @@ -1516,34 +1768,6 @@ RTDECL(int) RTDbgModLineByAddr(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR off RT_EXPORT_SYMBOL(RTDbgModLineByAddr); -/** - * Queries line number information by address. - * - * The returned line number is what the debug info interpreter considers the - * one most applicable to the specified address. This usually means a line - * number with an address equal or lower than the requested. - * - * @returns IPRT status code. - * @retval VERR_DBG_NO_LINE_NUMBERS if there aren't any line numbers. - * @retval VERR_DBG_LINE_NOT_FOUND if no suitable line number was found. - * @retval VERR_INVALID_HANDLE if hDbgMod is invalid. - * @retval VERR_DBG_INVALID_RVA if an image relative address is specified and - * it's not inside any of the segments defined by the module. - * @retval VERR_DBG_INVALID_SEGMENT_INDEX if the segment index isn't valid. - * @retval VERR_DBG_INVALID_SEGMENT_OFFSET if the segment offset is beyond the - * end of the segment. - * @retval VERR_NO_MEMORY if RTDbgLineAlloc fails. - * - * @param hDbgMod The module handle. - * @param iSeg The segment number. - * @param off The offset into the segment. - * @param poffDisp Where to store the distance between the - * specified address and the returned symbol. - * Optional. - * @param ppLineInfo Where to store the pointer to the returned line - * number information. Always set. Free with - * RTDbgLineFree. - */ RTDECL(int) RTDbgModLineByAddrA(RTDBGMOD hDbgMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTINTPTR poffDisp, PRTDBGLINE *ppLineInfo) { AssertPtr(ppLineInfo); diff --git a/src/VBox/Runtime/common/dbg/dbgmodcodeview.cpp b/src/VBox/Runtime/common/dbg/dbgmodcodeview.cpp new file mode 100644 index 00000000..55d41fbd --- /dev/null +++ b/src/VBox/Runtime/common/dbg/dbgmodcodeview.cpp @@ -0,0 +1,2786 @@ +/* $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 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#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/log.h> +#include <iprt/mem.h> +#include <iprt/param.h> +#include <iprt/path.h> +#include <iprt/string.h> +#include <iprt/strcache.h> +#include "internal/dbgmod.h" +#include "internal/ldrPE.h" +#include "internal/magics.h" + + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ +/** + * CodeView Header. There are two of this, base header at the start of the debug + * information and a trailing header at the end. + */ +typedef struct RTCVHDR +{ + /** The magic ('NBxx'), see RTCVHDR_MAGIC_XXX. */ + uint32_t u32Magic; + /** + * Base header: Subsection directory offset relative to this header (start). + * Trailing header: Offset of the base header relative to the end of the file. + * + * Called lfoBase, lfaBase, lfoDirectory, lfoDir and probably other things in + * the various specs/docs available. */ + uint32_t off; +} RTCVHDR; +/** Pointer to a CodeView header. */ +typedef RTCVHDR *PRTCVHDR; + +/** @name CodeView magic values (RTCVHDR::u32Magic). + * @{ */ +/** CodeView from Visual C++ 5.0. Specified in the 2001 MSDN specs.chm file. */ +#define RTCVHDR_MAGIC_NB11 RT_MAKE_U32_FROM_U8('N', 'B', '1', '1') +/** External PDB reference (often referred to as PDB 2.0). */ +#define RTCVHDR_MAGIC_NB10 RT_MAKE_U32_FROM_U8('N', 'B', '1', '0') +/** CodeView v4.10, packed. Specified in the TIS document. */ +#define RTCVHDR_MAGIC_NB09 RT_MAKE_U32_FROM_U8('N', 'B', '0', '9') +/** CodeView v4.00 thru v4.05. Specified in the TIS document? */ +#define RTCVHDR_MAGIC_NB08 RT_MAKE_U32_FROM_U8('N', 'B', '0', '8') +/** Quick C for Windows 1.0 debug info. */ +#define RTCVHDR_MAGIC_NB07 RT_MAKE_U32_FROM_U8('N', 'B', '0', '7') +/** Emitted by ILINK indicating incremental link. Comparable to NB05? */ +#define RTCVHDR_MAGIC_NB06 RT_MAKE_U32_FROM_U8('N', 'B', '0', '6') +/** Emitted by LINK version 5.20 and later before packing. */ +#define RTCVHDR_MAGIC_NB05 RT_MAKE_U32_FROM_U8('N', 'B', '0', '5') +/** Emitted by IBM ILINK for HLL (similar to NB02 in many ways). */ +#define RTCVHDR_MAGIC_NB04 RT_MAKE_U32_FROM_U8('N', 'B', '0', '4') +/** Emitted by LINK version 5.10 (or similar OMF linkers), as shipped with + * Microsoft C v6.0 for example. More or less entirely 16-bit. */ +#define RTCVHDR_MAGIC_NB02 RT_MAKE_U32_FROM_U8('N', 'B', '0', '2') +/* No idea what NB03 might have been. */ +/** AIX debugger format according to "IBM OS/2 16/32-bit Object Module Format + * (OMF) and Linear eXecutable Module Format (LX)" revision 10 (LXOMF.PDF). */ +#define RTCVHDR_MAGIC_NB01 RT_MAKE_U32_FROM_U8('N', 'B', '0', '1') +/** Ancient CodeView format according to LXOMF.PDF. */ +#define RTCVHDR_MAGIC_NB00 RT_MAKE_U32_FROM_U8('N', 'B', '0', '0') +/** @} */ + + +/** @name CV directory headers. + * @{ */ + +/** + * Really old CV directory header used with NB00 and NB02. + * + * Uses 16-bit directory entires (RTCVDIRENT16). + */ +typedef struct RTCVDIRHDR16 +{ + /** The number of directory entries. */ + uint16_t cEntries; +} RTCVDIRHDR16; +/** Pointer to a old CV directory header. */ +typedef RTCVDIRHDR16 *PRTCVDIRHDR16; + +/** + * Simple 32-bit CV directory base header, used by NB04 (aka IBM HLL). + */ +typedef struct RTCVDIRHDR32 +{ + /** The number of bytes of this header structure. */ + uint16_t cbHdr; + /** The number of bytes per entry. */ + uint16_t cbEntry; + /** The number of directory entries. */ + uint32_t cEntries; +} RTCVDIRHDR32; +/** Pointer to a 32-bit CV directory header. */ +typedef RTCVDIRHDR32 *PRTCVDIRHDR32; + +/** + * Extended 32-bit CV directory header as specified in the TIS doc. + * The two extra fields seems to never have been assigned any official purpose. + */ +typedef struct RTCVDIRHDR32EX +{ + /** This starts the same way as the NB04 header. */ + RTCVDIRHDR32 Core; + /** Tentatively decleared as the offset to the next directory generated by + * the incremental linker. Haven't seen this used yet. */ + uint32_t offNextDir; + /** Flags, non defined apparently, so MBZ. */ + uint32_t fFlags; +} RTCVDIRHDR32EX; +/** Pointer to an extended 32-bit CV directory header. */ +typedef RTCVDIRHDR32EX *PRTCVDIRHDR32EX; + +/** @} */ + + +/** + * 16-bit CV directory entry used with NB00 and NB02. + */ +typedef struct RTCVDIRENT16 +{ + /** Subsection type (RTCVSST). */ + uint16_t uSubSectType; + /** Which module (1-based, 0xffff is special). */ + uint16_t iMod; + /** The lowe offset of this subsection relative to the base CV header. */ + uint16_t offLow; + /** The high part of the subsection offset. */ + uint16_t offHigh; + /** The size of the subsection. */ + uint16_t cb; +} RTCVDIRENT16; +AssertCompileSize(RTCVDIRENT16, 10); +/** Pointer to a 16-bit CV directory entry. */ +typedef RTCVDIRENT16 *PRTCVDIRENT16; + + +/** + * 32-bit CV directory entry used starting with NB04. + */ +typedef struct RTCVDIRENT32 +{ + /** Subsection type (RTCVSST). */ + uint16_t uSubSectType; + /** Which module (1-based, 0xffff is special). */ + uint16_t iMod; + /** The offset of this subsection relative to the base CV header. */ + uint32_t off; + /** The size of the subsection. */ + uint32_t cb; +} RTCVDIRENT32; +AssertCompileSize(RTCVDIRENT32, 12); +/** Pointer to a 32-bit CV directory entry. */ +typedef RTCVDIRENT32 *PRTCVDIRENT32; +/** Pointer to a const 32-bit CV directory entry. */ +typedef RTCVDIRENT32 const *PCRTCVDIRENT32; + + +/** + * CodeView subsection types. + */ +typedef enum RTCVSST +{ + /** @name NB00, NB02 and NB04 subsection types. + * The actual format of each subsection varies between NB04 and the others, + * and it may further vary in NB04 depending on the module type. + * @{ */ + kCvSst_OldModule = 0x101, + kCvSst_OldPublic, + kCvSst_OldTypes, + kCvSst_OldSymbols, + kCvSst_OldSrcLines, + kCvSst_OldLibraries, + kCvSst_OldImports, + kCvSst_OldCompacted, + kCvSst_OldSrcLnSeg = 0x109, + kCvSst_OldSrcLines3 = 0x10b, + /** @} */ + + /** @name NB09, NB11 (and possibly NB05, NB06, NB07, and NB08) subsection types. + * @{ */ + kCvSst_Module = 0x120, + kCvSst_Types, + kCvSst_Public, + kCvSst_PublicSym, + kCvSst_Symbols, + kCvSst_AlignSym, + kCvSst_SrcLnSeg, + kCvSst_SrcModule, + kCvSst_Libraries, + kCvSst_GlobalSym, + kCvSst_GlobalPub, + kCvSst_GlobalTypes, + kCvSst_MPC, + kCvSst_SegMap, + kCvSst_SegName, + kCvSst_PreComp, + kCvSst_PreCompMap, + kCvSst_OffsetMap16, + kCvSst_OffsetMap32, + kCvSst_FileIndex = 0x133, + kCvSst_StaticSym + /** @} */ +} RTCVSST; +/** Pointer to a CV subsection type value. */ +typedef RTCVSST *PRTCVSST; +/** Pointer to a const CV subsection type value. */ +typedef RTCVSST const *PCRTCVSST; + + +/** + * CV4 module segment info. + */ +typedef struct RTCVMODSEGINFO32 +{ + /** The segment number. */ + uint16_t iSeg; + /** Explicit padding. */ + uint16_t u16Padding; + /** Offset into the segment. */ + uint32_t off; + /** The size of the contribution. */ + uint32_t cb; +} RTCVMODSEGINFO32; +typedef RTCVMODSEGINFO32 *PRTCVMODSEGINFO32; +typedef RTCVMODSEGINFO32 const *PCRTCVMODSEGINFO32; + + +/** + * CV4 segment map header. + */ +typedef struct RTCVSEGMAPHDR +{ + /** Number of segments descriptors in the table. */ + uint16_t cSegs; + /** Number of logical segment descriptors. */ + uint16_t cLogSegs; +} RTCVSEGMAPHDR; +/** Pointer to a CV4 segment map header. */ +typedef RTCVSEGMAPHDR *PRTCVSEGMAPHDR; +/** Pointer to a const CV4 segment map header. */ +typedef RTCVSEGMAPHDR const *PCRTCVSEGMAPHDR; + +/** + * CV4 Segment map descriptor entry. + */ +typedef struct RTCVSEGMAPDESC +{ + /** Segment flags. */ + uint16_t fFlags; + /** The overlay number. */ + uint16_t iOverlay; + /** Group index into this segment descriptor array. 0 if not relevant. + * The group descriptors are found in the second half of the table. */ + uint16_t iGroup; + /** Complicated. */ + uint16_t iFrame; + /** Offset (byte) into the kCvSst_SegName table of the segment name, or + * 0xffff. */ + uint16_t offSegName; + /** Offset (byte) into the kCvSst_SegName table of the class name, or 0xffff. */ + uint16_t offClassName; + /** Offset into the physical segment. */ + uint32_t off; + /** Size of segment. */ + uint32_t cb; +} RTCVSEGMAPDESC; +/** Pointer to a segment map descriptor entry. */ +typedef RTCVSEGMAPDESC *PRTCVSEGMAPDESC; +/** Pointer to a const segment map descriptor entry. */ +typedef RTCVSEGMAPDESC const *PCRTCVSEGMAPDESC; + +/** @name RTCVSEGMAPDESC_F_XXX - RTCVSEGMAPDESC::fFlags values. + * @{ */ +#define RTCVSEGMAPDESC_F_READ UINT16_C(0x0001) +#define RTCVSEGMAPDESC_F_WRITE UINT16_C(0x0002) +#define RTCVSEGMAPDESC_F_EXECUTE UINT16_C(0x0004) +#define RTCVSEGMAPDESC_F_32BIT UINT16_C(0x0008) +#define RTCVSEGMAPDESC_F_SEL UINT16_C(0x0100) +#define RTCVSEGMAPDESC_F_ABS UINT16_C(0x0200) +#define RTCVSEGMAPDESC_F_GROUP UINT16_C(0x1000) +#define RTCVSEGMAPDESC_F_RESERVED UINT16_C(0xecf0) +/** @} */ + +/** + * CV4 segment map subsection. + */ +typedef struct RTCVSEGMAP +{ + /** The header. */ + RTCVSEGMAPHDR Hdr; + /** Descriptor array. */ + RTCVSEGMAPDESC aDescs[1]; +} RTCVSEGMAP; +/** Pointer to a segment map subsection. */ +typedef RTCVSEGMAP *PRTCVSEGMAP; +/** Pointer to a const segment map subsection. */ +typedef RTCVSEGMAP const *PCRTCVSEGMAP; + + +/** + * Global symbol table header, used by kCvSst_GlobalSym and kCvSst_GlobalPub. + */ +typedef struct RTCVGLOBALSYMTABHDR +{ + /** The symbol hash function. */ + uint16_t uSymHash; + /** The address hash function. */ + uint16_t uAddrHash; + /** The amount of symbol information following immediately after the header. */ + uint32_t cbSymbols; + /** The amount of symbol hash tables following the symbols. */ + uint32_t cbSymHash; + /** The amount of address hash tables following the symbol hash tables. */ + uint32_t cbAddrHash; +} RTCVGLOBALSYMTABHDR; +/** Pointer to a global symbol table header. */ +typedef RTCVGLOBALSYMTABHDR *PRTCVGLOBALSYMTABHDR; +/** Pointer to a const global symbol table header. */ +typedef RTCVGLOBALSYMTABHDR const *PCRTCVGLOBALSYMTABHDR; + + +typedef enum RTCVSYMTYPE +{ + /** @name Symbols that doesn't change with compilation model or target machine. + * @{ */ + kCvSymType_Compile = 0x0001, + kCvSymType_Register, + kCvSymType_Constant, + kCvSymType_UDT, + kCvSymType_SSearch, + kCvSymType_End, + kCvSymType_Skip, + kCvSymType_CVReserve, + kCvSymType_ObjName, + kCvSymType_EndArg, + kCvSymType_CobolUDT, + kCvSymType_ManyReg, + kCvSymType_Return, + kCvSymType_EntryThis, + /** @} */ + + /** @name Symbols with 16:16 addresses. + * @{ */ + kCvSymType_BpRel16 = 0x0100, + kCvSymType_LData16, + kCvSymType_GData16, + kCvSymType_Pub16, + kCvSymType_LProc16, + kCvSymType_GProc16, + kCvSymType_Thunk16, + kCvSymType_BLock16, + kCvSymType_With16, + kCvSymType_Label16, + kCvSymType_CExModel16, + kCvSymType_VftPath16, + kCvSymType_RegRel16, + /** @} */ + + /** @name Symbols with 16:32 addresses. + * @{ */ + kCvSymType_BpRel32 = 0x0200, + kCvSymType_LData32, + kCvSymType_GData32, + kCvSymType_Pub32, + kCvSymType_LProc32, + kCvSymType_GProc32, + kCvSymType_Thunk32, + kCvSymType_Block32, + kCvSymType_With32, + kCvSymType_Label32, + kCvSymType_CExModel32, + kCvSymType_VftPath32, + kCvSymType_RegRel32, + kCvSymType_LThread32, + kCvSymType_GThread32, + /** @} */ + + /** @name Symbols for MIPS. + * @{ */ + kCvSymType_LProcMips = 0x0300, + kCvSymType_GProcMips, + /** @} */ + + /** @name Symbols for Microsoft CodeView. + * @{ */ + kCvSymType_ProcRef, + kCvSymType_DataRef, + kCvSymType_Align + /** @} */ +} RTCVSYMTYPE; +typedef RTCVSYMTYPE *PRTCVSYMTYPE; +typedef RTCVSYMTYPE const *PCRTCVSYMTYPE; + + +/** The $$SYMBOL table signature for CV4. */ +#define RTCVSYMBOLS_SIGNATURE_CV4 UINT32_C(0x00000001) + + +/** + * Directory sorting order. + */ +typedef enum RTCVDIRORDER +{ + RTCVDIRORDER_INVALID = 0, + /** Ordered by module. */ + RTCVDIRORDER_BY_MOD, + /** Ordered by module, but 0 modules at the end. */ + RTCVDIRORDER_BY_MOD_0, + /** Ordered by section, with global modules at the end. */ + RTCVDIRORDER_BY_SST_MOD +} RTCVDIRORDER; + + +/** + * 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; + /** The directory order. */ + RTCVDIRORDER enmDirOrder; + /** @} */ + + /** @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; + + /** @} */ + +} 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 DECLCALLBACK(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); \ + /*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__)); \ + /*return VERR_CV_BAD_FORMAT;*/ \ + } \ + } while (0) + + + + + +/** + * Reads CodeView information. + * + * @returns IPRT status. + * @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. + * @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; +} + + +/** + * 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; +} + + +/** + * 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. + */ +static int rtDbgModCvAddSymbol(PRTDBGMODCV pThis, uint32_t iSeg, uint64_t off, const char *pchName, + uint8_t cchName, uint32_t fFlags) +{ + const char *pszName = RTStrCacheEnterN(g_hDbgModStrCache, pchName, cchName); + if (!pszName) + return VERR_NO_STR_MEMORY; +#if 1 + Log2(("CV Sym: %04x:%08x %.*s\n", iSeg, off, cchName, pchName)); + if (iSeg == 0) + iSeg = RTDBGSEGIDX_ABS; + else if (pThis->pSegMap) + { + if (pThis->fHaveDosFrames) + { + if ( iSeg > pThis->pSegMap->Hdr.cSegs + || iSeg == 0) + { + Log(("Invalid segment index/offset %#06x:%08x for symbol %.*s\n", iSeg, off, cchName, pchName)); + return VERR_CV_BAD_FORMAT; + } + if (off <= pThis->pSegMap->aDescs[iSeg - 1].cb + pThis->pSegMap->aDescs[iSeg - 1].off) + off -= pThis->pSegMap->aDescs[iSeg - 1].off; + else + { + /* Workaround for VGABIOS where _DATA symbols like vgafont8 are + reported in the VGAROM segment. */ + uint64_t uAddrSym = off + ((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, off, j + 1, uAddrSym - uAddrFirst)); + off = uAddrSym - uAddrFirst; + iSeg = j + 1; + break; + } + } + if (j == UINT16_MAX) + { + Log(("Invalid segment index/offset %#06x:%08x for symbol %.*s [2]\n", iSeg, off, cchName, pchName)); + return VERR_CV_BAD_FORMAT; + } + } + } + else + { + if ( iSeg > pThis->pSegMap->Hdr.cSegs + || iSeg == 0 + || off > pThis->pSegMap->aDescs[iSeg - 1].cb) + { + Log(("Invalid segment index/offset %#06x:%08x for symbol %.*s\n", iSeg, off, cchName, pchName)); + return VERR_CV_BAD_FORMAT; + } + off += 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; + } + + int rc = RTDbgModSymbolAdd(pThis->hCnt, pszName, iSeg, off, 0, 0 /*fFlags*/, 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; + RTStrCacheRelease(g_hDbgModStrCache, pszName); + return rc; +#else + Log(("Symbol: %04x:%08x %.*s\n", iSeg, off, cchName, pchName)); + return VINF_SUCCESS; +#endif +} + + +/** + * Parses a CV4 symbol table, adding symbols to the container. + * + * @returns IPRT status code + * @param pThis The CodeView debug info reader instance. + * @param pbSymTab The symbol table. + * @param cbSymTab The size of the symbol table. + * @param fFlags Flags reserved for future exploits, MBZ. + */ +static int rtDbgModCvSsProcessV4SymTab(PRTDBGMODCV pThis, void const *pvSymTab, size_t cbSymTab, uint32_t fFlags) +{ + int rc = VINF_SUCCESS; + RTCPTRUNION uCursor; + uCursor.pv = pvSymTab; + + 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\n", pbRecStart - (uint8_t *)pvSymTab, uSymType, cbRec)); + 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); + 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); + break; + } + + /** @todo add GProc and LProc so we can gather sizes as well as just symbols. */ + } + } + /*else: shorter records can be used for alignment, I guess. */ + + /* next */ + uCursor.pu8 = pbRecStart + cbRec + 2; + cbSymTab -= cbRec + 2; + } + 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; + + /* + * 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 rtDbgModCvSsProcessV4SymTab(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) +{ + RTCPTRUNION uCursor; + uCursor.pv = pvSubSect; + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSubSect >= 2 + 2 + 2 + 2 + 0 + 1); + uint16_t iOverlay = *uCursor.pu16++; + uint16_t iLib = *uCursor.pu16++; + uint16_t cSegs = *uCursor.pu16++; + pThis->uCurStyle = *uCursor.pu16++; + if (pThis->uCurStyle == 0) + pThis->uCurStyle = RT_MAKE_U16('C', 'V'); + pThis->uCurStyleVer = 0; + uint8_t cchName = uCursor.pu8[cSegs * 12]; + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSubSect >= 2 + 2 + 2 + 2 + cSegs * 12U + 1 + cchName); + + 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)); + RTDBGMODCV_CHECK_NOMSG_RET_BF(pThis->uCurStyle == RT_MAKE_U16('C', 'V')); + + 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)); + + 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) +{ + 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, + ("%#x, expected %#x\n", u32Signature, RTCVSYMBOLS_SIGNATURE_CV4)); + + return rtDbgModCvSsProcessV4SymTab(pThis, (uint8_t const *)pvSubSect + 4, cbSubSect - 4, 0); +} + + +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)); + uint16_t idx = i < pHdr->cLogSegs ? i : i - 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", + idx, 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); + Log(("CV: %#010x LB %#010x %s uRVA=%#010x iBest=%u cOverlaps=%u\n", + uBestAddr, paDescs[iBest].cb, szName, uBestAddr - uImageBase, iBest, cOverlaps)); + rc = RTDbgModSegmentAdd(pThis->hCnt, uBestAddr - uImageBase, paDescs[iBest].cb, pszName, 0 /*fFlags*/, NULL); + + /* 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; + 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; +} + + +/** + * 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). + */ + 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\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\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)\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\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)) + { + /* + * Basic info validation and determining the directory ordering. + */ + bool fWatcom = 0; + uint16_t cGlobalMods = 0; + uint16_t cNormalMods = 0; + uint16_t iModLast = 0; + uint32_t const cbDbgInfo = pThis->cbDbgInfo; + uint32_t const cDirEnts = pThis->cDirEnts; + 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->iMod, 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", iModLast, pDirEnt->iMod)); + rc = VERR_CV_BAD_FORMAT; + } + iModLast = pDirEnt->iMod; + } + else if (pDirEnt->iMod < iModLast) + fWatcom = true; + cNormalMods++; + } + } + if (cGlobalMods == 0) + { + Log(("CV directory contains no global modules\n")); + rc = VERR_CV_BAD_FORMAT; + } + if (RT_SUCCESS(rc)) + { + if (fWatcom) + pThis->enmDirOrder = RTCVDIRORDER_BY_SST_MOD; + else if (pThis->paDirEnts[0].iMod == 0) + pThis->enmDirOrder = RTCVDIRORDER_BY_MOD_0; + else + pThis->enmDirOrder = RTCVDIRORDER_BY_MOD; + Log(("CV dir stats: %u total, %u normal, %u special, iModLast=%#x (%u), enmDirOrder=%d\n", + cDirEnts, cNormalMods, cGlobalMods, iModLast, iModLast, pThis->enmDirOrder)); + + + /* + * Validate the directory ordering. + */ + uint16_t i = 0; + + /* Old style with special modules up front. */ + if (pThis->enmDirOrder == RTCVDIRORDER_BY_MOD_0) + while (i < cGlobalMods) + { + if (pThis->paDirEnts[i].iMod != 0) + { + Log(("CV directory entry #%u: Expected iMod=%x instead of %x\n", i, 0, pThis->paDirEnts[i].iMod)); + rc = VERR_CV_BAD_FORMAT; + } + i++; + } + + /* 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) + { + 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++; + } + } + + } + + 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 subsection %#u %s\n", 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: + + case kCvSst_Types: + case kCvSst_Public: + case kCvSst_SrcLnSeg: + case kCvSst_SrcModule: + 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); + } + } + } + + return rc; +} + + +/* + * + * COFF Debug Info Parsing. + * COFF Debug Info Parsing. + * COFF Debug Info Parsing. + * + */ + +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; +} + + +/** + * 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) +{ + 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)); + } + 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,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]); + 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) +{ + /* + * 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 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 rtDbgModCvProbeCoff(PRTDBGMODINT pDbgMod, RTCVFILETYPE enmFileType, RTFILE hFile, + uint32_t off, uint32_t cb, const char *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 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; + + /* 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 %#RTfoff - 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); + + /* 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 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 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) || 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, + + /*.u32EndMagic = */ RTDBGMODVTDBG_MAGIC +}; + diff --git a/src/VBox/Runtime/common/dbg/dbgmodcontainer.cpp b/src/VBox/Runtime/common/dbg/dbgmodcontainer.cpp index 23325698..1e36e77b 100644 --- a/src/VBox/Runtime/common/dbg/dbgmodcontainer.cpp +++ b/src/VBox/Runtime/common/dbg/dbgmodcontainer.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -34,6 +34,10 @@ #include <iprt/avl.h> #include <iprt/err.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" @@ -134,6 +138,12 @@ typedef struct RTDBGMODCTN 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; @@ -250,7 +260,11 @@ static DECLCALLBACK(int) rtDbgModContainer_LineAdd(PRTDBGMODINT pMod, const char /* * 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; @@ -281,7 +295,11 @@ static DECLCALLBACK(int) rtDbgModContainer_LineAdd(PRTDBGMODINT pMod, const char } else rc = VERR_NO_MEMORY; +#ifdef RTDBGMODCNT_WITH_MEM_CACHE + RTMemCacheFree(pThis->hLineNumAllocator, pLine); +#else RTMemFree(pLine); +#endif return rc; } @@ -382,10 +400,18 @@ static DECLCALLBACK(int) rtDbgModContainer_SymbolAdd(PRTDBGMODINT pMod, const ch ("iSeg=%#x cSegs=%#x\n", pThis->cSegs), VERR_DBG_INVALID_SEGMENT_INDEX); AssertMsgReturn( iSeg >= RTDBGSEGIDX_SPECIAL_FIRST - || off + cb <= pThis->paSegs[iSeg].cb, + || 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. */ @@ -400,7 +426,6 @@ static DECLCALLBACK(int) rtDbgModContainer_SymbolAdd(PRTDBGMODINT pMod, const ch pSymbol->cb = cb; pSymbol->fFlags = fFlags; pSymbol->NameCore.pszString = RTStrCacheEnterN(g_hDbgModStrCache, pszSymbol, cchSymbol); - int rc; if (pSymbol->NameCore.pszString) { if (RTStrSpaceInsert(&pThis->Names, &pSymbol->NameCore)) @@ -415,7 +440,7 @@ static DECLCALLBACK(int) rtDbgModContainer_SymbolAdd(PRTDBGMODINT pMod, const ch if (piOrdinal) *piOrdinal = pThis->iNextSymbolOrdinal; pThis->iNextSymbolOrdinal++; - return VINF_SUCCESS; + return rc; } /* bail out */ @@ -480,7 +505,16 @@ static DECLCALLBACK(int) rtDbgModContainer_SegmentAdd(PRTDBGMODINT pMod, RTUINTP RTUINTPTR uCurRvaLast = uCurRva + RT_MAX(pThis->paSegs[iSeg].cb, 1) - 1; if ( uRva <= uCurRvaLast && uRvaLast >= uCurRva - && (cb != 0 || pThis->paSegs[iSeg].cb != 0)) /* HACK ALERT! Allow empty segments to share space (bios/watcom). */ + && ( /* 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, @@ -540,7 +574,9 @@ static DECLCALLBACK(RTDBGSEGIDX) rtDbgModContainer_RvaToSegOff(PRTDBGMODINT pMod PRTDBGMODCTN pThis = (PRTDBGMODCTN)pMod->pvDbgPriv; PCRTDBGMODCTNSEGMENT paSeg = pThis->paSegs; uint32_t const cSegs = pThis->cSegs; +#if 0 if (cSegs <= 7) +#endif { /* * Linear search. @@ -556,6 +592,7 @@ static DECLCALLBACK(RTDBGSEGIDX) rtDbgModContainer_RvaToSegOff(PRTDBGMODINT pMod } } } +#if 0 /** @todo binary search doesn't work if we've got empty segments in the list */ else { /* @@ -592,19 +629,36 @@ static DECLCALLBACK(RTDBGSEGIDX) rtDbgModContainer_RvaToSegOff(PRTDBGMODINT pMod } else { - /* between iSeg and iLast. */ + /* 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) { @@ -635,6 +689,13 @@ static DECLCALLBACK(int) rtDbgModContainer_Close(PRTDBGMODINT pMod) RTAvlrUIntPtrDestroy(&pThis->AbsAddrTree, rtDbgModContainer_DestroyTreeNode, NULL); 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; @@ -645,16 +706,16 @@ static DECLCALLBACK(int) rtDbgModContainer_Close(PRTDBGMODINT pMod) /** @copydoc RTDBGMODVTDBG::pfnTryOpen */ -static DECLCALLBACK(int) rtDbgModContainer_TryOpen(PRTDBGMODINT pMod) +static DECLCALLBACK(int) rtDbgModContainer_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch) { - NOREF(pMod); + NOREF(pMod); NOREF(enmArch); return VERR_INTERNAL_ERROR_5; } /** Virtual function table for the debug info container. */ -static RTDBGMODVTDBG const g_rtDbgModVtDbgContainer = +DECL_HIDDEN_CONST(RTDBGMODVTDBG) const g_rtDbgModVtDbgContainer = { /*.u32Magic = */ RTDBGMODVTDBG_MAGIC, /*.fSupports = */ 0, /* (Don't call my TryOpen, please.) */ @@ -686,6 +747,80 @@ static RTDBGMODVTDBG const g_rtDbgModVtDbgContainer = /** + * 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. @@ -712,21 +847,30 @@ int rtDbgModContainerCreate(PRTDBGMODINT pMod, RTUINTPTR cbSeg) pMod->pDbgVt = &g_rtDbgModVtDbgContainer; pMod->pvDbgPriv = pThis; - /* - * Add the initial segment. - */ - if (cbSeg) +#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)) { - int rc = rtDbgModContainer_SegmentAdd(pMod, 0, cbSeg, "default", sizeof("default") - 1, 0, NULL); - if (RT_FAILURE(rc)) - { - RTMemFree(pThis); - pMod->pDbgVt = NULL; - pMod->pvDbgPriv = NULL; + /* + * 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 } - return VINF_SUCCESS; + 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..96419771 --- /dev/null +++ b/src/VBox/Runtime/common/dbg/dbgmoddbghelp.cpp @@ -0,0 +1,502 @@ +/* $Id: dbgmoddbghelp.cpp $ */ +/** @file + * IPRT - Debug Info Reader Using DbgHelp.dll if Present. + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#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 "internal/dbgmod.h" + +#include <Windows.h> +#include <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; + + /** 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,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]); + 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((" %#018x %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 + || 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((" %#018x %05u %s [%Rrc]\n", pLineInfo->Address, pLineInfo->LineNumber, 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((" %#018x LB %#07x %s [SKIPPED - INVALID ADDRESS!]\n", pSymInfo->Address, cbSymbol, pSymInfo->Name)); + return TRUE; + } + if (pSymInfo->NameLen >= RTDBG_SYMBOL_NAME_LENGTH) + { + Log((" %#018x 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((" %#018x 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; + + 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]); + + if (!pSeg->RVA) + pArgs->uModAddr = pSeg->LinkAddress; + + RTLDRADDR cb = RT_MAX(pSeg->cb, pSeg->cbMapped); + return RTDbgModSegmentAdd(pArgs->hCnt, pSeg->RVA, 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()); + 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()); + 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, + + /*.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..1e2893e0 --- /dev/null +++ b/src/VBox/Runtime/common/dbg/dbgmoddeferred.cpp @@ -0,0 +1,642 @@ +/* $Id: dbgmoddeferred.cpp $ */ +/** @file + * IPRT - Debug Module Deferred Loading Stub. + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include <iprt/dbg.h> +#include "internal/iprt.h" + +#include <iprt/asm.h> +#include <iprt/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); + uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs); Assert(cRefs < 8); + if (!cRefs) + { + RTDbgCfgRelease(pThis->hDbgCfg); + pThis->hDbgCfg = NIL_RTDBGCFG; + 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,pfnLineByAddr} */ +static DECLCALLBACK(int) rtDbgModDeferredDbg_LineByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, + PRTINTPTR poffDisp, PRTDBGLINE pLineInfo) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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; + return pThis->cbImage; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnRvaToSegOff} */ +static DECLCALLBACK(RTDBGSEGIDX) rtDbgModDeferredDbg_RvaToSegOff(PRTDBGMODINT pMod, RTUINTPTR uRva, PRTUINTPTR poffSeg) +{ + 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->pvImgPriv); + 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, + + /*.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,pfnGetArch} */ +static DECLCALLBACK(RTLDRARCH) rtDbgModDeferredImg_GetArch(PRTDBGMODINT pMod) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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; + return pThis->cbImage; +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnRvaToSegOffset} */ +static DECLCALLBACK(int) rtDbgModDeferredImg_RvaToSegOffset(PRTDBGMODINT pMod, RTLDRADDR uRva, + PRTDBGSEGIDX piSeg, PRTLDRADDR poffSeg) +{ + int rc = rtDbgModDeferredDoIt(pMod, false /*fForceRetry*/); + if (RT_SUCCESS(rc)) + rc = pMod->pImgVt->pfnRvaToSegOffset(pMod, uRva, piSeg, poffSeg); + return rc; +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnLinkAddressToSegOffset} */ +static DECLCALLBACK(int) rtDbgModDeferredImg_LinkAddressToSegOffset(PRTDBGMODINT pMod, RTLDRADDR LinkAddress, + PRTDBGSEGIDX piSeg, PRTLDRADDR poffSeg) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + NOREF(enmArch); + 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, + + /*.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 ppDeferred Where to return the instance data. Can be NULL. + */ +DECLHIDDEN(int) rtDbgModDeferredCreate(PRTDBGMODINT pDbgMod, PFNRTDBGMODDEFERRED pfnDeferred, RTUINTPTR cbImage, + RTDBGCFG hDbgCfg, PRTDBGMODDEFERRED *ppDeferred) +{ + AssertReturn(!pDbgMod->pDbgVt, VERR_DBG_MOD_IPE); + + PRTDBGMODDEFERRED pDeferred = (PRTDBGMODDEFERRED)RTMemAllocZ(sizeof(*pDeferred)); + if (!pDeferred) + return VERR_NO_MEMORY; + + pDeferred->cbImage = cbImage; + pDeferred->cRefs = 1 + (pDbgMod->pImgVt == NULL); + if (hDbgCfg != NIL_RTDBGCFG) + RTDbgCfgRetain(hDbgCfg); + pDeferred->hDbgCfg = hDbgCfg; + pDeferred->pfnDeferred = pfnDeferred; + + 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 index b1757877..6e11da29 100644 --- a/src/VBox/Runtime/common/dbg/dbgmoddwarf.cpp +++ b/src/VBox/Runtime/common/dbg/dbgmoddwarf.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2011 Oracle Corporation + * Copyright (C) 2011-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -38,8 +38,13 @@ #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 "internal/dbgmod.h" @@ -136,6 +141,8 @@ #define DW_TAG_rvalue_reference_type UINT16_C(0x0042) #define DW_TAG_template_alias UINT16_C(0x0043) #define DW_TAG_lo_user UINT16_C(0x4080) +#define DW_TAG_GNU_call_site UINT16_C(0x4109) +#define DW_TAG_GNU_call_site_parameter UINT16_C(0x410a) #define DW_TAG_hi_user UINT16_C(0xffff) /** @} */ @@ -235,6 +242,8 @@ #define DW_AT_enum_class UINT16_C(0x006d) #define DW_AT_linkage_name UINT16_C(0x006e) #define DW_AT_lo_user UINT16_C(0x2000) +/** Used by GCC and others, same as DW_AT_linkage_name. See http://wiki.dwarfstd.org/index.php?title=DW_AT_linkage_name*/ +#define DW_AT_MIPS_linkage_name UINT16_C(0x2007) #define DW_AT_hi_user UINT16_C(0x3fff) /** @} */ @@ -279,6 +288,68 @@ /** @} */ +/** @name Location Expression Opcodes + * @{ */ +#define DW_OP_addr UINT8_C(0x03) /**< 1 operand, a constant address (size target specific). */ +#define DW_OP_deref UINT8_C(0x06) /**< 0 operands. */ +#define DW_OP_const1u UINT8_C(0x08) /**< 1 operand, a 1-byte constant. */ +#define DW_OP_const1s UINT8_C(0x09) /**< 1 operand, a 1-byte constant. */ +#define DW_OP_const2u UINT8_C(0x0a) /**< 1 operand, a 2-byte constant. */ +#define DW_OP_const2s UINT8_C(0x0b) /**< 1 operand, a 2-byte constant. */ +#define DW_OP_const4u UINT8_C(0x0c) /**< 1 operand, a 4-byte constant. */ +#define DW_OP_const4s UINT8_C(0x0d) /**< 1 operand, a 4-byte constant. */ +#define DW_OP_const8u UINT8_C(0x0e) /**< 1 operand, a 8-byte constant. */ +#define DW_OP_const8s UINT8_C(0x0f) /**< 1 operand, a 8-byte constant. */ +#define DW_OP_constu UINT8_C(0x10) /**< 1 operand, a ULEB128 constant. */ +#define DW_OP_consts UINT8_C(0x11) /**< 1 operand, a SLEB128 constant. */ +#define DW_OP_dup UINT8_C(0x12) /**< 0 operands. */ +#define DW_OP_drop UINT8_C(0x13) /**< 0 operands. */ +#define DW_OP_over UINT8_C(0x14) /**< 0 operands. */ +#define DW_OP_pick UINT8_C(0x15) /**< 1 operands, a 1-byte stack index. */ +#define DW_OP_swap UINT8_C(0x16) /**< 0 operands. */ +#define DW_OP_rot UINT8_C(0x17) /**< 0 operands. */ +#define DW_OP_xderef UINT8_C(0x18) /**< 0 operands. */ +#define DW_OP_abs UINT8_C(0x19) /**< 0 operands. */ +#define DW_OP_and UINT8_C(0x1a) /**< 0 operands. */ +#define DW_OP_div UINT8_C(0x1b) /**< 0 operands. */ +#define DW_OP_minus UINT8_C(0x1c) /**< 0 operands. */ +#define DW_OP_mod UINT8_C(0x1d) /**< 0 operands. */ +#define DW_OP_mul UINT8_C(0x1e) /**< 0 operands. */ +#define DW_OP_neg UINT8_C(0x1f) /**< 0 operands. */ +#define DW_OP_not UINT8_C(0x20) /**< 0 operands. */ +#define DW_OP_or UINT8_C(0x21) /**< 0 operands. */ +#define DW_OP_plus UINT8_C(0x22) /**< 0 operands. */ +#define DW_OP_plus_uconst UINT8_C(0x23) /**< 1 operands, a ULEB128 addend. */ +#define DW_OP_shl UINT8_C(0x24) /**< 0 operands. */ +#define DW_OP_shr UINT8_C(0x25) /**< 0 operands. */ +#define DW_OP_shra UINT8_C(0x26) /**< 0 operands. */ +#define DW_OP_xor UINT8_C(0x27) /**< 0 operands. */ +#define DW_OP_skip UINT8_C(0x2f) /**< 1 signed 2-byte constant. */ +#define DW_OP_bra UINT8_C(0x28) /**< 1 signed 2-byte constant. */ +#define DW_OP_eq UINT8_C(0x29) /**< 0 operands. */ +#define DW_OP_ge UINT8_C(0x2a) /**< 0 operands. */ +#define DW_OP_gt UINT8_C(0x2b) /**< 0 operands. */ +#define DW_OP_le UINT8_C(0x2c) /**< 0 operands. */ +#define DW_OP_lt UINT8_C(0x2d) /**< 0 operands. */ +#define DW_OP_ne UINT8_C(0x2e) /**< 0 operands. */ +#define DW_OP_lit0 UINT8_C(0x30) /**< 0 operands - literals 0..31 */ +#define DW_OP_lit31 UINT8_C(0x4f) /**< last litteral. */ +#define DW_OP_reg0 UINT8_C(0x50) /**< 0 operands - reg 0..31. */ +#define DW_OP_reg31 UINT8_C(0x6f) /**< last register. */ +#define DW_OP_breg0 UINT8_C(0x70) /**< 1 operand, a SLEB128 offset. */ +#define DW_OP_breg31 UINT8_C(0x8f) /**< last branch register. */ +#define DW_OP_regx UINT8_C(0x90) /**< 1 operand, a ULEB128 register. */ +#define DW_OP_fbreg UINT8_C(0x91) /**< 1 operand, a SLEB128 offset. */ +#define DW_OP_bregx UINT8_C(0x92) /**< 2 operands, a ULEB128 register followed by a SLEB128 offset. */ +#define DW_OP_piece UINT8_C(0x93) /**< 1 operand, a ULEB128 size of piece addressed. */ +#define DW_OP_deref_size UINT8_C(0x94) /**< 1 operand, a 1-byte size of data retrieved. */ +#define DW_OP_xderef_size UINT8_C(0x95) /**< 1 operand, a 1-byte size of data retrieved. */ +#define DW_OP_nop UINT8_C(0x96) /**< 0 operands. */ +#define DW_OP_lo_user UINT8_C(0xe0) /**< First user opcode */ +#define DW_OP_hi_user UINT8_C(0xff) /**< Last user opcode. */ +/** @} */ + + /******************************************************************************* * Structures and Typedefs * *******************************************************************************/ @@ -318,20 +389,38 @@ typedef enum krtDbgModDwarfSect */ typedef struct RTDWARFABBREV { - /** Whether this entry is filled in or not. */ - bool fFilled; /** Whether there are children or not. */ bool fChildren; /** 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. @@ -340,8 +429,12 @@ typedef struct RTDBGMODDWARF { /** The debug container containing doing the real work. */ RTDBGMOD hCnt; - /** Pointer to back to the debug info module (no reference ofc). */ - PRTDBGMODINT pMod; + /** 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 @@ -354,14 +447,14 @@ typedef struct RTDBGMODDWARF 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; - /** Used for range checking cache lookups. */ - uint32_t cCachedAbbrevs; /** Array of cached abbreviations, indexed by code. */ PRTDWARFABBREV paCachedAbbrevs; /** Used by rtDwarfAbbrev_Lookup when the result is uncachable. */ @@ -369,6 +462,34 @@ typedef struct RTDBGMODDWARF /** 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; @@ -424,6 +545,7 @@ typedef struct RTDWARFLINESTATE bool fEpilogueBegin; uint32_t uIsa; uint32_t uDiscriminator; + RTSEL uSegment; } Regs; /** @} */ @@ -481,11 +603,12 @@ typedef FNRTDWARFATTRDECODER *PFNRTDWARFATTRDECODER; typedef struct RTDWARFATTRDESC { /** The attribute. */ - uint8_t uAttr; - /** The data member size and initialization method. */ - uint8_t cbInit; + 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; @@ -494,8 +617,9 @@ typedef struct RTDWARFATTRDESC #define ATTR_ENTRY(a_uAttr, a_Struct, a_Member, a_Init, a_pfnDecoder) \ { \ a_uAttr, \ - a_Init | ((uint8_t)RT_SIZEOFMEMB(a_Struct, a_Member) & ATTR_SIZE_MASK), \ (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\ } @@ -541,7 +665,11 @@ typedef struct RTDWARFDIE uint8_t cDecodedAttrs; /** The number of unknown or otherwise unhandled attributes. */ uint8_t cUnhandledAttrs; - /** The date tag, indicating which union structure to use. */ +#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; @@ -571,6 +699,7 @@ typedef struct RTDWARFADDRRANGE uint8_t cAttrs : 2; uint8_t fHaveLowAddress : 1; uint8_t fHaveHighAddress : 1; + uint8_t fHaveHighIsAddress : 1; uint8_t fHaveRanges : 1; } RTDWARFADDRRANGE; typedef RTDWARFADDRRANGE *PRTDWARFADDRRANGE; @@ -602,6 +731,22 @@ 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 * @@ -614,6 +759,7 @@ static FNRTDWARFATTRDECODER rtDwarfDecode_Reference; static FNRTDWARFATTRDECODER rtDwarfDecode_SectOff; static FNRTDWARFATTRDECODER rtDwarfDecode_String; static FNRTDWARFATTRDECODER rtDwarfDecode_UnsignedInt; +static FNRTDWARFATTRDECODER rtDwarfDecode_SegmentLoc; /******************************************************************************* @@ -635,7 +781,7 @@ typedef struct RTDWARFDIECOMPILEUNIT /** The address range of the code belonging to this unit. */ RTDWARFADDRRANGE PcRange; /** The language name. */ - uint8_t uLanguage; + uint16_t uLanguage; /** The identifier case. */ uint8_t uIdentifierCase; /** String are UTF-8 encoded. If not set, the encoding is @@ -708,6 +854,10 @@ typedef struct RTDWARFDIESUBPROGRAM 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; @@ -720,16 +870,66 @@ 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. */ @@ -756,7 +956,7 @@ static const struct RTDWARFTAGDESC TAGDESC_EMPTY(), TAGDESC_CORE(TAG_imported_declaration), /* 0x08 */ TAGDESC_EMPTY(), - TAGDESC_CORE(TAG_label), + TAGDESC(TAG_label, &g_LabelDesc), TAGDESC_CORE(TAG_lexical_block), TAGDESC_EMPTY(), /* 0x0c */ TAGDESC_CORE(TAG_member), @@ -820,19 +1020,226 @@ static const struct RTDWARFTAGDESC }; +/******************************************************************************* +* 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) + +/** + * 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; +} + + +/** + * 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); + } + 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) rtDbgModHlpAddSegmentCallback(RTLDRMOD hLdrMod, PCRTLDRSEG pSeg, void *pvUser) +static DECLCALLBACK(int) rtDbgModDwarfScanSegmentsCallback(RTLDRMOD hLdrMod, PCRTLDRSEG pSeg, void *pvUser) { - PRTDBGMODINT pMod = (PRTDBGMODINT)pvUser; + PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pvUser; Log(("Segment %.*s: LinkAddress=%#llx RVA=%#llx cb=%#llx\n", - pSeg->cchName, pSeg->pchName, (uint64_t)pSeg->LinkAddress, (uint64_t)pSeg->RVA, pSeg->cb)); + 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 = RT_MAX(pSeg->cb, pSeg->cbMapped); -#if 1 - return pMod->pDbgVt->pfnSegmentAdd(pMod, pSeg->RVA, cb, pSeg->pchName, pSeg->cchName, 0 /*fFlags*/, NULL); -#else - return pMod->pDbgVt->pfnSegmentAdd(pMod, pSeg->LinkAddress, cb, pSeg->pchName, pSeg->cchName, 0 /*fFlags*/, NULL); -#endif + return RTDbgModSegmentAdd(pThis->hCnt, pSeg->RVA, cb, pSeg->pszName, 0 /*fFlags*/, NULL); } @@ -840,15 +1247,158 @@ static DECLCALLBACK(int) rtDbgModHlpAddSegmentCallback(RTLDRMOD hLdrMod, PCRTLDR * Calls pfnSegmentAdd for each segment in the executable image. * * @returns IPRT status code. - * @param pMod The debug module. + * @param pThis The DWARF instance. */ -DECLHIDDEN(int) rtDbgModHlpAddSegmentsFromImage(PRTDBGMODINT pMod) +static int rtDbgModDwarfAddSegmentsFromImage(PRTDBGMODDWARF pThis) { - AssertReturn(pMod->pImgVt, VERR_INTERNAL_ERROR_2); - return pMod->pImgVt->pfnEnumSegments(pMod, rtDbgModHlpAddSegmentCallback, pMod); + 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; +} /** @@ -887,8 +1437,11 @@ static int rtDbgModDwarfLoadSection(PRTDBGMODDWARF pThis, krtDbgModDwarfSect enm /* * Do the job. */ - return pThis->pMod->pImgVt->pfnMapPart(pThis->pMod, pThis->aSections[enmSect].offFile, pThis->aSections[enmSect].cb, - &pThis->aSections[enmSect].pv); + return pThis->pDbgInfoMod->pImgVt->pfnMapPart(pThis->pDbgInfoMod, + pThis->aSections[enmSect].iDbgInfo, + pThis->aSections[enmSect].offFile, + pThis->aSections[enmSect].cb, + &pThis->aSections[enmSect].pv); } @@ -905,7 +1458,7 @@ static int rtDbgModDwarfUnloadSection(PRTDBGMODDWARF pThis, krtDbgModDwarfSect e if (!pThis->aSections[enmSect].pv) return VINF_SUCCESS; - int rc = pThis->pMod->pImgVt->pfnUnmapPart(pThis->pMod, pThis->aSections[enmSect].cb, &pThis->aSections[enmSect].pv); + int rc = pThis->pDbgInfoMod->pImgVt->pfnUnmapPart(pThis->pDbgInfoMod, pThis->aSections[enmSect].cb, &pThis->aSections[enmSect].pv); AssertRC(rc); return rc; } @@ -934,14 +1487,28 @@ static int rtDbgModDwarfStringToUtf8(PRTDBGMODDWARF pThis, char **ppsz) * * @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, uint64_t LinkAddress, +static int rtDbgModDwarfLinkAddressToSegOffset(PRTDBGMODDWARF pThis, RTSEL uSegment, uint64_t LinkAddress, PRTDBGSEGIDX piSeg, PRTLDRADDR poffSeg) { - return pThis->pMod->pImgVt->pfnLinkAddressToSegOffset(pThis->pMod, LinkAddress, piSeg, 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); + return pThis->pImgMod->pImgVt->pfnRvaToSegOffset(pThis->pImgMod, LinkAddress, piSeg, poffSeg); } @@ -1348,6 +1915,32 @@ static uint64_t rtDwarfCursor_GetVarSizedU(PRTDWARFCURSOR pCursor, size_t cbValu } +#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. * @@ -1477,6 +2070,7 @@ static uint32_t rtDwarfCursor_CalcSectOffsetU32(PRTDWARFCURSOR pCursor) uint32_t offRet = (uint32_t)off; if (offRet != off) { + AssertFailed(); pCursor->rc = VERR_OUT_OF_RANGE; offRet = UINT32_MAX; } @@ -1644,6 +2238,39 @@ static int rtDwarfCursor_InitWithOffset(PRTDWARFCURSOR pCursor, PRTDBGMODDWARF p /** + * 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; +} + + +/** * Deletes a section reader initialized by rtDwarfCursor_Init. * * @returns @a rcOther or RTDWARCURSOR::rc. @@ -1730,22 +2357,32 @@ static int rtDwarfLine_DefineFileName(PRTDWARFLINESTATE pLnState, const char *ps */ static int rtDwarfLine_AddLine(PRTDWARFLINESTATE pLnState, uint32_t offOpCode) { - const char *pszFile = pLnState->Regs.iFile < pLnState->cFileNames - ? pLnState->papszFileNames[pLnState->Regs.iFile] - : "<bad file name index>"; - NOREF(offOpCode); - - RTDBGSEGIDX iSeg; - RTUINTPTR offSeg; - int rc = rtDbgModDwarfLinkAddressToSegOffset(pLnState->pDwarfMod, pLnState->Regs.uAddress, &iSeg, &offSeg); - if (RT_SUCCESS(rc)) + PRTDBGMODDWARF pThis = pLnState->pDwarfMod; + int rc; + if (pThis->iWatcomPass == 1) + rc = rtDbgModDwarfRecordSegOffset(pThis, pLnState->Regs.uSegment, pLnState->Regs.uAddress + 1); + else { - 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); + 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; + /* Ignore address conflicts for now. */ + if (rc == VERR_DBG_ADDRESS_CONFLICT) + rc = VINF_SUCCESS; + } + else + rc = VINF_SUCCESS; /* ignore failure */ } pLnState->Regs.fBasicBlock = false; @@ -1775,6 +2412,7 @@ static void rtDwarfLine_ResetState(PRTDWARFLINESTATE pLnState) pLnState->Regs.fEpilogueBegin = false; pLnState->Regs.uIsa = 0; pLnState->Regs.uDiscriminator = 0; + pLnState->Regs.uSegment = 0; } @@ -1811,7 +2449,7 @@ static int rtDwarfLine_RunProgram(PRTDWARFLINESTATE pLnState, PRTDWARFCURSOR pCu int32_t const cLineDelta = bOpCode % pLnState->Hdr.u8LineRange + (int32_t)pLnState->Hdr.s8LineBase; bOpCode /= pLnState->Hdr.u8LineRange; - uint64_t uTmp = bOpCode + pLnState->Regs.idxOp + bOpCode; + 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; @@ -1954,6 +2592,7 @@ static int rtDwarfLine_RunProgram(PRTDWARFLINESTATE pLnState, PRTDWARFCURSOR pCu rc = rtDwarfCursor_AdvanceToPos(pCursor, pbEndOfInstr); if (RT_SUCCESS(rc)) rc = rtDwarfLine_DefineFileName(pLnState, pszFilename, idxInc); + break; } /* @@ -1971,9 +2610,9 @@ static int rtDwarfLine_RunProgram(PRTDWARFLINESTATE pLnState, PRTDWARFCURSOR pCu else { uint64_t uSeg = rtDwarfCursor_GetVarSizedU(pCursor, cbInstr - 1, UINT64_MAX); - Log2(("%08x: DW_LNE_set_segment: %ll#x - Watcom Extension\n", offOpCode, uSeg)); - NOREF(uSeg); - /** @todo make use of this? */ + 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; @@ -2205,7 +2844,7 @@ static PCRTDWARFABBREV rtDwarfAbbrev_LookupMiss(PRTDBGMODDWARF pThis, uint32_t u bool fFillCache = true; if (pThis->cCachedAbbrevsAlloced < uCode) { - if (uCode > _64K) + if (uCode >= _64K) fFillCache = false; else { @@ -2215,8 +2854,11 @@ static PCRTDWARFABBREV rtDwarfAbbrev_LookupMiss(PRTDBGMODDWARF pThis, uint32_t u fFillCache = false; else { - pThis->cCachedAbbrevsAlloced = cNew; + 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; } } } @@ -2234,52 +2876,61 @@ static PCRTDWARFABBREV rtDwarfAbbrev_LookupMiss(PRTDBGMODDWARF pThis, uint32_t u { /* * 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'. */ - 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) + /* Read the 'header'. Skipping zero code bytes. */ + uint32_t const uCurCode = rtDwarfCursor_GetULeb128AsU32(&Cursor, 0); + if (pRet && (uCurCode == 0 || uCurCode < uPrevCode)) + break; /* probably end of unit. */ + if (uCurCode != 0) { - Cursor.rc = VERR_DWARF_BAD_ABBREV; - break; - } - - /* Cache it? */ - if (uCurCode <= pThis->cCachedAbbrevsAlloced) - { - PRTDWARFABBREV pEntry = &pThis->paCachedAbbrevs[uCurCode - 1]; - while (pThis->cCachedAbbrevs < uCurCode) + 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) { - pThis->paCachedAbbrevs[pThis->cCachedAbbrevs].fFilled = false; - pThis->cCachedAbbrevs++; + Cursor.rc = VERR_DWARF_BAD_ABBREV; + break; } - pEntry->fFilled = true; - pEntry->fChildren = RT_BOOL(uChildren); - pEntry->uTag = uCurTag; - pEntry->offSpec = rtDwarfCursor_CalcSectOffsetU32(&Cursor); - - if (uCurCode == uCode) + /* Cache it? */ + if (uCurCode <= pThis->cCachedAbbrevsAlloced) { - pRet = pEntry; - if (uCurCode == pThis->cCachedAbbrevsAlloced) - break; + 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); + + 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); + /* 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; @@ -2313,10 +2964,10 @@ static PCRTDWARFABBREV rtDwarfAbbrev_LookupMiss(PRTDBGMODDWARF pThis, uint32_t u if (uCurCode == uCode) { pRet = &pThis->LookupAbbrev; - pRet->fFilled = true; pRet->fChildren = RT_BOOL(uChildren); pRet->uTag = uCurTag; pRet->offSpec = rtDwarfCursor_CalcSectOffsetU32(&Cursor); + pRet->offAbbrev = pThis->offCachedAbbrev; break; } @@ -2347,8 +2998,8 @@ static PCRTDWARFABBREV rtDwarfAbbrev_LookupMiss(PRTDBGMODDWARF pThis, uint32_t u */ static PCRTDWARFABBREV rtDwarfAbbrev_Lookup(PRTDBGMODDWARF pThis, uint32_t uCode) { - if ( uCode - 1 >= pThis->cCachedAbbrevs - || !pThis->paCachedAbbrevs[uCode - 1].fFilled) + if ( uCode - 1 >= pThis->cCachedAbbrevsAlloced + || pThis->paCachedAbbrevs[uCode - 1].offAbbrev != pThis->offCachedAbbrev) return rtDwarfAbbrev_LookupMiss(pThis, uCode); return &pThis->paCachedAbbrevs[uCode - 1]; } @@ -2357,19 +3008,12 @@ static PCRTDWARFABBREV rtDwarfAbbrev_Lookup(PRTDBGMODDWARF pThis, uint32_t uCode /** * Sets the abbreviation offset of the current unit. * - * This will flush the cached abbreviation entries if the offset differs from - * the previous unit. - * * @param pThis The DWARF instance. * @param offAbbrev The offset into the abbreviation section. */ static void rtDwarfAbbrev_SetUnitOffset(PRTDBGMODDWARF pThis, uint32_t offAbbrev) { - if (pThis->offCachedAbbrev != offAbbrev) - { - pThis->offCachedAbbrev = offAbbrev; - pThis->cCachedAbbrevs = 0; - } + pThis->offCachedAbbrev = offAbbrev; } @@ -2408,7 +3052,7 @@ static PRTDWARFDIECOMPILEUNIT rtDwarfDie_GetCompileUnit(PRTDWARFDIE pDie) * @param pszErrValue What to return on failure (@a * pCursor->rc is set). */ -static const char *rtDwarfDecode_GetStrp(PRTDBGMODDWARF pThis, PRTDWARFCURSOR pCursor, const char *pszErrValue) +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)) @@ -2453,7 +3097,7 @@ static DECLCALLBACK(int) rtDwarfDecode_Address(PRTDWARFDIE pDie, uint8_t *pbMemb 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); + AssertMsgFailedReturn(("%#x (%s)\n", uForm, rtDwarfLog_FormName(uForm)), VERR_DWARF_UNEXPECTED_FORM); } if (RT_FAILURE(pCursor->rc)) return pCursor->rc; @@ -2461,6 +3105,7 @@ static DECLCALLBACK(int) rtDwarfDecode_Address(PRTDWARFDIE pDie, uint8_t *pbMemb PRTDWARFADDR pAddr = (PRTDWARFADDR)pbMember; pAddr->uAddress = uAddr; + Log4((" %-20s %#010llx [%s]\n", rtDwarfLog_AttrName(pDesc->uAttr), uAddr, rtDwarfLog_FormName(uForm))); return VINF_SUCCESS; } @@ -2479,7 +3124,10 @@ static DECLCALLBACK(int) rtDwarfDecode_Bool(PRTDWARFDIE pDie, uint8_t *pbMember, { 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; } @@ -2492,6 +3140,7 @@ static DECLCALLBACK(int) rtDwarfDecode_Bool(PRTDWARFDIE pDie, uint8_t *pbMember, AssertMsgFailedReturn(("%#x\n", uForm), VERR_DWARF_UNEXPECTED_FORM); } + Log4((" %-20s %RTbool [%s]\n", rtDwarfLog_AttrName(pDesc->uAttr), *pfMember, rtDwarfLog_FormName(uForm))); return VINF_SUCCESS; } @@ -2538,10 +3187,19 @@ static DECLCALLBACK(int) rtDwarfDecode_LowHighPc(PRTDWARFDIE pDie, uint8_t *pbMe return pCursor->rc = VERR_DWARF_BAD_INFO; } pRange->fHaveHighAddress = true; - pRange->uHighAddress = uAddr; + 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; } @@ -2551,16 +3209,17 @@ static DECLCALLBACK(int) rtDwarfDecode_Ranges(PRTDWARFDIE pDie, uint8_t *pbMembe 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); + 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_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); } @@ -2593,6 +3252,7 @@ static DECLCALLBACK(int) rtDwarfDecode_Ranges(PRTDWARFDIE pDie, uint8_t *pbMembe 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; } @@ -2605,7 +3265,7 @@ static DECLCALLBACK(int) rtDwarfDecode_Reference(PRTDWARFDIE pDie, uint8_t *pbMe /* Decode it. */ uint64_t off; - krtDwarfRef enmWrt = krtDwarfRef_InfoSection; + krtDwarfRef enmWrt = krtDwarfRef_SameUnit; switch (uForm) { case DW_FORM_ref1: off = rtDwarfCursor_GetU8(pCursor, 0); break; @@ -2657,6 +3317,7 @@ static DECLCALLBACK(int) rtDwarfDecode_Reference(PRTDWARFDIE pDie, uint8_t *pbMe pRef->enmWrt = enmWrt; pRef->off = off; + Log4((" %-20s %d:%#010llx [%s]\n", rtDwarfLog_AttrName(pDesc->uAttr), enmWrt, off, rtDwarfLog_FormName(uForm))); return VINF_SUCCESS; } @@ -2675,7 +3336,7 @@ static DECLCALLBACK(int) rtDwarfDecode_SectOff(PRTDWARFDIE pDie, uint8_t *pbMemb 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); + AssertMsgFailedReturn(("%#x (%s)\n", uForm, rtDwarfLog_FormName(uForm)), VERR_DWARF_UNEXPECTED_FORM); } if (RT_FAILURE(pCursor->rc)) return pCursor->rc; @@ -2687,18 +3348,24 @@ static DECLCALLBACK(int) rtDwarfDecode_SectOff(PRTDWARFDIE pDie, uint8_t *pbMemb 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\n", pDesc->uAttr), VERR_INTERNAL_ERROR_4); + default: + AssertMsgFailedReturn(("%u (%s)\n", pDesc->uAttr, rtDwarfLog_AttrName(pDesc->uAttr)), VERR_INTERNAL_ERROR_4); } - if (off >= pCursor->pDwarfMod->aSections[enmSect].cb) + size_t cbSect = pCursor->pDwarfMod->aSections[enmSect].cb; + if (off >= cbSect) { - Log(("rtDwarfDecode_SectOff: bad off=%#llx, attr %#x\n", off, pDesc->uAttr)); - return pCursor->rc = VERR_DWARF_BAD_POS; + /* 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; } @@ -2710,20 +3377,23 @@ static DECLCALLBACK(int) rtDwarfDecode_String(PRTDWARFDIE pDie, uint8_t *pbMembe AssertReturn(ATTR_GET_SIZE(pDesc) == sizeof(const char *), VERR_INTERNAL_ERROR_3); NOREF(pDie); + const char *psz; switch (uForm) { case DW_FORM_string: - *(const char **)pbMember = rtDwarfCursor_GetSZ(pCursor, NULL); + psz = rtDwarfCursor_GetSZ(pCursor, NULL); break; case DW_FORM_strp: - *(const char **)pbMember = rtDwarfDecode_GetStrp(pCursor->pDwarfMod, pCursor, NULL); + 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; } @@ -2752,25 +3422,37 @@ static DECLCALLBACK(int) rtDwarfDecode_UnsignedInt(PRTDWARFDIE pDie, uint8_t *pb 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: @@ -2780,6 +3462,256 @@ static DECLCALLBACK(int) rtDwarfDecode_UnsignedInt(PRTDWARFDIE pDie, uint8_t *pb } +/** + * 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) +{ + 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); + + RTDWARFLOCST LocSt; + int 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; + } + } + return rc; +} /* * @@ -2791,6 +3723,87 @@ static DECLCALLBACK(int) rtDwarfDecode_UnsignedInt(PRTDWARFDIE pDie, uint8_t *pb /** + * 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. @@ -2804,7 +3817,13 @@ static int rtDwarfInfo_SnoopSymbols(PRTDBGMODDWARF pThis, PRTDWARFDIE pDie) { case DW_TAG_subprogram: { - PCRTDWARFDIESUBPROGRAM pSubProgram = (PCRTDWARFDIESUBPROGRAM)pDie; + 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) @@ -2814,19 +3833,50 @@ static int rtDwarfInfo_SnoopSymbols(PRTDBGMODDWARF pThis, PRTDWARFDIE pDie) 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 + if ( ( pSubProgram->pszName || pSubProgram->pszLinkageName) && pSubProgram->PcRange.cAttrs == 2) { - RTDBGSEGIDX iSeg; - RTUINTPTR offSeg; - rc = rtDbgModDwarfLinkAddressToSegOffset(pThis, pSubProgram->PcRange.uLowAddress, - &iSeg, &offSeg); - if (RT_SUCCESS(rc)) - rc = RTDbgModSymbolAdd(pThis->hCnt, pSubProgram->pszName, iSeg, offSeg, - pSubProgram->PcRange.uHighAddress - pSubProgram->PcRange.uLowAddress, - 0 /*fFlags*/, NULL /*piOrdinal*/); + if (pThis->iWatcomPass == 1) + rc = rtDbgModDwarfRecordSegOffset(pThis, pSubProgram->uSegment, pSubProgram->PcRange.uHighAddress); else - Log5(("rtDbgModDwarfLinkAddressToSegOffset failed: %Rrc\n", rc)); + { + 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)); + } + } } } } @@ -2835,6 +3885,35 @@ static int rtDwarfInfo_SnoopSymbols(PRTDBGMODDWARF pThis, PRTDWARFDIE pDie) 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 + { + 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*/); + AssertRC(rc); + } + else + Log5(("rtDbgModDwarfLinkAddressToSegOffset failed: %Rrc\n", rc)); + } + + } + break; + } + } return rc; } @@ -2901,9 +3980,19 @@ static PRTDWARFDIE rtDwarfInfo_NewDie(PRTDBGMODDWARF pThis, PCRTDWARFDIEDESC pDi { 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; @@ -2921,6 +4010,47 @@ static PRTDWARFDIE rtDwarfInfo_NewDie(PRTDBGMODDWARF pThis, PCRTDWARFDIEDESC pDi /** + * Free all children of a DIE. + * + * @param pThis The DWARF instance. + * @param pParent 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. @@ -3026,16 +4156,21 @@ static int rtDwarfInfo_SkipDie(PRTDWARFCURSOR pCursor, PRTDWARFCURSOR pAbbrevCur * @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) + PRTDWARFCURSOR pCursor, PCRTDWARFABBREV pAbbrev, bool fInitDie) { RTDWARFCURSOR AbbrevCursor; int rc = rtDwarfCursor_InitWithOffset(&AbbrevCursor, pThis, krtDbgModDwarfSect_abbrev, pAbbrev->offSpec); if (RT_FAILURE(rc)) return rc; - rtDwarfInfo_InitDie(pDie, pDieDesc); + if (fInitDie) + rtDwarfInfo_InitDie(pDie, pDieDesc); for (;;) { uint32_t uAttr = rtDwarfCursor_GetULeb128AsU32(&AbbrevCursor, 0); @@ -3063,6 +4198,7 @@ static int rtDwarfInfo_ParseDie(PRTDBGMODDWARF pThis, PRTDWARFDIE pDie, PCRTDWAR { pDie->cUnhandledAttrs++; rc = rtDwarfInfo_SkipForm(pCursor, uForm); + Log4((" %-20s [%s]\n", rtDwarfLog_AttrName(uAttr), rtDwarfLog_FormName(uForm))); } if (RT_FAILURE(rc)) break; @@ -3073,9 +4209,9 @@ static int rtDwarfInfo_ParseDie(PRTDBGMODDWARF pThis, PRTDWARFDIE pDie, PCRTDWAR rc = pCursor->rc; /* - * Snoope up symbols on the way out. + * Snoop up symbols on the way out. */ - if (RT_SUCCESS(rc)) + if (RT_SUCCESS(rc) && fInitDie) { rc = rtDwarfInfo_SnoopSymbols(pThis, pDie); /* Ignore duplicates, get work done instead. */ @@ -3121,7 +4257,10 @@ static int rtDwarfInfo_LoadUnit(PRTDBGMODDWARF pThis, PRTDWARFCURSOR pCursor, bo * 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; @@ -3130,7 +4269,10 @@ static int rtDwarfInfo_LoadUnit(PRTDBGMODDWARF pThis, PRTDWARFCURSOR pCursor, bo */ 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; @@ -3152,7 +4294,7 @@ static int rtDwarfInfo_LoadUnit(PRTDBGMODDWARF pThis, PRTDWARFCURSOR pCursor, bo pUnit->uDwarfVer = (uint8_t)uVer; RTListAppend(&pThis->CompileUnitList, &pUnit->Core.SiblingNode); - int rc = rtDwarfInfo_ParseDie(pThis, &pUnit->Core, &g_CompileUnitDesc, pCursor, pAbbrev); + int rc = rtDwarfInfo_ParseDie(pThis, &pUnit->Core, &g_CompileUnitDesc, pCursor, pAbbrev, true /*fInitDie*/); if (RT_FAILURE(rc)) return rc; @@ -3163,28 +4305,19 @@ static int rtDwarfInfo_LoadUnit(PRTDBGMODDWARF pThis, PRTDWARFCURSOR pCursor, bo 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. */ - pParentDie = pParentDie->pParent; - if (!pParentDie) - { - if (!rtDwarfCursor_IsAtEndOfUnit(pCursor)) - return VERR_DWARF_BAD_INFO; - break; - } - cDepth--; - - /* Unlink and free child DIEs if told to do so. */ - if (!fKeepDies && pParentDie->pParent) + /* End of siblings, up one level. (Is this correct?) */ + if (pParentDie->pParent) { - PRTDWARFDIE pChild, pNextChild; - RTListForEachSafe(&pParentDie->ChildList, pChild, pNextChild, RTDWARFDIE, SiblingNode) - { - RTListNodeRemove(&pChild->SiblingNode); - RTMemFree(pChild); - } + pParentDie = pParentDie->pParent; + cDepth--; + if (!fKeepDies && pParentDie->pParent) + rtDwarfInfo_FreeChildren(pThis, pParentDie); } } else @@ -3207,10 +4340,10 @@ static int rtDwarfInfo_LoadUnit(PRTDBGMODDWARF pThis, PRTDWARFCURSOR pCursor, bo else { pszName = "<unknown>"; - pDieDesc = g_aTagDescs[0].pDesc; + pDieDesc = &g_CoreDieDesc; } - Log4((" %*stag=%s (%#x)%s\n", cDepth * 2, "", pszName, - pAbbrev->uTag, pAbbrev->fChildren ? " has children" : "")); + Log4(("%08x: %*stag=%s (%#x, abbrev %u)%s\n", offLog, cDepth * 2, "", pszName, + pAbbrev->uTag, uAbbrCode, pAbbrev->fChildren ? " has children" : "")); /* * Create a new internal DIE structure and parse the @@ -3226,12 +4359,20 @@ static int rtDwarfInfo_LoadUnit(PRTDBGMODDWARF pThis, PRTDWARFCURSOR pCursor, bo cDepth++; } - rc = rtDwarfInfo_ParseDie(pThis, pNewDie, pDieDesc, pCursor, pAbbrev); + 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; } @@ -3248,14 +4389,15 @@ static int rtDwarfInfo_LoadAll(PRTDBGMODDWARF pThis) { RTDWARFCURSOR Cursor; int rc = rtDwarfCursor_Init(&Cursor, pThis, krtDbgModDwarfSect_info); - if (RT_FAILURE(rc)) - return rc; - - while ( !rtDwarfCursor_IsAtEnd(&Cursor) - && RT_SUCCESS(rc)) - rc = rtDwarfInfo_LoadUnit(pThis, &Cursor, false /* fKeepDies */); + if (RT_SUCCESS(rc)) + { + while ( !rtDwarfCursor_IsAtEnd(&Cursor) + && RT_SUCCESS(rc)) + rc = rtDwarfInfo_LoadUnit(pThis, &Cursor, false /* fKeepDies */); - return rtDwarfCursor_Delete(&Cursor, rc); + rc = rtDwarfCursor_Delete(&Cursor, rc); + } + return rc; } @@ -3382,7 +4524,7 @@ static DECLCALLBACK(RTUINTPTR) rtDbgModDwarf_ImageSize(PRTDBGMODINT pMod) { PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pMod->pvDbgPriv; RTUINTPTR cb1 = RTDbgModImageSize(pThis->hCnt); - RTUINTPTR cb2 = pMod->pImgVt->pfnImageSize(pMod); + RTUINTPTR cb2 = pThis->pImgMod->pImgVt->pfnImageSize(pMod); return RT_MAX(cb1, cb2); } @@ -3402,10 +4544,28 @@ static DECLCALLBACK(int) rtDbgModDwarf_Close(PRTDBGMODINT pMod) for (unsigned iSect = 0; iSect < RT_ELEMENTS(pThis->aSections); iSect++) if (pThis->aSections[iSect].pv) - pThis->pMod->pImgVt->pfnUnmapPart(pThis->pMod, pThis->aSections[iSect].cb, &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; @@ -3413,38 +4573,42 @@ static DECLCALLBACK(int) rtDbgModDwarf_Close(PRTDBGMODINT pMod) /** @callback_method_impl{FNRTLDRENUMDBG} */ -static DECLCALLBACK(int) rtDbgModDwarfEnumCallback(RTLDRMOD hLdrMod, uint32_t iDbgInfo, RTLDRDBGINFOTYPE enmType, - uint16_t iMajorVer, uint16_t iMinorVer, const char *pszPartNm, - RTFOFF offFile, RTLDRADDR LinkAddress, RTLDRADDR cb, - const char *pszExtFile, void *pvUser) +static DECLCALLBACK(int) rtDbgModDwarfEnumCallback(RTLDRMOD hLdrMod, PCRTLDRDBGINFO pDbgInfo, void *pvUser) { - NOREF(hLdrMod); NOREF(iDbgInfo); NOREF(iMajorVer); NOREF(iMinorVer); NOREF(LinkAddress); - /* * Skip stuff we can't handle. */ - if ( enmType != RTLDRDBGINFOTYPE_DWARF - || !pszPartNm - || pszExtFile) + 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(pszPartNm, ".debug_", sizeof(".debug_") - 1)) /* ELF */ - pszPartNm += sizeof(".debug_") - 1; - else if (!strncmp(pszPartNm, "__debug_", sizeof("__debug_") - 1)) /* Mach-O */ - pszPartNm += sizeof("__debug_") - 1; + 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", pszPartNm), VINF_SUCCESS /*ignore*/); + 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(pszPartNm, #a_Name)) enmSect = krtDbgModDwarfSect_ ## a_Name +#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); @@ -3461,7 +4625,7 @@ static DECLCALLBACK(int) rtDbgModDwarfEnumCallback(RTLDRMOD hLdrMod, uint32_t iD #undef ELSE_IF_STRCMP_SET else { - AssertMsgFailed(("%s\n", pszPartNm)); + AssertMsgFailed(("%s\n", pszSection)); return VINF_SUCCESS; } @@ -3469,21 +4633,66 @@ static DECLCALLBACK(int) rtDbgModDwarfEnumCallback(RTLDRMOD hLdrMod, uint32_t iD * Record the section. */ PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)pvUser; - AssertMsgReturn(!pThis->aSections[enmSect].fPresent, ("duplicate %s\n", pszPartNm), VINF_SUCCESS /*ignore*/); + AssertMsgReturn(!pThis->aSections[enmSect].fPresent, ("duplicate %s\n", pszSection), VINF_SUCCESS /*ignore*/); pThis->aSections[enmSect].fPresent = true; - pThis->aSections[enmSect].offFile = offFile; + pThis->aSections[enmSect].offFile = pDbgInfo->offFile; pThis->aSections[enmSect].pv = NULL; - pThis->aSections[enmSect].cb = (size_t)cb; - if (pThis->aSections[enmSect].cb != cb) + 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); + 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) +static DECLCALLBACK(int) rtDbgModDwarf_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch) { /* * DWARF is only supported when part of an image. @@ -3492,15 +4701,54 @@ static DECLCALLBACK(int) rtDbgModDwarf_TryOpen(PRTDBGMODINT pMod) return VERR_DBG_NO_MATCHING_INTERPRETER; /* - * Enumerate the debug info in the module, looking for DWARF bits. + * Create the module instance data. */ PRTDBGMODDWARF pThis = (PRTDBGMODDWARF)RTMemAllocZ(sizeof(*pThis)); if (!pThis) return VERR_NO_MEMORY; - pThis->pMod = pMod; + pThis->pDbgInfoMod = pMod; + pThis->pImgMod = pMod; RTListInit(&pThis->CompileUnitList); + /** @todo better fUseLinkAddress heuristics! */ + if ( (pMod->pszDbgFile && strstr(pMod->pszDbgFile, "mach_kernel")) + || (pMod->pszImgFile && strstr(pMod->pszImgFile, "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 - int rc = pMod->pImgVt->pfnEnumDbgInfo(pMod, rtDbgModDwarfEnumCallback, pThis); + /* + * 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) @@ -3514,25 +4762,35 @@ static DECLCALLBACK(int) rtDbgModDwarf_TryOpen(PRTDBGMODINT pMod) { pMod->pvDbgPriv = pThis; - rc = rtDbgModHlpAddSegmentsFromImage(pMod); + rc = rtDbgModDwarfAddSegmentsFromImage(pThis); if (RT_SUCCESS(rc)) rc = rtDwarfInfo_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 = rtDwarfLine_ExplodeAll(pThis); + } if (RT_SUCCESS(rc)) { /* * Free the cached abbreviations and unload all sections. */ - pThis->cCachedAbbrevs = pThis->cCachedAbbrevsAlloced = 0; + 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->pMod->pImgVt->pfnUnmapPart(pThis->pMod, pThis->aSections[iSect].cb, - &pThis->aSections[iSect].pv); - + pThis->pDbgInfoMod->pImgVt->pfnUnmapPart(pThis->pDbgInfoMod, pThis->aSections[iSect].cb, + &pThis->aSections[iSect].pv); + /** @todo Kill pThis->CompileUnitList and the alloc caches. */ return VINF_SUCCESS; } @@ -3544,7 +4802,18 @@ static DECLCALLBACK(int) rtDbgModDwarf_TryOpen(PRTDBGMODINT pMod) else rc = VERR_DBG_NO_MATCHING_INTERPRETER; } + RTMemFree(pThis->paCachedAbbrevs); + +#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; diff --git a/src/VBox/Runtime/common/dbg/dbgmodexports.cpp b/src/VBox/Runtime/common/dbg/dbgmodexports.cpp new file mode 100644 index 00000000..d25bff56 --- /dev/null +++ b/src/VBox/Runtime/common/dbg/dbgmodexports.cpp @@ -0,0 +1,164 @@ +/* $Id: dbgmodexports.cpp $ */ +/** @file + * IPRT - Debug Module Using Image Exports. + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#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) + { + int rc = RTDbgModSymbolAdd(pArgs->pDbgMod, pszSymbol, RTDBGSEGIDX_RVA, Value - pArgs->uImageBase, + 0 /*cb*/, 0 /* fFlags */, NULL /*piOrdinal*/); + Log(("Symbol #%05u %#018x %s [%Rrc]\n", uSymbol, Value, pszSymbol, rc)); NOREF(rc); + } + else + Log(("Symbol #%05u %#018x %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) + 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/dbgmodldr.cpp b/src/VBox/Runtime/common/dbg/dbgmodldr.cpp index 724be4ad..64ad918b 100644 --- a/src/VBox/Runtime/common/dbg/dbgmodldr.cpp +++ b/src/VBox/Runtime/common/dbg/dbgmodldr.cpp @@ -40,6 +40,7 @@ #include <iprt/path.h> #include <iprt/string.h> #include "internal/dbgmod.h" +#include "internal/ldr.h" #include "internal/magics.h" @@ -53,13 +54,36 @@ typedef struct RTDBGMODLDR { /** The loader handle. */ RTLDRMOD hLdrMod; - /** File handle for the image. */ - RTFILE hFile; } RTDBGMODLDR; /** Pointer to instance data NM map reader. */ typedef RTDBGMODLDR *PRTDBGMODLDR; + +/** @interface_method_impl{RTDBGMODVTIMG,pfnGetArch} */ +static DECLCALLBACK(RTLDRARCH) rtDbgModLdr_GetArch(PRTDBGMODINT pMod) +{ + PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv; + return RTLdrGetArch(pThis->hLdrMod); +} + + +/** @interface_method_impl{RTDBGMODVTIMG,pfnGetFormat} */ +static DECLCALLBACK(RTLDRFMT) rtDbgModLdr_GetFormat(PRTDBGMODINT pMod) +{ + PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv; + 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; + 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) { @@ -71,7 +95,7 @@ static DECLCALLBACK(int) rtDbgModLdr_UnmapPart(PRTDBGMODINT pMod, size_t cb, voi /** @interface_method_impl{RTDBGMODVTIMG,pfnMapPart} */ -static DECLCALLBACK(int) rtDbgModLdr_MapPart(PRTDBGMODINT pMod, RTFOFF off, size_t cb, void const **ppvMap) +static DECLCALLBACK(int) rtDbgModLdr_MapPart(PRTDBGMODINT pMod, uint32_t iDbgInfo, RTFOFF off, size_t cb, void const **ppvMap) { PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv; @@ -79,7 +103,7 @@ static DECLCALLBACK(int) rtDbgModLdr_MapPart(PRTDBGMODINT pMod, RTFOFF off, size if (!pvMap) return VERR_NO_MEMORY; - int rc = RTFileReadAt(pThis->hFile, off, pvMap, cb, NULL); + int rc = rtLdrReadAt(pThis->hLdrMod, pvMap, iDbgInfo, off, cb); if (RT_SUCCESS(rc)) *ppvMap = pvMap; else @@ -99,6 +123,15 @@ static DECLCALLBACK(RTUINTPTR) rtDbgModLdr_GetLoadedSize(PRTDBGMODINT pMod) } +/** @interface_method_impl{RTDBGMODVTIMG,pfnRvaToSegOffset} */ +static DECLCALLBACK(int) rtDbgModLdr_RvaToSegOffset(PRTDBGMODINT pMod, RTLDRADDR uRva, + PRTDBGSEGIDX piSeg, PRTLDRADDR poffSeg) +{ + PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv; + return RTLdrRvaToSegOffset(pThis->hLdrMod, uRva, piSeg, poffSeg); +} + + /** @interface_method_impl{RTDBGMODVTIMG,pfnLinkAddressToSegOffset} */ static DECLCALLBACK(int) rtDbgModLdr_LinkAddressToSegOffset(PRTDBGMODINT pMod, RTLDRADDR LinkAddress, PRTDBGSEGIDX piSeg, PRTLDRADDR poffSeg) @@ -109,6 +142,15 @@ static DECLCALLBACK(int) rtDbgModLdr_LinkAddressToSegOffset(PRTDBGMODINT pMod, R /** @interface_method_impl{RTDBGMODVTIMG,pfnEnumSegments} */ +static DECLCALLBACK(int) rtDbgModLdr_EnumSymbols(PRTDBGMODINT pMod, uint32_t fFlags, RTLDRADDR BaseAddress, + PFNRTLDRENUMSYMS pfnCallback, void *pvUser) +{ + PRTDBGMODLDR pThis = (PRTDBGMODLDR)pMod->pvImgPriv; + 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; @@ -133,9 +175,6 @@ static DECLCALLBACK(int) rtDbgModLdr_Close(PRTDBGMODINT pMod) int rc = RTLdrClose(pThis->hLdrMod); AssertRC(rc); pThis->hLdrMod = NIL_RTLDRMOD; - rc = RTFileClose(pThis->hFile); AssertRC(rc); - pThis->hFile = NIL_RTFILE; - RTMemFree(pThis); return VINF_SUCCESS; @@ -143,30 +182,15 @@ static DECLCALLBACK(int) rtDbgModLdr_Close(PRTDBGMODINT pMod) /** @interface_method_impl{RTDBGMODVTIMG,pfnTryOpen} */ -static DECLCALLBACK(int) rtDbgModLdr_TryOpen(PRTDBGMODINT pMod) +static DECLCALLBACK(int) rtDbgModLdr_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch) { - RTFILE hFile; - int rc = RTFileOpen(&hFile, pMod->pszImgFile, RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN); + RTLDRMOD hLdrMod; + int rc = RTLdrOpen(pMod->pszImgFile, RTLDR_O_FOR_DEBUG, enmArch, &hLdrMod); if (RT_SUCCESS(rc)) { - RTLDRMOD hLdrMod; - rc = RTLdrOpen(pMod->pszImgFile, 0 /*fFlags*/, RTLDRARCH_WHATEVER, &hLdrMod); - if (RT_SUCCESS(rc)) - { - PRTDBGMODLDR pThis = (PRTDBGMODLDR)RTMemAllocZ(sizeof(RTDBGMODLDR)); - if (pThis) - { - pThis->hLdrMod = hLdrMod; - pThis->hFile = hFile; - pMod->pvImgPriv = pThis; - return VINF_SUCCESS; - } - - rc = VERR_NO_MEMORY; + rc = rtDbgModLdrOpenFromHandle(pMod, hLdrMod); + if (RT_FAILURE(rc)) RTLdrClose(hLdrMod); - } - - RTFileClose(hFile); } return rc; } @@ -182,11 +206,35 @@ DECL_HIDDEN_CONST(RTDBGMODVTIMG) const g_rtDbgModVtImgLdr = /*.pfnClose = */ rtDbgModLdr_Close, /*.pfnEnumDbgInfo = */ rtDbgModLdr_EnumDbgInfo, /*.pfnEnumSegments = */ rtDbgModLdr_EnumSegments, + /*.pfnEnumSymbols = */ rtDbgModLdr_EnumSymbols, /*.pfnGetLoadedSize = */ rtDbgModLdr_GetLoadedSize, /*.pfnLinkAddressToSegOffset = */ rtDbgModLdr_LinkAddressToSegOffset, + /*.pfnRvaToSegOffset= */ rtDbgModLdr_RvaToSegOffset, /*.pfnMapPart = */ rtDbgModLdr_MapPart, /*.pfnUnmapPart = */ rtDbgModLdr_UnmapPart, + /*.pfnReadAt = */ rtDbgModLdr_ReadAt, + /*.pfnGetFormat = */ rtDbgModLdr_GetFormat, + /*.pfnGetArch = */ rtDbgModLdr_GetArch, /*.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->hLdrMod = hLdrMod; + pDbgMod->pvImgPriv = pThis; + return VINF_SUCCESS; +} + diff --git a/src/VBox/Runtime/common/dbg/dbgmodnm.cpp b/src/VBox/Runtime/common/dbg/dbgmodnm.cpp index 8a908fc4..9a564c36 100644 --- a/src/VBox/Runtime/common/dbg/dbgmodnm.cpp +++ b/src/VBox/Runtime/common/dbg/dbgmodnm.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -477,8 +477,10 @@ static int rtDbgModNmScanFile(PRTDBGMODNM pThis, PRTSTREAM pStrm, bool fAddSymbo /** @interface_method_impl{RTDBGMODVTDBG,pfnTryOpen} */ -static DECLCALLBACK(int) rtDbgModNm_TryOpen(PRTDBGMODINT pMod) +static DECLCALLBACK(int) rtDbgModNm_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch) { + NOREF(enmArch); + /* * Fend off images. */ |