diff options
Diffstat (limited to '')
-rw-r--r-- | src/VBox/Runtime/common/dbg/dbgmodcodeview.cpp | 3187 |
1 files changed, 3187 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/dbg/dbgmodcodeview.cpp b/src/VBox/Runtime/common/dbg/dbgmodcodeview.cpp new file mode 100644 index 00000000..544a0948 --- /dev/null +++ b/src/VBox/Runtime/common/dbg/dbgmodcodeview.cpp @@ -0,0 +1,3187 @@ +/* $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-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_DBG +#include <iprt/dbg.h> +#include "internal/iprt.h" + +#include <iprt/alloca.h> +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/file.h> +#include <iprt/latin1.h> +#include <iprt/log.h> +#include <iprt/mem.h> +#include <iprt/param.h> +#include <iprt/path.h> +#include <iprt/sort.h> +#include <iprt/string.h> +#include <iprt/strcache.h> +#include "internal/dbgmod.h" +#include "internal/magics.h" + +#include <iprt/formats/codeview.h> +#include <iprt/formats/pecoff.h> + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * File type. + */ +typedef enum RTCVFILETYPE +{ + RTCVFILETYPE_INVALID = 0, + /** Executable image. */ + RTCVFILETYPE_IMAGE, + /** A DBG-file with a IMAGE_SEPARATE_DEBUG_HEADER. */ + RTCVFILETYPE_DBG, + /** A PDB file. */ + RTCVFILETYPE_PDB, + /** Some other kind of file with CV at the end. */ + RTCVFILETYPE_OTHER_AT_END, + /** The end of the valid values. */ + RTCVFILETYPE_END, + /** Type blowup. */ + RTCVFILETYPE_32BIT_HACK = 0x7fffffff +} RTCVFILETYPE; + + +/** + * CodeView debug info reader instance. + */ +typedef struct RTDBGMODCV +{ + /** Using a container for managing the debug info. */ + RTDBGMOD hCnt; + + /** @name Codeview details + * @{ */ + /** The code view magic (used as format indicator). */ + uint32_t u32CvMagic; + /** The offset of the CV debug info in the file. */ + uint32_t offBase; + /** The size of the CV debug info. */ + uint32_t cbDbgInfo; + /** The offset of the subsection directory (relative to offBase). */ + uint32_t offDir; + /** @} */ + + /** @name COFF details. + * @{ */ + /** Offset of the COFF header. */ + uint32_t offCoffDbgInfo; + /** The size of the COFF debug info. */ + uint32_t cbCoffDbgInfo; + /** The COFF debug info header. */ + IMAGE_COFF_SYMBOLS_HEADER CoffHdr; + /** @} */ + + /** The file type. */ + RTCVFILETYPE enmType; + /** The file handle (if external). */ + RTFILE hFile; + /** Pointer to the module (no reference retained). */ + PRTDBGMODINT pMod; + + /** The image size, if we know it. This is 0 if we don't know it. */ + uint32_t cbImage; + + /** Indicates that we've loaded segments intot he container already. */ + bool fHaveLoadedSegments; + /** Alternative address translation method for DOS frames. */ + bool fHaveDosFrames; + + /** @name Codeview Parsing state. + * @{ */ + /** Number of directory entries. */ + uint32_t cDirEnts; + /** The directory (converted to 32-bit). */ + PRTCVDIRENT32 paDirEnts; + /** Current debugging style when parsing modules. */ + uint16_t uCurStyle; + /** Current debugging style version (HLL only). */ + uint16_t uCurStyleVer; + + /** The segment map (if present). */ + PRTCVSEGMAP pSegMap; + /** Segment names. */ + char *pszzSegNames; + /** The size of the segment names. */ + uint32_t cbSegNames; + + /** Size of the block pchSrcStrings points to. */ + size_t cbSrcStrings; + /** Buffer space allocated for the source string table. */ + size_t cbSrcStringsAlloc; + /** Copy of the last CV8 source string table. */ + char *pchSrcStrings; + + /** The size of the current source information table. */ + size_t cbSrcInfo; + /** Buffer space allocated for the source information table. */ + size_t cbSrcInfoAlloc; + /** Copy of the last CV8 source information table. */ + uint8_t *pbSrcInfo; + + /** @} */ + +} RTDBGMODCV; +/** Pointer to a codeview debug info reader instance. */ +typedef RTDBGMODCV *PRTDBGMODCV; +/** Pointer to a const codeview debug info reader instance. */ +typedef RTDBGMODCV *PCRTDBGMODCV; + + + +/** + * Subsection callback. + * + * @returns IPRT status code. + * @param pThis The CodeView debug info reader instance. + * @param pvSubSect Pointer to the subsection data. + * @param cbSubSect The size of the subsection data. + * @param pDirEnt The directory entry. + */ +typedef 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); \ + /*AssertFailed();*/ \ + return VERR_CV_BAD_FORMAT; \ + } \ + } while (0) + + +/** Light weight assert + return w/ fixed status code. */ +#define RTDBGMODCV_CHECK_NOMSG_RET_BF(a_Expr) \ + do { \ + if (!(a_Expr)) \ + { \ + Log(("RTDbgCv: Check failed on line %d: " #a_Expr "\n", __LINE__)); \ + /*AssertFailed();*/ \ + return VERR_CV_BAD_FORMAT; \ + } \ + } while (0) + + + + + +/** + * Reads CodeView information. + * + * @returns IPRT status code. + * @param pThis The CodeView reader instance. + * @param off The offset to start reading at, relative to the + * CodeView base header. + * @param pvBuf The buffer to read into. + * @param cb How many bytes to read. + */ +static int rtDbgModCvReadAt(PRTDBGMODCV pThis, uint32_t off, void *pvBuf, size_t cb) +{ + int rc; + if (pThis->hFile == NIL_RTFILE) + rc = pThis->pMod->pImgVt->pfnReadAt(pThis->pMod, UINT32_MAX, off + pThis->offBase, pvBuf, cb); + else + rc = RTFileReadAt(pThis->hFile, off + pThis->offBase, pvBuf, cb, NULL); + return rc; +} + + +/** + * Reads CodeView information into an allocated buffer. + * + * @returns IPRT status code. + * @param pThis The CodeView reader instance. + * @param off The offset to start reading at, relative to the + * CodeView base header. + * @param ppvBuf Where to return the allocated buffer on success. + * @param cb How many bytes to read. + */ +static int rtDbgModCvReadAtAlloc(PRTDBGMODCV pThis, uint32_t off, void **ppvBuf, size_t cb) +{ + int rc; + void *pvBuf = *ppvBuf = RTMemAlloc(cb); + if (pvBuf) + { + if (pThis->hFile == NIL_RTFILE) + rc = pThis->pMod->pImgVt->pfnReadAt(pThis->pMod, UINT32_MAX, off + pThis->offBase, pvBuf, cb); + else + rc = RTFileReadAt(pThis->hFile, off + pThis->offBase, pvBuf, cb, NULL); + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + + RTMemFree(pvBuf); + *ppvBuf = NULL; + } + else + rc = VERR_NO_MEMORY; + return rc; +} + + +#ifdef LOG_ENABLED +/** + * Gets a name string for a subsection type. + * + * @returns Section name (read only). + * @param uSubSectType The subsection type. + */ +static const char *rtDbgModCvGetSubSectionName(uint16_t uSubSectType) +{ + switch (uSubSectType) + { + case kCvSst_OldModule: return "sstOldModule"; + case kCvSst_OldPublic: return "sstOldPublic"; + case kCvSst_OldTypes: return "sstOldTypes"; + case kCvSst_OldSymbols: return "sstOldSymbols"; + case kCvSst_OldSrcLines: return "sstOldSrcLines"; + case kCvSst_OldLibraries: return "sstOldLibraries"; + case kCvSst_OldImports: return "sstOldImports"; + case kCvSst_OldCompacted: return "sstOldCompacted"; + case kCvSst_OldSrcLnSeg: return "sstOldSrcLnSeg"; + case kCvSst_OldSrcLines3: return "sstOldSrcLines3"; + + case kCvSst_Module: return "sstModule"; + case kCvSst_Types: return "sstTypes"; + case kCvSst_Public: return "sstPublic"; + case kCvSst_PublicSym: return "sstPublicSym"; + case kCvSst_Symbols: return "sstSymbols"; + case kCvSst_AlignSym: return "sstAlignSym"; + case kCvSst_SrcLnSeg: return "sstSrcLnSeg"; + case kCvSst_SrcModule: return "sstSrcModule"; + case kCvSst_Libraries: return "sstLibraries"; + case kCvSst_GlobalSym: return "sstGlobalSym"; + case kCvSst_GlobalPub: return "sstGlobalPub"; + case kCvSst_GlobalTypes: return "sstGlobalTypes"; + case kCvSst_MPC: return "sstMPC"; + case kCvSst_SegMap: return "sstSegMap"; + case kCvSst_SegName: return "sstSegName"; + case kCvSst_PreComp: return "sstPreComp"; + case kCvSst_PreCompMap: return "sstPreCompMap"; + case kCvSst_OffsetMap16: return "sstOffsetMap16"; + case kCvSst_OffsetMap32: return "sstOffsetMap32"; + case kCvSst_FileIndex: return "sstFileIndex"; + case kCvSst_StaticSym: return "sstStaticSym"; + } + static char s_sz[32]; + RTStrPrintf(s_sz, sizeof(s_sz), "Unknown%#x", uSubSectType); + return s_sz; +} +#endif /* LOG_ENABLED */ + + +#ifdef LOG_ENABLED +/** + * Gets a name string for a symbol type. + * + * @returns symbol type name (read only). + * @param enmSymType The symbol type to name. + */ +static const char *rtDbgModCvSsSymTypeName(RTCVSYMTYPE enmSymType) +{ + switch (enmSymType) + { +# define CASE_RET_STR(Name) case kCvSymType_##Name: return #Name; + CASE_RET_STR(Compile); + CASE_RET_STR(Register); + CASE_RET_STR(Constant); + CASE_RET_STR(UDT); + CASE_RET_STR(SSearch); + CASE_RET_STR(End); + CASE_RET_STR(Skip); + CASE_RET_STR(CVReserve); + CASE_RET_STR(ObjName); + CASE_RET_STR(EndArg); + CASE_RET_STR(CobolUDT); + CASE_RET_STR(ManyReg); + CASE_RET_STR(Return); + CASE_RET_STR(EntryThis); + CASE_RET_STR(BpRel16); + CASE_RET_STR(LData16); + CASE_RET_STR(GData16); + CASE_RET_STR(Pub16); + CASE_RET_STR(LProc16); + CASE_RET_STR(GProc16); + CASE_RET_STR(Thunk16); + CASE_RET_STR(BLock16); + CASE_RET_STR(With16); + CASE_RET_STR(Label16); + CASE_RET_STR(CExModel16); + CASE_RET_STR(VftPath16); + CASE_RET_STR(RegRel16); + CASE_RET_STR(BpRel32); + CASE_RET_STR(LData32); + CASE_RET_STR(GData32); + CASE_RET_STR(Pub32); + CASE_RET_STR(LProc32); + CASE_RET_STR(GProc32); + CASE_RET_STR(Thunk32); + CASE_RET_STR(Block32); + CASE_RET_STR(With32); + CASE_RET_STR(Label32); + CASE_RET_STR(CExModel32); + CASE_RET_STR(VftPath32); + CASE_RET_STR(RegRel32); + CASE_RET_STR(LThread32); + CASE_RET_STR(GThread32); + CASE_RET_STR(LProcMips); + CASE_RET_STR(GProcMips); + CASE_RET_STR(ProcRef); + CASE_RET_STR(DataRef); + CASE_RET_STR(Align); + CASE_RET_STR(LProcRef); + CASE_RET_STR(V2_Register); + CASE_RET_STR(V2_Constant); + CASE_RET_STR(V2_Udt); + CASE_RET_STR(V2_CobolUdt); + CASE_RET_STR(V2_ManyReg); + CASE_RET_STR(V2_BpRel); + CASE_RET_STR(V2_LData); + CASE_RET_STR(V2_GData); + CASE_RET_STR(V2_Pub); + CASE_RET_STR(V2_LProc); + CASE_RET_STR(V2_GProc); + CASE_RET_STR(V2_VftTable); + CASE_RET_STR(V2_RegRel); + CASE_RET_STR(V2_LThread); + CASE_RET_STR(V2_GThread); + CASE_RET_STR(V2_Unknown_1010); + CASE_RET_STR(V2_Unknown_1011); + CASE_RET_STR(V2_FrameInfo); + CASE_RET_STR(V2_Compliand); + CASE_RET_STR(V3_Compliand); + CASE_RET_STR(V3_Thunk); + CASE_RET_STR(V3_Block); + CASE_RET_STR(V3_Unknown_1104); + CASE_RET_STR(V3_Label); + CASE_RET_STR(V3_Register); + CASE_RET_STR(V3_Constant); + CASE_RET_STR(V3_Udt); + CASE_RET_STR(V3_Unknown_1109); + CASE_RET_STR(V3_Unknown_110a); + CASE_RET_STR(V3_BpRel); + CASE_RET_STR(V3_LData); + CASE_RET_STR(V3_GData); + CASE_RET_STR(V3_Pub); + CASE_RET_STR(V3_LProc); + CASE_RET_STR(V3_GProc); + CASE_RET_STR(V3_RegRel); + CASE_RET_STR(V3_LThread); + CASE_RET_STR(V3_GThread); + CASE_RET_STR(V3_Unknown_1114); + CASE_RET_STR(V3_Unknown_1115); + CASE_RET_STR(V3_MSTool); + CASE_RET_STR(V3_PubFunc1); + CASE_RET_STR(V3_PubFunc2); + CASE_RET_STR(V3_SectInfo); + CASE_RET_STR(V3_SubSectInfo); + CASE_RET_STR(V3_Entrypoint); + CASE_RET_STR(V3_Unknown_1139); + CASE_RET_STR(V3_SecuCookie); + CASE_RET_STR(V3_Unknown_113b); + CASE_RET_STR(V3_MsToolInfo); + CASE_RET_STR(V3_MsToolEnv); + CASE_RET_STR(VS2013_Local); + CASE_RET_STR(VS2013_FpOff); + CASE_RET_STR(VS2013_LProc32); + CASE_RET_STR(VS2013_GProc32); +#undef CASE_RET_STR + case kCvSymType_EndOfValues: break; + } + return "<unknown type>"; +} +#endif /* LOG_ENABLED */ + + +/** + * Adds a string to the g_hDbgModStrCache after sanitizing it. + * + * IPRT only deals with UTF-8 strings, so the string will be forced to UTF-8 + * encoding. Also, codeview generally have length prefixed + * + * @returns String cached copy of the string. + * @param pch The string to copy to the cache. + * @param cch The length of the string. RTSTR_MAX if zero + * terminated. + */ +static const char *rtDbgModCvAddSanitizedStringToCache(const char *pch, size_t cch) +{ + /* + * If the string is valid UTF-8 and or the right length, we're good. + * This is usually the case. + */ + const char *pszRet; + int rc; + if (cch != RTSTR_MAX) + rc = RTStrValidateEncodingEx(pch, cch, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH); + else + rc = RTStrValidateEncodingEx(pch, cch, 0); + if (RT_SUCCESS(rc)) + pszRet = RTStrCacheEnterN(g_hDbgModStrCache, pch, cch); + else + { + /* + * Need to sanitize the string, so make a copy of it. + */ + char *pszCopy = (char *)RTMemDupEx(pch, cch, 1); + AssertPtrReturn(pszCopy, NULL); + + /* Deal with anyembedded zero chars. */ + char *psz = RTStrEnd(pszCopy, cch); + while (psz) + { + *psz = '_'; + psz = RTStrEnd(psz, cch - (psz - pszCopy)); + } + + /* Force valid UTF-8 encoding. */ + RTStrPurgeEncoding(pszCopy); + Assert(strlen(pszCopy) == cch); + + /* Enter it into the cache and free the temp copy. */ + pszRet = RTStrCacheEnterN(g_hDbgModStrCache, pszCopy, cch); + RTMemFree(pszCopy); + } + return pszRet; +} + + +/** + * Translates a codeview segment and offset into our segment layout. + * + * @returns + * @param pThis . + * @param piSeg . + * @param poff . + */ +DECLINLINE(int) rtDbgModCvAdjustSegAndOffset(PRTDBGMODCV pThis, uint32_t *piSeg, uint64_t *poff) +{ + uint32_t iSeg = *piSeg; + if (iSeg == 0) + iSeg = RTDBGSEGIDX_ABS; + else if (pThis->pSegMap) + { + if (pThis->fHaveDosFrames) + { + if ( iSeg > pThis->pSegMap->Hdr.cSegs + || iSeg == 0) + return VERR_CV_BAD_FORMAT; + if (*poff <= pThis->pSegMap->aDescs[iSeg - 1].cb + pThis->pSegMap->aDescs[iSeg - 1].off) + *poff -= pThis->pSegMap->aDescs[iSeg - 1].off; + else + { + /* Workaround for VGABIOS where _DATA symbols like vgafont8 are + reported in the VGAROM segment. */ + uint64_t uAddrSym = *poff + ((uint32_t)pThis->pSegMap->aDescs[iSeg - 1].iFrame << 4); + uint16_t j = pThis->pSegMap->Hdr.cSegs; + while (j-- > 0) + { + uint64_t uAddrFirst = (uint64_t)pThis->pSegMap->aDescs[j].off + + ((uint32_t)pThis->pSegMap->aDescs[j].iFrame << 4); + if (uAddrSym - uAddrFirst < pThis->pSegMap->aDescs[j].cb) + { + Log(("CV addr fix: %04x:%08x -> %04x:%08x\n", iSeg, *poff, j + 1, uAddrSym - uAddrFirst)); + *poff = uAddrSym - uAddrFirst; + iSeg = j + 1; + break; + } + } + if (j == UINT16_MAX) + return VERR_CV_BAD_FORMAT; + } + } + else + { + if ( iSeg > pThis->pSegMap->Hdr.cSegs + || iSeg == 0 + || *poff > pThis->pSegMap->aDescs[iSeg - 1].cb) + return VERR_CV_BAD_FORMAT; + *poff += pThis->pSegMap->aDescs[iSeg - 1].off; + } + if (pThis->pSegMap->aDescs[iSeg - 1].fFlags & RTCVSEGMAPDESC_F_ABS) + iSeg = RTDBGSEGIDX_ABS; + else + iSeg = pThis->pSegMap->aDescs[iSeg - 1].iGroup; + } + *piSeg = iSeg; + return VINF_SUCCESS; +} + + +/** + * Adds a symbol to the container. + * + * @returns IPRT status code + * @param pThis The CodeView debug info reader instance. + * @param iSeg Segment number. + * @param off Offset into the segment + * @param pchName The symbol name (not necessarily terminated). + * @param cchName The symbol name length. + * @param fFlags Flags reserved for future exploits, MBZ. + * @param cbSym Symbol size, 0 if not available. + */ +static int rtDbgModCvAddSymbol(PRTDBGMODCV pThis, uint32_t iSeg, uint64_t off, const char *pchName, + uint32_t cchName, uint32_t fFlags, uint32_t cbSym) +{ + RT_NOREF_PV(fFlags); + const char *pszName = rtDbgModCvAddSanitizedStringToCache(pchName, cchName); + int rc; + if (pszName) + { +#if 1 + Log2(("CV Sym: %04x:%08x %.*s\n", iSeg, off, cchName, pchName)); + rc = rtDbgModCvAdjustSegAndOffset(pThis, &iSeg, &off); + if (RT_SUCCESS(rc)) + { + rc = RTDbgModSymbolAdd(pThis->hCnt, pszName, iSeg, off, cbSym, RTDBGSYMBOLADD_F_ADJUST_SIZES_ON_CONFLICT, NULL); + + /* Simple duplicate symbol mangling, just to get more details. */ + if (rc == VERR_DBG_DUPLICATE_SYMBOL && cchName < _2K) + { + char szTmpName[_2K + 96]; + memcpy(szTmpName, pszName, cchName); + szTmpName[cchName] = '_'; + for (uint32_t i = 1; i < 32; i++) + { + RTStrFormatU32(&szTmpName[cchName + 1], 80, i, 10, 0, 0, 0); + rc = RTDbgModSymbolAdd(pThis->hCnt, szTmpName, iSeg, off, cbSym, 0 /*fFlags*/, NULL); + if (rc != VERR_DBG_DUPLICATE_SYMBOL) + break; + } + + } + else if (rc == VERR_DBG_ADDRESS_CONFLICT && cbSym) + rc = RTDbgModSymbolAdd(pThis->hCnt, pszName, iSeg, off, cbSym, + RTDBGSYMBOLADD_F_REPLACE_SAME_ADDR | RTDBGSYMBOLADD_F_ADJUST_SIZES_ON_CONFLICT, NULL); + + Log(("Symbol: %04x:%08x %.*s [%Rrc]\n", iSeg, off, cchName, pchName, rc)); + if (rc == VERR_DBG_ADDRESS_CONFLICT || rc == VERR_DBG_DUPLICATE_SYMBOL) + rc = VINF_SUCCESS; + } + else + Log(("Invalid segment index/offset %#06x:%08x for symbol %.*s\n", iSeg, off, cchName, pchName)); + +#else + Log(("Symbol: %04x:%08x %.*s\n", iSeg, off, cchName, pchName)); + rc = VINF_SUCCESS; +#endif + RTStrCacheRelease(g_hDbgModStrCache, pszName); + } + else + rc = VERR_NO_STR_MEMORY; + return rc; +} + + +/** + * Validates the a zero terminated string. + * + * @returns String length if valid, UINT16_MAX if invalid. + * @param pszString The string to validate. + * @param pvRec The pointer to the record containing the string. + * @param cbRec The record length. + */ +static uint16_t rtDbgModCvValidateZeroString(const char *pszString, void const *pvRec, uint16_t cbRec) +{ + size_t offStrMember = (uintptr_t)pszString - (uintptr_t)pvRec; + AssertReturn(offStrMember < _1K, UINT16_MAX); + AssertReturn(offStrMember <= cbRec, UINT16_MAX); + cbRec -= (uint16_t)offStrMember; + + const char *pchEnd = RTStrEnd(pszString, cbRec); + AssertReturn(pchEnd, UINT16_MAX); + + int rc = RTStrValidateEncoding(pszString); + AssertRCReturn(rc, UINT16_MAX); + + return (uint16_t)(pchEnd - pszString); +} + + +/** + * Parses a CV4 symbol table, adding symbols to the container. + * + * @returns IPRT status code + * @param pThis The CodeView debug info reader instance. + * @param pvSymTab The symbol table. + * @param cbSymTab The size of the symbol table. + * @param fFlags Flags reserved for future exploits, MBZ. + */ +static int rtDbgModCvSsProcessV4PlusSymTab(PRTDBGMODCV pThis, void const *pvSymTab, size_t cbSymTab, uint32_t fFlags) +{ + int rc = VINF_SUCCESS; + RTCPTRUNION uCursor; + uCursor.pv = pvSymTab; + + RT_NOREF_PV(fFlags); + + while (cbSymTab > 0 && RT_SUCCESS(rc)) + { + uint8_t const * const pbRecStart = uCursor.pu8; + uint16_t cbRec = *uCursor.pu16++; + if (cbRec >= 2) + { + uint16_t uSymType = *uCursor.pu16++; + + Log3((" %p: uSymType=%#06x LB %#x %s\n", + pbRecStart - (uint8_t *)pvSymTab, uSymType, cbRec, rtDbgModCvSsSymTypeName((RTCVSYMTYPE)uSymType))); + RTDBGMODCV_CHECK_RET_BF(cbRec >= 2 && cbRec <= cbSymTab, ("cbRec=%#x cbSymTab=%#x\n", cbRec, cbSymTab)); + + switch (uSymType) + { + case kCvSymType_LData16: + case kCvSymType_GData16: + case kCvSymType_Pub16: + { + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec > 2 + 2+2+2+1); + uint16_t off = *uCursor.pu16++; + uint16_t iSeg = *uCursor.pu16++; + /*uint16_t iType =*/ *uCursor.pu16++; + uint8_t cchName = *uCursor.pu8++; + RTDBGMODCV_CHECK_NOMSG_RET_BF(cchName > 0); + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= 2 + 2+2+2+1 + cchName); + + rc = rtDbgModCvAddSymbol(pThis, iSeg, off, uCursor.pch, cchName, 0, 0); + break; + } + + case kCvSymType_LData32: + case kCvSymType_GData32: + case kCvSymType_Pub32: + { + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec > 2 + 4+2+2+1); + uint32_t off = *uCursor.pu32++; + uint16_t iSeg = *uCursor.pu16++; + /*uint16_t iType =*/ *uCursor.pu16++; + uint8_t cchName = *uCursor.pu8++; + RTDBGMODCV_CHECK_NOMSG_RET_BF(cchName > 0); + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= 2 + 4+2+2+1 + cchName); + + rc = rtDbgModCvAddSymbol(pThis, iSeg, off, uCursor.pch, cchName, 0, 0); + break; + } + + case kCvSymType_LProc16: + case kCvSymType_GProc16: + { + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec > 2 + 4+4+4+2+2+2+2+2+2+1+1); + /*uint32_t uParent =*/ *uCursor.pu32++; + /*uint32_t uEnd =*/ *uCursor.pu32++; + /*uint32_t uNext =*/ *uCursor.pu32++; + uint16_t cbProc = *uCursor.pu16++; + /*uint16_t offDebugStart =*/ *uCursor.pu16++; + /*uint16_t offDebugEnd =*/ *uCursor.pu16++; + uint16_t off = *uCursor.pu16++; + uint16_t iSeg = *uCursor.pu16++; + /*uint16_t iProcType =*/ *uCursor.pu16++; + /*uint8_t fbType =*/ *uCursor.pu8++; + uint8_t cchName = *uCursor.pu8++; + RTDBGMODCV_CHECK_NOMSG_RET_BF(cchName > 0); + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= 2 + 4+4+4+2+2+2+2+2+2+1+1 + cchName); + + rc = rtDbgModCvAddSymbol(pThis, iSeg, off, uCursor.pch, cchName, 0, cbProc); + break; + } + + case kCvSymType_LProc32: + case kCvSymType_GProc32: + { + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec > 2 + 4+4+4+4+4+4+4+2+2+1+1); + /*uint32_t uParent =*/ *uCursor.pu32++; + /*uint32_t uEnd =*/ *uCursor.pu32++; + /*uint32_t uNext =*/ *uCursor.pu32++; + /*uint32_t cbProc =*/ *uCursor.pu32++; + /*uint32_t offDebugStart =*/ *uCursor.pu32++; + /*uint32_t offDebugEnd =*/ *uCursor.pu32++; + uint32_t off = *uCursor.pu32++; + uint16_t iSeg = *uCursor.pu16++; + /*uint16_t iProcType =*/ *uCursor.pu16++; + /*uint8_t fbType =*/ *uCursor.pu8++; + uint8_t cchName = *uCursor.pu8++; + RTDBGMODCV_CHECK_NOMSG_RET_BF(cchName > 0); + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= 2 + 4+4+4+4+4+4+4+2+2+1+1 + cchName); + + rc = rtDbgModCvAddSymbol(pThis, iSeg, off, uCursor.pch, cchName, 0, 0); + break; + } + + case kCvSymType_V3_Label: + { + PCRTCVSYMV3LABEL pLabel = (PCRTCVSYMV3LABEL)uCursor.pv; + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= sizeof(*pLabel)); + uint16_t cchName = rtDbgModCvValidateZeroString(pLabel->szName, pLabel, cbRec); + if (cchName != UINT16_MAX && cchName > 0) + rc = rtDbgModCvAddSymbol(pThis, pLabel->iSection, pLabel->offSection, pLabel->szName, cchName, 0, 0); + else + Log3((" cchName=%#x sec:off=%#x:%#x %.*Rhxs\n", + cchName, pLabel->iSection, pLabel->offSection, cbRec, pLabel)); + break; + } + + case kCvSymType_V3_LData: + case kCvSymType_V3_GData: + case kCvSymType_V3_Pub: + { + PCRTCVSYMV3TYPEDNAME pData = (PCRTCVSYMV3TYPEDNAME)uCursor.pv; + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= sizeof(*pData)); + uint16_t cchName = rtDbgModCvValidateZeroString(pData->szName, pData, cbRec); + if (cchName != UINT16_MAX && cchName > 0) + rc = rtDbgModCvAddSymbol(pThis, pData->iSection, pData->offSection, pData->szName, cchName, 0, 0); + else + Log3((" cchName=%#x sec:off=%#x:%#x idType=%#x %.*Rhxs\n", + cchName, pData->iSection, pData->offSection, pData->idType, cbRec, pData)); + break; + } + + case kCvSymType_V3_LProc: + case kCvSymType_V3_GProc: + { + PCRTCVSYMV3PROC pProc = (PCRTCVSYMV3PROC)uCursor.pv; + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= sizeof(*pProc)); + uint16_t cchName = rtDbgModCvValidateZeroString(pProc->szName, pProc, cbRec); + if (cchName != UINT16_MAX && cchName > 0) + rc = rtDbgModCvAddSymbol(pThis, pProc->iSection, pProc->offSection, pProc->szName, cchName, + 0, pProc->cbProc); + else + Log3((" cchName=%#x sec:off=%#x:%#x LB %#x\n", + cchName, pProc->iSection, pProc->offSection, pProc->cbProc)); + break; + } + + } + } + /*else: shorter records can be used for alignment, I guess. */ + + /* next */ + uCursor.pu8 = pbRecStart + cbRec + 2; + cbSymTab -= cbRec + 2; + } + return rc; +} + + +/** + * Makes a copy of the CV8 source string table. + * + * It will be references in a subsequent source information table, and again by + * line number tables thru that. + * + * @returns IPRT status code + * @param pThis The CodeView debug info reader instance. + * @param pvSrcStrings The source string table. + * @param cbSrcStrings The size of the source strings. + * @param fFlags Flags reserved for future exploits, MBZ. + */ +static int rtDbgModCvSsProcessV8SrcStrings(PRTDBGMODCV pThis, void const *pvSrcStrings, size_t cbSrcStrings, uint32_t fFlags) +{ + RT_NOREF_PV(fFlags); + + if (pThis->cbSrcStrings) + Log(("\n!!More than one source file string table for this module!!\n\n")); + + if (cbSrcStrings >= pThis->cbSrcStringsAlloc) + { + void *pvNew = RTMemRealloc(pThis->pchSrcStrings, cbSrcStrings + 1); + AssertReturn(pvNew, VERR_NO_MEMORY); + pThis->pchSrcStrings = (char *)pvNew; + pThis->cbSrcStringsAlloc = cbSrcStrings + 1; + } + memcpy(pThis->pchSrcStrings, pvSrcStrings, cbSrcStrings); + pThis->pchSrcStrings[cbSrcStrings] = '\0'; + pThis->cbSrcStrings = cbSrcStrings; + Log2((" saved %#x bytes of CV8 source strings\n", cbSrcStrings)); + + if (LogIs3Enabled()) + { + size_t iFile = 0; + size_t off = pThis->pchSrcStrings[0] != '\0' ? 0 : 1; + while (off < cbSrcStrings) + { + size_t cch = strlen(&pThis->pchSrcStrings[off]); + Log3((" %010zx #%03zu: %s\n", off, iFile, &pThis->pchSrcStrings[off])); + off += cch + 1; + iFile++; + } + } + + return VINF_SUCCESS; +} + + +/** + * Makes a copy of the CV8 source information table. + * + * It will be references in subsequent line number tables. + * + * @returns IPRT status code + * @param pThis The CodeView debug info reader instance. + * @param pvSrcInfo The source information table. + * @param cbSrcInfo The size of the source information table (bytes). + * @param fFlags Flags reserved for future exploits, MBZ. + */ +static int rtDbgModCvSsProcessV8SrcInfo(PRTDBGMODCV pThis, void const *pvSrcInfo, size_t cbSrcInfo, uint32_t fFlags) +{ + RT_NOREF_PV(fFlags); + + if (pThis->cbSrcInfo) + Log(("\n!!More than one source file info table for this module!!\n\n")); + + if (cbSrcInfo + sizeof(RTCV8SRCINFO) > pThis->cbSrcInfoAlloc) + { + void *pvNew = RTMemRealloc(pThis->pbSrcInfo, cbSrcInfo + sizeof(RTCV8SRCINFO)); + AssertReturn(pvNew, VERR_NO_MEMORY); + pThis->pbSrcInfo = (uint8_t *)pvNew; + pThis->cbSrcInfoAlloc = cbSrcInfo + sizeof(RTCV8SRCINFO); + } + memcpy(pThis->pbSrcInfo, pvSrcInfo, cbSrcInfo); + memset(&pThis->pbSrcInfo[cbSrcInfo], 0, sizeof(RTCV8SRCINFO)); + pThis->cbSrcInfo = cbSrcInfo; + Log2((" saved %#x bytes of CV8 source file info\n", cbSrcInfo)); + return VINF_SUCCESS; +} + + +/** + * Makes a copy of the CV8 source string table. + * + * It will be references in subsequent line number tables. + * + * @returns IPRT status code + * @param pThis The CodeView debug info reader instance. + * @param pvSectLines The section source line table. + * @param cbSectLines The size of the section source line table. + * @param fFlags Flags reserved for future exploits, MBZ. + */ +static int rtDbgModCvSsProcessV8SectLines(PRTDBGMODCV pThis, void const *pvSectLines, size_t cbSectLines, uint32_t fFlags) +{ + RT_NOREF_PV(fFlags); + + /* + * Starts with header. + */ + PCRTCV8LINESHDR pHdr = (PCRTCV8LINESHDR)pvSectLines; + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSectLines >= sizeof(*pHdr)); + cbSectLines -= sizeof(*pHdr); + Log2(("RTDbgModCv: seg #%u, off %#x LB %#x \n", pHdr->iSection, pHdr->offSection, pHdr->cbSectionCovered)); + + RTCPTRUNION uCursor; + uCursor.pv = pHdr + 1; + while (cbSectLines > 0) + { + /* Source file header. */ + PCRTCV8LINESSRCMAP pSrcHdr = (PCRTCV8LINESSRCMAP)uCursor.pv; + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSectLines >= sizeof(*pSrcHdr)); + RTDBGMODCV_CHECK_NOMSG_RET_BF(pSrcHdr->cb == pSrcHdr->cLines * sizeof(RTCV8LINEPAIR) + sizeof(RTCV8LINESSRCMAP)); + RTDBGMODCV_CHECK_RET_BF(!(pSrcHdr->offSourceInfo & 3), ("offSourceInfo=%#x\n", pSrcHdr->offSourceInfo)); + if (pSrcHdr->offSourceInfo + sizeof(uint32_t) <= pThis->cbSrcInfo) + { + PCRTCV8SRCINFO pSrcInfo = (PCRTCV8SRCINFO)&pThis->pbSrcInfo[pSrcHdr->offSourceInfo]; + const char *pszName = pSrcInfo->offSourceName < pThis->cbSrcStrings + ? &pThis->pchSrcStrings[pSrcInfo->offSourceName] : "unknown.c"; + pszName = rtDbgModCvAddSanitizedStringToCache(pszName, RTSTR_MAX); + Log2(("RTDbgModCv: #%u lines, %#x bytes, %#x=%s\n", pSrcHdr->cLines, pSrcHdr->cb, pSrcInfo->offSourceName, pszName)); + + if (pszName) + { + /* Process the line/offset pairs. */ + uint32_t cLeft = pSrcHdr->cLines; + PCRTCV8LINEPAIR pPair = (PCRTCV8LINEPAIR)(pSrcHdr + 1); + while (cLeft-- > 0) + { + uint32_t idxSeg = pHdr->iSection; + uint64_t off = pPair->offSection + pHdr->offSection; + int rc = rtDbgModCvAdjustSegAndOffset(pThis, &idxSeg, &off); + if (RT_SUCCESS(rc)) + rc = RTDbgModLineAdd(pThis->hCnt, pszName, pPair->uLineNumber, idxSeg, off, NULL); + if (RT_SUCCESS(rc)) + Log3(("RTDbgModCv: %#x:%#010llx %0u\n", idxSeg, off, pPair->uLineNumber)); + else + Log(( "RTDbgModCv: %#x:%#010llx %0u - rc=%Rrc!! (org: idxSeg=%#x off=%#x)\n", + idxSeg, off, pPair->uLineNumber, rc, pHdr->iSection, pPair->offSection)); + + /* next */ + pPair++; + } + Assert((uintptr_t)pPair - (uintptr_t)pSrcHdr == pSrcHdr->cb); + } + } + else + Log(("RTDbgModCv: offSourceInfo=%#x cbSrcInfo=%#x!\n", pSrcHdr->offSourceInfo, pThis->cbSrcInfo)); + + /* next */ + cbSectLines -= pSrcHdr->cb; + uCursor.pu8 += pSrcHdr->cb; + } + + return VINF_SUCCESS; +} + + + +/** + * Parses a CV8 symbol table, adding symbols to the container. + * + * @returns IPRT status code + * @param pThis The CodeView debug info reader instance. + * @param pvSymTab The symbol table. + * @param cbSymTab The size of the symbol table. + * @param fFlags Flags reserved for future exploits, MBZ. + */ +static int rtDbgModCvSsProcessV8SymTab(PRTDBGMODCV pThis, void const *pvSymTab, size_t cbSymTab, uint32_t fFlags) +{ + size_t const cbSymTabSaved = cbSymTab; + int rc = VINF_SUCCESS; + + /* + * First pass looks for source information and source strings tables. + * Microsoft puts the 0xf3 and 0xf4 last, usually with 0xf4 first. + * + * We ASSUME one string and one info table per module! + */ + RTCPTRUNION uCursor; + uCursor.pv = pvSymTab; + for (;;) + { + RTDBGMODCV_CHECK_RET_BF(cbSymTab > sizeof(RTCV8SYMBOLSBLOCK), ("cbSymTab=%zu\n", cbSymTab)); + PCRTCV8SYMBOLSBLOCK pBlockHdr = (PCRTCV8SYMBOLSBLOCK)uCursor.pv; + Log3((" %p: pass #1 uType=%#04x LB %#x\n", (uint8_t *)pBlockHdr - (uint8_t *)pvSymTab, pBlockHdr->uType, pBlockHdr->cb)); + RTDBGMODCV_CHECK_RET_BF(pBlockHdr->cb <= cbSymTab - sizeof(RTCV8SYMBOLSBLOCK), + ("cb=%#u cbSymTab=%zu\n", pBlockHdr->cb, cbSymTab)); + + switch (pBlockHdr->uType) + { + case RTCV8SYMBLOCK_TYPE_SRC_STR: + rc = rtDbgModCvSsProcessV8SrcStrings(pThis, pBlockHdr + 1, pBlockHdr->cb, fFlags); + break; + + case RTCV8SYMBLOCK_TYPE_SRC_INFO: + rc = rtDbgModCvSsProcessV8SrcInfo(pThis, pBlockHdr + 1, pBlockHdr->cb, fFlags); + break; + + case RTCV8SYMBLOCK_TYPE_SECT_LINES: + case RTCV8SYMBLOCK_TYPE_SYMBOLS: + break; + default: + Log(("rtDbgModCvSsProcessV8SymTab: Unknown block type %#x (LB %#x)\n", pBlockHdr->uType, pBlockHdr->cb)); + break; + } + uint32_t cbAligned = RT_ALIGN_32(sizeof(*pBlockHdr) + pBlockHdr->cb, 4); + if (RT_SUCCESS(rc) && cbSymTab > cbAligned) + { + uCursor.pu8 += cbAligned; + cbSymTab -= cbAligned; + } + else + break; + } + + /* + * Log the source info now that we've gathered both it and the strings. + */ + if (LogIs3Enabled() && pThis->cbSrcInfo) + { + Log3((" Source file info table:\n")); + size_t iFile = 0; + size_t off = 0; + while (off + 4 <= pThis->cbSrcInfo) + { + PCRTCV8SRCINFO pSrcInfo = (PCRTCV8SRCINFO)&pThis->pbSrcInfo[off]; +#ifdef LOG_ENABLED + const char *pszName = pSrcInfo->offSourceName < pThis->cbSrcStrings + ? &pThis->pchSrcStrings[pSrcInfo->offSourceName] : "out-of-bounds.c!"; + if (pSrcInfo->uDigestType == RTCV8SRCINFO_DIGEST_TYPE_MD5) + Log3((" %010zx #%03zu: %RTuuid %#x=%s\n", off, iFile, &pSrcInfo->Digest.md5, pSrcInfo->offSourceName, pszName)); + else if (pSrcInfo->uDigestType == RTCV8SRCINFO_DIGEST_TYPE_NONE) + Log3((" %010zx #%03zu: <none> %#x=%s\n", off, iFile, pSrcInfo->offSourceName, pszName)); + else + Log3((" %010zx #%03zu: !%#x! %#x=%s\n", off, iFile, pSrcInfo->uDigestType, pSrcInfo->offSourceName, pszName)); +#endif + off += pSrcInfo->uDigestType == RTCV8SRCINFO_DIGEST_TYPE_MD5 ? sizeof(*pSrcInfo) : 8; + iFile++; + } + } + + /* + * Second pass, process symbols and line numbers. + */ + uCursor.pv = pvSymTab; + cbSymTab = cbSymTabSaved; + for (;;) + { + RTDBGMODCV_CHECK_RET_BF(cbSymTab > sizeof(RTCV8SYMBOLSBLOCK), ("cbSymTab=%zu\n", cbSymTab)); + PCRTCV8SYMBOLSBLOCK pBlockHdr = (PCRTCV8SYMBOLSBLOCK)uCursor.pv; + Log3((" %p: pass #2 uType=%#04x LB %#x\n", (uint8_t *)pBlockHdr - (uint8_t *)pvSymTab, pBlockHdr->uType, pBlockHdr->cb)); + RTDBGMODCV_CHECK_RET_BF(pBlockHdr->cb <= cbSymTab - sizeof(RTCV8SYMBOLSBLOCK), + ("cb=%#u cbSymTab=%zu\n", pBlockHdr->cb, cbSymTab)); + + switch (pBlockHdr->uType) + { + case RTCV8SYMBLOCK_TYPE_SYMBOLS: + rc = rtDbgModCvSsProcessV4PlusSymTab(pThis, pBlockHdr + 1, pBlockHdr->cb, fFlags); + break; + + case RTCV8SYMBLOCK_TYPE_SECT_LINES: + rc = rtDbgModCvSsProcessV8SectLines(pThis, pBlockHdr + 1, pBlockHdr->cb, fFlags); + break; + + case RTCV8SYMBLOCK_TYPE_SRC_INFO: + case RTCV8SYMBLOCK_TYPE_SRC_STR: + break; + default: + Log(("rtDbgModCvSsProcessV8SymTab: Unknown block type %#x (LB %#x)\n", pBlockHdr->uType, pBlockHdr->cb)); + break; + } + uint32_t cbAligned = RT_ALIGN_32(sizeof(*pBlockHdr) + pBlockHdr->cb, 4); + if (RT_SUCCESS(rc) && cbSymTab > cbAligned) + { + uCursor.pu8 += cbAligned; + cbSymTab -= cbAligned; + } + else + break; + } + return rc; +} + + +/** @callback_method_impl{FNDBGMODCVSUBSECTCALLBACK, + * Parses kCvSst_GlobalPub\, kCvSst_GlobalSym and kCvSst_StaticSym subsections\, + * adding symbols it finds to the container.} */ +static DECLCALLBACK(int) +rtDbgModCvSs_GlobalPub_GlobalSym_StaticSym(PRTDBGMODCV pThis, void const *pvSubSect, size_t cbSubSect, PCRTCVDIRENT32 pDirEnt) +{ + PCRTCVGLOBALSYMTABHDR pHdr = (PCRTCVGLOBALSYMTABHDR)pvSubSect; + RT_NOREF_PV(pDirEnt); + + /* + * Quick data validation. + */ + Log2(("RTDbgModCv: %s: uSymHash=%#x uAddrHash=%#x cbSymbols=%#x cbSymHash=%#x cbAddrHash=%#x\n", + rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType), pHdr->uSymHash, + pHdr->uAddrHash, pHdr->cbSymbols, pHdr->cbSymHash, pHdr->cbAddrHash)); + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSubSect >= sizeof(RTCVGLOBALSYMTABHDR)); + RTDBGMODCV_CHECK_NOMSG_RET_BF((uint64_t)pHdr->cbSymbols + pHdr->cbSymHash + pHdr->cbAddrHash <= cbSubSect - sizeof(*pHdr)); + RTDBGMODCV_CHECK_NOMSG_RET_BF(pHdr->uSymHash < 0x20); + RTDBGMODCV_CHECK_NOMSG_RET_BF(pHdr->uAddrHash < 0x20); + if (!pHdr->cbSymbols) + return VINF_SUCCESS; + + /* + * Parse the symbols. + */ + return rtDbgModCvSsProcessV4PlusSymTab(pThis, pHdr + 1, pHdr->cbSymbols, 0); +} + + +/** @callback_method_impl{FNDBGMODCVSUBSECTCALLBACK, + * Parses kCvSst_Module subsection\, storing the debugging style in pThis.} */ +static DECLCALLBACK(int) +rtDbgModCvSs_Module(PRTDBGMODCV pThis, void const *pvSubSect, size_t cbSubSect, PCRTCVDIRENT32 pDirEnt) +{ + RT_NOREF_PV(pDirEnt); + + RTCPTRUNION uCursor; + uCursor.pv = pvSubSect; + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSubSect >= 2 + 2 + 2 + 2 + 0 + 1); + uint16_t iOverlay = *uCursor.pu16++; NOREF(iOverlay); + uint16_t iLib = *uCursor.pu16++; NOREF(iLib); + uint16_t cSegs = *uCursor.pu16++; + pThis->uCurStyle = *uCursor.pu16++; + if (pThis->uCurStyle == 0) + pThis->uCurStyle = RT_MAKE_U16('C', 'V'); + pThis->uCurStyleVer = 0; + pThis->cbSrcInfo = 0; + pThis->cbSrcStrings = 0; + uint8_t cchName = uCursor.pu8[cSegs * 12]; + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSubSect >= 2 + 2 + 2 + 2 + cSegs * 12U + 1 + cchName); + +#ifdef LOG_ENABLED + const char *pchName = (const char *)&uCursor.pu8[cSegs * 12 + 1]; + Log2(("RTDbgModCv: Module: iOverlay=%#x iLib=%#x cSegs=%#x Style=%c%c (%#x) %.*s\n", iOverlay, iLib, cSegs, + RT_BYTE1(pThis->uCurStyle), RT_BYTE2(pThis->uCurStyle), pThis->uCurStyle, cchName, pchName)); +#endif + RTDBGMODCV_CHECK_NOMSG_RET_BF(pThis->uCurStyle == RT_MAKE_U16('C', 'V')); + +#ifdef LOG_ENABLED + PCRTCVMODSEGINFO32 paSegs = (PCRTCVMODSEGINFO32)uCursor.pv; + for (uint16_t iSeg = 0; iSeg < cSegs; iSeg++) + Log2((" #%02u: %04x:%08x LB %08x\n", iSeg, paSegs[iSeg].iSeg, paSegs[iSeg].off, paSegs[iSeg].cb)); +#endif + + return VINF_SUCCESS; +} + + +/** @callback_method_impl{FNDBGMODCVSUBSECTCALLBACK, + * Parses kCvSst_Symbols\, kCvSst_PublicSym and kCvSst_AlignSym subsections\, + * adding symbols it finds to the container.} */ +static DECLCALLBACK(int) +rtDbgModCvSs_Symbols_PublicSym_AlignSym(PRTDBGMODCV pThis, void const *pvSubSect, size_t cbSubSect, PCRTCVDIRENT32 pDirEnt) +{ + RT_NOREF_PV(pDirEnt); + RTDBGMODCV_CHECK_NOMSG_RET_BF(pThis->uCurStyle == RT_MAKE_U16('C', 'V')); + RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSubSect >= 8); + + uint32_t u32Signature = *(uint32_t const *)pvSubSect; + RTDBGMODCV_CHECK_RET_BF(u32Signature == RTCVSYMBOLS_SIGNATURE_CV4 || u32Signature == RTCVSYMBOLS_SIGNATURE_CV8, + ("%#x, expected %#x\n", u32Signature, RTCVSYMBOLS_SIGNATURE_CV4)); + if (u32Signature == RTCVSYMBOLS_SIGNATURE_CV8) + return rtDbgModCvSsProcessV8SymTab(pThis, (uint8_t const *)pvSubSect + 4, cbSubSect - 4, 0); + return rtDbgModCvSsProcessV4PlusSymTab(pThis, (uint8_t const *)pvSubSect + 4, cbSubSect - 4, 0); +} + + +/** @callback_method_impl{FNDBGMODCVSUBSECTCALLBACK, + * Parses kCvSst_SrcModule adding line numbers it finds to the container.} + */ +static DECLCALLBACK(int) +rtDbgModCvSs_SrcModule(PRTDBGMODCV pThis, void const *pvSubSect, size_t cbSubSect, PCRTCVDIRENT32 pDirEnt) +{ + RT_NOREF_PV(pDirEnt); + Log(("rtDbgModCvSs_SrcModule: uCurStyle=%#x\n%.*Rhxd\n", pThis->uCurStyle, cbSubSect, pvSubSect)); + + /* Check the header. */ + PCRTCVSRCMODULE pHdr = (PCRTCVSRCMODULE)pvSubSect; + AssertReturn(cbSubSect >= RT_UOFFSETOF(RTCVSRCMODULE, aoffSrcFiles), VERR_CV_BAD_FORMAT); + size_t cbHdr = sizeof(RTCVSRCMODULE) + + pHdr->cFiles * sizeof(uint32_t) + + pHdr->cSegs * sizeof(uint32_t) * 2 + + pHdr->cSegs * sizeof(uint16_t); + Log2(("RTDbgModCv: SrcModule: cFiles=%u cSegs=%u\n", pHdr->cFiles, pHdr->cFiles)); + RTDBGMODCV_CHECK_RET_BF(cbSubSect >= cbHdr, ("cbSubSect=%#x cbHdr=%zx\n", cbSubSect, cbHdr)); +#ifdef LOG_ENABLED + if (LogIs2Enabled()) + { + for (uint32_t i = 0; i < pHdr->cFiles; i++) + Log2(("RTDbgModCv: source file #%u: %#x\n", i, pHdr->aoffSrcFiles[i])); + PCRTCVSRCRANGE paSegRanges = (PCRTCVSRCRANGE)&pHdr->aoffSrcFiles[pHdr->cFiles]; + uint16_t const *paidxSegs = (uint16_t const *)&paSegRanges[pHdr->cSegs]; + for (uint32_t i = 0; i < pHdr->cSegs; i++) + Log2(("RTDbgModCv: seg #%u: %#010x-%#010x\n", paidxSegs[i], paSegRanges[i].offStart, paSegRanges[i].offEnd)); + } +#endif + + /* + * Work over the source files. + */ + for (uint32_t i = 0; i < pHdr->cFiles; i++) + { + uint32_t const offSrcFile = pHdr->aoffSrcFiles[i]; + RTDBGMODCV_CHECK_RET_BF(cbSubSect - RT_UOFFSETOF(RTCVSRCFILE, aoffSrcLines) >= offSrcFile, + ("cbSubSect=%#x (- %#x) aoffSrcFiles[%u]=%#x\n", + cbSubSect, RT_UOFFSETOF(RTCVSRCFILE, aoffSrcLines), i, offSrcFile)); + PCRTCVSRCFILE pSrcFile = (PCRTCVSRCFILE)((uint8_t const *)pvSubSect + offSrcFile); + size_t cbSrcFileHdr = RT_UOFFSETOF_DYN(RTCVSRCFILE, aoffSrcLines[pSrcFile->cSegs]) + + sizeof(RTCVSRCRANGE) * pSrcFile->cSegs + + sizeof(uint8_t); + RTDBGMODCV_CHECK_RET_BF(cbSubSect >= offSrcFile + cbSrcFileHdr && cbSubSect > cbSrcFileHdr, + ("cbSubSect=%#x aoffSrcFiles[%u]=%#x cbSrcFileHdr=%#x\n", cbSubSect, offSrcFile, i, cbSrcFileHdr)); + PCRTCVSRCRANGE paSegRanges = (PCRTCVSRCRANGE)&pSrcFile->aoffSrcLines[pSrcFile->cSegs]; + uint8_t const *pcchName = (uint8_t const *)&paSegRanges[pSrcFile->cSegs]; /** @todo TIS NB09 docs say 16-bit length... */ + const char *pchName = (const char *)(pcchName + 1); + RTDBGMODCV_CHECK_RET_BF(cbSubSect >= offSrcFile + cbSrcFileHdr + *pcchName, + ("cbSubSect=%#x offSrcFile=%#x cbSubSect=%#x *pcchName=%#x\n", + cbSubSect, offSrcFile, cbSubSect, *pcchName)); + Log2(("RTDbgModCv: source file #%u/%#x: cSegs=%#x '%.*s'\n", i, offSrcFile, pSrcFile->cSegs, *pcchName, pchName)); + const char *pszName = rtDbgModCvAddSanitizedStringToCache(pchName, *pcchName); + + /* + * Work the segments this source file contributes code to. + */ + for (uint32_t iSeg = 0; iSeg < pSrcFile->cSegs; iSeg++) + { + uint32_t const offSrcLine = pSrcFile->aoffSrcLines[iSeg]; + RTDBGMODCV_CHECK_RET_BF(cbSubSect - RT_UOFFSETOF(RTCVSRCLINE, aoffLines) >= offSrcLine, + ("cbSubSect=%#x (- %#x) aoffSrcFiles[%u]=%#x\n", + cbSubSect, RT_UOFFSETOF(RTCVSRCLINE, aoffLines), iSeg, offSrcLine)); + PCRTCVSRCLINE pSrcLine = (PCRTCVSRCLINE)((uint8_t const *)pvSubSect + offSrcLine); + size_t cbSrcLine = RT_UOFFSETOF_DYN(RTCVSRCLINE, aoffLines[pSrcLine->cPairs]) + + pSrcLine->cPairs * sizeof(uint16_t); + RTDBGMODCV_CHECK_RET_BF(cbSubSect >= offSrcLine + cbSrcLine, + ("cbSubSect=%#x aoffSrcFiles[%u]=%#x cbSrcLine=%#x\n", + cbSubSect, iSeg, offSrcLine, cbSrcLine)); + uint16_t const *paiLines = (uint16_t const *)&pSrcLine->aoffLines[pSrcLine->cPairs]; + Log2(("RTDbgModCv: seg #%u, %u pairs (off %#x)\n", pSrcLine->idxSeg, pSrcLine->cPairs, offSrcLine)); + for (uint32_t iPair = 0; iPair < pSrcLine->cPairs; iPair++) + { + + uint32_t idxSeg = pSrcLine->idxSeg; + uint64_t off = pSrcLine->aoffLines[iPair]; + int rc = rtDbgModCvAdjustSegAndOffset(pThis, &idxSeg, &off); + if (RT_SUCCESS(rc)) + rc = RTDbgModLineAdd(pThis->hCnt, pszName, paiLines[iPair], idxSeg, off, NULL); + if (RT_SUCCESS(rc)) + Log3(("RTDbgModCv: %#x:%#010llx %0u\n", idxSeg, off, paiLines[iPair])); + /* Note! Wlink produces the sstSrcModule subsections from LINNUM records, however the + CVGenLines() function assumes there is only one segment contributing to the + line numbers. So, when we do assembly that jumps between segments, it emits + the wrong addresses for some line numbers and we end up here, typically with + VERR_DBG_ADDRESS_CONFLICT. */ + else + Log(( "RTDbgModCv: %#x:%#010llx %0u - rc=%Rrc!! (org: idxSeg=%#x off=%#x)\n", + idxSeg, off, paiLines[iPair], rc, pSrcLine->idxSeg, pSrcLine->aoffLines[iPair])); + } + } + } + + return VINF_SUCCESS; +} + + +static int rtDbgModCvLoadSegmentMap(PRTDBGMODCV pThis) +{ + /* + * Search for the segment map and segment names. They will be at the end of the directory. + */ + uint32_t iSegMap = UINT32_MAX; + uint32_t iSegNames = UINT32_MAX; + uint32_t i = pThis->cDirEnts; + while (i-- > 0) + { + if ( pThis->paDirEnts[i].iMod != 0xffff + && pThis->paDirEnts[i].iMod != 0x0000) + break; + if (pThis->paDirEnts[i].uSubSectType == kCvSst_SegMap) + iSegMap = i; + else if (pThis->paDirEnts[i].uSubSectType == kCvSst_SegName) + iSegNames = i; + } + if (iSegMap == UINT32_MAX) + { + Log(("RTDbgModCv: No segment map present, using segment indexes as is then...\n")); + return VINF_SUCCESS; + } + RTDBGMODCV_CHECK_RET_BF(pThis->paDirEnts[iSegMap].cb >= sizeof(RTCVSEGMAPHDR), + ("Bad sstSegMap entry: cb=%#x\n", pThis->paDirEnts[iSegMap].cb)); + RTDBGMODCV_CHECK_NOMSG_RET_BF(iSegNames == UINT32_MAX || pThis->paDirEnts[iSegNames].cb > 0); + + /* + * Read them into memory. + */ + int rc = rtDbgModCvReadAtAlloc(pThis, pThis->paDirEnts[iSegMap].off, (void **)&pThis->pSegMap, + pThis->paDirEnts[iSegMap].cb); + if (iSegNames != UINT32_MAX && RT_SUCCESS(rc)) + { + pThis->cbSegNames = pThis->paDirEnts[iSegNames].cb; + rc = rtDbgModCvReadAtAlloc(pThis, pThis->paDirEnts[iSegNames].off, (void **)&pThis->pszzSegNames, + pThis->paDirEnts[iSegNames].cb); + } + if (RT_FAILURE(rc)) + return rc; + RTDBGMODCV_CHECK_NOMSG_RET_BF(!pThis->pszzSegNames || !pThis->pszzSegNames[pThis->cbSegNames - 1]); /* must be terminated */ + + /* Use local pointers to avoid lots of indirection and typing. */ + PCRTCVSEGMAPHDR pHdr = &pThis->pSegMap->Hdr; + PRTCVSEGMAPDESC paDescs = &pThis->pSegMap->aDescs[0]; + + /* + * If there are only logical segments, assume a direct mapping. + * PE images, like the NT4 kernel, does it like this. + */ + bool const fNoGroups = pHdr->cSegs == pHdr->cLogSegs; + + /* + * The PE image has an extra section/segment for the headers, the others + * doesn't. PE images doesn't have DOS frames. So, figure the image type now. + */ + RTLDRFMT enmImgFmt = RTLDRFMT_INVALID; + if (pThis->pMod->pImgVt) + enmImgFmt = pThis->pMod->pImgVt->pfnGetFormat(pThis->pMod); + + /* + * Validate and display it all. + */ + Log2(("RTDbgModCv: SegMap: cSegs=%#x cLogSegs=%#x (cbSegNames=%#x)\n", pHdr->cSegs, pHdr->cLogSegs, pThis->cbSegNames)); + RTDBGMODCV_CHECK_RET_BF(pThis->paDirEnts[iSegMap].cb >= sizeof(*pHdr) + pHdr->cSegs * sizeof(paDescs[0]), + ("SegMap is out of bounds: cbSubSect=%#x cSegs=%#x\n", pThis->paDirEnts[iSegMap].cb, pHdr->cSegs)); + RTDBGMODCV_CHECK_NOMSG_RET_BF(pHdr->cSegs >= pHdr->cLogSegs); + + Log2(("Logical segment descriptors: %u\n", pHdr->cLogSegs)); + + bool fHaveDosFrames = false; + for (i = 0; i < pHdr->cSegs; i++) + { + if (i == pHdr->cLogSegs) + Log2(("Group/Physical descriptors: %u\n", pHdr->cSegs - pHdr->cLogSegs)); + char szFlags[16]; + memset(szFlags, '-', sizeof(szFlags)); + if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_READ) + szFlags[0] = 'R'; + if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_WRITE) + szFlags[1] = 'W'; + if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_EXECUTE) + szFlags[2] = 'X'; + if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_32BIT) + szFlags[3] = '3', szFlags[4] = '2'; + if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_SEL) + szFlags[5] = 'S'; + if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_ABS) + szFlags[6] = 'A'; + if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_GROUP) + szFlags[7] = 'G'; + szFlags[8] = '\0'; + if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_RESERVED) + szFlags[8] = '!', szFlags[9] = '\0'; + Log2((" #%02u: %#010x LB %#010x flags=%#06x ovl=%#06x group=%#06x frame=%#06x iSegName=%#06x iClassName=%#06x %s\n", + i < pHdr->cLogSegs ? i : i - pHdr->cLogSegs, paDescs[i].off, paDescs[i].cb, paDescs[i].fFlags, paDescs[i].iOverlay, + paDescs[i].iGroup, paDescs[i].iFrame, paDescs[i].offSegName, paDescs[i].offClassName, szFlags)); + + RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].offSegName == UINT16_MAX || paDescs[i].offSegName < pThis->cbSegNames); + RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].offClassName == UINT16_MAX || paDescs[i].offClassName < pThis->cbSegNames); + const char *pszName = paDescs[i].offSegName != UINT16_MAX + ? pThis->pszzSegNames + paDescs[i].offSegName + : NULL; + const char *pszClass = paDescs[i].offClassName != UINT16_MAX + ? pThis->pszzSegNames + paDescs[i].offClassName + : NULL; + if (pszName || pszClass) + Log2((" pszName=%s pszClass=%s\n", pszName, pszClass)); + + /* Validate the group link. */ + RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].iGroup == 0 || !(paDescs[i].fFlags & RTCVSEGMAPDESC_F_GROUP)); + RTDBGMODCV_CHECK_NOMSG_RET_BF( paDescs[i].iGroup == 0 + || ( paDescs[i].iGroup >= pHdr->cLogSegs + && paDescs[i].iGroup < pHdr->cSegs)); + RTDBGMODCV_CHECK_NOMSG_RET_BF( paDescs[i].iGroup == 0 + || (paDescs[paDescs[i].iGroup].fFlags & RTCVSEGMAPDESC_F_GROUP)); + RTDBGMODCV_CHECK_NOMSG_RET_BF(!(paDescs[i].fFlags & RTCVSEGMAPDESC_F_GROUP) || paDescs[i].off == 0); /* assumed below */ + + if (fNoGroups) + { + RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].iGroup == 0); + if ( !fHaveDosFrames + && paDescs[i].iFrame != 0 + && (paDescs[i].fFlags & (RTCVSEGMAPDESC_F_SEL | RTCVSEGMAPDESC_F_ABS)) + && paDescs[i].iOverlay == 0 + && enmImgFmt != RTLDRFMT_PE + && pThis->enmType != RTCVFILETYPE_DBG) + fHaveDosFrames = true; /* BIOS, only groups with frames. */ + } + } + + /* + * Further valiations based on fHaveDosFrames or not. + */ + if (fNoGroups) + { + if (fHaveDosFrames) + for (i = 0; i < pHdr->cSegs; i++) + { + RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].iOverlay == 0); + RTDBGMODCV_CHECK_NOMSG_RET_BF( (paDescs[i].fFlags & (RTCVSEGMAPDESC_F_SEL | RTCVSEGMAPDESC_F_ABS)) + == RTCVSEGMAPDESC_F_SEL + || (paDescs[i].fFlags & (RTCVSEGMAPDESC_F_SEL | RTCVSEGMAPDESC_F_ABS)) + == RTCVSEGMAPDESC_F_ABS); + RTDBGMODCV_CHECK_NOMSG_RET_BF(!(paDescs[i].fFlags & RTCVSEGMAPDESC_F_ABS)); + } + else + for (i = 0; i < pHdr->cSegs; i++) + RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].off == 0); + } + + /* + * Modify the groups index to be the loader segment index instead, also + * add the segments to the container if we haven't done that already. + */ + + /* Guess work: Group can be implicit if used. Observed Visual C++ v1.5, + omitting the CODE group. */ + const char *pszGroup0 = NULL; + uint64_t cbGroup0 = 0; + if (!fNoGroups && !fHaveDosFrames) + { + for (i = 0; i < pHdr->cSegs; i++) + if ( !(paDescs[i].fFlags & (RTCVSEGMAPDESC_F_GROUP | RTCVSEGMAPDESC_F_ABS)) + && paDescs[i].iGroup == 0) + { + if (pszGroup0 == NULL && paDescs[i].offClassName != UINT16_MAX) + pszGroup0 = pThis->pszzSegNames + paDescs[i].offClassName; + uint64_t offEnd = (uint64_t)paDescs[i].off + paDescs[i].cb; + if (offEnd > cbGroup0) + cbGroup0 = offEnd; + } + } + + /* Add the segments. + Note! The RVAs derived from this exercise are all wrong. :-/ + Note! We don't have an image loader, so we cannot add any fake sections. */ + /** @todo Try see if we can figure something out from the frame value later. */ + if (!pThis->fHaveLoadedSegments) + { + uint16_t iSeg = 0; + if (!fHaveDosFrames) + { + Assert(!pThis->pMod->pImgVt); Assert(pThis->enmType != RTCVFILETYPE_DBG); + uint64_t uRva = 0; + if (cbGroup0 && !fNoGroups) + { + rc = RTDbgModSegmentAdd(pThis->hCnt, 0, cbGroup0, pszGroup0 ? pszGroup0 : "Seg00", 0 /*fFlags*/, NULL); + uRva += cbGroup0; + iSeg++; + } + + for (i = 0; RT_SUCCESS(rc) && i < pHdr->cSegs; i++) + if ((paDescs[i].fFlags & RTCVSEGMAPDESC_F_GROUP) || fNoGroups) + { + char szName[16]; + char *pszName = szName; + if (paDescs[i].offSegName != UINT16_MAX) + pszName = pThis->pszzSegNames + paDescs[i].offSegName; + else + RTStrPrintf(szName, sizeof(szName), "Seg%02u", iSeg); + rc = RTDbgModSegmentAdd(pThis->hCnt, uRva, paDescs[i].cb, pszName, 0 /*fFlags*/, NULL); + uRva += paDescs[i].cb; + iSeg++; + } + } + else + { + /* The map is not sorted by RVA, very annoying, but I'm countering + by being lazy and slow about it. :-) Btw. this is the BIOS case. */ + Assert(fNoGroups); +#if 1 /** @todo need more inputs */ + + /* Figure image base address. */ + uint64_t uImageBase = UINT64_MAX; + for (i = 0; RT_SUCCESS(rc) && i < pHdr->cSegs; i++) + { + uint64_t uAddr = (uint64_t)paDescs[i].off + ((uint32_t)paDescs[i].iFrame << 4); + if (uAddr < uImageBase) + uImageBase = uAddr; + } + + /* Add the segments. */ + uint64_t uMinAddr = uImageBase; + for (i = 0; RT_SUCCESS(rc) && i < pHdr->cSegs; i++) + { + /* Figure out the next one. */ + uint16_t cOverlaps = 0; + uint16_t iBest = UINT16_MAX; + uint64_t uBestAddr = UINT64_MAX; + for (uint16_t j = 0; j < pHdr->cSegs; j++) + { + uint64_t uAddr = (uint64_t)paDescs[j].off + ((uint32_t)paDescs[j].iFrame << 4); + if (uAddr >= uMinAddr && uAddr < uBestAddr) + { + uBestAddr = uAddr; + iBest = j; + } + else if (uAddr == uBestAddr) + { + cOverlaps++; + if (paDescs[j].cb > paDescs[iBest].cb) + { + uBestAddr = uAddr; + iBest = j; + } + } + } + if (iBest == UINT16_MAX && RT_SUCCESS(rc)) + { + rc = VERR_CV_IPE; + break; + } + + /* Add it. */ + char szName[16]; + char *pszName = szName; + if (paDescs[iBest].offSegName != UINT16_MAX) + pszName = pThis->pszzSegNames + paDescs[iBest].offSegName; + else + RTStrPrintf(szName, sizeof(szName), "Seg%02u", iSeg); + RTDBGSEGIDX idxDbgSeg = NIL_RTDBGSEGIDX; + rc = RTDbgModSegmentAdd(pThis->hCnt, uBestAddr - uImageBase, paDescs[iBest].cb, pszName, 0 /*fFlags*/, &idxDbgSeg); + Log(("CV: %#010x LB %#010x %s uRVA=%#010x iBest=%u cOverlaps=%u [idxDbgSeg=%#x iSeg=%#x]\n", + uBestAddr, paDescs[iBest].cb, szName, uBestAddr - uImageBase, iBest, cOverlaps, idxDbgSeg, idxDbgSeg)); + + /* Update translations. */ + paDescs[iBest].iGroup = iSeg; + if (cOverlaps > 0) + { + for (uint16_t j = 0; j < pHdr->cSegs; j++) + if ((uint64_t)paDescs[j].off + ((uint32_t)paDescs[j].iFrame << 4) == uBestAddr) + paDescs[iBest].iGroup = iSeg; + i += cOverlaps; + } + + /* Advance. */ + uMinAddr = uBestAddr + 1; + iSeg++; + } + + pThis->fHaveDosFrames = true; +#else + uint32_t iFrameFirst = UINT32_MAX; + uint16_t iSeg = 0; + uint32_t iFrameMin = 0; + do + { + /* Find next frame. */ + uint32_t iFrame = UINT32_MAX; + for (uint16_t j = 0; j < pHdr->cSegs; j++) + if (paDescs[j].iFrame >= iFrameMin && paDescs[j].iFrame < iFrame) + iFrame = paDescs[j].iFrame; + if (iFrame == UINT32_MAX) + break; + + /* Figure the frame span. */ + uint32_t offFirst = UINT32_MAX; + uint64_t offEnd = 0; + for (uint16_t j = 0; j < pHdr->cSegs; j++) + if (paDescs[j].iFrame == iFrame) + { + uint64_t offThisEnd = paDescs[j].off + paDescs[j].cb; + if (offThisEnd > offEnd) + offEnd = offThisEnd; + if (paDescs[j].off < offFirst) + offFirst = paDescs[j].off; + } + + if (offFirst < offEnd) + { + /* Add it. */ + char szName[16]; + RTStrPrintf(szName, sizeof(szName), "Frame_%04x", iFrame); + Log(("CV: %s offEnd=%#x offFirst=%#x\n", szName, offEnd, offFirst)); + if (iFrameFirst == UINT32_MAX) + iFrameFirst = iFrame; + rc = RTDbgModSegmentAdd(pThis->hCnt, (iFrame - iFrameFirst) << 4, offEnd, szName, 0 /*fFlags*/, NULL); + + /* Translation updates. */ + for (uint16_t j = 0; j < pHdr->cSegs; j++) + if (paDescs[j].iFrame == iFrame) + { + paDescs[j].iGroup = iSeg; + paDescs[j].off = 0; + paDescs[j].cb = offEnd > UINT32_MAX ? UINT32_MAX : (uint32_t)offEnd; + } + + iSeg++; + } + + iFrameMin = iFrame + 1; + } while (RT_SUCCESS(rc)); +#endif + } + + if (RT_FAILURE(rc)) + { + Log(("RTDbgModCv: %Rrc while adding segments from SegMap\n", rc)); + return rc; + } + + pThis->fHaveLoadedSegments = true; + + /* Skip the stuff below if we have DOS frames since we did it all above. */ + if (fHaveDosFrames) + return VINF_SUCCESS; + } + + /* Pass one: Fixate the group segment indexes. */ + uint16_t iSeg0 = enmImgFmt == RTLDRFMT_PE || pThis->enmType == RTCVFILETYPE_DBG ? 1 : 0; + uint16_t iSeg = iSeg0 + (cbGroup0 > 0); /** @todo probably wrong... */ + for (i = 0; i < pHdr->cSegs; i++) + if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_ABS) + paDescs[i].iGroup = (uint16_t)(RTDBGSEGIDX_ABS & UINT16_MAX); + else if ((paDescs[i].fFlags & RTCVSEGMAPDESC_F_GROUP) || fNoGroups) + paDescs[i].iGroup = iSeg++; + + /* Pass two: Resolve group references in to segment indexes. */ + Log2(("Mapped segments (both kinds):\n")); + for (i = 0; i < pHdr->cSegs; i++) + { + if (!fNoGroups && !(paDescs[i].fFlags & (RTCVSEGMAPDESC_F_GROUP | RTCVSEGMAPDESC_F_ABS))) + paDescs[i].iGroup = paDescs[i].iGroup == 0 ? iSeg0 : paDescs[paDescs[i].iGroup].iGroup; + + Log2((" #%02u: %#010x LB %#010x -> %#06x (flags=%#06x ovl=%#06x frame=%#06x)\n", + i, paDescs[i].off, paDescs[i].cb, paDescs[i].iGroup, + paDescs[i].fFlags, paDescs[i].iOverlay, paDescs[i].iFrame)); + } + + return VINF_SUCCESS; +} + + +/** + * @callback_method_impl{PFNRTSORTCMP, + * Used by rtDbgModCvLoadDirectory to sort the directory.} + */ +static DECLCALLBACK(int) rtDbgModCvDirEntCmp(void const *pvElement1, void const *pvElement2, void *pvUser) +{ + PRTCVDIRENT32 pEntry1 = (PRTCVDIRENT32)pvElement1; + PRTCVDIRENT32 pEntry2 = (PRTCVDIRENT32)pvElement2; + if (pEntry1->iMod < pEntry2->iMod) + return -1; + if (pEntry1->iMod > pEntry2->iMod) + return 1; + if (pEntry1->uSubSectType < pEntry2->uSubSectType) + return -1; + if (pEntry1->uSubSectType > pEntry2->uSubSectType) + return 1; + + RT_NOREF_PV(pvUser); + return 0; +} + + +/** + * Loads the directory into memory (RTDBGMODCV::paDirEnts and + * RTDBGMODCV::cDirEnts). + * + * Converting old format version into the newer format to simplifying the code + * using the directory. + * + * + * @returns IPRT status code. (May leave with paDirEnts allocated on failure.) + * @param pThis The CV reader instance. + */ +static int rtDbgModCvLoadDirectory(PRTDBGMODCV pThis) +{ + /* + * Read in the CV directory. + */ + int rc; + if ( pThis->u32CvMagic == RTCVHDR_MAGIC_NB00 + || pThis->u32CvMagic == RTCVHDR_MAGIC_NB02) + { + /* + * 16-bit type. + */ + RTCVDIRHDR16 DirHdr; + rc = rtDbgModCvReadAt(pThis, pThis->offDir, &DirHdr, sizeof(DirHdr)); + if (RT_SUCCESS(rc)) + { + if (DirHdr.cEntries > 2 && DirHdr.cEntries < _64K - 32U) + { + pThis->cDirEnts = DirHdr.cEntries; + pThis->paDirEnts = (PRTCVDIRENT32)RTMemAlloc(DirHdr.cEntries * sizeof(pThis->paDirEnts[0])); + if (pThis->paDirEnts) + { + rc = rtDbgModCvReadAt(pThis, pThis->offDir + sizeof(DirHdr), + pThis->paDirEnts, DirHdr.cEntries * sizeof(RTCVDIRENT16)); + if (RT_SUCCESS(rc)) + { + /* Convert the entries (from the end). */ + uint32_t cLeft = DirHdr.cEntries; + RTCVDIRENT32 volatile *pDst = pThis->paDirEnts + cLeft; + RTCVDIRENT16 volatile *pSrc = (RTCVDIRENT16 volatile *)pThis->paDirEnts + cLeft; + while (cLeft--) + { + pDst--; + pSrc--; + + pDst->cb = pSrc->cb; + pDst->off = RT_MAKE_U32(pSrc->offLow, pSrc->offHigh); + pDst->iMod = pSrc->iMod; + pDst->uSubSectType = pSrc->uSubSectType; + } + } + } + else + rc = VERR_NO_MEMORY; + } + else + { + Log(("Old CV directory count is out of considered valid range: %#x\n", DirHdr.cEntries)); + rc = VERR_CV_BAD_FORMAT; + } + } + } + else + { + /* + * 32-bit type (reading too much for NB04 is no problem). + * + * Note! The watcom linker (v1.9) seems to overwrite the directory + * header and more under some conditions. So, if this code fails + * you might be so lucky as to have reproduce that issue... + */ + RTCVDIRHDR32EX DirHdr; + rc = rtDbgModCvReadAt(pThis, pThis->offDir, &DirHdr, sizeof(DirHdr)); + if (RT_SUCCESS(rc)) + { + if ( DirHdr.Core.cbHdr != sizeof(DirHdr.Core) + && DirHdr.Core.cbHdr != sizeof(DirHdr)) + { + Log(("Unexpected CV directory size: %#x [wlink screwup?]\n", DirHdr.Core.cbHdr)); + rc = VERR_CV_BAD_FORMAT; + } + if ( DirHdr.Core.cbHdr == sizeof(DirHdr) + && ( DirHdr.offNextDir != 0 + || DirHdr.fFlags != 0) ) + { + Log(("Extended CV directory headers fields are not zero: fFlags=%#x offNextDir=%#x [wlink screwup?]\n", + DirHdr.fFlags, DirHdr.offNextDir)); + rc = VERR_CV_BAD_FORMAT; + } + if (DirHdr.Core.cbEntry != sizeof(RTCVDIRENT32)) + { + Log(("Unexpected CV directory entry size: %#x (expected %#x) [wlink screwup?]\n", DirHdr.Core.cbEntry, sizeof(RTCVDIRENT32))); + rc = VERR_CV_BAD_FORMAT; + } + if (DirHdr.Core.cEntries < 2 || DirHdr.Core.cEntries >= _512K) + { + Log(("CV directory count is out of considered valid range: %#x [wlink screwup?]\n", DirHdr.Core.cEntries)); + rc = VERR_CV_BAD_FORMAT; + } + if (RT_SUCCESS(rc)) + { + pThis->cDirEnts = DirHdr.Core.cEntries; + pThis->paDirEnts = (PRTCVDIRENT32)RTMemAlloc(DirHdr.Core.cEntries * sizeof(pThis->paDirEnts[0])); + if (pThis->paDirEnts) + rc = rtDbgModCvReadAt(pThis, pThis->offDir + DirHdr.Core.cbHdr, + pThis->paDirEnts, DirHdr.Core.cEntries * sizeof(RTCVDIRENT32)); + else + rc = VERR_NO_MEMORY; + } + } + } + + if (RT_SUCCESS(rc)) + { + uint32_t const cbDbgInfo = pThis->cbDbgInfo; + uint32_t const cDirEnts = pThis->cDirEnts; + + /* + * Just sort the directory in a way we like, no need to make + * complicated demands on the linker output. + */ + RTSortShell(pThis->paDirEnts, cDirEnts, sizeof(pThis->paDirEnts[0]), rtDbgModCvDirEntCmp, NULL); + + /* + * Basic info validation. + */ + uint16_t cGlobalMods = 0; + uint16_t cNormalMods = 0; + uint16_t iModLast = 0; + Log2(("RTDbgModCv: %u (%#x) directory entries:\n", cDirEnts, cDirEnts)); + for (uint32_t i = 0; i < cDirEnts; i++) + { + PCRTCVDIRENT32 pDirEnt = &pThis->paDirEnts[i]; + Log2((" #%04u mod=%#06x sst=%#06x at %#010x LB %#07x %s\n", + i, pDirEnt->iMod, pDirEnt->uSubSectType, pDirEnt->off, pDirEnt->cb, + rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType))); + + if ( pDirEnt->off >= cbDbgInfo + || pDirEnt->cb >= cbDbgInfo + || pDirEnt->off + pDirEnt->cb > cbDbgInfo) + { + Log(("CV directory entry #%u is out of bounds: %#x LB %#x, max %#x\n", i, pDirEnt->off, pDirEnt->cb, cbDbgInfo)); + rc = VERR_CV_BAD_FORMAT; + } + if ( pDirEnt->iMod == 0 + && pThis->u32CvMagic != RTCVHDR_MAGIC_NB04 + && pThis->u32CvMagic != RTCVHDR_MAGIC_NB02 + && pThis->u32CvMagic != RTCVHDR_MAGIC_NB00) + { + Log(("CV directory entry #%u uses module index 0 (uSubSectType=%#x)\n", i, pDirEnt->uSubSectType)); + rc = VERR_CV_BAD_FORMAT; + } + if (pDirEnt->iMod == 0 || pDirEnt->iMod == 0xffff) + cGlobalMods++; + else + { + if (pDirEnt->iMod > iModLast) + { + if ( pDirEnt->uSubSectType != kCvSst_Module + && pDirEnt->uSubSectType != kCvSst_OldModule) + { + Log(("CV directory entry #%u: expected module subsection first, found %s (%#x)\n", + i, rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType), pDirEnt->uSubSectType)); + rc = VERR_CV_BAD_FORMAT; + } + if (pDirEnt->iMod != iModLast + 1) + { + Log(("CV directory entry #%u: skips from mod %#x to %#x modules\n", i, iModLast, pDirEnt->iMod)); + rc = VERR_CV_BAD_FORMAT; + } + iModLast = pDirEnt->iMod; + } + cNormalMods++; + } + } + if (cGlobalMods == 0) + { + Log(("CV directory contains no global modules\n")); + rc = VERR_CV_BAD_FORMAT; + } + if (RT_SUCCESS(rc)) + { + Log(("CV dir stats: %u total, %u normal, %u special, iModLast=%#x (%u)\n", + cDirEnts, cNormalMods, cGlobalMods, iModLast, iModLast)); + +#if 0 /* skip this stuff */ + /* + * Validate the directory ordering. + */ + uint16_t i = 0; + + /* Normal modules. */ + if (pThis->enmDirOrder != RTCVDIRORDER_BY_SST_MOD) + { + uint16_t iEndNormalMods = cNormalMods + (pThis->enmDirOrder == RTCVDIRORDER_BY_MOD_0 ? cGlobalMods : 0); + while (i < iEndNormalMods) + { + if (pThis->paDirEnts[i].iMod == 0 || pThis->paDirEnts[i].iMod == 0xffff) + { + Log(("CV directory entry #%u: Unexpected global module entry.\n", i)); + rc = VERR_CV_BAD_FORMAT; + } + i++; + } + } + else + { + uint32_t fSeen = RT_BIT_32(kCvSst_Module - kCvSst_Module) + | RT_BIT_32(kCvSst_Libraries - kCvSst_Module) + | RT_BIT_32(kCvSst_GlobalSym - kCvSst_Module) + | RT_BIT_32(kCvSst_GlobalPub - kCvSst_Module) + | RT_BIT_32(kCvSst_GlobalTypes - kCvSst_Module) + | RT_BIT_32(kCvSst_SegName - kCvSst_Module) + | RT_BIT_32(kCvSst_SegMap - kCvSst_Module) + | RT_BIT_32(kCvSst_StaticSym - kCvSst_Module) + | RT_BIT_32(kCvSst_FileIndex - kCvSst_Module) + | RT_BIT_32(kCvSst_MPC - kCvSst_Module); + uint16_t iMod = 0; + uint16_t uSst = kCvSst_Module; + while (i < cNormalMods) + { + PCRTCVDIRENT32 pDirEnt = &pThis->paDirEnts[i]; + if ( pDirEnt->iMod > iMod + || pDirEnt->iMod == iMod) /* wlink subjected to MSVC 2010 /Z7 files with multiple .debug$S. */ + { + if (pDirEnt->uSubSectType != uSst) + { + Log(("CV directory entry #%u: Expected %s (%#x), found %s (%#x).\n", + i, rtDbgModCvGetSubSectionName(uSst), uSst, + rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType), pDirEnt->uSubSectType)); + rc = VERR_CV_BAD_FORMAT; + } + } + else + { + uint32_t iBit = pDirEnt->uSubSectType - kCvSst_Module; + if (iBit >= 32U || (fSeen & RT_BIT_32(iBit))) + { + Log(("CV directory entry #%u: SST %s (%#x) has already been seen or is for globals.\n", + i, rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType), pDirEnt->uSubSectType)); + rc = VERR_CV_BAD_FORMAT; + } + fSeen |= RT_BIT_32(iBit); + } + + uSst = pDirEnt->uSubSectType; + iMod = pDirEnt->iMod; + i++; + } + } + + /* New style with special modules at the end. */ + if (pThis->enmDirOrder != RTCVDIRORDER_BY_MOD_0) + while (i < cDirEnts) + { + if (pThis->paDirEnts[i].iMod != 0 && pThis->paDirEnts[i].iMod != 0xffff) + { + Log(("CV directory entry #%u: Expected global module entry, not %#x.\n", i, + pThis->paDirEnts[i].iMod)); + rc = VERR_CV_BAD_FORMAT; + } + i++; + } +#endif + } + } + + return rc; +} + + +static int rtDbgModCvLoadCodeViewInfo(PRTDBGMODCV pThis) +{ + /* + * Load the directory, the segment map (if any) and then scan for segments + * if necessary. + */ + int rc = rtDbgModCvLoadDirectory(pThis); + if (RT_SUCCESS(rc)) + rc = rtDbgModCvLoadSegmentMap(pThis); + if (RT_SUCCESS(rc) && !pThis->fHaveLoadedSegments) + { + rc = VERR_CV_TODO; /** @todo Scan anything containing address, in particular sstSegMap and sstModule, + * and reconstruct the segments from that information. */ + pThis->cbImage = 0x1000; + rc = VINF_SUCCESS; + } + + /* + * Process the directory. + */ + for (uint32_t i = 0; RT_SUCCESS(rc) && i < pThis->cDirEnts; i++) + { + PCRTCVDIRENT32 pDirEnt = &pThis->paDirEnts[i]; + Log3(("Processing module %#06x subsection #%04u %s\n", pDirEnt->iMod, i, rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType))); + PFNDBGMODCVSUBSECTCALLBACK pfnCallback = NULL; + switch (pDirEnt->uSubSectType) + { + case kCvSst_GlobalPub: + case kCvSst_GlobalSym: + case kCvSst_StaticSym: + pfnCallback = rtDbgModCvSs_GlobalPub_GlobalSym_StaticSym; + break; + case kCvSst_Module: + pfnCallback = rtDbgModCvSs_Module; + break; + case kCvSst_PublicSym: + case kCvSst_Symbols: + case kCvSst_AlignSym: + pfnCallback = rtDbgModCvSs_Symbols_PublicSym_AlignSym; + break; + + case kCvSst_OldModule: + case kCvSst_OldPublic: + case kCvSst_OldTypes: + case kCvSst_OldSymbols: + case kCvSst_OldSrcLines: + case kCvSst_OldLibraries: + case kCvSst_OldImports: + case kCvSst_OldCompacted: + case kCvSst_OldSrcLnSeg: + case kCvSst_OldSrcLines3: + /** @todo implement more. */ + break; + + case kCvSst_Types: + case kCvSst_Public: + case kCvSst_SrcLnSeg: + /** @todo implement more. */ + break; + case kCvSst_SrcModule: + pfnCallback = rtDbgModCvSs_SrcModule; + break; + case kCvSst_Libraries: + case kCvSst_GlobalTypes: + case kCvSst_MPC: + case kCvSst_PreComp: + case kCvSst_PreCompMap: + case kCvSst_OffsetMap16: + case kCvSst_OffsetMap32: + case kCvSst_FileIndex: + + default: + /** @todo implement more. */ + break; + + /* Skip because we've already processed them: */ + case kCvSst_SegMap: + case kCvSst_SegName: + pfnCallback = NULL; + break; + } + + if (pfnCallback) + { + void *pvSubSect; + rc = rtDbgModCvReadAtAlloc(pThis, pDirEnt->off, &pvSubSect, pDirEnt->cb); + if (RT_SUCCESS(rc)) + { + rc = pfnCallback(pThis, pvSubSect, pDirEnt->cb, pDirEnt); + RTMemFree(pvSubSect); + } + } + } + + /* + * Free temporary parsing objects. + */ + if (pThis->pbSrcInfo) + { + RTMemFree(pThis->pbSrcInfo); + pThis->pbSrcInfo = NULL; + pThis->cbSrcInfo = 0; + pThis->cbSrcInfoAlloc = 0; + } + if (pThis->pchSrcStrings) + { + RTMemFree(pThis->pchSrcStrings); + pThis->pchSrcStrings = NULL; + pThis->cbSrcStrings = 0; + pThis->cbSrcStringsAlloc = 0; + } + + return rc; +} + + +/* + * + * COFF Debug Info Parsing. + * COFF Debug Info Parsing. + * COFF Debug Info Parsing. + * + */ + +#ifdef LOG_ENABLED +static const char *rtDbgModCvGetCoffStorageClassName(uint8_t bStorageClass) +{ + switch (bStorageClass) + { + case IMAGE_SYM_CLASS_END_OF_FUNCTION: return "END_OF_FUNCTION"; + case IMAGE_SYM_CLASS_NULL: return "NULL"; + case IMAGE_SYM_CLASS_AUTOMATIC: return "AUTOMATIC"; + case IMAGE_SYM_CLASS_EXTERNAL: return "EXTERNAL"; + case IMAGE_SYM_CLASS_STATIC: return "STATIC"; + case IMAGE_SYM_CLASS_REGISTER: return "REGISTER"; + case IMAGE_SYM_CLASS_EXTERNAL_DEF: return "EXTERNAL_DEF"; + case IMAGE_SYM_CLASS_LABEL: return "LABEL"; + case IMAGE_SYM_CLASS_UNDEFINED_LABEL: return "UNDEFINED_LABEL"; + case IMAGE_SYM_CLASS_MEMBER_OF_STRUCT: return "MEMBER_OF_STRUCT"; + case IMAGE_SYM_CLASS_ARGUMENT: return "ARGUMENT"; + case IMAGE_SYM_CLASS_STRUCT_TAG: return "STRUCT_TAG"; + case IMAGE_SYM_CLASS_MEMBER_OF_UNION: return "MEMBER_OF_UNION"; + case IMAGE_SYM_CLASS_UNION_TAG: return "UNION_TAG"; + case IMAGE_SYM_CLASS_TYPE_DEFINITION: return "TYPE_DEFINITION"; + case IMAGE_SYM_CLASS_UNDEFINED_STATIC: return "UNDEFINED_STATIC"; + case IMAGE_SYM_CLASS_ENUM_TAG: return "ENUM_TAG"; + case IMAGE_SYM_CLASS_MEMBER_OF_ENUM: return "MEMBER_OF_ENUM"; + case IMAGE_SYM_CLASS_REGISTER_PARAM: return "REGISTER_PARAM"; + case IMAGE_SYM_CLASS_BIT_FIELD: return "BIT_FIELD"; + case IMAGE_SYM_CLASS_FAR_EXTERNAL: return "FAR_EXTERNAL"; + case IMAGE_SYM_CLASS_BLOCK: return "BLOCK"; + case IMAGE_SYM_CLASS_FUNCTION: return "FUNCTION"; + case IMAGE_SYM_CLASS_END_OF_STRUCT: return "END_OF_STRUCT"; + case IMAGE_SYM_CLASS_FILE: return "FILE"; + case IMAGE_SYM_CLASS_SECTION: return "SECTION"; + case IMAGE_SYM_CLASS_WEAK_EXTERNAL: return "WEAK_EXTERNAL"; + case IMAGE_SYM_CLASS_CLR_TOKEN: return "CLR_TOKEN"; + } + + static char s_szName[32]; + RTStrPrintf(s_szName, sizeof(s_szName), "Unknown%#04x", bStorageClass); + return s_szName; +} +#endif /* LOG_ENABLED */ + + +/** + * Adds a chunk of COFF line numbers. + * + * @param pThis The COFF/CodeView reader instance. + * @param pszFile The source file name. + * @param iSection The section number. + * @param paLines Pointer to the first line number table entry. + * @param cLines The number of line number table entries to add. + */ +static void rtDbgModCvAddCoffLineNumbers(PRTDBGMODCV pThis, const char *pszFile, uint32_t iSection, + PCIMAGE_LINENUMBER paLines, uint32_t cLines) +{ + RT_NOREF_PV(iSection); + Log4(("Adding %u line numbers in section #%u for %s\n", cLines, iSection, pszFile)); + PCIMAGE_LINENUMBER pCur = paLines; + while (cLines-- > 0) + { + if (pCur->Linenumber) + { + int rc = RTDbgModLineAdd(pThis->hCnt, pszFile, pCur->Linenumber, RTDBGSEGIDX_RVA, pCur->Type.VirtualAddress, NULL); + Log4((" %#010x: %u [%Rrc]\n", pCur->Type.VirtualAddress, pCur->Linenumber, rc)); NOREF(rc); + } + pCur++; + } +} + + +/** + * Adds a COFF symbol. + * + * @returns IPRT status (ignored) + * @param pThis The COFF/CodeView reader instance. + * @param idxSeg IPRT RVA or ABS segment index indicator. + * @param uValue The symbol value. + * @param pszName The symbol name. + */ +static int rtDbgModCvAddCoffSymbol(PRTDBGMODCV pThis, uint32_t idxSeg, uint32_t uValue, const char *pszName) +{ + int rc = RTDbgModSymbolAdd(pThis->hCnt, pszName, idxSeg, uValue, 0, 0 /*fFlags*/, NULL); + Log(("Symbol: %s:%08x %s [%Rrc]\n", idxSeg == RTDBGSEGIDX_RVA ? "rva" : "abs", uValue, pszName, rc)); + if (rc == VERR_DBG_ADDRESS_CONFLICT || rc == VERR_DBG_DUPLICATE_SYMBOL) + rc = VINF_SUCCESS; + return rc; +} + + +/** + * Processes the COFF symbol table. + * + * @returns IPRT status code + * @param pThis The COFF/CodeView reader instance. + * @param paSymbols Pointer to the symbol table. + * @param cSymbols The number of entries in the symbol table. + * @param paLines Pointer to the line number table. + * @param cLines The number of entires in the line number table. + * @param pszzStrTab Pointer to the string table. + * @param cbStrTab Size of the string table. + */ +static int rtDbgModCvProcessCoffSymbolTable(PRTDBGMODCV pThis, + PCIMAGE_SYMBOL paSymbols, uint32_t cSymbols, + PCIMAGE_LINENUMBER paLines, uint32_t cLines, + const char *pszzStrTab, uint32_t cbStrTab) +{ + Log3(("Processing COFF symbol table with %#x symbols\n", cSymbols)); + + /* + * Making some bold assumption that the line numbers for the section in + * the file are allocated sequentially, we do multiple passes until we've + * gathered them all. + */ + int rc = VINF_SUCCESS; + uint32_t cSections = 1; + uint32_t iLineSect = 1; + uint32_t iLine = 0; + do + { + /* + * Process the symbols. + */ + char szShort[9]; + char szFile[RTPATH_MAX]; + uint32_t iSymbol = 0; + szFile[0] = '\0'; + szShort[8] = '\0'; /* avoid having to terminate it all the time. */ + + while (iSymbol < cSymbols && RT_SUCCESS(rc)) + { + /* Copy the symbol in and hope it works around the misalignment + issues everywhere. */ + IMAGE_SYMBOL Sym; + memcpy(&Sym, &paSymbols[iSymbol], sizeof(Sym)); + RTDBGMODCV_CHECK_NOMSG_RET_BF(Sym.NumberOfAuxSymbols < cSymbols); + + /* Calc a zero terminated symbol name. */ + const char *pszName; + if (Sym.N.Name.Short) + pszName = (const char *)memcpy(szShort, &Sym.N, 8); + else + { + RTDBGMODCV_CHECK_NOMSG_RET_BF(Sym.N.Name.Long < cbStrTab); + pszName = pszzStrTab + Sym.N.Name.Long; + } + + /* Only log stuff and count sections the in the first pass.*/ + if (iLineSect == 1) + { + Log3(("%04x: s=%#06x v=%#010x t=%#06x a=%#04x c=%#04x (%s) name='%s'\n", + iSymbol, Sym.SectionNumber, Sym.Value, Sym.Type, Sym.NumberOfAuxSymbols, + Sym.StorageClass, rtDbgModCvGetCoffStorageClassName(Sym.StorageClass), pszName)); + if ((int16_t)cSections <= Sym.SectionNumber && Sym.SectionNumber > 0) + cSections = Sym.SectionNumber + 1; + } + + /* + * Use storage class to pick what we need (which isn't much because, + * MS only provides a very restricted set of symbols). + */ + IMAGE_AUX_SYMBOL Aux; + switch (Sym.StorageClass) + { + case IMAGE_SYM_CLASS_NULL: + /* a NOP */ + break; + + case IMAGE_SYM_CLASS_FILE: + { + /* Change the current file name (for line numbers). Pretend + ANSI and ISO-8859-1 are similar enough for out purposes... */ + RTDBGMODCV_CHECK_NOMSG_RET_BF(Sym.NumberOfAuxSymbols > 0); + const char *pszFile = (const char *)&paSymbols[iSymbol + 1]; + char *pszDst = szFile; + rc = RTLatin1ToUtf8Ex(pszFile, Sym.NumberOfAuxSymbols * sizeof(IMAGE_SYMBOL), &pszDst, sizeof(szFile), NULL); + if (RT_FAILURE(rc)) + Log(("Error converting COFF filename: %Rrc\n", rc)); + else if (iLineSect == 1) + Log3((" filename='%s'\n", szFile)); + break; + } + + case IMAGE_SYM_CLASS_STATIC: + if ( Sym.NumberOfAuxSymbols == 1 + && ( iLineSect == 1 + || Sym.SectionNumber == (int32_t)iLineSect) ) + { + memcpy(&Aux, &paSymbols[iSymbol + 1], sizeof(Aux)); + if (iLineSect == 1) + Log3((" section: cb=%#010x #relocs=%#06x #lines=%#06x csum=%#x num=%#x sel=%x rvd=%u\n", + Aux.Section.Length, Aux.Section.NumberOfRelocations, + Aux.Section.NumberOfLinenumbers, + Aux.Section.CheckSum, + RT_MAKE_U32(Aux.Section.Number, Aux.Section.HighNumber), + Aux.Section.Selection, + Aux.Section.bReserved)); + if ( Sym.SectionNumber == (int32_t)iLineSect + && Aux.Section.NumberOfLinenumbers > 0) + { + uint32_t cLinesToAdd = RT_MIN(Aux.Section.NumberOfLinenumbers, cLines - iLine); + if (iLine < cLines && szFile[0]) + rtDbgModCvAddCoffLineNumbers(pThis, szFile, iLineSect, &paLines[iLine], cLinesToAdd); + iLine += cLinesToAdd; + } + } + /* Not so sure about the quality here, but might be useful. */ + else if ( iLineSect == 1 + && Sym.NumberOfAuxSymbols == 0 + && Sym.SectionNumber != IMAGE_SYM_UNDEFINED + && Sym.SectionNumber != IMAGE_SYM_ABSOLUTE + && Sym.SectionNumber != IMAGE_SYM_DEBUG + && Sym.Value > 0 + && *pszName) + rtDbgModCvAddCoffSymbol(pThis, RTDBGSEGIDX_RVA, Sym.Value, pszName); + break; + + case IMAGE_SYM_CLASS_EXTERNAL: + /* Add functions (first pass only). */ + if ( iLineSect == 1 + && (ISFCN(Sym.Type) || Sym.Type == 0) + && Sym.NumberOfAuxSymbols == 0 + && *pszName ) + { + if (Sym.SectionNumber == IMAGE_SYM_ABSOLUTE) + rtDbgModCvAddCoffSymbol(pThis, RTDBGSEGIDX_ABS, Sym.Value, pszName); + else if ( Sym.SectionNumber != IMAGE_SYM_UNDEFINED + && Sym.SectionNumber != IMAGE_SYM_DEBUG) + rtDbgModCvAddCoffSymbol(pThis, RTDBGSEGIDX_RVA, Sym.Value, pszName); + } + break; + + case IMAGE_SYM_CLASS_FUNCTION: + /* Not sure this is really used. */ + break; + + case IMAGE_SYM_CLASS_END_OF_FUNCTION: + case IMAGE_SYM_CLASS_AUTOMATIC: + case IMAGE_SYM_CLASS_REGISTER: + case IMAGE_SYM_CLASS_EXTERNAL_DEF: + case IMAGE_SYM_CLASS_LABEL: + case IMAGE_SYM_CLASS_UNDEFINED_LABEL: + case IMAGE_SYM_CLASS_MEMBER_OF_STRUCT: + case IMAGE_SYM_CLASS_ARGUMENT: + case IMAGE_SYM_CLASS_STRUCT_TAG: + case IMAGE_SYM_CLASS_MEMBER_OF_UNION: + case IMAGE_SYM_CLASS_UNION_TAG: + case IMAGE_SYM_CLASS_TYPE_DEFINITION: + case IMAGE_SYM_CLASS_UNDEFINED_STATIC: + case IMAGE_SYM_CLASS_ENUM_TAG: + case IMAGE_SYM_CLASS_MEMBER_OF_ENUM: + case IMAGE_SYM_CLASS_REGISTER_PARAM: + case IMAGE_SYM_CLASS_BIT_FIELD: + case IMAGE_SYM_CLASS_FAR_EXTERNAL: + case IMAGE_SYM_CLASS_BLOCK: + case IMAGE_SYM_CLASS_END_OF_STRUCT: + case IMAGE_SYM_CLASS_SECTION: + case IMAGE_SYM_CLASS_WEAK_EXTERNAL: + case IMAGE_SYM_CLASS_CLR_TOKEN: + /* Not used by MS, I think. */ + break; + + default: + Log(("RTDbgCv: Unexpected COFF storage class %#x (%u)\n", Sym.StorageClass, Sym.StorageClass)); + break; + } + + /* next symbol */ + iSymbol += 1 + Sym.NumberOfAuxSymbols; + } + + /* Next section with line numbers. */ + iLineSect++; + } while (iLine < cLines && iLineSect < cSections && RT_SUCCESS(rc)); + + return rc; +} + + +/** + * Loads COFF debug information into the container. + * + * @returns IPRT status code. + * @param pThis The COFF/CodeView debug reader instance. + */ +static int rtDbgModCvLoadCoffInfo(PRTDBGMODCV pThis) +{ + /* + * Read the whole section into memory. + * Note! Cannot use rtDbgModCvReadAt or rtDbgModCvReadAtAlloc here. + */ + int rc; + uint8_t *pbDbgSect = (uint8_t *)RTMemAlloc(pThis->cbCoffDbgInfo); + if (pbDbgSect) + { + if (pThis->hFile == NIL_RTFILE) + rc = pThis->pMod->pImgVt->pfnReadAt(pThis->pMod, UINT32_MAX, pThis->offCoffDbgInfo, pbDbgSect, pThis->cbCoffDbgInfo); + else + rc = RTFileReadAt(pThis->hFile, pThis->offCoffDbgInfo, pbDbgSect, pThis->cbCoffDbgInfo, NULL); + if (RT_SUCCESS(rc)) + { + /* The string table follows after the symbol table. */ + const char *pszzStrTab = (const char *)( pbDbgSect + + pThis->CoffHdr.LvaToFirstSymbol + + pThis->CoffHdr.NumberOfSymbols * sizeof(IMAGE_SYMBOL)); + uint32_t cbStrTab = (uint32_t)((uintptr_t)(pbDbgSect + pThis->cbCoffDbgInfo) - (uintptr_t)pszzStrTab); + /** @todo The symbol table starts with a size. Read it and checking. Also verify + * that the symtab ends with a terminator character. */ + + rc = rtDbgModCvProcessCoffSymbolTable(pThis, + (PCIMAGE_SYMBOL)(pbDbgSect + pThis->CoffHdr.LvaToFirstSymbol), + pThis->CoffHdr.NumberOfSymbols, + (PCIMAGE_LINENUMBER)(pbDbgSect + pThis->CoffHdr.LvaToFirstLinenumber), + pThis->CoffHdr.NumberOfLinenumbers, + pszzStrTab, cbStrTab); + } + RTMemFree(pbDbgSect); + } + else + rc = VERR_NO_MEMORY; + return rc; +} + + + + + + +/* + * + * CodeView Debug module implementation. + * CodeView Debug module implementation. + * CodeView Debug module implementation. + * + */ + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnUnwindFrame} */ +static DECLCALLBACK(int) rtDbgModCv_UnwindFrame(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState) +{ + RT_NOREF(pMod, iSeg, off, pState); + return VERR_DBG_NO_UNWIND_INFO; +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByAddr} */ +static DECLCALLBACK(int) rtDbgModCv_LineByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, + PRTINTPTR poffDisp, PRTDBGLINE pLineInfo) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + return RTDbgModLineByAddr(pThis->hCnt, iSeg, off, poffDisp, pLineInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByOrdinal} */ +static DECLCALLBACK(int) rtDbgModCv_LineByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + return RTDbgModLineByOrdinal(pThis->hCnt, iOrdinal, pLineInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineCount} */ +static DECLCALLBACK(uint32_t) rtDbgModCv_LineCount(PRTDBGMODINT pMod) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + return RTDbgModLineCount(pThis->hCnt); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnLineAdd} */ +static DECLCALLBACK(int) rtDbgModCv_LineAdd(PRTDBGMODINT pMod, const char *pszFile, size_t cchFile, uint32_t uLineNo, + uint32_t iSeg, RTUINTPTR off, uint32_t *piOrdinal) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + Assert(!pszFile[cchFile]); NOREF(cchFile); + return RTDbgModLineAdd(pThis->hCnt, pszFile, uLineNo, iSeg, off, piOrdinal); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByAddr} */ +static DECLCALLBACK(int) rtDbgModCv_SymbolByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags, + PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + return RTDbgModSymbolByAddr(pThis->hCnt, iSeg, off, fFlags, poffDisp, pSymInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByName} */ +static DECLCALLBACK(int) rtDbgModCv_SymbolByName(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol, + PRTDBGSYMBOL pSymInfo) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + Assert(!pszSymbol[cchSymbol]); RT_NOREF_PV(cchSymbol); + return RTDbgModSymbolByName(pThis->hCnt, pszSymbol/*, cchSymbol*/, pSymInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByOrdinal} */ +static DECLCALLBACK(int) rtDbgModCv_SymbolByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + return RTDbgModSymbolByOrdinal(pThis->hCnt, iOrdinal, pSymInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolCount} */ +static DECLCALLBACK(uint32_t) rtDbgModCv_SymbolCount(PRTDBGMODINT pMod) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + return RTDbgModSymbolCount(pThis->hCnt); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolAdd} */ +static DECLCALLBACK(int) rtDbgModCv_SymbolAdd(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol, + RTDBGSEGIDX iSeg, RTUINTPTR off, RTUINTPTR cb, uint32_t fFlags, + uint32_t *piOrdinal) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + Assert(!pszSymbol[cchSymbol]); NOREF(cchSymbol); + return RTDbgModSymbolAdd(pThis->hCnt, pszSymbol, iSeg, off, cb, fFlags, piOrdinal); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentByIndex} */ +static DECLCALLBACK(int) rtDbgModCv_SegmentByIndex(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + return RTDbgModSegmentByIndex(pThis->hCnt, iSeg, pSegInfo); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentCount} */ +static DECLCALLBACK(RTDBGSEGIDX) rtDbgModCv_SegmentCount(PRTDBGMODINT pMod) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + return RTDbgModSegmentCount(pThis->hCnt); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentAdd} */ +static DECLCALLBACK(int) rtDbgModCv_SegmentAdd(PRTDBGMODINT pMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName, size_t cchName, + uint32_t fFlags, PRTDBGSEGIDX piSeg) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + Assert(!pszName[cchName]); NOREF(cchName); + return RTDbgModSegmentAdd(pThis->hCnt, uRva, cb, pszName, fFlags, piSeg); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnImageSize} */ +static DECLCALLBACK(RTUINTPTR) rtDbgModCv_ImageSize(PRTDBGMODINT pMod) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + if (pThis->cbImage) + return pThis->cbImage; + return RTDbgModImageSize(pThis->hCnt); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnRvaToSegOff} */ +static DECLCALLBACK(RTDBGSEGIDX) rtDbgModCv_RvaToSegOff(PRTDBGMODINT pMod, RTUINTPTR uRva, PRTUINTPTR poffSeg) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + return RTDbgModRvaToSegOff(pThis->hCnt, uRva, poffSeg); +} + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnClose} */ +static DECLCALLBACK(int) rtDbgModCv_Close(PRTDBGMODINT pMod) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + + RTDbgModRelease(pThis->hCnt); + if (pThis->hFile != NIL_RTFILE) + RTFileClose(pThis->hFile); + RTMemFree(pThis->paDirEnts); + RTMemFree(pThis); + + pMod->pvDbgPriv = NULL; /* for internal use */ + return VINF_SUCCESS; +} + + +/* + * + * Probing code used by rtDbgModCv_TryOpen. + * Probing code used by rtDbgModCv_TryOpen. + * + */ + + + +/** + * @callback_method_impl{FNRTLDRENUMSEGS, Used to add segments from the image} + */ +static DECLCALLBACK(int) rtDbgModCvAddSegmentsCallback(RTLDRMOD hLdrMod, PCRTLDRSEG pSeg, void *pvUser) +{ + PRTDBGMODCV pThis = (PRTDBGMODCV)pvUser; + Log(("Segment %s: LinkAddress=%#llx RVA=%#llx cb=%#llx\n", + pSeg->pszName, (uint64_t)pSeg->LinkAddress, (uint64_t)pSeg->RVA, pSeg->cb)); + NOREF(hLdrMod); + + /* If the segment doesn't have a mapping, just add a dummy so the indexing + works out correctly (same as for the image). */ + if (pSeg->RVA == NIL_RTLDRADDR) + return RTDbgModSegmentAdd(pThis->hCnt, 0, 0, pSeg->pszName, 0 /*fFlags*/, NULL); + + RTLDRADDR cb = RT_MAX(pSeg->cb, pSeg->cbMapped); + return RTDbgModSegmentAdd(pThis->hCnt, pSeg->RVA, cb, pSeg->pszName, 0 /*fFlags*/, NULL); +} + + +/** + * Copies the sections over from the DBG file. + * + * Called if we don't have an associated executable image. + * + * @returns IPRT status code. + * @param pThis The CV module instance. + * @param pDbgHdr The DBG file header. + * @param pszFilename The filename (for logging). + */ +static int rtDbgModCvAddSegmentsFromDbg(PRTDBGMODCV pThis, PCIMAGE_SEPARATE_DEBUG_HEADER pDbgHdr, const char *pszFilename) +{ + RT_NOREF_PV(pszFilename); + + /* + * Validate the header fields a little. + */ + if ( pDbgHdr->NumberOfSections < 1 + || pDbgHdr->NumberOfSections > 4096) + { + Log(("RTDbgModCv: Bad NumberOfSections: %d\n", pDbgHdr->NumberOfSections)); + return VERR_CV_BAD_FORMAT; + } + if (!RT_IS_POWER_OF_TWO(pDbgHdr->SectionAlignment)) + { + Log(("RTDbgModCv: Bad SectionAlignment: %#x\n", pDbgHdr->SectionAlignment)); + return VERR_CV_BAD_FORMAT; + } + + /* + * Read the section table. + */ + size_t cbShs = pDbgHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER); + PIMAGE_SECTION_HEADER paShs = (PIMAGE_SECTION_HEADER)RTMemAlloc(cbShs); + if (!paShs) + return VERR_NO_MEMORY; + int rc = RTFileReadAt(pThis->hFile, sizeof(*pDbgHdr), paShs, cbShs, NULL); + if (RT_SUCCESS(rc)) + { + /* + * Do some basic validation. + */ + uint32_t cbHeaders = 0; + uint32_t uRvaPrev = 0; + for (uint32_t i = 0; i < pDbgHdr->NumberOfSections; i++) + { + Log3(("RTDbgModCv: Section #%02u %#010x LB %#010x %.*s\n", + i, paShs[i].VirtualAddress, paShs[i].Misc.VirtualSize, sizeof(paShs[i].Name), paShs[i].Name)); + + if (paShs[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD) + continue; + + if (paShs[i].VirtualAddress < uRvaPrev) + { + Log(("RTDbgModCv: %s: Overlap or soring error, VirtualAddress=%#x uRvaPrev=%#x - section #%d '%.*s'!!!\n", + pszFilename, paShs[i].VirtualAddress, uRvaPrev, i, sizeof(paShs[i].Name), paShs[i].Name)); + rc = VERR_CV_BAD_FORMAT; + } + else if ( paShs[i].VirtualAddress > pDbgHdr->SizeOfImage + || paShs[i].Misc.VirtualSize > pDbgHdr->SizeOfImage + || paShs[i].VirtualAddress + paShs[i].Misc.VirtualSize > pDbgHdr->SizeOfImage) + { + Log(("RTDbgModCv: %s: VirtualAddress=%#x VirtualSize=%#x (total %x) - beyond image size (%#x) - section #%d '%.*s'!!!\n", + pszFilename, paShs[i].VirtualAddress, paShs[i].Misc.VirtualSize, + paShs[i].VirtualAddress + paShs[i].Misc.VirtualSize, + pThis->cbImage, i, sizeof(paShs[i].Name), paShs[i].Name)); + rc = VERR_CV_BAD_FORMAT; + } + else if (paShs[i].VirtualAddress & (pDbgHdr->SectionAlignment - 1)) + { + Log(("RTDbgModCv: %s: VirtualAddress=%#x misaligned (%#x) - section #%d '%.*s'!!!\n", + pszFilename, paShs[i].VirtualAddress, pDbgHdr->SectionAlignment, i, sizeof(paShs[i].Name), paShs[i].Name)); + rc = VERR_CV_BAD_FORMAT; + } + else + { + if (uRvaPrev == 0) + cbHeaders = paShs[i].VirtualAddress; + uRvaPrev = paShs[i].VirtualAddress + paShs[i].Misc.VirtualSize; + continue; + } + } + if (RT_SUCCESS(rc) && uRvaPrev == 0) + { + Log(("RTDbgModCv: %s: No loadable sections.\n", pszFilename)); + rc = VERR_CV_BAD_FORMAT; + } + if (RT_SUCCESS(rc) && cbHeaders == 0) + { + Log(("RTDbgModCv: %s: No space for PE headers.\n", pszFilename)); + rc = VERR_CV_BAD_FORMAT; + } + if (RT_SUCCESS(rc)) + { + /* + * Add sections. + */ + rc = RTDbgModSegmentAdd(pThis->hCnt, 0, cbHeaders, "NtHdrs", 0 /*fFlags*/, NULL); + for (uint32_t i = 0; RT_SUCCESS(rc) && i < pDbgHdr->NumberOfSections; i++) + { + char szName[sizeof(paShs[i].Name) + 1]; + memcpy(szName, paShs[i].Name, sizeof(paShs[i].Name)); + szName[sizeof(szName) - 1] = '\0'; + + if (paShs[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD) + rc = RTDbgModSegmentAdd(pThis->hCnt, 0, 0, szName, 0 /*fFlags*/, NULL); + else + rc = RTDbgModSegmentAdd(pThis->hCnt, paShs[i].VirtualAddress, paShs[i].Misc.VirtualSize, szName, + 0 /*fFlags*/, NULL); + } + if (RT_SUCCESS(rc)) + pThis->fHaveLoadedSegments = true; + } + } + + RTMemFree(paShs); + return rc; +} + + +/** + * Instantiates the CV/COFF reader. + * + * @returns IPRT status code + * @param pDbgMod The debug module instance. + * @param enmFileType The type of input file. + * @param hFile The file handle, NIL_RTFILE of image. + * @param ppThis Where to return the reader instance. + */ +static int rtDbgModCvCreateInstance(PRTDBGMODINT pDbgMod, RTCVFILETYPE enmFileType, RTFILE hFile, PRTDBGMODCV *ppThis) +{ + /* + * Do we already have an instance? Happens if we find multiple debug + * formats we support. + */ + PRTDBGMODCV pThis = (PRTDBGMODCV)pDbgMod->pvDbgPriv; + if (pThis) + { + Assert(pThis->enmType == enmFileType); + Assert(pThis->hFile == hFile); + Assert(pThis->pMod == pDbgMod); + *ppThis = pThis; + return VINF_SUCCESS; + } + + /* + * Create a new instance. + */ + pThis = (PRTDBGMODCV)RTMemAllocZ(sizeof(RTDBGMODCV)); + if (!pThis) + return VERR_NO_MEMORY; + int rc = RTDbgModCreate(&pThis->hCnt, pDbgMod->pszName, 0 /*cbSeg*/, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + pDbgMod->pvDbgPriv = pThis; + pThis->enmType = enmFileType; + pThis->hFile = hFile; + pThis->pMod = pDbgMod; + pThis->offBase = UINT32_MAX; + pThis->offCoffDbgInfo = UINT32_MAX; + *ppThis = pThis; + return VINF_SUCCESS; + } + RTMemFree(pThis); + return rc; +} + + +/** + * Common part of the COFF probing. + * + * @returns status code. + * @param pDbgMod The debug module instance. On success pvDbgPriv + * will point to a valid RTDBGMODCV. + * @param enmFileType The kind of file this is we're probing. + * @param hFile The file with debug info in it. + * @param off The offset where to expect CV debug info. + * @param cb The number of bytes of debug info. + * @param pszFilename The path to the file (for logging). + */ +static int rtDbgModCvProbeCoff(PRTDBGMODINT pDbgMod, RTCVFILETYPE enmFileType, RTFILE hFile, + uint32_t off, uint32_t cb, const char *pszFilename) +{ + RT_NOREF_PV(pszFilename); + + /* + * Check that there is sufficient data for a header, then read it. + */ + if (cb < sizeof(IMAGE_COFF_SYMBOLS_HEADER)) + { + Log(("RTDbgModCv: Not enough room for COFF header.\n")); + return VERR_BAD_EXE_FORMAT; + } + if (cb >= UINT32_C(128) * _1M) + { + Log(("RTDbgModCv: COFF debug information is to large (%'u bytes), max is 128MB\n", cb)); + return VERR_BAD_EXE_FORMAT; + } + + int rc; + IMAGE_COFF_SYMBOLS_HEADER Hdr; + if (hFile == NIL_RTFILE) + rc = pDbgMod->pImgVt->pfnReadAt(pDbgMod, UINT32_MAX, off, &Hdr, sizeof(Hdr)); + else + rc = RTFileReadAt(hFile, off, &Hdr, sizeof(Hdr), NULL); + if (RT_FAILURE(rc)) + { + Log(("RTDbgModCv: Error reading COFF header: %Rrc\n", rc)); + return rc; + } + + Log2(("RTDbgModCv: Found COFF debug info header at %#x (LB %#x) in %s\n", off, cb, pszFilename)); + Log2((" NumberOfSymbols = %#010x\n", Hdr.NumberOfSymbols)); + Log2((" LvaToFirstSymbol = %#010x\n", Hdr.LvaToFirstSymbol)); + Log2((" NumberOfLinenumbers = %#010x\n", Hdr.NumberOfLinenumbers)); + Log2((" LvaToFirstLinenumber = %#010x\n", Hdr.LvaToFirstLinenumber)); + Log2((" RvaToFirstByteOfCode = %#010x\n", Hdr.RvaToFirstByteOfCode)); + Log2((" RvaToLastByteOfCode = %#010x\n", Hdr.RvaToLastByteOfCode)); + Log2((" RvaToFirstByteOfData = %#010x\n", Hdr.RvaToFirstByteOfData)); + Log2((" RvaToLastByteOfData = %#010x\n", Hdr.RvaToLastByteOfData)); + + /* + * Validate the COFF header. + */ + if ( (uint64_t)Hdr.LvaToFirstSymbol + (uint64_t)Hdr.NumberOfSymbols * sizeof(IMAGE_SYMBOL) > cb + || (Hdr.LvaToFirstSymbol < sizeof(Hdr) && Hdr.NumberOfSymbols > 0)) + { + Log(("RTDbgModCv: Bad COFF symbol count or/and offset: LvaToFirstSymbol=%#x, NumberOfSymbols=%#x cbCoff=%#x\n", + Hdr.LvaToFirstSymbol, Hdr.NumberOfSymbols, cb)); + return VERR_BAD_EXE_FORMAT; + } + if ( (uint64_t)Hdr.LvaToFirstLinenumber + (uint64_t)Hdr.NumberOfLinenumbers * sizeof(IMAGE_LINENUMBER) > cb + || (Hdr.LvaToFirstLinenumber < sizeof(Hdr) && Hdr.NumberOfLinenumbers > 0)) + { + Log(("RTDbgModCv: Bad COFF symbol count or/and offset: LvaToFirstSymbol=%#x, NumberOfSymbols=%#x cbCoff=%#x\n", + Hdr.LvaToFirstSymbol, Hdr.NumberOfSymbols, cb)); + return VERR_BAD_EXE_FORMAT; + } + if (Hdr.NumberOfSymbols < 2) + { + Log(("RTDbgModCv: The COFF symbol table is too short to be of any worth... (%u syms)\n", Hdr.NumberOfSymbols)); + return VERR_NO_DATA; + } + + /* + * What we care about looks fine, use it. + */ + PRTDBGMODCV pThis; + rc = rtDbgModCvCreateInstance(pDbgMod, enmFileType, hFile, &pThis); + if (RT_SUCCESS(rc)) + { + pThis->offCoffDbgInfo = off; + pThis->cbCoffDbgInfo = cb; + pThis->CoffHdr = Hdr; + } + + return rc; +} + + +/** + * Common part of the CodeView probing. + * + * @returns status code. + * @param pDbgMod The debug module instance. On success pvDbgPriv + * will point to a valid RTDBGMODCV. + * @param pCvHdr The CodeView base header. + * @param enmFileType The kind of file this is we're probing. + * @param hFile The file with debug info in it. + * @param off The offset where to expect CV debug info. + * @param cb The number of bytes of debug info. + * @param enmArch The desired image architecture. + * @param pszFilename The path to the file (for logging). + */ +static int rtDbgModCvProbeCommon(PRTDBGMODINT pDbgMod, PRTCVHDR pCvHdr, RTCVFILETYPE enmFileType, RTFILE hFile, + uint32_t off, uint32_t cb, RTLDRARCH enmArch, const char *pszFilename) +{ + int rc = VERR_DBG_NO_MATCHING_INTERPRETER; + RT_NOREF_PV(enmArch); RT_NOREF_PV(pszFilename); + + /* Is a codeview format we (wish to) support? */ + if ( pCvHdr->u32Magic == RTCVHDR_MAGIC_NB11 + || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB09 + || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB08 + || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB05 + || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB04 + || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB02 + || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB00 + ) + { + /* We're assuming it's a base header, so the offset must be within + the area defined by the debug info we got from the loader. */ + if (pCvHdr->off < cb && pCvHdr->off >= sizeof(*pCvHdr)) + { + Log(("RTDbgModCv: Found %c%c%c%c at %#x - size %#x, directory at %#x. file type %d\n", + RT_BYTE1(pCvHdr->u32Magic), RT_BYTE2(pCvHdr->u32Magic), RT_BYTE3(pCvHdr->u32Magic), RT_BYTE4(pCvHdr->u32Magic), + off, cb, pCvHdr->off, enmFileType)); + + /* + * Create a module instance, if not already done. + */ + PRTDBGMODCV pThis; + rc = rtDbgModCvCreateInstance(pDbgMod, enmFileType, hFile, &pThis); + if (RT_SUCCESS(rc)) + { + pThis->u32CvMagic = pCvHdr->u32Magic; + pThis->offBase = off; + pThis->cbDbgInfo = cb; + pThis->offDir = pCvHdr->off; + return VINF_SUCCESS; + } + } + } + + return rc; +} + + +/** @callback_method_impl{FNRTLDRENUMDBG} */ +static DECLCALLBACK(int) rtDbgModCvEnumCallback(RTLDRMOD hLdrMod, PCRTLDRDBGINFO pDbgInfo, void *pvUser) +{ + PRTDBGMODINT pDbgMod = (PRTDBGMODINT)pvUser; + Assert(!pDbgMod->pvDbgPriv); + RT_NOREF_PV(hLdrMod); + + /* Skip external files, RTDbgMod will deal with those + via RTDBGMODINT::pszDbgFile. */ + if (pDbgInfo->pszExtFile) + return VINF_SUCCESS; + + /* We only handle the codeview sections. */ + if (pDbgInfo->enmType == RTLDRDBGINFOTYPE_CODEVIEW) + { + /* Read the specified header and check if we like it. */ + RTCVHDR CvHdr; + int rc = pDbgMod->pImgVt->pfnReadAt(pDbgMod, pDbgInfo->iDbgInfo, pDbgInfo->offFile, &CvHdr, sizeof(CvHdr)); + if (RT_SUCCESS(rc)) + rc = rtDbgModCvProbeCommon(pDbgMod, &CvHdr, RTCVFILETYPE_IMAGE, NIL_RTFILE, pDbgInfo->offFile, pDbgInfo->cb, + pDbgMod->pImgVt->pfnGetArch(pDbgMod), pDbgMod->pszImgFile); + } + else if (pDbgInfo->enmType == RTLDRDBGINFOTYPE_COFF) + { + /* Join paths with the DBG code. */ + rtDbgModCvProbeCoff(pDbgMod, RTCVFILETYPE_IMAGE, NIL_RTFILE, pDbgInfo->offFile, pDbgInfo->cb, pDbgMod->pszImgFile); + } + + return VINF_SUCCESS; +} + + +/** + * Part two of the external file probing. + * + * @returns status code. + * @param pThis The debug module instance. On success pvDbgPriv + * will point to a valid RTDBGMODCV. + * @param enmFileType The kind of file this is we're probing. + * @param hFile The file with debug info in it. + * @param off The offset where to expect CV debug info. + * @param cb The number of bytes of debug info. + * @param enmArch The desired image architecture. + * @param pszFilename The path to the file (for logging). + */ +static int rtDbgModCvProbeFile2(PRTDBGMODINT pThis, RTCVFILETYPE enmFileType, RTFILE hFile, uint32_t off, uint32_t cb, + RTLDRARCH enmArch, const char *pszFilename) +{ + RTCVHDR CvHdr; + int rc = RTFileReadAt(hFile, off, &CvHdr, sizeof(CvHdr), NULL); + if (RT_SUCCESS(rc)) + rc = rtDbgModCvProbeCommon(pThis, &CvHdr, enmFileType, hFile, off, cb, enmArch, pszFilename); + return rc; +} + + +/** + * Probes an external file for CodeView information. + * + * @returns status code. + * @param pDbgMod The debug module instance. On success pvDbgPriv + * will point to a valid RTDBGMODCV. + * @param pszFilename The path to the file to probe. + * @param enmArch The desired image architecture. + */ +static int rtDbgModCvProbeFile(PRTDBGMODINT pDbgMod, const char *pszFilename, RTLDRARCH enmArch) +{ + RTFILE hFile; + int rc = RTFileOpen(&hFile, pszFilename, RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN); + if (RT_FAILURE(rc)) + return rc; + + /* + * Check for .DBG file + */ + IMAGE_SEPARATE_DEBUG_HEADER DbgHdr; + rc = RTFileReadAt(hFile, 0, &DbgHdr, sizeof(DbgHdr), NULL); + if ( RT_SUCCESS(rc) + && DbgHdr.Signature == IMAGE_SEPARATE_DEBUG_SIGNATURE) + { + Log2(("RTDbgModCv: Found separate debug header in %s:\n", pszFilename)); + Log2((" Flags = %#x\n", DbgHdr.Flags)); + Log2((" Machine = %#x\n", DbgHdr.Machine)); + Log2((" Characteristics = %#x\n", DbgHdr.Characteristics)); + Log2((" TimeDateStamp = %#x\n", DbgHdr.TimeDateStamp)); + Log2((" CheckSum = %#x\n", DbgHdr.CheckSum)); + Log2((" ImageBase = %#x\n", DbgHdr.ImageBase)); + Log2((" SizeOfImage = %#x\n", DbgHdr.SizeOfImage)); + Log2((" NumberOfSections = %#x\n", DbgHdr.NumberOfSections)); + Log2((" ExportedNamesSize = %#x\n", DbgHdr.ExportedNamesSize)); + Log2((" DebugDirectorySize = %#x\n", DbgHdr.DebugDirectorySize)); + Log2((" SectionAlignment = %#x\n", DbgHdr.SectionAlignment)); + + /* + * Match up the architecture if specified. + */ + switch (enmArch) + { + case RTLDRARCH_X86_32: + if (DbgHdr.Machine != IMAGE_FILE_MACHINE_I386) + rc = VERR_LDR_ARCH_MISMATCH; + break; + case RTLDRARCH_AMD64: + if (DbgHdr.Machine != IMAGE_FILE_MACHINE_AMD64) + rc = VERR_LDR_ARCH_MISMATCH; + break; + + default: + case RTLDRARCH_HOST: + AssertFailed(); + case RTLDRARCH_WHATEVER: + break; + } + if (RT_FAILURE(rc)) + { + RTFileClose(hFile); + return rc; + } + + /* + * Probe for readable debug info in the debug directory. + */ + uint32_t offDbgDir = sizeof(DbgHdr) + + DbgHdr.NumberOfSections * sizeof(IMAGE_SECTION_HEADER) + + DbgHdr.ExportedNamesSize; + + uint32_t cEntries = DbgHdr.DebugDirectorySize / sizeof(IMAGE_DEBUG_DIRECTORY); + for (uint32_t i = 0; i < cEntries; i++, offDbgDir += sizeof(IMAGE_DEBUG_DIRECTORY)) + { + IMAGE_DEBUG_DIRECTORY DbgDir; + rc = RTFileReadAt(hFile, offDbgDir, &DbgDir, sizeof(DbgDir), NULL); + if (RT_FAILURE(rc)) + break; + if (DbgDir.Type == IMAGE_DEBUG_TYPE_CODEVIEW) + rc = rtDbgModCvProbeFile2(pDbgMod, RTCVFILETYPE_DBG, hFile, + DbgDir.PointerToRawData, DbgDir.SizeOfData, + enmArch, pszFilename); + else if (DbgDir.Type == IMAGE_DEBUG_TYPE_COFF) + rc = rtDbgModCvProbeCoff(pDbgMod, RTCVFILETYPE_DBG, hFile, + DbgDir.PointerToRawData, DbgDir.SizeOfData, pszFilename); + } + + /* + * If we get down here with an instance, it prooves that we've found + * something, regardless of any errors. Add the sections and such. + */ + PRTDBGMODCV pThis = (PRTDBGMODCV)pDbgMod->pvDbgPriv; + if (pThis) + { + pThis->cbImage = DbgHdr.SizeOfImage; + if (pDbgMod->pImgVt) + rc = VINF_SUCCESS; + else + { + rc = rtDbgModCvAddSegmentsFromDbg(pThis, &DbgHdr, pszFilename); + if (RT_FAILURE(rc)) + rtDbgModCv_Close(pDbgMod); + } + return rc; + } + + /* Failed to find CV or smth, look at the end of the file just to be sure... */ + } + + /* + * Look for CV tail header. + */ + uint64_t cbFile; + rc = RTFileSeek(hFile, -(RTFOFF)sizeof(RTCVHDR), RTFILE_SEEK_END, &cbFile); + if (RT_SUCCESS(rc)) + { + cbFile += sizeof(RTCVHDR); + RTCVHDR CvHdr; + rc = RTFileRead(hFile, &CvHdr, sizeof(CvHdr), NULL); + if (RT_SUCCESS(rc)) + rc = rtDbgModCvProbeFile2(pDbgMod, RTCVFILETYPE_OTHER_AT_END, hFile, + cbFile - CvHdr.off, CvHdr.off, enmArch, pszFilename); + } + + if (RT_FAILURE(rc)) + RTFileClose(hFile); + return rc; +} + + + +/** @interface_method_impl{RTDBGMODVTDBG,pfnTryOpen} */ +static DECLCALLBACK(int) rtDbgModCv_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch) +{ + /* + * Look for debug info. + */ + int rc = VERR_DBG_NO_MATCHING_INTERPRETER; + if (pMod->pszDbgFile) + rc = rtDbgModCvProbeFile(pMod, pMod->pszDbgFile, enmArch); + + if (!pMod->pvDbgPriv && pMod->pImgVt) + { + int rc2 = pMod->pImgVt->pfnEnumDbgInfo(pMod, rtDbgModCvEnumCallback, pMod); + if (RT_FAILURE(rc2)) + rc = rc2; + + if (!pMod->pvDbgPriv) + { + /* Try the executable in case it has a NBxx tail header. */ + rc2 = rtDbgModCvProbeFile(pMod, pMod->pszImgFile, enmArch); + if (RT_FAILURE(rc2) && (RT_SUCCESS(rc) || rc == VERR_DBG_NO_MATCHING_INTERPRETER)) + rc = rc2; + } + } + + PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv; + if (!pThis) + return RT_SUCCESS_NP(rc) ? VERR_DBG_NO_MATCHING_INTERPRETER : rc; + Assert(pThis->offBase != UINT32_MAX || pThis->offCoffDbgInfo != UINT32_MAX); + + /* + * Load the debug info. + */ + if (pMod->pImgVt) + { + rc = pMod->pImgVt->pfnEnumSegments(pMod, rtDbgModCvAddSegmentsCallback, pThis); + pThis->fHaveLoadedSegments = true; + } + if (RT_SUCCESS(rc) && pThis->offBase != UINT32_MAX) + rc = rtDbgModCvLoadCodeViewInfo(pThis); + if (RT_SUCCESS(rc) && pThis->offCoffDbgInfo != UINT32_MAX) + rc = rtDbgModCvLoadCoffInfo(pThis); + if (RT_SUCCESS(rc)) + { + Log(("RTDbgCv: Successfully loaded debug info\n")); + return VINF_SUCCESS; + } + + Log(("RTDbgCv: Debug info load error %Rrc\n", rc)); + rtDbgModCv_Close(pMod); + return rc; +} + + + + + +/** Virtual function table for the CodeView debug info reader. */ +DECL_HIDDEN_CONST(RTDBGMODVTDBG) const g_rtDbgModVtDbgCodeView = +{ + /*.u32Magic = */ RTDBGMODVTDBG_MAGIC, + /*.fSupports = */ RT_DBGTYPE_CODEVIEW, + /*.pszName = */ "codeview", + /*.pfnTryOpen = */ rtDbgModCv_TryOpen, + /*.pfnClose = */ rtDbgModCv_Close, + + /*.pfnRvaToSegOff = */ rtDbgModCv_RvaToSegOff, + /*.pfnImageSize = */ rtDbgModCv_ImageSize, + + /*.pfnSegmentAdd = */ rtDbgModCv_SegmentAdd, + /*.pfnSegmentCount = */ rtDbgModCv_SegmentCount, + /*.pfnSegmentByIndex = */ rtDbgModCv_SegmentByIndex, + + /*.pfnSymbolAdd = */ rtDbgModCv_SymbolAdd, + /*.pfnSymbolCount = */ rtDbgModCv_SymbolCount, + /*.pfnSymbolByOrdinal = */ rtDbgModCv_SymbolByOrdinal, + /*.pfnSymbolByName = */ rtDbgModCv_SymbolByName, + /*.pfnSymbolByAddr = */ rtDbgModCv_SymbolByAddr, + + /*.pfnLineAdd = */ rtDbgModCv_LineAdd, + /*.pfnLineCount = */ rtDbgModCv_LineCount, + /*.pfnLineByOrdinal = */ rtDbgModCv_LineByOrdinal, + /*.pfnLineByAddr = */ rtDbgModCv_LineByAddr, + + /*.pfnUnwindFrame = */ rtDbgModCv_UnwindFrame, + + /*.u32EndMagic = */ RTDBGMODVTDBG_MAGIC +}; + |