/* $Id: dbgmodcodeview.cpp $ */ /** @file * IPRT - Debug Module Reader For Microsoft CodeView and COFF. * * Based on the following documentation (plus guess work and googling): * * - "Tools Interface Standard (TIS) Formats Specification for Windows", * dated February 1993, version 1.0. * * - "Visual C++ 5.0 Symbolic Debug Information Specification" chapter of * SPECS.CHM from MSDN Library October 2001. * * - "High Level Languages Debug Table Documentation", aka HLLDBG.HTML, aka * IBMHLL.HTML, last changed 1996-07-08. * * Testcases using RTLdrFlt: * - VBoxPcBios.sym at 0xf0000. * - NT4 kernel PE image (coff syms). */ /* * Copyright (C) 2013-2023 Oracle and/or its affiliates. * * This file is part of VirtualBox base platform packages, as * available from https://www.virtualbox.org. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, in version 3 of the * License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * The contents of this file may alternatively be used under the terms * of the Common Development and Distribution License Version 1.0 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included * in the VirtualBox distribution, in which case the provisions of the * CDDL are applicable instead of those of the GPL. * * You may elect to license modified versions of this file under the * terms and conditions of either the GPL or the CDDL or both. * * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #define LOG_GROUP RTLOGGROUP_DBG #include #include "internal/iprt.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "internal/dbgmod.h" #include "internal/magics.h" #include #include /********************************************************************************************************************************* * Structures and Typedefs * *********************************************************************************************************************************/ /** * File type. */ typedef enum RTCVFILETYPE { RTCVFILETYPE_INVALID = 0, /** Executable image. */ RTCVFILETYPE_IMAGE, /** A DBG-file with a IMAGE_SEPARATE_DEBUG_HEADER. */ RTCVFILETYPE_DBG, /** A PDB file. */ RTCVFILETYPE_PDB, /** Some other kind of file with CV at the end. */ RTCVFILETYPE_OTHER_AT_END, /** The end of the valid values. */ RTCVFILETYPE_END, /** Type blowup. */ RTCVFILETYPE_32BIT_HACK = 0x7fffffff } RTCVFILETYPE; /** * CodeView debug info reader instance. */ typedef struct RTDBGMODCV { /** Using a container for managing the debug info. */ RTDBGMOD hCnt; /** @name Codeview details * @{ */ /** The code view magic (used as format indicator). */ uint32_t u32CvMagic; /** The offset of the CV debug info in the file. */ uint32_t offBase; /** The size of the CV debug info. */ uint32_t cbDbgInfo; /** The offset of the subsection directory (relative to offBase). */ uint32_t offDir; /** @} */ /** @name COFF details. * @{ */ /** Offset of the COFF header. */ uint32_t offCoffDbgInfo; /** The size of the COFF debug info. */ uint32_t cbCoffDbgInfo; /** The COFF debug info header. */ IMAGE_COFF_SYMBOLS_HEADER CoffHdr; /** @} */ /** The file type. */ RTCVFILETYPE enmType; /** The file handle (if external). */ RTFILE hFile; /** Pointer to the module (no reference retained). */ PRTDBGMODINT pMod; /** The image size, if we know it. This is 0 if we don't know it. */ uint32_t cbImage; /** Indicates that we've loaded segments intot he container already. */ bool fHaveLoadedSegments; /** Alternative address translation method for DOS frames. */ bool fHaveDosFrames; /** @name Codeview Parsing state. * @{ */ /** Number of directory entries. */ uint32_t cDirEnts; /** The directory (converted to 32-bit). */ PRTCVDIRENT32 paDirEnts; /** Current debugging style when parsing modules. */ uint16_t uCurStyle; /** Current debugging style version (HLL only). */ uint16_t uCurStyleVer; /** The segment map (if present). */ PRTCVSEGMAP pSegMap; /** Segment names. */ char *pszzSegNames; /** The size of the segment names. */ uint32_t cbSegNames; /** Size of the block pchSrcStrings points to. */ size_t cbSrcStrings; /** Buffer space allocated for the source string table. */ size_t cbSrcStringsAlloc; /** Copy of the last CV8 source string table. */ char *pchSrcStrings; /** The size of the current source information table. */ size_t cbSrcInfo; /** Buffer space allocated for the source information table. */ size_t cbSrcInfoAlloc; /** Copy of the last CV8 source information table. */ uint8_t *pbSrcInfo; /** @} */ } RTDBGMODCV; /** Pointer to a codeview debug info reader instance. */ typedef RTDBGMODCV *PRTDBGMODCV; /** Pointer to a const codeview debug info reader instance. */ typedef RTDBGMODCV *PCRTDBGMODCV; /** * Subsection callback. * * @returns IPRT status code. * @param pThis The CodeView debug info reader instance. * @param pvSubSect Pointer to the subsection data. * @param cbSubSect The size of the subsection data. * @param pDirEnt The directory entry. */ typedef DECLCALLBACKTYPE(int, FNDBGMODCVSUBSECTCALLBACK,(PRTDBGMODCV pThis, void const *pvSubSect, size_t cbSubSect, PCRTCVDIRENT32 pDirEnt)); /** Pointer to a subsection callback. */ typedef FNDBGMODCVSUBSECTCALLBACK *PFNDBGMODCVSUBSECTCALLBACK; /********************************************************************************************************************************* * Defined Constants And Macros * *********************************************************************************************************************************/ /** Light weight assert + return w/ fixed status code. */ #define RTDBGMODCV_CHECK_RET_BF(a_Expr, a_LogArgs) \ do { \ if (!(a_Expr)) \ { \ Log(("RTDbgCv: Check failed on line %d: " #a_Expr "\n", __LINE__)); \ Log(a_LogArgs); \ /*AssertFailed();*/ \ return VERR_CV_BAD_FORMAT; \ } \ } while (0) /** Light weight assert + return w/ fixed status code. */ #define RTDBGMODCV_CHECK_NOMSG_RET_BF(a_Expr) \ do { \ if (!(a_Expr)) \ { \ Log(("RTDbgCv: Check failed on line %d: " #a_Expr "\n", __LINE__)); \ /*AssertFailed();*/ \ return VERR_CV_BAD_FORMAT; \ } \ } while (0) /** * Reads CodeView information. * * @returns IPRT status code. * @param pThis The CodeView reader instance. * @param off The offset to start reading at, relative to the * CodeView base header. * @param pvBuf The buffer to read into. * @param cb How many bytes to read. */ static int rtDbgModCvReadAt(PRTDBGMODCV pThis, uint32_t off, void *pvBuf, size_t cb) { int rc; if (pThis->hFile == NIL_RTFILE) rc = pThis->pMod->pImgVt->pfnReadAt(pThis->pMod, UINT32_MAX, off + pThis->offBase, pvBuf, cb); else rc = RTFileReadAt(pThis->hFile, off + pThis->offBase, pvBuf, cb, NULL); return rc; } /** * Reads CodeView information into an allocated buffer. * * @returns IPRT status code. * @param pThis The CodeView reader instance. * @param off The offset to start reading at, relative to the * CodeView base header. * @param ppvBuf Where to return the allocated buffer on success. * @param cb How many bytes to read. */ static int rtDbgModCvReadAtAlloc(PRTDBGMODCV pThis, uint32_t off, void **ppvBuf, size_t cb) { int rc; void *pvBuf = *ppvBuf = RTMemAlloc(cb); if (pvBuf) { if (pThis->hFile == NIL_RTFILE) rc = pThis->pMod->pImgVt->pfnReadAt(pThis->pMod, UINT32_MAX, off + pThis->offBase, pvBuf, cb); else rc = RTFileReadAt(pThis->hFile, off + pThis->offBase, pvBuf, cb, NULL); if (RT_SUCCESS(rc)) return VINF_SUCCESS; RTMemFree(pvBuf); *ppvBuf = NULL; } else rc = VERR_NO_MEMORY; return rc; } #ifdef LOG_ENABLED /** * Gets a name string for a subsection type. * * @returns Section name (read only). * @param uSubSectType The subsection type. */ static const char *rtDbgModCvGetSubSectionName(uint16_t uSubSectType) { switch (uSubSectType) { case kCvSst_OldModule: return "sstOldModule"; case kCvSst_OldPublic: return "sstOldPublic"; case kCvSst_OldTypes: return "sstOldTypes"; case kCvSst_OldSymbols: return "sstOldSymbols"; case kCvSst_OldSrcLines: return "sstOldSrcLines"; case kCvSst_OldLibraries: return "sstOldLibraries"; case kCvSst_OldImports: return "sstOldImports"; case kCvSst_OldCompacted: return "sstOldCompacted"; case kCvSst_OldSrcLnSeg: return "sstOldSrcLnSeg"; case kCvSst_OldSrcLines3: return "sstOldSrcLines3"; case kCvSst_Module: return "sstModule"; case kCvSst_Types: return "sstTypes"; case kCvSst_Public: return "sstPublic"; case kCvSst_PublicSym: return "sstPublicSym"; case kCvSst_Symbols: return "sstSymbols"; case kCvSst_AlignSym: return "sstAlignSym"; case kCvSst_SrcLnSeg: return "sstSrcLnSeg"; case kCvSst_SrcModule: return "sstSrcModule"; case kCvSst_Libraries: return "sstLibraries"; case kCvSst_GlobalSym: return "sstGlobalSym"; case kCvSst_GlobalPub: return "sstGlobalPub"; case kCvSst_GlobalTypes: return "sstGlobalTypes"; case kCvSst_MPC: return "sstMPC"; case kCvSst_SegMap: return "sstSegMap"; case kCvSst_SegName: return "sstSegName"; case kCvSst_PreComp: return "sstPreComp"; case kCvSst_PreCompMap: return "sstPreCompMap"; case kCvSst_OffsetMap16: return "sstOffsetMap16"; case kCvSst_OffsetMap32: return "sstOffsetMap32"; case kCvSst_FileIndex: return "sstFileIndex"; case kCvSst_StaticSym: return "sstStaticSym"; } static char s_sz[32]; RTStrPrintf(s_sz, sizeof(s_sz), "Unknown%#x", uSubSectType); return s_sz; } #endif /* LOG_ENABLED */ #ifdef LOG_ENABLED /** * Gets a name string for a symbol type. * * @returns symbol type name (read only). * @param enmSymType The symbol type to name. */ static const char *rtDbgModCvSsSymTypeName(RTCVSYMTYPE enmSymType) { switch (enmSymType) { # define CASE_RET_STR(Name) case kCvSymType_##Name: return #Name; CASE_RET_STR(Compile); CASE_RET_STR(Register); CASE_RET_STR(Constant); CASE_RET_STR(UDT); CASE_RET_STR(SSearch); CASE_RET_STR(End); CASE_RET_STR(Skip); CASE_RET_STR(CVReserve); CASE_RET_STR(ObjName); CASE_RET_STR(EndArg); CASE_RET_STR(CobolUDT); CASE_RET_STR(ManyReg); CASE_RET_STR(Return); CASE_RET_STR(EntryThis); CASE_RET_STR(BpRel16); CASE_RET_STR(LData16); CASE_RET_STR(GData16); CASE_RET_STR(Pub16); CASE_RET_STR(LProc16); CASE_RET_STR(GProc16); CASE_RET_STR(Thunk16); CASE_RET_STR(BLock16); CASE_RET_STR(With16); CASE_RET_STR(Label16); CASE_RET_STR(CExModel16); CASE_RET_STR(VftPath16); CASE_RET_STR(RegRel16); CASE_RET_STR(BpRel32); CASE_RET_STR(LData32); CASE_RET_STR(GData32); CASE_RET_STR(Pub32); CASE_RET_STR(LProc32); CASE_RET_STR(GProc32); CASE_RET_STR(Thunk32); CASE_RET_STR(Block32); CASE_RET_STR(With32); CASE_RET_STR(Label32); CASE_RET_STR(CExModel32); CASE_RET_STR(VftPath32); CASE_RET_STR(RegRel32); CASE_RET_STR(LThread32); CASE_RET_STR(GThread32); CASE_RET_STR(LProcMips); CASE_RET_STR(GProcMips); CASE_RET_STR(ProcRef); CASE_RET_STR(DataRef); CASE_RET_STR(Align); CASE_RET_STR(LProcRef); CASE_RET_STR(V2_Register); CASE_RET_STR(V2_Constant); CASE_RET_STR(V2_Udt); CASE_RET_STR(V2_CobolUdt); CASE_RET_STR(V2_ManyReg); CASE_RET_STR(V2_BpRel); CASE_RET_STR(V2_LData); CASE_RET_STR(V2_GData); CASE_RET_STR(V2_Pub); CASE_RET_STR(V2_LProc); CASE_RET_STR(V2_GProc); CASE_RET_STR(V2_VftTable); CASE_RET_STR(V2_RegRel); CASE_RET_STR(V2_LThread); CASE_RET_STR(V2_GThread); CASE_RET_STR(V2_Unknown_1010); CASE_RET_STR(V2_Unknown_1011); CASE_RET_STR(V2_FrameInfo); CASE_RET_STR(V2_Compliand); CASE_RET_STR(V3_Compliand); CASE_RET_STR(V3_Thunk); CASE_RET_STR(V3_Block); CASE_RET_STR(V3_Unknown_1104); CASE_RET_STR(V3_Label); CASE_RET_STR(V3_Register); CASE_RET_STR(V3_Constant); CASE_RET_STR(V3_Udt); CASE_RET_STR(V3_Unknown_1109); CASE_RET_STR(V3_Unknown_110a); CASE_RET_STR(V3_BpRel); CASE_RET_STR(V3_LData); CASE_RET_STR(V3_GData); CASE_RET_STR(V3_Pub); CASE_RET_STR(V3_LProc); CASE_RET_STR(V3_GProc); CASE_RET_STR(V3_RegRel); CASE_RET_STR(V3_LThread); CASE_RET_STR(V3_GThread); CASE_RET_STR(V3_Unknown_1114); CASE_RET_STR(V3_Unknown_1115); CASE_RET_STR(V3_MSTool); CASE_RET_STR(V3_PubFunc1); CASE_RET_STR(V3_PubFunc2); CASE_RET_STR(V3_SectInfo); CASE_RET_STR(V3_SubSectInfo); CASE_RET_STR(V3_Entrypoint); CASE_RET_STR(V3_Unknown_1139); CASE_RET_STR(V3_SecuCookie); CASE_RET_STR(V3_Unknown_113b); CASE_RET_STR(V3_MsToolInfo); CASE_RET_STR(V3_MsToolEnv); CASE_RET_STR(VS2013_Local); CASE_RET_STR(VS2013_FpOff); CASE_RET_STR(VS2013_LProc32); CASE_RET_STR(VS2013_GProc32); #undef CASE_RET_STR case kCvSymType_EndOfValues: break; } return ""; } #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: %#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 };