VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/dbg/dbgmodcodeview.cpp

Last change on this file was 103415, checked in by vboxsync, 3 months ago

Additions,Main,VMM,Runtime: Fix some unused expression warnings, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 157.7 KB
Line 
1/* $Id: dbgmodcodeview.cpp 103415 2024-02-19 07:52:27Z vboxsync $ */
2/** @file
3 * IPRT - Debug Module Reader For Microsoft CodeView and COFF.
4 *
5 * Based on the following documentation (plus guess work and googling):
6 *
7 * - "Tools Interface Standard (TIS) Formats Specification for Windows",
8 * dated February 1993, version 1.0.
9 *
10 * - "Visual C++ 5.0 Symbolic Debug Information Specification" chapter of
11 * SPECS.CHM from MSDN Library October 2001.
12 *
13 * - "High Level Languages Debug Table Documentation", aka HLLDBG.HTML, aka
14 * IBMHLL.HTML, last changed 1996-07-08.
15 *
16 * Testcases using RTLdrFlt:
17 * - VBoxPcBios.sym at 0xf0000.
18 * - NT4 kernel PE image (coff syms).
19 */
20
21/*
22 * Copyright (C) 2013-2023 Oracle and/or its affiliates.
23 *
24 * This file is part of VirtualBox base platform packages, as
25 * available from https://www.virtualbox.org.
26 *
27 * This program is free software; you can redistribute it and/or
28 * modify it under the terms of the GNU General Public License
29 * as published by the Free Software Foundation, in version 3 of the
30 * License.
31 *
32 * This program is distributed in the hope that it will be useful, but
33 * WITHOUT ANY WARRANTY; without even the implied warranty of
34 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
35 * General Public License for more details.
36 *
37 * You should have received a copy of the GNU General Public License
38 * along with this program; if not, see <https://www.gnu.org/licenses>.
39 *
40 * The contents of this file may alternatively be used under the terms
41 * of the Common Development and Distribution License Version 1.0
42 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
43 * in the VirtualBox distribution, in which case the provisions of the
44 * CDDL are applicable instead of those of the GPL.
45 *
46 * You may elect to license modified versions of this file under the
47 * terms and conditions of either the GPL or the CDDL or both.
48 *
49 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
50 */
51
52
53/*********************************************************************************************************************************
54* Header Files *
55*********************************************************************************************************************************/
56#define LOG_GROUP RTLOGGROUP_DBG
57#include <iprt/dbg.h>
58#include "internal/iprt.h"
59
60#include <iprt/alloca.h>
61#include <iprt/asm.h>
62#include <iprt/assert.h>
63#include <iprt/err.h>
64#include <iprt/file.h>
65#include <iprt/fsvfs.h>
66#include <iprt/latin1.h>
67#include <iprt/log.h>
68#include <iprt/mem.h>
69#include <iprt/param.h>
70#include <iprt/path.h>
71#include <iprt/sort.h>
72#include <iprt/string.h>
73#include <iprt/strcache.h>
74#include <iprt/vfs.h>
75#include "internal/dbgmod.h"
76#include "internal/magics.h"
77
78#include <iprt/formats/codeview.h>
79#include <iprt/formats/pecoff.h>
80
81
82/*********************************************************************************************************************************
83* Structures and Typedefs *
84*********************************************************************************************************************************/
85/**
86 * File type.
87 */
88typedef enum RTCVFILETYPE
89{
90 RTCVFILETYPE_INVALID = 0,
91 /** Executable image. */
92 RTCVFILETYPE_IMAGE,
93 /** A DBG-file with a IMAGE_SEPARATE_DEBUG_HEADER. */
94 RTCVFILETYPE_DBG,
95 /** A PDB file. */
96 RTCVFILETYPE_PDB,
97 /** Some other kind of file with CV at the end. */
98 RTCVFILETYPE_OTHER_AT_END,
99 /** The end of the valid values. */
100 RTCVFILETYPE_END,
101 /** Type blowup. */
102 RTCVFILETYPE_32BIT_HACK = 0x7fffffff
103} RTCVFILETYPE;
104
105
106/**
107 * CodeView debug info reader instance.
108 */
109typedef struct RTDBGMODCV
110{
111 /** Using a container for managing the debug info. */
112 RTDBGMOD hCnt;
113
114 /** @name Codeview details
115 * @{ */
116 /** The code view magic (used as format indicator). */
117 uint32_t u32CvMagic;
118 /** The offset of the CV debug info in the file. */
119 uint32_t offBase;
120 /** The size of the CV debug info. */
121 uint32_t cbDbgInfo;
122 /** The offset of the subsection directory (relative to offBase). */
123 uint32_t offDir;
124 /** @} */
125
126 /** @name COFF details.
127 * @{ */
128 /** Offset of the COFF header. */
129 uint32_t offCoffDbgInfo;
130 /** The size of the COFF debug info. */
131 uint32_t cbCoffDbgInfo;
132 /** The COFF debug info header. */
133 IMAGE_COFF_SYMBOLS_HEADER CoffHdr;
134 /** @} */
135
136 /** @name External PDB (v2) with a DBG file.
137 * @{ */
138 /** The resolved PDB path (RTStrFree). */
139 char *pszPdbFilename;
140 /** The PDB VFS. This is only valid between rtDbgModCvOpenPdb20Callback()
141 * and the end of rtDbgModCv_TryOpen(). */
142 RTVFS hVfsPdb;
143 /** @} */
144
145 /** The file type. */
146 RTCVFILETYPE enmType;
147 /** The file handle (if external). */
148 RTFILE hFile;
149 /** Pointer to the module (no reference retained). */
150 PRTDBGMODINT pMod;
151
152 /** The image size, if we know it. This is 0 if we don't know it. */
153 uint32_t cbImage;
154
155 /** Indicates that we've loaded segments into the container already. */
156 bool fHaveLoadedSegments;
157 /** Alternative address translation method for DOS frames. */
158 bool fHaveDosFrames;
159
160 /** @name Codeview Parsing state.
161 * @{ */
162 /** Number of directory entries. */
163 uint32_t cDirEnts;
164 /** The directory (converted to 32-bit). */
165 PRTCVDIRENT32 paDirEnts;
166 /** Current debugging style when parsing modules. */
167 uint16_t uCurStyle;
168 /** Current debugging style version (HLL only). */
169 uint16_t uCurStyleVer;
170
171 /** The segment map (if present). */
172 PRTCVSEGMAP pSegMap;
173 /** Segment names. */
174 char *pszzSegNames;
175 /** The size of the segment names. */
176 uint32_t cbSegNames;
177
178 /** Size of the block pchSrcStrings points to. */
179 size_t cbSrcStrings;
180 /** Buffer space allocated for the source string table. */
181 size_t cbSrcStringsAlloc;
182 /** Copy of the last CV8 source string table. */
183 char *pchSrcStrings;
184
185 /** The size of the current source information table. */
186 size_t cbSrcInfo;
187 /** Buffer space allocated for the source information table. */
188 size_t cbSrcInfoAlloc;
189 /** Copy of the last CV8 source information table. */
190 uint8_t *pbSrcInfo;
191
192 /** @} */
193
194} RTDBGMODCV;
195/** Pointer to a codeview debug info reader instance. */
196typedef RTDBGMODCV *PRTDBGMODCV;
197/** Pointer to a const codeview debug info reader instance. */
198typedef RTDBGMODCV *PCRTDBGMODCV;
199
200
201
202/**
203 * Subsection callback.
204 *
205 * @returns IPRT status code.
206 * @param pThis The CodeView debug info reader instance.
207 * @param pvSubSect Pointer to the subsection data.
208 * @param cbSubSect The size of the subsection data.
209 * @param pDirEnt The directory entry.
210 */
211typedef DECLCALLBACKTYPE(int, FNDBGMODCVSUBSECTCALLBACK,(PRTDBGMODCV pThis, void const *pvSubSect, size_t cbSubSect,
212 PCRTCVDIRENT32 pDirEnt));
213/** Pointer to a subsection callback. */
214typedef FNDBGMODCVSUBSECTCALLBACK *PFNDBGMODCVSUBSECTCALLBACK;
215
216
217
218/*********************************************************************************************************************************
219* Defined Constants And Macros *
220*********************************************************************************************************************************/
221/** Light weight assert + return w/ fixed status code. */
222#define RTDBGMODCV_CHECK_RET_BF(a_Expr, a_LogArgs) \
223 do { \
224 if (!(a_Expr)) \
225 { \
226 Log(("RTDbgCv: Check failed on line %d: " #a_Expr "\n", __LINE__)); \
227 Log(a_LogArgs); \
228 /*AssertFailed();*/ \
229 return VERR_CV_BAD_FORMAT; \
230 } \
231 } while (0)
232
233
234/** Light weight assert + return w/ fixed status code. */
235#define RTDBGMODCV_CHECK_NOMSG_RET_BF(a_Expr) \
236 do { \
237 if (!(a_Expr)) \
238 { \
239 Log(("RTDbgCv: Check failed on line %d: " #a_Expr "\n", __LINE__)); \
240 /*AssertFailed();*/ \
241 return VERR_CV_BAD_FORMAT; \
242 } \
243 } while (0)
244
245
246
247/*********************************************************************************************************************************
248* Internal Functions *
249*********************************************************************************************************************************/
250static int rtDbgModPdb_CommonOpenWorker(PRTDBGMODCV pThis, const char *pszFilename, RTVFS hVfsPdb, RTDBGCFG hDbgCfg);
251
252
253
254/**
255 * Reads CodeView information.
256 *
257 * @returns IPRT status code.
258 * @param pThis The CodeView reader instance.
259 * @param off The offset to start reading at, relative to the
260 * CodeView base header.
261 * @param pvBuf The buffer to read into.
262 * @param cb How many bytes to read.
263 */
264static int rtDbgModCvReadAt(PRTDBGMODCV pThis, uint32_t off, void *pvBuf, size_t cb)
265{
266 int rc;
267 if (pThis->hFile == NIL_RTFILE)
268 rc = pThis->pMod->pImgVt->pfnReadAt(pThis->pMod, UINT32_MAX, off + pThis->offBase, pvBuf, cb);
269 else
270 rc = RTFileReadAt(pThis->hFile, off + pThis->offBase, pvBuf, cb, NULL);
271 return rc;
272}
273
274
275/**
276 * Reads CodeView information into an allocated buffer.
277 *
278 * @returns IPRT status code.
279 * @param pThis The CodeView reader instance.
280 * @param off The offset to start reading at, relative to the
281 * CodeView base header.
282 * @param ppvBuf Where to return the allocated buffer on success.
283 * @param cb How many bytes to read.
284 */
285static int rtDbgModCvReadAtAlloc(PRTDBGMODCV pThis, uint32_t off, void **ppvBuf, size_t cb)
286{
287 int rc;
288 void *pvBuf = *ppvBuf = RTMemAlloc(cb);
289 if (pvBuf)
290 {
291 if (pThis->hFile == NIL_RTFILE)
292 rc = pThis->pMod->pImgVt->pfnReadAt(pThis->pMod, UINT32_MAX, off + pThis->offBase, pvBuf, cb);
293 else
294 rc = RTFileReadAt(pThis->hFile, off + pThis->offBase, pvBuf, cb, NULL);
295 if (RT_SUCCESS(rc))
296 return VINF_SUCCESS;
297
298 RTMemFree(pvBuf);
299 *ppvBuf = NULL;
300 }
301 else
302 rc = VERR_NO_MEMORY;
303 return rc;
304}
305
306
307#ifdef LOG_ENABLED
308/**
309 * Gets a name string for a subsection type.
310 *
311 * @returns Section name (read only).
312 * @param uSubSectType The subsection type.
313 */
314static const char *rtDbgModCvGetSubSectionName(uint16_t uSubSectType)
315{
316 switch (uSubSectType)
317 {
318 case kCvSst_OldModule: return "sstOldModule";
319 case kCvSst_OldPublic: return "sstOldPublic";
320 case kCvSst_OldTypes: return "sstOldTypes";
321 case kCvSst_OldSymbols: return "sstOldSymbols";
322 case kCvSst_OldSrcLines: return "sstOldSrcLines";
323 case kCvSst_OldLibraries: return "sstOldLibraries";
324 case kCvSst_OldImports: return "sstOldImports";
325 case kCvSst_OldCompacted: return "sstOldCompacted";
326 case kCvSst_OldSrcLnSeg: return "sstOldSrcLnSeg";
327 case kCvSst_OldSrcLines3: return "sstOldSrcLines3";
328
329 case kCvSst_Module: return "sstModule";
330 case kCvSst_Types: return "sstTypes";
331 case kCvSst_Public: return "sstPublic";
332 case kCvSst_PublicSym: return "sstPublicSym";
333 case kCvSst_Symbols: return "sstSymbols";
334 case kCvSst_AlignSym: return "sstAlignSym";
335 case kCvSst_SrcLnSeg: return "sstSrcLnSeg";
336 case kCvSst_SrcModule: return "sstSrcModule";
337 case kCvSst_Libraries: return "sstLibraries";
338 case kCvSst_GlobalSym: return "sstGlobalSym";
339 case kCvSst_GlobalPub: return "sstGlobalPub";
340 case kCvSst_GlobalTypes: return "sstGlobalTypes";
341 case kCvSst_MPC: return "sstMPC";
342 case kCvSst_SegMap: return "sstSegMap";
343 case kCvSst_SegName: return "sstSegName";
344 case kCvSst_PreComp: return "sstPreComp";
345 case kCvSst_PreCompMap: return "sstPreCompMap";
346 case kCvSst_OffsetMap16: return "sstOffsetMap16";
347 case kCvSst_OffsetMap32: return "sstOffsetMap32";
348 case kCvSst_FileIndex: return "sstFileIndex";
349 case kCvSst_StaticSym: return "sstStaticSym";
350 }
351 static char s_sz[32];
352 RTStrPrintf(s_sz, sizeof(s_sz), "Unknown%#x", uSubSectType);
353 return s_sz;
354}
355#endif /* LOG_ENABLED */
356
357
358#ifdef LOG_ENABLED
359/**
360 * Gets a name string for a symbol type.
361 *
362 * @returns symbol type name (read only).
363 * @param enmSymType The symbol type to name.
364 */
365static const char *rtDbgModCvSsSymTypeName(RTCVSYMTYPE enmSymType)
366{
367 switch (enmSymType)
368 {
369# define CASE_RET_STR(Name) case kCvSymType_##Name: return #Name;
370 CASE_RET_STR(Compile);
371 CASE_RET_STR(Register);
372 CASE_RET_STR(Constant);
373 CASE_RET_STR(UDT);
374 CASE_RET_STR(SSearch);
375 CASE_RET_STR(End);
376 CASE_RET_STR(Skip);
377 CASE_RET_STR(CVReserve);
378 CASE_RET_STR(ObjName);
379 CASE_RET_STR(EndArg);
380 CASE_RET_STR(CobolUDT);
381 CASE_RET_STR(ManyReg);
382 CASE_RET_STR(Return);
383 CASE_RET_STR(EntryThis);
384 CASE_RET_STR(BpRel16);
385 CASE_RET_STR(LData16);
386 CASE_RET_STR(GData16);
387 CASE_RET_STR(Pub16);
388 CASE_RET_STR(LProc16);
389 CASE_RET_STR(GProc16);
390 CASE_RET_STR(Thunk16);
391 CASE_RET_STR(BLock16);
392 CASE_RET_STR(With16);
393 CASE_RET_STR(Label16);
394 CASE_RET_STR(CExModel16);
395 CASE_RET_STR(VftPath16);
396 CASE_RET_STR(RegRel16);
397 CASE_RET_STR(BpRel32);
398 CASE_RET_STR(LData32);
399 CASE_RET_STR(GData32);
400 CASE_RET_STR(Pub32);
401 CASE_RET_STR(LProc32);
402 CASE_RET_STR(GProc32);
403 CASE_RET_STR(Thunk32);
404 CASE_RET_STR(Block32);
405 CASE_RET_STR(With32);
406 CASE_RET_STR(Label32);
407 CASE_RET_STR(CExModel32);
408 CASE_RET_STR(VftPath32);
409 CASE_RET_STR(RegRel32);
410 CASE_RET_STR(LThread32);
411 CASE_RET_STR(GThread32);
412 CASE_RET_STR(LProcMips);
413 CASE_RET_STR(GProcMips);
414 CASE_RET_STR(ProcRef);
415 CASE_RET_STR(DataRef);
416 CASE_RET_STR(Align);
417 CASE_RET_STR(LProcRef);
418 CASE_RET_STR(V2_Register);
419 CASE_RET_STR(V2_Constant);
420 CASE_RET_STR(V2_Udt);
421 CASE_RET_STR(V2_CobolUdt);
422 CASE_RET_STR(V2_ManyReg);
423 CASE_RET_STR(V2_BpRel);
424 CASE_RET_STR(V2_LData);
425 CASE_RET_STR(V2_GData);
426 CASE_RET_STR(V2_Pub);
427 CASE_RET_STR(V2_LProc);
428 CASE_RET_STR(V2_GProc);
429 CASE_RET_STR(V2_VftTable);
430 CASE_RET_STR(V2_RegRel);
431 CASE_RET_STR(V2_LThread);
432 CASE_RET_STR(V2_GThread);
433 CASE_RET_STR(V2_LProcMips);
434 CASE_RET_STR(V2_GProcMips);
435 CASE_RET_STR(V2_FrameInfo);
436 CASE_RET_STR(V2_Compliand);
437 CASE_RET_STR(V3_Compliand);
438 CASE_RET_STR(V3_Thunk);
439 CASE_RET_STR(V3_Block);
440 CASE_RET_STR(V3_Unknown_1104);
441 CASE_RET_STR(V3_Label);
442 CASE_RET_STR(V3_Register);
443 CASE_RET_STR(V3_Constant);
444 CASE_RET_STR(V3_Udt);
445 CASE_RET_STR(V3_Unknown_1109);
446 CASE_RET_STR(V3_Unknown_110a);
447 CASE_RET_STR(V3_BpRel);
448 CASE_RET_STR(V3_LData);
449 CASE_RET_STR(V3_GData);
450 CASE_RET_STR(V3_Pub);
451 CASE_RET_STR(V3_LProc);
452 CASE_RET_STR(V3_GProc);
453 CASE_RET_STR(V3_RegRel);
454 CASE_RET_STR(V3_LThread);
455 CASE_RET_STR(V3_GThread);
456 CASE_RET_STR(V3_Unknown_1114);
457 CASE_RET_STR(V3_Unknown_1115);
458 CASE_RET_STR(V3_MSTool);
459 CASE_RET_STR(V3_ProcRef);
460 CASE_RET_STR(V3_LProcRef);
461 CASE_RET_STR(V3_Unknown_1128);
462 CASE_RET_STR(V3_SectInfo);
463 CASE_RET_STR(V3_SubSectInfo);
464 CASE_RET_STR(V3_Entrypoint);
465 CASE_RET_STR(V3_Unknown_1139);
466 CASE_RET_STR(V3_SecuCookie);
467 CASE_RET_STR(V3_Unknown_113b);
468 CASE_RET_STR(V3_MsToolInfo);
469 CASE_RET_STR(V3_MsToolEnv);
470 CASE_RET_STR(VS2013_Local);
471 CASE_RET_STR(VS2013_FpOff);
472 CASE_RET_STR(VS2013_LProc32);
473 CASE_RET_STR(VS2013_GProc32);
474#undef CASE_RET_STR
475 case kCvSymType_EndOfValues: break;
476 }
477 return "<unknown type>";
478}
479#endif /* LOG_ENABLED */
480
481
482/**
483 * Adds a string to the g_hDbgModStrCache after sanitizing it.
484 *
485 * IPRT only deals with UTF-8 strings, so the string will be forced to UTF-8
486 * encoding. Also, codeview generally have length prefixed
487 *
488 * @returns String cached copy of the string.
489 * @param pch The string to copy to the cache.
490 * @param cch The length of the string. RTSTR_MAX if zero
491 * terminated.
492 */
493static const char *rtDbgModCvAddSanitizedStringToCache(const char *pch, size_t cch)
494{
495 /*
496 * If the string is valid UTF-8 and or the right length, we're good.
497 * This is usually the case.
498 */
499 const char *pszRet;
500 int rc;
501 if (cch != RTSTR_MAX)
502 rc = RTStrValidateEncodingEx(pch, cch, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
503 else
504 rc = RTStrValidateEncodingEx(pch, cch, 0);
505 if (RT_SUCCESS(rc))
506 pszRet = RTStrCacheEnterN(g_hDbgModStrCache, pch, cch);
507 else
508 {
509 /*
510 * Need to sanitize the string, so make a copy of it.
511 */
512 char *pszCopy = (char *)RTMemDupEx(pch, cch, 1);
513 AssertPtrReturn(pszCopy, NULL);
514
515 /* Deal with anyembedded zero chars. */
516 char *psz = RTStrEnd(pszCopy, cch);
517 while (psz)
518 {
519 *psz = '_';
520 psz = RTStrEnd(psz, cch - (psz - pszCopy));
521 }
522
523 /* Force valid UTF-8 encoding. */
524 RTStrPurgeEncoding(pszCopy);
525 Assert(strlen(pszCopy) == cch);
526
527 /* Enter it into the cache and free the temp copy. */
528 pszRet = RTStrCacheEnterN(g_hDbgModStrCache, pszCopy, cch);
529 RTMemFree(pszCopy);
530 }
531 return pszRet;
532}
533
534
535/**
536 * Translates a codeview segment and offset into our segment layout.
537 *
538 * @returns
539 * @param pThis .
540 * @param piSeg .
541 * @param poff .
542 */
543DECLINLINE(int) rtDbgModCvAdjustSegAndOffset(PRTDBGMODCV pThis, uint32_t *piSeg, uint64_t *poff)
544{
545 uint32_t iSeg = *piSeg;
546 if (iSeg == 0)
547 iSeg = RTDBGSEGIDX_ABS;
548 else if (pThis->pSegMap)
549 {
550 if (pThis->fHaveDosFrames)
551 {
552 if ( iSeg > pThis->pSegMap->Hdr.cSegs
553 || iSeg == 0)
554 return VERR_CV_BAD_FORMAT;
555 if (*poff <= pThis->pSegMap->aDescs[iSeg - 1].cb + pThis->pSegMap->aDescs[iSeg - 1].off)
556 *poff -= pThis->pSegMap->aDescs[iSeg - 1].off;
557 else
558 {
559 /* Workaround for VGABIOS where _DATA symbols like vgafont8 are
560 reported in the VGAROM segment. */
561 uint64_t uAddrSym = *poff + ((uint32_t)pThis->pSegMap->aDescs[iSeg - 1].iFrame << 4);
562 uint16_t j = pThis->pSegMap->Hdr.cSegs;
563 while (j-- > 0)
564 {
565 uint64_t uAddrFirst = (uint64_t)pThis->pSegMap->aDescs[j].off
566 + ((uint32_t)pThis->pSegMap->aDescs[j].iFrame << 4);
567 if (uAddrSym - uAddrFirst < pThis->pSegMap->aDescs[j].cb)
568 {
569 Log(("CV addr fix: %04x:%08x -> %04x:%08x\n", iSeg, *poff, j + 1, uAddrSym - uAddrFirst));
570 *poff = uAddrSym - uAddrFirst;
571 iSeg = j + 1;
572 break;
573 }
574 }
575 if (j == UINT16_MAX)
576 return VERR_CV_BAD_FORMAT;
577 }
578 }
579 else
580 {
581 if ( iSeg > pThis->pSegMap->Hdr.cSegs
582 || iSeg == 0
583 || *poff > pThis->pSegMap->aDescs[iSeg - 1].cb)
584 return VERR_CV_BAD_FORMAT;
585 *poff += pThis->pSegMap->aDescs[iSeg - 1].off;
586 }
587 if (pThis->pSegMap->aDescs[iSeg - 1].fFlags & RTCVSEGMAPDESC_F_ABS)
588 iSeg = RTDBGSEGIDX_ABS;
589 else
590 iSeg = pThis->pSegMap->aDescs[iSeg - 1].iGroup;
591 }
592 *piSeg = iSeg;
593 return VINF_SUCCESS;
594}
595
596
597/**
598 * Adds a symbol to the container.
599 *
600 * @returns IPRT status code
601 * @param pThis The CodeView debug info reader instance.
602 * @param iSeg Segment number.
603 * @param off Offset into the segment
604 * @param pchName The symbol name (not necessarily terminated).
605 * @param cchName The symbol name length.
606 * @param fFlags Flags reserved for future exploits, MBZ.
607 * @param cbSym Symbol size, 0 if not available.
608 */
609static int rtDbgModCvAddSymbol(PRTDBGMODCV pThis, uint32_t iSeg, uint64_t off, const char *pchName,
610 uint32_t cchName, uint32_t fFlags, uint32_t cbSym)
611{
612 RT_NOREF_PV(fFlags);
613 const char *pszName = rtDbgModCvAddSanitizedStringToCache(pchName, cchName);
614 int rc;
615 if (pszName)
616 {
617#if 1
618 Log2(("CV Sym: %04x:%08x %.*s\n", iSeg, off, cchName, pchName));
619 rc = rtDbgModCvAdjustSegAndOffset(pThis, &iSeg, &off);
620 if (RT_SUCCESS(rc))
621 {
622 rc = RTDbgModSymbolAdd(pThis->hCnt, pszName, iSeg, off, cbSym, RTDBGSYMBOLADD_F_ADJUST_SIZES_ON_CONFLICT, NULL);
623
624 /* Simple duplicate symbol mangling, just to get more details. */
625 if (rc == VERR_DBG_DUPLICATE_SYMBOL && cchName < _2K)
626 {
627 char szTmpName[_2K + 96];
628 memcpy(szTmpName, pszName, cchName);
629 szTmpName[cchName] = '_';
630 for (uint32_t i = 1; i < 32; i++)
631 {
632 RTStrFormatU32(&szTmpName[cchName + 1], 80, i, 10, 0, 0, 0);
633 rc = RTDbgModSymbolAdd(pThis->hCnt, szTmpName, iSeg, off, cbSym, 0 /*fFlags*/, NULL);
634 if (rc != VERR_DBG_DUPLICATE_SYMBOL)
635 break;
636 }
637
638 }
639 else if (rc == VERR_DBG_ADDRESS_CONFLICT && cbSym)
640 rc = RTDbgModSymbolAdd(pThis->hCnt, pszName, iSeg, off, cbSym,
641 RTDBGSYMBOLADD_F_REPLACE_SAME_ADDR | RTDBGSYMBOLADD_F_ADJUST_SIZES_ON_CONFLICT, NULL);
642
643 Log(("Symbol: %04x:%08x %.*s [%Rrc]\n", iSeg, off, cchName, pchName, rc));
644 if (rc == VERR_DBG_ADDRESS_CONFLICT || rc == VERR_DBG_DUPLICATE_SYMBOL)
645 rc = VINF_SUCCESS;
646 }
647 else
648 Log(("Invalid segment index/offset %#06x:%08x for symbol %.*s\n", iSeg, off, cchName, pchName));
649
650#else
651 Log(("Symbol: %04x:%08x %.*s\n", iSeg, off, cchName, pchName));
652 rc = VINF_SUCCESS;
653#endif
654 RTStrCacheRelease(g_hDbgModStrCache, pszName);
655 }
656 else
657 rc = VERR_NO_STR_MEMORY;
658 return rc;
659}
660
661/**
662 * Validates the a length prefixed string (aka pascal string).
663 *
664 * @returns String length if valid, UINT16_MAX if invalid.
665 * @param cchString The string length.
666 * @param pchString The string to validate.
667 * @param pvRec The pointer to the record containing the string.
668 * @param cbRec The record length.
669 */
670static uint16_t rtDbgModCvValidatePascalString(uint8_t cchString, const char *pchString, void const *pvRec, uint16_t cbRec)
671{
672 size_t offStrMember = (uintptr_t)pchString - (uintptr_t)pvRec;
673 AssertReturn(offStrMember <= cbRec, UINT16_MAX);
674 cbRec -= (uint16_t)offStrMember;
675 AssertReturn(cchString <= cbRec, UINT16_MAX);
676
677 int rc = RTStrValidateEncodingEx(pchString, cchString, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
678 AssertRCReturn(rc, UINT16_MAX);
679
680 return cchString;
681}
682
683
684/**
685 * Validates the a zero terminated string.
686 *
687 * @returns String length if valid, UINT16_MAX if invalid.
688 * @param pszString The string to validate.
689 * @param pvRec The pointer to the record containing the string.
690 * @param cbRec The record length.
691 */
692static uint16_t rtDbgModCvValidateZeroString(const char *pszString, void const *pvRec, uint16_t cbRec)
693{
694 size_t offStrMember = (uintptr_t)pszString - (uintptr_t)pvRec;
695 AssertReturn(offStrMember < _1K, UINT16_MAX);
696 AssertReturn(offStrMember <= cbRec, UINT16_MAX);
697 cbRec -= (uint16_t)offStrMember;
698
699 const char *pchEnd = RTStrEnd(pszString, cbRec);
700 AssertReturn(pchEnd, UINT16_MAX);
701
702 int rc = RTStrValidateEncoding(pszString);
703 AssertRCReturn(rc, UINT16_MAX);
704
705 return (uint16_t)(pchEnd - pszString);
706}
707
708
709/**
710 * Parses a CV4 symbol table, adding symbols to the container.
711 *
712 * @returns IPRT status code
713 * @param pThis The CodeView debug info reader instance.
714 * @param pvSymTab The symbol table.
715 * @param cbSymTab The size of the symbol table.
716 * @param fFlags Flags reserved for future exploits, MBZ.
717 */
718static int rtDbgModCvSsProcessV4PlusSymTab(PRTDBGMODCV pThis, void const *pvSymTab, size_t cbSymTab, uint32_t fFlags)
719{
720 int rc = VINF_SUCCESS;
721 RTCPTRUNION uCursor;
722 uCursor.pv = pvSymTab;
723
724 RT_NOREF_PV(fFlags);
725
726 while (cbSymTab > 0 && RT_SUCCESS(rc))
727 {
728 uint8_t const * const pbRecStart = uCursor.pu8;
729 uint16_t cbRec = *uCursor.pu16++;
730 if (cbRec >= 2)
731 {
732 uint16_t uSymType = *uCursor.pu16++;
733
734 Log3((" %p: uSymType=%#06x LB %#x %s\n",
735 pbRecStart - (uint8_t *)pvSymTab, uSymType, cbRec, rtDbgModCvSsSymTypeName((RTCVSYMTYPE)uSymType)));
736 RTDBGMODCV_CHECK_RET_BF(cbRec >= 2 && cbRec <= cbSymTab, ("cbRec=%#x cbSymTab=%#x\n", cbRec, cbSymTab));
737
738 switch (uSymType)
739 {
740 case kCvSymType_LData16:
741 case kCvSymType_GData16:
742 case kCvSymType_Pub16:
743 {
744 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec > 2 + 2+2+2+1);
745 uint16_t off = *uCursor.pu16++;
746 uint16_t iSeg = *uCursor.pu16++;
747 /*uint16_t iType = * */ uCursor.pu16++;
748 uint8_t cchName = *uCursor.pu8++;
749 RTDBGMODCV_CHECK_NOMSG_RET_BF(cchName > 0);
750 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= 2 + 2+2+2+1 + cchName);
751
752 rc = rtDbgModCvAddSymbol(pThis, iSeg, off, uCursor.pch, cchName, 0, 0);
753 break;
754 }
755
756 case kCvSymType_LData32:
757 case kCvSymType_GData32:
758 case kCvSymType_Pub32:
759 {
760 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec > 2 + 4+2+2+1);
761 uint32_t off = *uCursor.pu32++;
762 uint16_t iSeg = *uCursor.pu16++;
763 /*uint16_t iType = * */ uCursor.pu16++;
764 uint8_t cchName = *uCursor.pu8++;
765 RTDBGMODCV_CHECK_NOMSG_RET_BF(cchName > 0);
766 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= 2 + 4+2+2+1 + cchName);
767
768 rc = rtDbgModCvAddSymbol(pThis, iSeg, off, uCursor.pch, cchName, 0, 0);
769 break;
770 }
771
772 case kCvSymType_LProc16:
773 case kCvSymType_GProc16:
774 {
775 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec > 2 + 4+4+4+2+2+2+2+2+2+1+1);
776 /*uint32_t uParent = * */ uCursor.pu32++;
777 /*uint32_t uEnd = * */ uCursor.pu32++;
778 /*uint32_t uNext = * */ uCursor.pu32++;
779 uint16_t cbProc = *uCursor.pu16++;
780 /*uint16_t offDebugStart = * */ uCursor.pu16++;
781 /*uint16_t offDebugEnd = * */ uCursor.pu16++;
782 uint16_t off = *uCursor.pu16++;
783 uint16_t iSeg = *uCursor.pu16++;
784 /*uint16_t iProcType = * */ uCursor.pu16++;
785 /*uint8_t fbType = * */ uCursor.pu8++;
786 uint8_t cchName = *uCursor.pu8++;
787 RTDBGMODCV_CHECK_NOMSG_RET_BF(cchName > 0);
788 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= 2 + 4+4+4+2+2+2+2+2+2+1+1 + cchName);
789
790 rc = rtDbgModCvAddSymbol(pThis, iSeg, off, uCursor.pch, cchName, 0, cbProc);
791 break;
792 }
793
794 case kCvSymType_LProc32:
795 case kCvSymType_GProc32:
796 {
797 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec > 2 + 4+4+4+4+4+4+4+2+2+1+1);
798 /*uint32_t uParent = * */ uCursor.pu32++;
799 /*uint32_t uEnd = * */ uCursor.pu32++;
800 /*uint32_t uNext = * */ uCursor.pu32++;
801 /*uint32_t cbProc = * */ uCursor.pu32++;
802 /*uint32_t offDebugStart = * */ uCursor.pu32++;
803 /*uint32_t offDebugEnd = * */ uCursor.pu32++;
804 uint32_t off = *uCursor.pu32++;
805 uint16_t iSeg = *uCursor.pu16++;
806 /*uint16_t iProcType = * */ uCursor.pu16++;
807 /*uint8_t fbType = * */ uCursor.pu8++;
808 uint8_t cchName = *uCursor.pu8++;
809 RTDBGMODCV_CHECK_NOMSG_RET_BF(cchName > 0);
810 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= 2 + 4+4+4+4+4+4+4+2+2+1+1 + cchName);
811
812 rc = rtDbgModCvAddSymbol(pThis, iSeg, off, uCursor.pch, cchName, 0, 0);
813 break;
814 }
815
816 case kCvSymType_V2_Pub:
817 case kCvSymType_V2_LData:
818 case kCvSymType_V2_GData:
819 {
820 PCRTCVSYMV2TYPEDNAME pData = (PCRTCVSYMV2TYPEDNAME)uCursor.pv;
821 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= 2 + RT_UOFFSETOF(RTCVSYMV2TYPEDNAME, achName));
822 uint16_t const cchName = rtDbgModCvValidatePascalString(pData->cchName, pData->achName, pData, cbRec);
823 if (cchName != UINT16_MAX && cchName > 0)
824 rc = rtDbgModCvAddSymbol(pThis, pData->iSection, pData->offSection, pData->achName, cchName, 0, 0);
825 else
826 Log3((" cchName=%#x sec:off=%#x:%#x %.*Rhxs\n",
827 cchName, pData->iSection, pData->offSection, cbRec, pData));
828 break;
829 }
830
831 case kCvSymType_V3_Label:
832 {
833 PCRTCVSYMV3LABEL pLabel = (PCRTCVSYMV3LABEL)uCursor.pv;
834 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= 2 + sizeof(*pLabel));
835 uint16_t cchName = rtDbgModCvValidateZeroString(pLabel->szName, pLabel, cbRec);
836 if (cchName != UINT16_MAX && cchName > 0)
837 rc = rtDbgModCvAddSymbol(pThis, pLabel->iSection, pLabel->offSection, pLabel->szName, cchName, 0, 0);
838 else
839 Log3((" cchName=%#x sec:off=%#x:%#x %.*Rhxs\n",
840 cchName, pLabel->iSection, pLabel->offSection, cbRec, pLabel));
841 break;
842 }
843
844 case kCvSymType_V3_LData:
845 case kCvSymType_V3_GData:
846 case kCvSymType_V3_Pub:
847 {
848 PCRTCVSYMV3TYPEDNAME pData = (PCRTCVSYMV3TYPEDNAME)uCursor.pv;
849 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= sizeof(*pData));
850 uint16_t cchName = rtDbgModCvValidateZeroString(pData->szName, pData, cbRec);
851 if (cchName != UINT16_MAX && cchName > 0)
852 rc = rtDbgModCvAddSymbol(pThis, pData->iSection, pData->offSection, pData->szName, cchName, 0, 0);
853 else
854 Log3((" cchName=%#x sec:off=%#x:%#x idType=%#x %.*Rhxs\n",
855 cchName, pData->iSection, pData->offSection, pData->idType, cbRec, pData));
856 break;
857 }
858
859 case kCvSymType_V3_LProc:
860 case kCvSymType_V3_GProc:
861 {
862 PCRTCVSYMV3PROC pProc = (PCRTCVSYMV3PROC)uCursor.pv;
863 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= sizeof(*pProc));
864 uint16_t cchName = rtDbgModCvValidateZeroString(pProc->szName, pProc, cbRec);
865 if (cchName != UINT16_MAX && cchName > 0)
866 rc = rtDbgModCvAddSymbol(pThis, pProc->iSection, pProc->offSection, pProc->szName, cchName,
867 0, pProc->cbProc);
868 else
869 Log3((" cchName=%#x sec:off=%#x:%#x LB %#x\n",
870 cchName, pProc->iSection, pProc->offSection, pProc->cbProc));
871 break;
872 }
873
874 }
875 }
876 /*else: shorter records can be used for alignment, I guess. */
877
878 /* next */
879 uCursor.pu8 = pbRecStart + cbRec + 2;
880 cbSymTab -= cbRec + 2;
881 }
882 return rc;
883}
884
885
886/**
887 * Makes a copy of the CV8 source string table.
888 *
889 * It will be references in a subsequent source information table, and again by
890 * line number tables thru that.
891 *
892 * @returns IPRT status code
893 * @param pThis The CodeView debug info reader instance.
894 * @param pvSrcStrings The source string table.
895 * @param cbSrcStrings The size of the source strings.
896 * @param fFlags Flags reserved for future exploits, MBZ.
897 */
898static int rtDbgModCvSsProcessV8SrcStrings(PRTDBGMODCV pThis, void const *pvSrcStrings, size_t cbSrcStrings, uint32_t fFlags)
899{
900 RT_NOREF_PV(fFlags);
901
902 if (pThis->cbSrcStrings)
903 Log(("\n!!More than one source file string table for this module!!\n\n"));
904
905 if (cbSrcStrings >= pThis->cbSrcStringsAlloc)
906 {
907 void *pvNew = RTMemRealloc(pThis->pchSrcStrings, cbSrcStrings + 1);
908 AssertReturn(pvNew, VERR_NO_MEMORY);
909 pThis->pchSrcStrings = (char *)pvNew;
910 pThis->cbSrcStringsAlloc = cbSrcStrings + 1;
911 }
912 memcpy(pThis->pchSrcStrings, pvSrcStrings, cbSrcStrings);
913 pThis->pchSrcStrings[cbSrcStrings] = '\0';
914 pThis->cbSrcStrings = cbSrcStrings;
915 Log2((" saved %#x bytes of CV8 source strings\n", cbSrcStrings));
916
917 if (LogIs3Enabled())
918 {
919 size_t iFile = 0;
920 size_t off = pThis->pchSrcStrings[0] != '\0' ? 0 : 1;
921 while (off < cbSrcStrings)
922 {
923 size_t cch = strlen(&pThis->pchSrcStrings[off]);
924 Log3((" %010zx #%03zu: %s\n", off, iFile, &pThis->pchSrcStrings[off]));
925 off += cch + 1;
926 iFile++;
927 }
928 }
929
930 return VINF_SUCCESS;
931}
932
933
934/**
935 * Makes a copy of the CV8 source information table.
936 *
937 * It will be references in subsequent line number tables.
938 *
939 * @returns IPRT status code
940 * @param pThis The CodeView debug info reader instance.
941 * @param pvSrcInfo The source information table.
942 * @param cbSrcInfo The size of the source information table (bytes).
943 * @param fFlags Flags reserved for future exploits, MBZ.
944 */
945static int rtDbgModCvSsProcessV8SrcInfo(PRTDBGMODCV pThis, void const *pvSrcInfo, size_t cbSrcInfo, uint32_t fFlags)
946{
947 RT_NOREF_PV(fFlags);
948
949 if (pThis->cbSrcInfo)
950 Log(("\n!!More than one source file info table for this module!!\n\n"));
951
952 if (cbSrcInfo + sizeof(RTCV8SRCINFO) > pThis->cbSrcInfoAlloc)
953 {
954 void *pvNew = RTMemRealloc(pThis->pbSrcInfo, cbSrcInfo + sizeof(RTCV8SRCINFO));
955 AssertReturn(pvNew, VERR_NO_MEMORY);
956 pThis->pbSrcInfo = (uint8_t *)pvNew;
957 pThis->cbSrcInfoAlloc = cbSrcInfo + sizeof(RTCV8SRCINFO);
958 }
959 memcpy(pThis->pbSrcInfo, pvSrcInfo, cbSrcInfo);
960 memset(&pThis->pbSrcInfo[cbSrcInfo], 0, sizeof(RTCV8SRCINFO));
961 pThis->cbSrcInfo = cbSrcInfo;
962 Log2((" saved %#x bytes of CV8 source file info\n", cbSrcInfo));
963 return VINF_SUCCESS;
964}
965
966
967/**
968 * Makes a copy of the CV8 source string table.
969 *
970 * It will be references in subsequent line number tables.
971 *
972 * @returns IPRT status code
973 * @param pThis The CodeView debug info reader instance.
974 * @param pvSectLines The section source line table.
975 * @param cbSectLines The size of the section source line table.
976 * @param fFlags Flags reserved for future exploits, MBZ.
977 */
978static int rtDbgModCvSsProcessV8SectLines(PRTDBGMODCV pThis, void const *pvSectLines, size_t cbSectLines, uint32_t fFlags)
979{
980 RT_NOREF_PV(fFlags);
981
982 /*
983 * Starts with header.
984 */
985 PCRTCV8LINESHDR pHdr = (PCRTCV8LINESHDR)pvSectLines;
986 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSectLines >= sizeof(*pHdr));
987 cbSectLines -= sizeof(*pHdr);
988 Log2(("RTDbgModCv: seg #%u, off %#x LB %#x \n", pHdr->iSection, pHdr->offSection, pHdr->cbSectionCovered));
989
990 RTCPTRUNION uCursor;
991 uCursor.pv = pHdr + 1;
992 while (cbSectLines > 0)
993 {
994 /* Source file header. */
995 PCRTCV8LINESSRCMAP pSrcHdr = (PCRTCV8LINESSRCMAP)uCursor.pv;
996 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSectLines >= sizeof(*pSrcHdr));
997 RTDBGMODCV_CHECK_NOMSG_RET_BF(pSrcHdr->cb == pSrcHdr->cLines * sizeof(RTCV8LINEPAIR) + sizeof(RTCV8LINESSRCMAP));
998 RTDBGMODCV_CHECK_RET_BF(!(pSrcHdr->offSourceInfo & 3), ("offSourceInfo=%#x\n", pSrcHdr->offSourceInfo));
999 if (pSrcHdr->offSourceInfo + sizeof(uint32_t) <= pThis->cbSrcInfo)
1000 {
1001 PCRTCV8SRCINFO pSrcInfo = (PCRTCV8SRCINFO)&pThis->pbSrcInfo[pSrcHdr->offSourceInfo];
1002 const char *pszName = pSrcInfo->offSourceName < pThis->cbSrcStrings
1003 ? &pThis->pchSrcStrings[pSrcInfo->offSourceName] : "unknown.c";
1004 pszName = rtDbgModCvAddSanitizedStringToCache(pszName, RTSTR_MAX);
1005 Log2(("RTDbgModCv: #%u lines, %#x bytes, %#x=%s\n", pSrcHdr->cLines, pSrcHdr->cb, pSrcInfo->offSourceName, pszName));
1006
1007 if (pszName)
1008 {
1009 /* Process the line/offset pairs. */
1010 uint32_t cLeft = pSrcHdr->cLines;
1011 PCRTCV8LINEPAIR pPair = (PCRTCV8LINEPAIR)(pSrcHdr + 1);
1012 while (cLeft-- > 0)
1013 {
1014 uint32_t idxSeg = pHdr->iSection;
1015 uint64_t off = pPair->offSection + pHdr->offSection;
1016 int rc = rtDbgModCvAdjustSegAndOffset(pThis, &idxSeg, &off);
1017 if (RT_SUCCESS(rc))
1018 rc = RTDbgModLineAdd(pThis->hCnt, pszName, pPair->uLineNumber, idxSeg, off, NULL);
1019 if (RT_SUCCESS(rc))
1020 Log3(("RTDbgModCv: %#x:%#010llx %0u\n", idxSeg, off, pPair->uLineNumber));
1021 else
1022 Log(( "RTDbgModCv: %#x:%#010llx %0u - rc=%Rrc!! (org: idxSeg=%#x off=%#x)\n",
1023 idxSeg, off, pPair->uLineNumber, rc, pHdr->iSection, pPair->offSection));
1024
1025 /* next */
1026 pPair++;
1027 }
1028 Assert((uintptr_t)pPair - (uintptr_t)pSrcHdr == pSrcHdr->cb);
1029 }
1030 }
1031 else
1032 Log(("RTDbgModCv: offSourceInfo=%#x cbSrcInfo=%#x!\n", pSrcHdr->offSourceInfo, pThis->cbSrcInfo));
1033
1034 /* next */
1035 cbSectLines -= pSrcHdr->cb;
1036 uCursor.pu8 += pSrcHdr->cb;
1037 }
1038
1039 return VINF_SUCCESS;
1040}
1041
1042
1043
1044/**
1045 * Parses a CV8 symbol table, adding symbols to the container.
1046 *
1047 * @returns IPRT status code
1048 * @param pThis The CodeView debug info reader instance.
1049 * @param pvSymTab The symbol table.
1050 * @param cbSymTab The size of the symbol table.
1051 * @param fFlags Flags reserved for future exploits, MBZ.
1052 */
1053static int rtDbgModCvSsProcessV8SymTab(PRTDBGMODCV pThis, void const *pvSymTab, size_t cbSymTab, uint32_t fFlags)
1054{
1055 size_t const cbSymTabSaved = cbSymTab;
1056 int rc = VINF_SUCCESS;
1057
1058 /*
1059 * First pass looks for source information and source strings tables.
1060 * Microsoft puts the 0xf3 and 0xf4 last, usually with 0xf4 first.
1061 *
1062 * We ASSUME one string and one info table per module!
1063 */
1064 RTCPTRUNION uCursor;
1065 uCursor.pv = pvSymTab;
1066 for (;;)
1067 {
1068 RTDBGMODCV_CHECK_RET_BF(cbSymTab > sizeof(RTCV8SYMBOLSBLOCK), ("cbSymTab=%zu\n", cbSymTab));
1069 PCRTCV8SYMBOLSBLOCK pBlockHdr = (PCRTCV8SYMBOLSBLOCK)uCursor.pv;
1070 Log3((" %p: pass #1 uType=%#04x LB %#x\n", (uint8_t *)pBlockHdr - (uint8_t *)pvSymTab, pBlockHdr->uType, pBlockHdr->cb));
1071 RTDBGMODCV_CHECK_RET_BF(pBlockHdr->cb <= cbSymTab - sizeof(RTCV8SYMBOLSBLOCK),
1072 ("cb=%#u cbSymTab=%zu\n", pBlockHdr->cb, cbSymTab));
1073
1074 switch (pBlockHdr->uType)
1075 {
1076 case RTCV8SYMBLOCK_TYPE_SRC_STR:
1077 rc = rtDbgModCvSsProcessV8SrcStrings(pThis, pBlockHdr + 1, pBlockHdr->cb, fFlags);
1078 break;
1079
1080 case RTCV8SYMBLOCK_TYPE_SRC_INFO:
1081 rc = rtDbgModCvSsProcessV8SrcInfo(pThis, pBlockHdr + 1, pBlockHdr->cb, fFlags);
1082 break;
1083
1084 case RTCV8SYMBLOCK_TYPE_SECT_LINES:
1085 case RTCV8SYMBLOCK_TYPE_SYMBOLS:
1086 break;
1087 default:
1088 Log(("rtDbgModCvSsProcessV8SymTab: Unknown block type %#x (LB %#x)\n", pBlockHdr->uType, pBlockHdr->cb));
1089 break;
1090 }
1091 uint32_t cbAligned = RT_ALIGN_32(sizeof(*pBlockHdr) + pBlockHdr->cb, 4);
1092 if (RT_SUCCESS(rc) && cbSymTab > cbAligned)
1093 {
1094 uCursor.pu8 += cbAligned;
1095 cbSymTab -= cbAligned;
1096 }
1097 else
1098 break;
1099 }
1100
1101 /*
1102 * Log the source info now that we've gathered both it and the strings.
1103 */
1104 if (LogIs3Enabled() && pThis->cbSrcInfo)
1105 {
1106 Log3((" Source file info table:\n"));
1107 size_t iFile = 0;
1108 size_t off = 0;
1109 while (off + 4 <= pThis->cbSrcInfo)
1110 {
1111 PCRTCV8SRCINFO pSrcInfo = (PCRTCV8SRCINFO)&pThis->pbSrcInfo[off];
1112#ifdef LOG_ENABLED
1113 const char *pszName = pSrcInfo->offSourceName < pThis->cbSrcStrings
1114 ? &pThis->pchSrcStrings[pSrcInfo->offSourceName] : "out-of-bounds.c!";
1115 if (pSrcInfo->uDigestType == RTCV8SRCINFO_DIGEST_TYPE_MD5)
1116 Log3((" %010zx #%03zu: %RTuuid %#x=%s\n", off, iFile, &pSrcInfo->Digest.md5, pSrcInfo->offSourceName, pszName));
1117 else if (pSrcInfo->uDigestType == RTCV8SRCINFO_DIGEST_TYPE_NONE)
1118 Log3((" %010zx #%03zu: <none> %#x=%s\n", off, iFile, pSrcInfo->offSourceName, pszName));
1119 else
1120 Log3((" %010zx #%03zu: !%#x! %#x=%s\n", off, iFile, pSrcInfo->uDigestType, pSrcInfo->offSourceName, pszName));
1121#endif
1122 off += pSrcInfo->uDigestType == RTCV8SRCINFO_DIGEST_TYPE_MD5 ? sizeof(*pSrcInfo) : 8;
1123 iFile++;
1124 }
1125 }
1126
1127 /*
1128 * Second pass, process symbols and line numbers.
1129 */
1130 uCursor.pv = pvSymTab;
1131 cbSymTab = cbSymTabSaved;
1132 for (;;)
1133 {
1134 RTDBGMODCV_CHECK_RET_BF(cbSymTab > sizeof(RTCV8SYMBOLSBLOCK), ("cbSymTab=%zu\n", cbSymTab));
1135 PCRTCV8SYMBOLSBLOCK pBlockHdr = (PCRTCV8SYMBOLSBLOCK)uCursor.pv;
1136 Log3((" %p: pass #2 uType=%#04x LB %#x\n", (uint8_t *)pBlockHdr - (uint8_t *)pvSymTab, pBlockHdr->uType, pBlockHdr->cb));
1137 RTDBGMODCV_CHECK_RET_BF(pBlockHdr->cb <= cbSymTab - sizeof(RTCV8SYMBOLSBLOCK),
1138 ("cb=%#u cbSymTab=%zu\n", pBlockHdr->cb, cbSymTab));
1139
1140 switch (pBlockHdr->uType)
1141 {
1142 case RTCV8SYMBLOCK_TYPE_SYMBOLS:
1143 rc = rtDbgModCvSsProcessV4PlusSymTab(pThis, pBlockHdr + 1, pBlockHdr->cb, fFlags);
1144 break;
1145
1146 case RTCV8SYMBLOCK_TYPE_SECT_LINES:
1147 rc = rtDbgModCvSsProcessV8SectLines(pThis, pBlockHdr + 1, pBlockHdr->cb, fFlags);
1148 break;
1149
1150 case RTCV8SYMBLOCK_TYPE_SRC_INFO:
1151 case RTCV8SYMBLOCK_TYPE_SRC_STR:
1152 break;
1153 default:
1154 Log(("rtDbgModCvSsProcessV8SymTab: Unknown block type %#x (LB %#x)\n", pBlockHdr->uType, pBlockHdr->cb));
1155 break;
1156 }
1157 uint32_t cbAligned = RT_ALIGN_32(sizeof(*pBlockHdr) + pBlockHdr->cb, 4);
1158 if (RT_SUCCESS(rc) && cbSymTab > cbAligned)
1159 {
1160 uCursor.pu8 += cbAligned;
1161 cbSymTab -= cbAligned;
1162 }
1163 else
1164 break;
1165 }
1166 return rc;
1167}
1168
1169
1170/** @callback_method_impl{FNDBGMODCVSUBSECTCALLBACK,
1171 * Parses kCvSst_GlobalPub\, kCvSst_GlobalSym and kCvSst_StaticSym subsections\,
1172 * adding symbols it finds to the container.} */
1173static DECLCALLBACK(int)
1174rtDbgModCvSs_GlobalPub_GlobalSym_StaticSym(PRTDBGMODCV pThis, void const *pvSubSect, size_t cbSubSect, PCRTCVDIRENT32 pDirEnt)
1175{
1176 PCRTCVGLOBALSYMTABHDR pHdr = (PCRTCVGLOBALSYMTABHDR)pvSubSect;
1177 RT_NOREF_PV(pDirEnt);
1178
1179 /*
1180 * Quick data validation.
1181 */
1182 Log2(("RTDbgModCv: %s: uSymHash=%#x uAddrHash=%#x cbSymbols=%#x cbSymHash=%#x cbAddrHash=%#x\n",
1183 rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType), pHdr->uSymHash,
1184 pHdr->uAddrHash, pHdr->cbSymbols, pHdr->cbSymHash, pHdr->cbAddrHash));
1185 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSubSect >= sizeof(RTCVGLOBALSYMTABHDR));
1186 RTDBGMODCV_CHECK_NOMSG_RET_BF((uint64_t)pHdr->cbSymbols + pHdr->cbSymHash + pHdr->cbAddrHash <= cbSubSect - sizeof(*pHdr));
1187 RTDBGMODCV_CHECK_NOMSG_RET_BF(pHdr->uSymHash < 0x20);
1188 RTDBGMODCV_CHECK_NOMSG_RET_BF(pHdr->uAddrHash < 0x20);
1189 if (!pHdr->cbSymbols)
1190 return VINF_SUCCESS;
1191
1192 /*
1193 * Parse the symbols.
1194 */
1195 return rtDbgModCvSsProcessV4PlusSymTab(pThis, pHdr + 1, pHdr->cbSymbols, 0);
1196}
1197
1198
1199/** @callback_method_impl{FNDBGMODCVSUBSECTCALLBACK,
1200 * Parses kCvSst_Module subsection\, storing the debugging style in pThis.} */
1201static DECLCALLBACK(int)
1202rtDbgModCvSs_Module(PRTDBGMODCV pThis, void const *pvSubSect, size_t cbSubSect, PCRTCVDIRENT32 pDirEnt)
1203{
1204 RT_NOREF_PV(pDirEnt);
1205
1206 RTCPTRUNION uCursor;
1207 uCursor.pv = pvSubSect;
1208 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSubSect >= 2 + 2 + 2 + 2 + 0 + 1);
1209 uint16_t iOverlay = *uCursor.pu16++; NOREF(iOverlay);
1210 uint16_t iLib = *uCursor.pu16++; NOREF(iLib);
1211 uint16_t cSegs = *uCursor.pu16++;
1212 pThis->uCurStyle = *uCursor.pu16++;
1213 if (pThis->uCurStyle == 0)
1214 pThis->uCurStyle = RT_MAKE_U16('C', 'V');
1215 pThis->uCurStyleVer = 0;
1216 pThis->cbSrcInfo = 0;
1217 pThis->cbSrcStrings = 0;
1218 uint8_t cchName = uCursor.pu8[cSegs * 12];
1219 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSubSect >= 2 + 2 + 2 + 2 + cSegs * 12U + 1 + cchName);
1220
1221#ifdef LOG_ENABLED
1222 const char *pchName = (const char *)&uCursor.pu8[cSegs * 12 + 1];
1223 Log2(("RTDbgModCv: Module: iOverlay=%#x iLib=%#x cSegs=%#x Style=%c%c (%#x) %.*s\n", iOverlay, iLib, cSegs,
1224 RT_BYTE1(pThis->uCurStyle), RT_BYTE2(pThis->uCurStyle), pThis->uCurStyle, cchName, pchName));
1225#endif
1226 RTDBGMODCV_CHECK_NOMSG_RET_BF(pThis->uCurStyle == RT_MAKE_U16('C', 'V'));
1227
1228#ifdef LOG_ENABLED
1229 PCRTCVMODSEGINFO32 paSegs = (PCRTCVMODSEGINFO32)uCursor.pv;
1230 for (uint16_t iSeg = 0; iSeg < cSegs; iSeg++)
1231 Log2((" #%02u: %04x:%08x LB %08x\n", iSeg, paSegs[iSeg].iSeg, paSegs[iSeg].off, paSegs[iSeg].cb));
1232#endif
1233
1234 return VINF_SUCCESS;
1235}
1236
1237
1238/** @callback_method_impl{FNDBGMODCVSUBSECTCALLBACK,
1239 * Parses kCvSst_Symbols\, kCvSst_PublicSym and kCvSst_AlignSym subsections\,
1240 * adding symbols it finds to the container.} */
1241static DECLCALLBACK(int)
1242rtDbgModCvSs_Symbols_PublicSym_AlignSym(PRTDBGMODCV pThis, void const *pvSubSect, size_t cbSubSect, PCRTCVDIRENT32 pDirEnt)
1243{
1244 RT_NOREF_PV(pDirEnt);
1245 RTDBGMODCV_CHECK_NOMSG_RET_BF(pThis->uCurStyle == RT_MAKE_U16('C', 'V'));
1246 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSubSect >= 8);
1247
1248 uint32_t u32Signature = *(uint32_t const *)pvSubSect;
1249 RTDBGMODCV_CHECK_RET_BF(u32Signature == RTCVSYMBOLS_SIGNATURE_CV4 || u32Signature == RTCVSYMBOLS_SIGNATURE_CV8,
1250 ("%#x, expected %#x\n", u32Signature, RTCVSYMBOLS_SIGNATURE_CV4));
1251 if (u32Signature == RTCVSYMBOLS_SIGNATURE_CV8)
1252 return rtDbgModCvSsProcessV8SymTab(pThis, (uint8_t const *)pvSubSect + 4, cbSubSect - 4, 0);
1253 return rtDbgModCvSsProcessV4PlusSymTab(pThis, (uint8_t const *)pvSubSect + 4, cbSubSect - 4, 0);
1254}
1255
1256
1257/** @callback_method_impl{FNDBGMODCVSUBSECTCALLBACK,
1258 * Parses kCvSst_SrcModule adding line numbers it finds to the container.}
1259 */
1260static DECLCALLBACK(int)
1261rtDbgModCvSs_SrcModule(PRTDBGMODCV pThis, void const *pvSubSect, size_t cbSubSect, PCRTCVDIRENT32 pDirEnt)
1262{
1263 RT_NOREF_PV(pDirEnt);
1264 Log(("rtDbgModCvSs_SrcModule: uCurStyle=%#x\n%.*Rhxd\n", pThis->uCurStyle, cbSubSect, pvSubSect));
1265
1266 /* Check the header. */
1267 PCRTCVSRCMODULE pHdr = (PCRTCVSRCMODULE)pvSubSect;
1268 AssertReturn(cbSubSect >= RT_UOFFSETOF(RTCVSRCMODULE, aoffSrcFiles), VERR_CV_BAD_FORMAT);
1269 size_t cbHdr = sizeof(RTCVSRCMODULE)
1270 + pHdr->cFiles * sizeof(uint32_t)
1271 + pHdr->cSegs * sizeof(uint32_t) * 2
1272 + pHdr->cSegs * sizeof(uint16_t);
1273 Log2(("RTDbgModCv: SrcModule: cFiles=%u cSegs=%u\n", pHdr->cFiles, pHdr->cFiles));
1274 RTDBGMODCV_CHECK_RET_BF(cbSubSect >= cbHdr, ("cbSubSect=%#x cbHdr=%zx\n", cbSubSect, cbHdr));
1275#ifdef LOG_ENABLED
1276 if (LogIs2Enabled())
1277 {
1278 for (uint32_t i = 0; i < pHdr->cFiles; i++)
1279 Log2(("RTDbgModCv: source file #%u: %#x\n", i, pHdr->aoffSrcFiles[i]));
1280 PCRTCVSRCRANGE paSegRanges = (PCRTCVSRCRANGE)&pHdr->aoffSrcFiles[pHdr->cFiles];
1281 uint16_t const *paidxSegs = (uint16_t const *)&paSegRanges[pHdr->cSegs];
1282 for (uint32_t i = 0; i < pHdr->cSegs; i++)
1283 Log2(("RTDbgModCv: seg #%u: %#010x-%#010x\n", paidxSegs[i], paSegRanges[i].offStart, paSegRanges[i].offEnd));
1284 }
1285#endif
1286
1287 /*
1288 * Work over the source files.
1289 */
1290 for (uint32_t i = 0; i < pHdr->cFiles; i++)
1291 {
1292 uint32_t const offSrcFile = pHdr->aoffSrcFiles[i];
1293 RTDBGMODCV_CHECK_RET_BF(cbSubSect - RT_UOFFSETOF(RTCVSRCFILE, aoffSrcLines) >= offSrcFile,
1294 ("cbSubSect=%#x (- %#x) aoffSrcFiles[%u]=%#x\n",
1295 cbSubSect, RT_UOFFSETOF(RTCVSRCFILE, aoffSrcLines), i, offSrcFile));
1296 PCRTCVSRCFILE pSrcFile = (PCRTCVSRCFILE)((uint8_t const *)pvSubSect + offSrcFile);
1297 size_t cbSrcFileHdr = RT_UOFFSETOF_DYN(RTCVSRCFILE, aoffSrcLines[pSrcFile->cSegs])
1298 + sizeof(RTCVSRCRANGE) * pSrcFile->cSegs
1299 + sizeof(uint8_t);
1300 RTDBGMODCV_CHECK_RET_BF(cbSubSect >= offSrcFile + cbSrcFileHdr && cbSubSect > cbSrcFileHdr,
1301 ("cbSubSect=%#x aoffSrcFiles[%u]=%#x cbSrcFileHdr=%#x\n", cbSubSect, offSrcFile, i, cbSrcFileHdr));
1302 PCRTCVSRCRANGE paSegRanges = (PCRTCVSRCRANGE)&pSrcFile->aoffSrcLines[pSrcFile->cSegs];
1303 uint8_t const *pcchName = (uint8_t const *)&paSegRanges[pSrcFile->cSegs]; /** @todo TIS NB09 docs say 16-bit length... */
1304 const char *pchName = (const char *)(pcchName + 1);
1305 RTDBGMODCV_CHECK_RET_BF(cbSubSect >= offSrcFile + cbSrcFileHdr + *pcchName,
1306 ("cbSubSect=%#x offSrcFile=%#x cbSubSect=%#x *pcchName=%#x\n",
1307 cbSubSect, offSrcFile, cbSubSect, *pcchName));
1308 Log2(("RTDbgModCv: source file #%u/%#x: cSegs=%#x '%.*s'\n", i, offSrcFile, pSrcFile->cSegs, *pcchName, pchName));
1309 const char *pszName = rtDbgModCvAddSanitizedStringToCache(pchName, *pcchName);
1310
1311 /*
1312 * Work the segments this source file contributes code to.
1313 */
1314 for (uint32_t iSeg = 0; iSeg < pSrcFile->cSegs; iSeg++)
1315 {
1316 uint32_t const offSrcLine = pSrcFile->aoffSrcLines[iSeg];
1317 RTDBGMODCV_CHECK_RET_BF(cbSubSect - RT_UOFFSETOF(RTCVSRCLINE, aoffLines) >= offSrcLine,
1318 ("cbSubSect=%#x (- %#x) aoffSrcFiles[%u]=%#x\n",
1319 cbSubSect, RT_UOFFSETOF(RTCVSRCLINE, aoffLines), iSeg, offSrcLine));
1320 PCRTCVSRCLINE pSrcLine = (PCRTCVSRCLINE)((uint8_t const *)pvSubSect + offSrcLine);
1321 size_t cbSrcLine = RT_UOFFSETOF_DYN(RTCVSRCLINE, aoffLines[pSrcLine->cPairs])
1322 + pSrcLine->cPairs * sizeof(uint16_t);
1323 RTDBGMODCV_CHECK_RET_BF(cbSubSect >= offSrcLine + cbSrcLine,
1324 ("cbSubSect=%#x aoffSrcFiles[%u]=%#x cbSrcLine=%#x\n",
1325 cbSubSect, iSeg, offSrcLine, cbSrcLine));
1326 uint16_t const *paiLines = (uint16_t const *)&pSrcLine->aoffLines[pSrcLine->cPairs];
1327 Log2(("RTDbgModCv: seg #%u, %u pairs (off %#x)\n", pSrcLine->idxSeg, pSrcLine->cPairs, offSrcLine));
1328 for (uint32_t iPair = 0; iPair < pSrcLine->cPairs; iPair++)
1329 {
1330
1331 uint32_t idxSeg = pSrcLine->idxSeg;
1332 uint64_t off = pSrcLine->aoffLines[iPair];
1333 int rc = rtDbgModCvAdjustSegAndOffset(pThis, &idxSeg, &off);
1334 if (RT_SUCCESS(rc))
1335 rc = RTDbgModLineAdd(pThis->hCnt, pszName, paiLines[iPair], idxSeg, off, NULL);
1336 if (RT_SUCCESS(rc))
1337 Log3(("RTDbgModCv: %#x:%#010llx %0u\n", idxSeg, off, paiLines[iPair]));
1338 /* Note! Wlink produces the sstSrcModule subsections from LINNUM records, however the
1339 CVGenLines() function assumes there is only one segment contributing to the
1340 line numbers. So, when we do assembly that jumps between segments, it emits
1341 the wrong addresses for some line numbers and we end up here, typically with
1342 VERR_DBG_ADDRESS_CONFLICT. */
1343 else
1344 Log(( "RTDbgModCv: %#x:%#010llx %0u - rc=%Rrc!! (org: idxSeg=%#x off=%#x)\n",
1345 idxSeg, off, paiLines[iPair], rc, pSrcLine->idxSeg, pSrcLine->aoffLines[iPair]));
1346 }
1347 }
1348 }
1349
1350 return VINF_SUCCESS;
1351}
1352
1353
1354/**
1355 * Processes a freshly loaded raw segment (section) map.
1356 *
1357 * This is used by both CV and PDB module types.
1358 */
1359static int rtDbgModCvProcessSegmentMap(PRTDBGMODCV pThis)
1360{
1361 /* Use local pointers to avoid lots of indirection and typing. */
1362 PCRTCVSEGMAPHDR pHdr = &pThis->pSegMap->Hdr;
1363 PRTCVSEGMAPDESC paDescs = &pThis->pSegMap->aDescs[0];
1364
1365 /*
1366 * If there are only logical segments, assume a direct mapping.
1367 * PE images, like the NT4 kernel, does it like this.
1368 */
1369 bool const fNoGroups = pHdr->cSegs == pHdr->cLogSegs;
1370
1371 /*
1372 * The PE image has an extra section/segment for the headers, the others
1373 * doesn't. PE images doesn't have DOS frames. So, figure the image type now.
1374 */
1375 RTLDRFMT enmImgFmt = RTLDRFMT_INVALID;
1376 if (pThis->pMod->pImgVt)
1377 enmImgFmt = pThis->pMod->pImgVt->pfnGetFormat(pThis->pMod);
1378
1379 /*
1380 * Validate and display it all.
1381 */
1382 Log2(("RTDbgModCv: SegMap: cSegs=%#x cLogSegs=%#x (cbSegNames=%#x)\n", pHdr->cSegs, pHdr->cLogSegs, pThis->cbSegNames));
1383 RTDBGMODCV_CHECK_NOMSG_RET_BF(pHdr->cSegs >= pHdr->cLogSegs);
1384
1385 Log2(("Logical segment descriptors: %u\n", pHdr->cLogSegs));
1386
1387 bool fHaveDosFrames = false;
1388 for (uint32_t i = 0; i < pHdr->cSegs; i++)
1389 {
1390 if (i == pHdr->cLogSegs)
1391 Log2(("Group/Physical descriptors: %u\n", pHdr->cSegs - pHdr->cLogSegs));
1392 char szFlags[16];
1393 memset(szFlags, '-', sizeof(szFlags));
1394 if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_READ)
1395 szFlags[0] = 'R';
1396 if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_WRITE)
1397 szFlags[1] = 'W';
1398 if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_EXECUTE)
1399 szFlags[2] = 'X';
1400 if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_32BIT)
1401 szFlags[3] = '3', szFlags[4] = '2';
1402 if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_SEL)
1403 szFlags[5] = 'S';
1404 if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_ABS)
1405 szFlags[6] = 'A';
1406 if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_GROUP)
1407 szFlags[7] = 'G';
1408 szFlags[8] = '\0';
1409 if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_RESERVED)
1410 szFlags[8] = '!', szFlags[9] = '\0';
1411 Log2((" #%02u: %#010x LB %#010x flags=%#06x ovl=%#06x group=%#06x frame=%#06x iSegName=%#06x iClassName=%#06x %s\n",
1412 i < pHdr->cLogSegs ? i : i - pHdr->cLogSegs, paDescs[i].off, paDescs[i].cb, paDescs[i].fFlags, paDescs[i].iOverlay,
1413 paDescs[i].iGroup, paDescs[i].iFrame, paDescs[i].offSegName, paDescs[i].offClassName, szFlags));
1414
1415 RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].offSegName == UINT16_MAX || paDescs[i].offSegName < pThis->cbSegNames);
1416 RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].offClassName == UINT16_MAX || paDescs[i].offClassName < pThis->cbSegNames);
1417 const char *pszName = paDescs[i].offSegName != UINT16_MAX
1418 ? pThis->pszzSegNames + paDescs[i].offSegName
1419 : NULL;
1420 const char *pszClass = paDescs[i].offClassName != UINT16_MAX
1421 ? pThis->pszzSegNames + paDescs[i].offClassName
1422 : NULL;
1423 if (pszName || pszClass)
1424 Log2((" pszName=%s pszClass=%s\n", pszName, pszClass));
1425
1426 /* Validate the group link. */
1427 RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].iGroup == 0 || !(paDescs[i].fFlags & RTCVSEGMAPDESC_F_GROUP));
1428 RTDBGMODCV_CHECK_NOMSG_RET_BF( paDescs[i].iGroup == 0
1429 || ( paDescs[i].iGroup >= pHdr->cLogSegs
1430 && paDescs[i].iGroup < pHdr->cSegs));
1431 RTDBGMODCV_CHECK_NOMSG_RET_BF( paDescs[i].iGroup == 0
1432 || (paDescs[paDescs[i].iGroup].fFlags & RTCVSEGMAPDESC_F_GROUP));
1433 RTDBGMODCV_CHECK_NOMSG_RET_BF(!(paDescs[i].fFlags & RTCVSEGMAPDESC_F_GROUP) || paDescs[i].off == 0); /* assumed below */
1434
1435 if (fNoGroups)
1436 {
1437 RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].iGroup == 0);
1438 if ( !fHaveDosFrames
1439 && paDescs[i].iFrame != 0
1440 && (paDescs[i].fFlags & (RTCVSEGMAPDESC_F_SEL | RTCVSEGMAPDESC_F_ABS))
1441 && paDescs[i].iOverlay == 0
1442 && enmImgFmt != RTLDRFMT_PE
1443 && pThis->enmType != RTCVFILETYPE_DBG
1444 && pThis->enmType != RTCVFILETYPE_PDB)
1445 fHaveDosFrames = true; /* BIOS, only groups with frames. */
1446 }
1447 }
1448
1449 /*
1450 * Further valiations based on fHaveDosFrames or not.
1451 */
1452 if (fNoGroups)
1453 {
1454 if (fHaveDosFrames)
1455 for (uint32_t i = 0; i < pHdr->cSegs; i++)
1456 {
1457 RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].iOverlay == 0);
1458 RTDBGMODCV_CHECK_NOMSG_RET_BF( (paDescs[i].fFlags & (RTCVSEGMAPDESC_F_SEL | RTCVSEGMAPDESC_F_ABS))
1459 == RTCVSEGMAPDESC_F_SEL
1460 || (paDescs[i].fFlags & (RTCVSEGMAPDESC_F_SEL | RTCVSEGMAPDESC_F_ABS))
1461 == RTCVSEGMAPDESC_F_ABS);
1462 RTDBGMODCV_CHECK_NOMSG_RET_BF(!(paDescs[i].fFlags & RTCVSEGMAPDESC_F_ABS));
1463 }
1464 else
1465 for (uint32_t i = 0; i < pHdr->cSegs; i++)
1466 RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].off == 0);
1467 }
1468
1469 /*
1470 * Modify the groups index to be the loader segment index instead, also
1471 * add the segments to the container if we haven't done that already.
1472 */
1473
1474 /* Guess work: Group can be implicit if used. Observed Visual C++ v1.5,
1475 omitting the CODE group. */
1476 const char *pszGroup0 = NULL;
1477 uint64_t cbGroup0 = 0;
1478 if (!fNoGroups && !fHaveDosFrames)
1479 {
1480 for (uint32_t i = 0; i < pHdr->cSegs; i++)
1481 if ( !(paDescs[i].fFlags & (RTCVSEGMAPDESC_F_GROUP | RTCVSEGMAPDESC_F_ABS))
1482 && paDescs[i].iGroup == 0)
1483 {
1484 if (pszGroup0 == NULL && paDescs[i].offClassName != UINT16_MAX)
1485 pszGroup0 = pThis->pszzSegNames + paDescs[i].offClassName;
1486 uint64_t offEnd = (uint64_t)paDescs[i].off + paDescs[i].cb;
1487 if (offEnd > cbGroup0)
1488 cbGroup0 = offEnd;
1489 }
1490 }
1491
1492 /* Add the segments.
1493 Note! The RVAs derived from this exercise are all wrong. :-/
1494 Note! We don't have an image loader, so we cannot add any fake sections. */
1495 /** @todo Try see if we can figure something out from the frame value later. */
1496 if (!pThis->fHaveLoadedSegments)
1497 {
1498 int rc = VINF_SUCCESS;
1499 uint16_t iSeg = 0;
1500 if (!fHaveDosFrames)
1501 {
1502 Assert(!pThis->pMod->pImgVt); Assert(pThis->enmType != RTCVFILETYPE_DBG);
1503 uint64_t uRva = 0;
1504 if (cbGroup0 && !fNoGroups)
1505 {
1506 rc = RTDbgModSegmentAdd(pThis->hCnt, 0, cbGroup0, pszGroup0 ? pszGroup0 : "Seg00", 0 /*fFlags*/, NULL);
1507 uRva += cbGroup0;
1508 iSeg++;
1509 }
1510
1511 for (uint32_t i = 0; RT_SUCCESS(rc) && i < pHdr->cSegs; i++)
1512 if ((paDescs[i].fFlags & RTCVSEGMAPDESC_F_GROUP) || fNoGroups)
1513 {
1514 char szName[16];
1515 char *pszName = szName;
1516 if (paDescs[i].offSegName != UINT16_MAX)
1517 pszName = pThis->pszzSegNames + paDescs[i].offSegName;
1518 else
1519 RTStrPrintf(szName, sizeof(szName), "Seg%02u", iSeg);
1520 rc = RTDbgModSegmentAdd(pThis->hCnt, uRva, paDescs[i].cb, pszName, 0 /*fFlags*/, NULL);
1521 uRva += paDescs[i].cb;
1522 iSeg++;
1523 }
1524 }
1525 else
1526 {
1527 /* The map is not sorted by RVA, very annoying, but I'm countering
1528 by being lazy and slow about it. :-) Btw. this is the BIOS case. */
1529 Assert(fNoGroups);
1530#if 1 /** @todo need more inputs */
1531
1532 /* Figure image base address. */
1533 uint64_t uImageBase = UINT64_MAX;
1534 for (uint32_t i = 0; RT_SUCCESS(rc) && i < pHdr->cSegs; i++)
1535 {
1536 uint64_t uAddr = (uint64_t)paDescs[i].off + ((uint32_t)paDescs[i].iFrame << 4);
1537 if (uAddr < uImageBase)
1538 uImageBase = uAddr;
1539 }
1540
1541 /* Add the segments. */
1542 uint64_t uMinAddr = uImageBase;
1543 for (uint32_t i = 0; RT_SUCCESS(rc) && i < pHdr->cSegs; i++)
1544 {
1545 /* Figure out the next one. */
1546 uint16_t cOverlaps = 0;
1547 uint16_t iBest = UINT16_MAX;
1548 uint64_t uBestAddr = UINT64_MAX;
1549 for (uint16_t j = 0; j < pHdr->cSegs; j++)
1550 {
1551 uint64_t uAddr = (uint64_t)paDescs[j].off + ((uint32_t)paDescs[j].iFrame << 4);
1552 if (uAddr >= uMinAddr && uAddr < uBestAddr)
1553 {
1554 uBestAddr = uAddr;
1555 iBest = j;
1556 }
1557 else if (uAddr == uBestAddr)
1558 {
1559 cOverlaps++;
1560 if (paDescs[j].cb > paDescs[iBest].cb)
1561 {
1562 uBestAddr = uAddr;
1563 iBest = j;
1564 }
1565 }
1566 }
1567 if (iBest == UINT16_MAX && RT_SUCCESS(rc))
1568 {
1569 rc = VERR_CV_IPE;
1570 break;
1571 }
1572
1573 /* Add it. */
1574 char szName[16];
1575 char *pszName = szName;
1576 if (paDescs[iBest].offSegName != UINT16_MAX)
1577 pszName = pThis->pszzSegNames + paDescs[iBest].offSegName;
1578 else
1579 RTStrPrintf(szName, sizeof(szName), "Seg%02u", iSeg);
1580 RTDBGSEGIDX idxDbgSeg = NIL_RTDBGSEGIDX;
1581 rc = RTDbgModSegmentAdd(pThis->hCnt, uBestAddr - uImageBase, paDescs[iBest].cb, pszName, 0 /*fFlags*/, &idxDbgSeg);
1582 Log(("CV: %#010x LB %#010x %s uRVA=%#010x iBest=%u cOverlaps=%u [idxDbgSeg=%#x iSeg=%#x]\n",
1583 uBestAddr, paDescs[iBest].cb, szName, uBestAddr - uImageBase, iBest, cOverlaps, idxDbgSeg, idxDbgSeg));
1584
1585 /* Update translations. */
1586 paDescs[iBest].iGroup = iSeg;
1587 if (cOverlaps > 0)
1588 {
1589 for (uint16_t j = 0; j < pHdr->cSegs; j++)
1590 if ((uint64_t)paDescs[j].off + ((uint32_t)paDescs[j].iFrame << 4) == uBestAddr)
1591 paDescs[iBest].iGroup = iSeg;
1592 i += cOverlaps;
1593 }
1594
1595 /* Advance. */
1596 uMinAddr = uBestAddr + 1;
1597 iSeg++;
1598 }
1599
1600 pThis->fHaveDosFrames = true;
1601#else
1602 uint32_t iFrameFirst = UINT32_MAX;
1603 uint16_t iSeg = 0;
1604 uint32_t iFrameMin = 0;
1605 do
1606 {
1607 /* Find next frame. */
1608 uint32_t iFrame = UINT32_MAX;
1609 for (uint16_t j = 0; j < pHdr->cSegs; j++)
1610 if (paDescs[j].iFrame >= iFrameMin && paDescs[j].iFrame < iFrame)
1611 iFrame = paDescs[j].iFrame;
1612 if (iFrame == UINT32_MAX)
1613 break;
1614
1615 /* Figure the frame span. */
1616 uint32_t offFirst = UINT32_MAX;
1617 uint64_t offEnd = 0;
1618 for (uint16_t j = 0; j < pHdr->cSegs; j++)
1619 if (paDescs[j].iFrame == iFrame)
1620 {
1621 uint64_t offThisEnd = paDescs[j].off + paDescs[j].cb;
1622 if (offThisEnd > offEnd)
1623 offEnd = offThisEnd;
1624 if (paDescs[j].off < offFirst)
1625 offFirst = paDescs[j].off;
1626 }
1627
1628 if (offFirst < offEnd)
1629 {
1630 /* Add it. */
1631 char szName[16];
1632 RTStrPrintf(szName, sizeof(szName), "Frame_%04x", iFrame);
1633 Log(("CV: %s offEnd=%#x offFirst=%#x\n", szName, offEnd, offFirst));
1634 if (iFrameFirst == UINT32_MAX)
1635 iFrameFirst = iFrame;
1636 rc = RTDbgModSegmentAdd(pThis->hCnt, (iFrame - iFrameFirst) << 4, offEnd, szName, 0 /*fFlags*/, NULL);
1637
1638 /* Translation updates. */
1639 for (uint16_t j = 0; j < pHdr->cSegs; j++)
1640 if (paDescs[j].iFrame == iFrame)
1641 {
1642 paDescs[j].iGroup = iSeg;
1643 paDescs[j].off = 0;
1644 paDescs[j].cb = offEnd > UINT32_MAX ? UINT32_MAX : (uint32_t)offEnd;
1645 }
1646
1647 iSeg++;
1648 }
1649
1650 iFrameMin = iFrame + 1;
1651 } while (RT_SUCCESS(rc));
1652#endif
1653 }
1654
1655 if (RT_FAILURE(rc))
1656 {
1657 Log(("RTDbgModCv: %Rrc while adding segments from SegMap\n", rc));
1658 return rc;
1659 }
1660
1661 pThis->fHaveLoadedSegments = true;
1662
1663 /* Skip the stuff below if we have DOS frames since we did it all above. */
1664 if (fHaveDosFrames)
1665 return VINF_SUCCESS;
1666 }
1667
1668 /* Pass one: Fixate the group segment indexes. */
1669 uint16_t iSeg0 = enmImgFmt == RTLDRFMT_PE || pThis->enmType == RTCVFILETYPE_DBG || pThis->enmType == RTCVFILETYPE_PDB ? 1 : 0;
1670 uint16_t iSeg = iSeg0 + (cbGroup0 > 0); /** @todo probably wrong... */
1671 for (uint32_t i = 0; i < pHdr->cSegs; i++)
1672 if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_ABS)
1673 paDescs[i].iGroup = (uint16_t)(RTDBGSEGIDX_ABS & UINT16_MAX);
1674 else if ((paDescs[i].fFlags & RTCVSEGMAPDESC_F_GROUP) || fNoGroups)
1675 paDescs[i].iGroup = iSeg++;
1676
1677 /* Pass two: Resolve group references in to segment indexes. */
1678 Log2(("Mapped segments (both kinds):\n"));
1679 for (uint32_t i = 0; i < pHdr->cSegs; i++)
1680 {
1681 if (!fNoGroups && !(paDescs[i].fFlags & (RTCVSEGMAPDESC_F_GROUP | RTCVSEGMAPDESC_F_ABS)))
1682 paDescs[i].iGroup = paDescs[i].iGroup == 0 ? iSeg0 : paDescs[paDescs[i].iGroup].iGroup;
1683
1684 Log2((" #%02u: %#010x LB %#010x -> %#06x (flags=%#06x ovl=%#06x frame=%#06x)\n",
1685 i, paDescs[i].off, paDescs[i].cb, paDescs[i].iGroup,
1686 paDescs[i].fFlags, paDescs[i].iOverlay, paDescs[i].iFrame));
1687 }
1688
1689 return VINF_SUCCESS;
1690}
1691
1692
1693/**
1694 * Loads and processes the segment map.
1695 */
1696static int rtDbgModCvLoadSegmentMap(PRTDBGMODCV pThis)
1697{
1698 /*
1699 * Search for the segment map and segment names. They will be at the end of the directory.
1700 */
1701 uint32_t iSegMap = UINT32_MAX;
1702 uint32_t iSegNames = UINT32_MAX;
1703 uint32_t i = pThis->cDirEnts;
1704 while (i-- > 0)
1705 {
1706 if ( pThis->paDirEnts[i].iMod != 0xffff
1707 && pThis->paDirEnts[i].iMod != 0x0000)
1708 break;
1709 if (pThis->paDirEnts[i].uSubSectType == kCvSst_SegMap)
1710 iSegMap = i;
1711 else if (pThis->paDirEnts[i].uSubSectType == kCvSst_SegName)
1712 iSegNames = i;
1713 }
1714 if (iSegMap == UINT32_MAX)
1715 {
1716 Log(("RTDbgModCv: No segment map present, using segment indexes as is then...\n"));
1717 return VINF_SUCCESS;
1718 }
1719 RTDBGMODCV_CHECK_RET_BF(pThis->paDirEnts[iSegMap].cb >= sizeof(RTCVSEGMAPHDR),
1720 ("Bad sstSegMap entry: cb=%#x\n", pThis->paDirEnts[iSegMap].cb));
1721 RTDBGMODCV_CHECK_NOMSG_RET_BF(iSegNames == UINT32_MAX || pThis->paDirEnts[iSegNames].cb > 0);
1722
1723 /*
1724 * Read them into memory.
1725 */
1726 int rc = rtDbgModCvReadAtAlloc(pThis, pThis->paDirEnts[iSegMap].off, (void **)&pThis->pSegMap,
1727 pThis->paDirEnts[iSegMap].cb);
1728 if (iSegNames != UINT32_MAX && RT_SUCCESS(rc))
1729 {
1730 pThis->cbSegNames = pThis->paDirEnts[iSegNames].cb;
1731 rc = rtDbgModCvReadAtAlloc(pThis, pThis->paDirEnts[iSegNames].off, (void **)&pThis->pszzSegNames,
1732 pThis->paDirEnts[iSegNames].cb);
1733 }
1734 if (RT_FAILURE(rc))
1735 return rc;
1736 RTDBGMODCV_CHECK_NOMSG_RET_BF(!pThis->pszzSegNames || !pThis->pszzSegNames[pThis->cbSegNames - 1]); /* must be terminated */
1737 RTDBGMODCV_CHECK_RET_BF(pThis->paDirEnts[iSegMap].cb >= RT_UOFFSETOF_DYN(RTCVSEGMAP, aDescs[pThis->pSegMap->Hdr.cSegs]),
1738 ("SegMap is out of bounds: cbSubSect=%#x cSegs=%#x\n",
1739 pThis->paDirEnts[iSegMap].cb, pThis->pSegMap->Hdr.cSegs));
1740
1741 /*
1742 * Join paths with the PDB handling.
1743 */
1744 return rtDbgModCvProcessSegmentMap(pThis);
1745}
1746
1747
1748
1749
1750/**
1751 * @callback_method_impl{PFNRTSORTCMP,
1752 * Used by rtDbgModCvLoadDirectory to sort the directory.}
1753 */
1754static DECLCALLBACK(int) rtDbgModCvDirEntCmp(void const *pvElement1, void const *pvElement2, void *pvUser)
1755{
1756 PRTCVDIRENT32 pEntry1 = (PRTCVDIRENT32)pvElement1;
1757 PRTCVDIRENT32 pEntry2 = (PRTCVDIRENT32)pvElement2;
1758 if (pEntry1->iMod < pEntry2->iMod)
1759 return -1;
1760 if (pEntry1->iMod > pEntry2->iMod)
1761 return 1;
1762 if (pEntry1->uSubSectType < pEntry2->uSubSectType)
1763 return -1;
1764 if (pEntry1->uSubSectType > pEntry2->uSubSectType)
1765 return 1;
1766
1767 RT_NOREF_PV(pvUser);
1768 return 0;
1769}
1770
1771
1772/**
1773 * Loads the directory into memory (RTDBGMODCV::paDirEnts and
1774 * RTDBGMODCV::cDirEnts).
1775 *
1776 * Converting old format version into the newer format to simplifying the code
1777 * using the directory.
1778 *
1779 *
1780 * @returns IPRT status code. (May leave with paDirEnts allocated on failure.)
1781 * @param pThis The CV reader instance.
1782 */
1783static int rtDbgModCvLoadDirectory(PRTDBGMODCV pThis)
1784{
1785 /*
1786 * Read in the CV directory.
1787 */
1788 int rc;
1789 if ( pThis->u32CvMagic == RTCVHDR_MAGIC_NB00
1790 || pThis->u32CvMagic == RTCVHDR_MAGIC_NB02)
1791 {
1792 /*
1793 * 16-bit type.
1794 */
1795 RTCVDIRHDR16 DirHdr;
1796 rc = rtDbgModCvReadAt(pThis, pThis->offDir, &DirHdr, sizeof(DirHdr));
1797 if (RT_SUCCESS(rc))
1798 {
1799 if (DirHdr.cEntries > 2 && DirHdr.cEntries < _64K - 32U)
1800 {
1801 pThis->cDirEnts = DirHdr.cEntries;
1802 pThis->paDirEnts = (PRTCVDIRENT32)RTMemAlloc(DirHdr.cEntries * sizeof(pThis->paDirEnts[0]));
1803 if (pThis->paDirEnts)
1804 {
1805 rc = rtDbgModCvReadAt(pThis, pThis->offDir + sizeof(DirHdr),
1806 pThis->paDirEnts, DirHdr.cEntries * sizeof(RTCVDIRENT16));
1807 if (RT_SUCCESS(rc))
1808 {
1809 /* Convert the entries (from the end). */
1810 uint32_t cLeft = DirHdr.cEntries;
1811 RTCVDIRENT32 volatile *pDst = pThis->paDirEnts + cLeft;
1812 RTCVDIRENT16 volatile *pSrc = (RTCVDIRENT16 volatile *)pThis->paDirEnts + cLeft;
1813 while (cLeft--)
1814 {
1815 pDst--;
1816 pSrc--;
1817
1818 pDst->cb = pSrc->cb;
1819 pDst->off = RT_MAKE_U32(pSrc->offLow, pSrc->offHigh);
1820 pDst->iMod = pSrc->iMod;
1821 pDst->uSubSectType = pSrc->uSubSectType;
1822 }
1823 }
1824 }
1825 else
1826 rc = VERR_NO_MEMORY;
1827 }
1828 else
1829 {
1830 Log(("Old CV directory count is out of considered valid range: %#x\n", DirHdr.cEntries));
1831 rc = VERR_CV_BAD_FORMAT;
1832 }
1833 }
1834 }
1835 else
1836 {
1837 /*
1838 * 32-bit type (reading too much for NB04 is no problem).
1839 *
1840 * Note! The watcom linker (v1.9) seems to overwrite the directory
1841 * header and more under some conditions. So, if this code fails
1842 * you might be so lucky as to have reproduce that issue...
1843 */
1844 RTCVDIRHDR32EX DirHdr;
1845 rc = rtDbgModCvReadAt(pThis, pThis->offDir, &DirHdr, sizeof(DirHdr));
1846 if (RT_SUCCESS(rc))
1847 {
1848 if ( DirHdr.Core.cbHdr != sizeof(DirHdr.Core)
1849 && DirHdr.Core.cbHdr != sizeof(DirHdr))
1850 {
1851 Log(("Unexpected CV directory size: %#x [wlink screwup?]\n", DirHdr.Core.cbHdr));
1852 rc = VERR_CV_BAD_FORMAT;
1853 }
1854 if ( DirHdr.Core.cbHdr == sizeof(DirHdr)
1855 && ( DirHdr.offNextDir != 0
1856 || DirHdr.fFlags != 0) )
1857 {
1858 Log(("Extended CV directory headers fields are not zero: fFlags=%#x offNextDir=%#x [wlink screwup?]\n",
1859 DirHdr.fFlags, DirHdr.offNextDir));
1860 rc = VERR_CV_BAD_FORMAT;
1861 }
1862 if (DirHdr.Core.cbEntry != sizeof(RTCVDIRENT32))
1863 {
1864 Log(("Unexpected CV directory entry size: %#x (expected %#x) [wlink screwup?]\n", DirHdr.Core.cbEntry, sizeof(RTCVDIRENT32)));
1865 rc = VERR_CV_BAD_FORMAT;
1866 }
1867 if (DirHdr.Core.cEntries < 2 || DirHdr.Core.cEntries >= _512K)
1868 {
1869 Log(("CV directory count is out of considered valid range: %#x [wlink screwup?]\n", DirHdr.Core.cEntries));
1870 rc = VERR_CV_BAD_FORMAT;
1871 }
1872 if (RT_SUCCESS(rc))
1873 {
1874 pThis->cDirEnts = DirHdr.Core.cEntries;
1875 pThis->paDirEnts = (PRTCVDIRENT32)RTMemAlloc(DirHdr.Core.cEntries * sizeof(pThis->paDirEnts[0]));
1876 if (pThis->paDirEnts)
1877 rc = rtDbgModCvReadAt(pThis, pThis->offDir + DirHdr.Core.cbHdr,
1878 pThis->paDirEnts, DirHdr.Core.cEntries * sizeof(RTCVDIRENT32));
1879 else
1880 rc = VERR_NO_MEMORY;
1881 }
1882 }
1883 }
1884
1885 if (RT_SUCCESS(rc))
1886 {
1887 uint32_t const cbDbgInfo = pThis->cbDbgInfo;
1888 uint32_t const cDirEnts = pThis->cDirEnts;
1889
1890 /*
1891 * Just sort the directory in a way we like, no need to make
1892 * complicated demands on the linker output.
1893 */
1894 RTSortShell(pThis->paDirEnts, cDirEnts, sizeof(pThis->paDirEnts[0]), rtDbgModCvDirEntCmp, NULL);
1895
1896 /*
1897 * Basic info validation.
1898 */
1899 uint16_t cGlobalMods = 0;
1900 uint16_t cNormalMods = 0;
1901 uint16_t iModLast = 0;
1902 Log2(("RTDbgModCv: %u (%#x) directory entries:\n", cDirEnts, cDirEnts));
1903 for (uint32_t i = 0; i < cDirEnts; i++)
1904 {
1905 PCRTCVDIRENT32 pDirEnt = &pThis->paDirEnts[i];
1906 Log2((" #%04u mod=%#06x sst=%#06x at %#010x LB %#07x %s\n",
1907 i, pDirEnt->iMod, pDirEnt->uSubSectType, pDirEnt->off, pDirEnt->cb,
1908 rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType)));
1909
1910 if ( pDirEnt->off >= cbDbgInfo
1911 || pDirEnt->cb >= cbDbgInfo
1912 || pDirEnt->off + pDirEnt->cb > cbDbgInfo)
1913 {
1914 Log(("CV directory entry #%u is out of bounds: %#x LB %#x, max %#x\n", i, pDirEnt->off, pDirEnt->cb, cbDbgInfo));
1915 rc = VERR_CV_BAD_FORMAT;
1916 }
1917 if ( pDirEnt->iMod == 0
1918 && pThis->u32CvMagic != RTCVHDR_MAGIC_NB04
1919 && pThis->u32CvMagic != RTCVHDR_MAGIC_NB02
1920 && pThis->u32CvMagic != RTCVHDR_MAGIC_NB00)
1921 {
1922 Log(("CV directory entry #%u uses module index 0 (uSubSectType=%#x)\n", i, pDirEnt->uSubSectType));
1923 rc = VERR_CV_BAD_FORMAT;
1924 }
1925 if (pDirEnt->iMod == 0 || pDirEnt->iMod == 0xffff)
1926 cGlobalMods++;
1927 else
1928 {
1929 if (pDirEnt->iMod > iModLast)
1930 {
1931 if ( pDirEnt->uSubSectType != kCvSst_Module
1932 && pDirEnt->uSubSectType != kCvSst_OldModule)
1933 {
1934 Log(("CV directory entry #%u: expected module subsection first, found %s (%#x)\n",
1935 i, rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType), pDirEnt->uSubSectType));
1936 rc = VERR_CV_BAD_FORMAT;
1937 }
1938 if (pDirEnt->iMod != iModLast + 1)
1939 {
1940 Log(("CV directory entry #%u: skips from mod %#x to %#x modules\n", i, iModLast, pDirEnt->iMod));
1941 rc = VERR_CV_BAD_FORMAT;
1942 }
1943 iModLast = pDirEnt->iMod;
1944 }
1945 cNormalMods++;
1946 }
1947 }
1948 if (cGlobalMods == 0)
1949 {
1950 Log(("CV directory contains no global modules\n"));
1951 rc = VERR_CV_BAD_FORMAT;
1952 }
1953 if (RT_SUCCESS(rc))
1954 {
1955 Log(("CV dir stats: %u total, %u normal, %u special, iModLast=%#x (%u)\n",
1956 cDirEnts, cNormalMods, cGlobalMods, iModLast, iModLast));
1957
1958#if 0 /* skip this stuff */
1959 /*
1960 * Validate the directory ordering.
1961 */
1962 uint16_t i = 0;
1963
1964 /* Normal modules. */
1965 if (pThis->enmDirOrder != RTCVDIRORDER_BY_SST_MOD)
1966 {
1967 uint16_t iEndNormalMods = cNormalMods + (pThis->enmDirOrder == RTCVDIRORDER_BY_MOD_0 ? cGlobalMods : 0);
1968 while (i < iEndNormalMods)
1969 {
1970 if (pThis->paDirEnts[i].iMod == 0 || pThis->paDirEnts[i].iMod == 0xffff)
1971 {
1972 Log(("CV directory entry #%u: Unexpected global module entry.\n", i));
1973 rc = VERR_CV_BAD_FORMAT;
1974 }
1975 i++;
1976 }
1977 }
1978 else
1979 {
1980 uint32_t fSeen = RT_BIT_32(kCvSst_Module - kCvSst_Module)
1981 | RT_BIT_32(kCvSst_Libraries - kCvSst_Module)
1982 | RT_BIT_32(kCvSst_GlobalSym - kCvSst_Module)
1983 | RT_BIT_32(kCvSst_GlobalPub - kCvSst_Module)
1984 | RT_BIT_32(kCvSst_GlobalTypes - kCvSst_Module)
1985 | RT_BIT_32(kCvSst_SegName - kCvSst_Module)
1986 | RT_BIT_32(kCvSst_SegMap - kCvSst_Module)
1987 | RT_BIT_32(kCvSst_StaticSym - kCvSst_Module)
1988 | RT_BIT_32(kCvSst_FileIndex - kCvSst_Module)
1989 | RT_BIT_32(kCvSst_MPC - kCvSst_Module);
1990 uint16_t iMod = 0;
1991 uint16_t uSst = kCvSst_Module;
1992 while (i < cNormalMods)
1993 {
1994 PCRTCVDIRENT32 pDirEnt = &pThis->paDirEnts[i];
1995 if ( pDirEnt->iMod > iMod
1996 || pDirEnt->iMod == iMod) /* wlink subjected to MSVC 2010 /Z7 files with multiple .debug$S. */
1997 {
1998 if (pDirEnt->uSubSectType != uSst)
1999 {
2000 Log(("CV directory entry #%u: Expected %s (%#x), found %s (%#x).\n",
2001 i, rtDbgModCvGetSubSectionName(uSst), uSst,
2002 rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType), pDirEnt->uSubSectType));
2003 rc = VERR_CV_BAD_FORMAT;
2004 }
2005 }
2006 else
2007 {
2008 uint32_t iBit = pDirEnt->uSubSectType - kCvSst_Module;
2009 if (iBit >= 32U || (fSeen & RT_BIT_32(iBit)))
2010 {
2011 Log(("CV directory entry #%u: SST %s (%#x) has already been seen or is for globals.\n",
2012 i, rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType), pDirEnt->uSubSectType));
2013 rc = VERR_CV_BAD_FORMAT;
2014 }
2015 fSeen |= RT_BIT_32(iBit);
2016 }
2017
2018 uSst = pDirEnt->uSubSectType;
2019 iMod = pDirEnt->iMod;
2020 i++;
2021 }
2022 }
2023
2024 /* New style with special modules at the end. */
2025 if (pThis->enmDirOrder != RTCVDIRORDER_BY_MOD_0)
2026 while (i < cDirEnts)
2027 {
2028 if (pThis->paDirEnts[i].iMod != 0 && pThis->paDirEnts[i].iMod != 0xffff)
2029 {
2030 Log(("CV directory entry #%u: Expected global module entry, not %#x.\n", i,
2031 pThis->paDirEnts[i].iMod));
2032 rc = VERR_CV_BAD_FORMAT;
2033 }
2034 i++;
2035 }
2036#endif
2037 }
2038 }
2039
2040 return rc;
2041}
2042
2043
2044static int rtDbgModCvLoadCodeViewInfo(PRTDBGMODCV pThis)
2045{
2046 /*
2047 * Load the directory, the segment map (if any) and then scan for segments
2048 * if necessary.
2049 */
2050 int rc = rtDbgModCvLoadDirectory(pThis);
2051 if (RT_SUCCESS(rc))
2052 rc = rtDbgModCvLoadSegmentMap(pThis);
2053 if (RT_SUCCESS(rc) && !pThis->fHaveLoadedSegments)
2054 {
2055 rc = VERR_CV_TODO; /** @todo Scan anything containing address, in particular sstSegMap and sstModule,
2056 * and reconstruct the segments from that information. */
2057 pThis->cbImage = 0x1000;
2058 rc = VINF_SUCCESS;
2059 }
2060
2061 /*
2062 * Process the directory.
2063 */
2064 for (uint32_t i = 0; RT_SUCCESS(rc) && i < pThis->cDirEnts; i++)
2065 {
2066 PCRTCVDIRENT32 pDirEnt = &pThis->paDirEnts[i];
2067 Log3(("Processing module %#06x subsection #%04u %s\n", pDirEnt->iMod, i, rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType)));
2068 PFNDBGMODCVSUBSECTCALLBACK pfnCallback = NULL;
2069 switch (pDirEnt->uSubSectType)
2070 {
2071 case kCvSst_GlobalPub:
2072 case kCvSst_GlobalSym:
2073 case kCvSst_StaticSym:
2074 pfnCallback = rtDbgModCvSs_GlobalPub_GlobalSym_StaticSym;
2075 break;
2076 case kCvSst_Module:
2077 pfnCallback = rtDbgModCvSs_Module;
2078 break;
2079 case kCvSst_PublicSym:
2080 case kCvSst_Symbols:
2081 case kCvSst_AlignSym:
2082 pfnCallback = rtDbgModCvSs_Symbols_PublicSym_AlignSym;
2083 break;
2084
2085 case kCvSst_OldModule:
2086 case kCvSst_OldPublic:
2087 case kCvSst_OldTypes:
2088 case kCvSst_OldSymbols:
2089 case kCvSst_OldSrcLines:
2090 case kCvSst_OldLibraries:
2091 case kCvSst_OldImports:
2092 case kCvSst_OldCompacted:
2093 case kCvSst_OldSrcLnSeg:
2094 case kCvSst_OldSrcLines3:
2095 /** @todo implement more. */
2096 break;
2097
2098 case kCvSst_Types:
2099 case kCvSst_Public:
2100 case kCvSst_SrcLnSeg:
2101 /** @todo implement more. */
2102 break;
2103 case kCvSst_SrcModule:
2104 pfnCallback = rtDbgModCvSs_SrcModule;
2105 break;
2106 case kCvSst_Libraries:
2107 case kCvSst_GlobalTypes:
2108 case kCvSst_MPC:
2109 case kCvSst_PreComp:
2110 case kCvSst_PreCompMap:
2111 case kCvSst_OffsetMap16:
2112 case kCvSst_OffsetMap32:
2113 case kCvSst_FileIndex:
2114
2115 default:
2116 /** @todo implement more. */
2117 break;
2118
2119 /* Skip because we've already processed them: */
2120 case kCvSst_SegMap:
2121 case kCvSst_SegName:
2122 pfnCallback = NULL;
2123 break;
2124 }
2125
2126 if (pfnCallback)
2127 {
2128 void *pvSubSect;
2129 rc = rtDbgModCvReadAtAlloc(pThis, pDirEnt->off, &pvSubSect, pDirEnt->cb);
2130 if (RT_SUCCESS(rc))
2131 {
2132 rc = pfnCallback(pThis, pvSubSect, pDirEnt->cb, pDirEnt);
2133 RTMemFree(pvSubSect);
2134 }
2135 }
2136 }
2137
2138 /*
2139 * Free temporary parsing objects.
2140 */
2141 if (pThis->pbSrcInfo)
2142 {
2143 RTMemFree(pThis->pbSrcInfo);
2144 pThis->pbSrcInfo = NULL;
2145 pThis->cbSrcInfo = 0;
2146 pThis->cbSrcInfoAlloc = 0;
2147 }
2148 if (pThis->pchSrcStrings)
2149 {
2150 RTMemFree(pThis->pchSrcStrings);
2151 pThis->pchSrcStrings = NULL;
2152 pThis->cbSrcStrings = 0;
2153 pThis->cbSrcStringsAlloc = 0;
2154 }
2155
2156 return rc;
2157}
2158
2159
2160/*
2161 *
2162 * COFF Debug Info Parsing.
2163 * COFF Debug Info Parsing.
2164 * COFF Debug Info Parsing.
2165 *
2166 */
2167
2168#ifdef LOG_ENABLED
2169static const char *rtDbgModCvGetCoffStorageClassName(uint8_t bStorageClass)
2170{
2171 switch (bStorageClass)
2172 {
2173 case IMAGE_SYM_CLASS_END_OF_FUNCTION: return "END_OF_FUNCTION";
2174 case IMAGE_SYM_CLASS_NULL: return "NULL";
2175 case IMAGE_SYM_CLASS_AUTOMATIC: return "AUTOMATIC";
2176 case IMAGE_SYM_CLASS_EXTERNAL: return "EXTERNAL";
2177 case IMAGE_SYM_CLASS_STATIC: return "STATIC";
2178 case IMAGE_SYM_CLASS_REGISTER: return "REGISTER";
2179 case IMAGE_SYM_CLASS_EXTERNAL_DEF: return "EXTERNAL_DEF";
2180 case IMAGE_SYM_CLASS_LABEL: return "LABEL";
2181 case IMAGE_SYM_CLASS_UNDEFINED_LABEL: return "UNDEFINED_LABEL";
2182 case IMAGE_SYM_CLASS_MEMBER_OF_STRUCT: return "MEMBER_OF_STRUCT";
2183 case IMAGE_SYM_CLASS_ARGUMENT: return "ARGUMENT";
2184 case IMAGE_SYM_CLASS_STRUCT_TAG: return "STRUCT_TAG";
2185 case IMAGE_SYM_CLASS_MEMBER_OF_UNION: return "MEMBER_OF_UNION";
2186 case IMAGE_SYM_CLASS_UNION_TAG: return "UNION_TAG";
2187 case IMAGE_SYM_CLASS_TYPE_DEFINITION: return "TYPE_DEFINITION";
2188 case IMAGE_SYM_CLASS_UNDEFINED_STATIC: return "UNDEFINED_STATIC";
2189 case IMAGE_SYM_CLASS_ENUM_TAG: return "ENUM_TAG";
2190 case IMAGE_SYM_CLASS_MEMBER_OF_ENUM: return "MEMBER_OF_ENUM";
2191 case IMAGE_SYM_CLASS_REGISTER_PARAM: return "REGISTER_PARAM";
2192 case IMAGE_SYM_CLASS_BIT_FIELD: return "BIT_FIELD";
2193 case IMAGE_SYM_CLASS_FAR_EXTERNAL: return "FAR_EXTERNAL";
2194 case IMAGE_SYM_CLASS_BLOCK: return "BLOCK";
2195 case IMAGE_SYM_CLASS_FUNCTION: return "FUNCTION";
2196 case IMAGE_SYM_CLASS_END_OF_STRUCT: return "END_OF_STRUCT";
2197 case IMAGE_SYM_CLASS_FILE: return "FILE";
2198 case IMAGE_SYM_CLASS_SECTION: return "SECTION";
2199 case IMAGE_SYM_CLASS_WEAK_EXTERNAL: return "WEAK_EXTERNAL";
2200 case IMAGE_SYM_CLASS_CLR_TOKEN: return "CLR_TOKEN";
2201 }
2202
2203 static char s_szName[32];
2204 RTStrPrintf(s_szName, sizeof(s_szName), "Unknown%#04x", bStorageClass);
2205 return s_szName;
2206}
2207#endif /* LOG_ENABLED */
2208
2209
2210/**
2211 * Adds a chunk of COFF line numbers.
2212 *
2213 * @param pThis The COFF/CodeView reader instance.
2214 * @param pszFile The source file name.
2215 * @param iSection The section number.
2216 * @param paLines Pointer to the first line number table entry.
2217 * @param cLines The number of line number table entries to add.
2218 */
2219static void rtDbgModCvAddCoffLineNumbers(PRTDBGMODCV pThis, const char *pszFile, uint32_t iSection,
2220 PCIMAGE_LINENUMBER paLines, uint32_t cLines)
2221{
2222 RT_NOREF_PV(iSection);
2223 Log4(("Adding %u line numbers in section #%u for %s\n", cLines, iSection, pszFile));
2224 PCIMAGE_LINENUMBER pCur = paLines;
2225 while (cLines-- > 0)
2226 {
2227 if (pCur->Linenumber)
2228 {
2229 int rc = RTDbgModLineAdd(pThis->hCnt, pszFile, pCur->Linenumber, RTDBGSEGIDX_RVA, pCur->Type.VirtualAddress, NULL);
2230 Log4((" %#010x: %u [%Rrc]\n", pCur->Type.VirtualAddress, pCur->Linenumber, rc)); NOREF(rc);
2231 }
2232 pCur++;
2233 }
2234}
2235
2236
2237/**
2238 * Adds a COFF symbol.
2239 *
2240 * @returns IPRT status (ignored)
2241 * @param pThis The COFF/CodeView reader instance.
2242 * @param idxSeg IPRT RVA or ABS segment index indicator.
2243 * @param uValue The symbol value.
2244 * @param pszName The symbol name.
2245 */
2246static int rtDbgModCvAddCoffSymbol(PRTDBGMODCV pThis, uint32_t idxSeg, uint32_t uValue, const char *pszName)
2247{
2248 int rc = RTDbgModSymbolAdd(pThis->hCnt, pszName, idxSeg, uValue, 0, 0 /*fFlags*/, NULL);
2249 Log(("Symbol: %s:%08x %s [%Rrc]\n", idxSeg == RTDBGSEGIDX_RVA ? "rva" : "abs", uValue, pszName, rc));
2250 if (rc == VERR_DBG_ADDRESS_CONFLICT || rc == VERR_DBG_DUPLICATE_SYMBOL)
2251 rc = VINF_SUCCESS;
2252 return rc;
2253}
2254
2255
2256/**
2257 * Processes the COFF symbol table.
2258 *
2259 * @returns IPRT status code
2260 * @param pThis The COFF/CodeView reader instance.
2261 * @param paSymbols Pointer to the symbol table.
2262 * @param cSymbols The number of entries in the symbol table.
2263 * @param paLines Pointer to the line number table.
2264 * @param cLines The number of entires in the line number table.
2265 * @param pszzStrTab Pointer to the string table.
2266 * @param cbStrTab Size of the string table.
2267 */
2268static int rtDbgModCvProcessCoffSymbolTable(PRTDBGMODCV pThis,
2269 PCIMAGE_SYMBOL paSymbols, uint32_t cSymbols,
2270 PCIMAGE_LINENUMBER paLines, uint32_t cLines,
2271 const char *pszzStrTab, uint32_t cbStrTab)
2272{
2273 Log3(("Processing COFF symbol table with %#x symbols\n", cSymbols));
2274
2275 /*
2276 * Making some bold assumption that the line numbers for the section in
2277 * the file are allocated sequentially, we do multiple passes until we've
2278 * gathered them all.
2279 */
2280 int rc = VINF_SUCCESS;
2281 uint32_t cSections = 1;
2282 uint32_t iLineSect = 1;
2283 uint32_t iLine = 0;
2284 do
2285 {
2286 /*
2287 * Process the symbols.
2288 */
2289 char szShort[9];
2290 char szFile[RTPATH_MAX];
2291 uint32_t iSymbol = 0;
2292 szFile[0] = '\0';
2293 szShort[8] = '\0'; /* avoid having to terminate it all the time. */
2294
2295 while (iSymbol < cSymbols && RT_SUCCESS(rc))
2296 {
2297 /* Copy the symbol in and hope it works around the misalignment
2298 issues everywhere. */
2299 IMAGE_SYMBOL Sym;
2300 memcpy(&Sym, &paSymbols[iSymbol], sizeof(Sym));
2301 RTDBGMODCV_CHECK_NOMSG_RET_BF(Sym.NumberOfAuxSymbols < cSymbols);
2302
2303 /* Calc a zero terminated symbol name. */
2304 const char *pszName;
2305 if (Sym.N.Name.Short)
2306 pszName = (const char *)memcpy(szShort, &Sym.N, 8);
2307 else
2308 {
2309 RTDBGMODCV_CHECK_NOMSG_RET_BF(Sym.N.Name.Long < cbStrTab);
2310 pszName = pszzStrTab + Sym.N.Name.Long;
2311 }
2312
2313 /* Only log stuff and count sections the in the first pass.*/
2314 if (iLineSect == 1)
2315 {
2316 Log3(("%04x: s=%#06x v=%#010x t=%#06x a=%#04x c=%#04x (%s) name='%s'\n",
2317 iSymbol, Sym.SectionNumber, Sym.Value, Sym.Type, Sym.NumberOfAuxSymbols,
2318 Sym.StorageClass, rtDbgModCvGetCoffStorageClassName(Sym.StorageClass), pszName));
2319 if ((int16_t)cSections <= Sym.SectionNumber && Sym.SectionNumber > 0)
2320 cSections = Sym.SectionNumber + 1;
2321 }
2322
2323 /*
2324 * Use storage class to pick what we need (which isn't much because,
2325 * MS only provides a very restricted set of symbols).
2326 */
2327 IMAGE_AUX_SYMBOL Aux;
2328 switch (Sym.StorageClass)
2329 {
2330 case IMAGE_SYM_CLASS_NULL:
2331 /* a NOP */
2332 break;
2333
2334 case IMAGE_SYM_CLASS_FILE:
2335 {
2336 /* Change the current file name (for line numbers). Pretend
2337 ANSI and ISO-8859-1 are similar enough for out purposes... */
2338 RTDBGMODCV_CHECK_NOMSG_RET_BF(Sym.NumberOfAuxSymbols > 0);
2339 const char *pszFile = (const char *)&paSymbols[iSymbol + 1];
2340 char *pszDst = szFile;
2341 rc = RTLatin1ToUtf8Ex(pszFile, Sym.NumberOfAuxSymbols * sizeof(IMAGE_SYMBOL), &pszDst, sizeof(szFile), NULL);
2342 if (RT_FAILURE(rc))
2343 Log(("Error converting COFF filename: %Rrc\n", rc));
2344 else if (iLineSect == 1)
2345 Log3((" filename='%s'\n", szFile));
2346 break;
2347 }
2348
2349 case IMAGE_SYM_CLASS_STATIC:
2350 if ( Sym.NumberOfAuxSymbols == 1
2351 && ( iLineSect == 1
2352 || Sym.SectionNumber == (int32_t)iLineSect) )
2353 {
2354 memcpy(&Aux, &paSymbols[iSymbol + 1], sizeof(Aux));
2355 if (iLineSect == 1)
2356 Log3((" section: cb=%#010x #relocs=%#06x #lines=%#06x csum=%#x num=%#x sel=%x rvd=%u\n",
2357 Aux.Section.Length, Aux.Section.NumberOfRelocations,
2358 Aux.Section.NumberOfLinenumbers,
2359 Aux.Section.CheckSum,
2360 RT_MAKE_U32(Aux.Section.Number, Aux.Section.HighNumber),
2361 Aux.Section.Selection,
2362 Aux.Section.bReserved));
2363 if ( Sym.SectionNumber == (int32_t)iLineSect
2364 && Aux.Section.NumberOfLinenumbers > 0)
2365 {
2366 uint32_t cLinesToAdd = RT_MIN(Aux.Section.NumberOfLinenumbers, cLines - iLine);
2367 if (iLine < cLines && szFile[0])
2368 rtDbgModCvAddCoffLineNumbers(pThis, szFile, iLineSect, &paLines[iLine], cLinesToAdd);
2369 iLine += cLinesToAdd;
2370 }
2371 }
2372 /* Not so sure about the quality here, but might be useful. */
2373 else if ( iLineSect == 1
2374 && Sym.NumberOfAuxSymbols == 0
2375 && Sym.SectionNumber != IMAGE_SYM_UNDEFINED
2376 && Sym.SectionNumber != IMAGE_SYM_ABSOLUTE
2377 && Sym.SectionNumber != IMAGE_SYM_DEBUG
2378 && Sym.Value > 0
2379 && *pszName)
2380 rtDbgModCvAddCoffSymbol(pThis, RTDBGSEGIDX_RVA, Sym.Value, pszName);
2381 break;
2382
2383 case IMAGE_SYM_CLASS_EXTERNAL:
2384 /* Add functions (first pass only). */
2385 if ( iLineSect == 1
2386 && (ISFCN(Sym.Type) || Sym.Type == 0)
2387 && Sym.NumberOfAuxSymbols == 0
2388 && *pszName )
2389 {
2390 if (Sym.SectionNumber == IMAGE_SYM_ABSOLUTE)
2391 rtDbgModCvAddCoffSymbol(pThis, RTDBGSEGIDX_ABS, Sym.Value, pszName);
2392 else if ( Sym.SectionNumber != IMAGE_SYM_UNDEFINED
2393 && Sym.SectionNumber != IMAGE_SYM_DEBUG)
2394 rtDbgModCvAddCoffSymbol(pThis, RTDBGSEGIDX_RVA, Sym.Value, pszName);
2395 }
2396 break;
2397
2398 case IMAGE_SYM_CLASS_FUNCTION:
2399 /* Not sure this is really used. */
2400 break;
2401
2402 case IMAGE_SYM_CLASS_END_OF_FUNCTION:
2403 case IMAGE_SYM_CLASS_AUTOMATIC:
2404 case IMAGE_SYM_CLASS_REGISTER:
2405 case IMAGE_SYM_CLASS_EXTERNAL_DEF:
2406 case IMAGE_SYM_CLASS_LABEL:
2407 case IMAGE_SYM_CLASS_UNDEFINED_LABEL:
2408 case IMAGE_SYM_CLASS_MEMBER_OF_STRUCT:
2409 case IMAGE_SYM_CLASS_ARGUMENT:
2410 case IMAGE_SYM_CLASS_STRUCT_TAG:
2411 case IMAGE_SYM_CLASS_MEMBER_OF_UNION:
2412 case IMAGE_SYM_CLASS_UNION_TAG:
2413 case IMAGE_SYM_CLASS_TYPE_DEFINITION:
2414 case IMAGE_SYM_CLASS_UNDEFINED_STATIC:
2415 case IMAGE_SYM_CLASS_ENUM_TAG:
2416 case IMAGE_SYM_CLASS_MEMBER_OF_ENUM:
2417 case IMAGE_SYM_CLASS_REGISTER_PARAM:
2418 case IMAGE_SYM_CLASS_BIT_FIELD:
2419 case IMAGE_SYM_CLASS_FAR_EXTERNAL:
2420 case IMAGE_SYM_CLASS_BLOCK:
2421 case IMAGE_SYM_CLASS_END_OF_STRUCT:
2422 case IMAGE_SYM_CLASS_SECTION:
2423 case IMAGE_SYM_CLASS_WEAK_EXTERNAL:
2424 case IMAGE_SYM_CLASS_CLR_TOKEN:
2425 /* Not used by MS, I think. */
2426 break;
2427
2428 default:
2429 Log(("RTDbgCv: Unexpected COFF storage class %#x (%u)\n", Sym.StorageClass, Sym.StorageClass));
2430 break;
2431 }
2432
2433 /* next symbol */
2434 iSymbol += 1 + Sym.NumberOfAuxSymbols;
2435 }
2436
2437 /* Next section with line numbers. */
2438 iLineSect++;
2439 } while (iLine < cLines && iLineSect < cSections && RT_SUCCESS(rc));
2440
2441 return rc;
2442}
2443
2444
2445/**
2446 * Loads COFF debug information into the container.
2447 *
2448 * @returns IPRT status code.
2449 * @param pThis The COFF/CodeView debug reader instance.
2450 */
2451static int rtDbgModCvLoadCoffInfo(PRTDBGMODCV pThis)
2452{
2453 /*
2454 * Read the whole section into memory.
2455 * Note! Cannot use rtDbgModCvReadAt or rtDbgModCvReadAtAlloc here.
2456 */
2457 int rc;
2458 uint8_t *pbDbgSect = (uint8_t *)RTMemAlloc(pThis->cbCoffDbgInfo);
2459 if (pbDbgSect)
2460 {
2461 if (pThis->hFile == NIL_RTFILE)
2462 rc = pThis->pMod->pImgVt->pfnReadAt(pThis->pMod, UINT32_MAX, pThis->offCoffDbgInfo, pbDbgSect, pThis->cbCoffDbgInfo);
2463 else
2464 rc = RTFileReadAt(pThis->hFile, pThis->offCoffDbgInfo, pbDbgSect, pThis->cbCoffDbgInfo, NULL);
2465 if (RT_SUCCESS(rc))
2466 {
2467 /* The string table follows after the symbol table. */
2468 const char *pszzStrTab = (const char *)( pbDbgSect
2469 + pThis->CoffHdr.LvaToFirstSymbol
2470 + pThis->CoffHdr.NumberOfSymbols * sizeof(IMAGE_SYMBOL));
2471 uint32_t cbStrTab = (uint32_t)((uintptr_t)(pbDbgSect + pThis->cbCoffDbgInfo) - (uintptr_t)pszzStrTab);
2472 /** @todo The symbol table starts with a size. Read it and checking. Also verify
2473 * that the symtab ends with a terminator character. */
2474
2475 rc = rtDbgModCvProcessCoffSymbolTable(pThis,
2476 (PCIMAGE_SYMBOL)(pbDbgSect + pThis->CoffHdr.LvaToFirstSymbol),
2477 pThis->CoffHdr.NumberOfSymbols,
2478 (PCIMAGE_LINENUMBER)(pbDbgSect + pThis->CoffHdr.LvaToFirstLinenumber),
2479 pThis->CoffHdr.NumberOfLinenumbers,
2480 pszzStrTab, cbStrTab);
2481 }
2482 RTMemFree(pbDbgSect);
2483 }
2484 else
2485 rc = VERR_NO_MEMORY;
2486 return rc;
2487}
2488
2489
2490
2491
2492
2493
2494/*
2495 *
2496 * CodeView Debug module implementation.
2497 * CodeView Debug module implementation.
2498 * CodeView Debug module implementation.
2499 *
2500 */
2501
2502
2503/** @interface_method_impl{RTDBGMODVTDBG,pfnUnwindFrame} */
2504static DECLCALLBACK(int) rtDbgModCv_UnwindFrame(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState)
2505{
2506 RT_NOREF(pMod, iSeg, off, pState);
2507 return VERR_DBG_NO_UNWIND_INFO;
2508}
2509
2510
2511/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByAddr} */
2512static DECLCALLBACK(int) rtDbgModCv_LineByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off,
2513 PRTINTPTR poffDisp, PRTDBGLINE pLineInfo)
2514{
2515 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2516 return RTDbgModLineByAddr(pThis->hCnt, iSeg, off, poffDisp, pLineInfo);
2517}
2518
2519
2520/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByOrdinal} */
2521static DECLCALLBACK(int) rtDbgModCv_LineByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo)
2522{
2523 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2524 return RTDbgModLineByOrdinal(pThis->hCnt, iOrdinal, pLineInfo);
2525}
2526
2527
2528/** @interface_method_impl{RTDBGMODVTDBG,pfnLineCount} */
2529static DECLCALLBACK(uint32_t) rtDbgModCv_LineCount(PRTDBGMODINT pMod)
2530{
2531 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2532 return RTDbgModLineCount(pThis->hCnt);
2533}
2534
2535
2536/** @interface_method_impl{RTDBGMODVTDBG,pfnLineAdd} */
2537static DECLCALLBACK(int) rtDbgModCv_LineAdd(PRTDBGMODINT pMod, const char *pszFile, size_t cchFile, uint32_t uLineNo,
2538 uint32_t iSeg, RTUINTPTR off, uint32_t *piOrdinal)
2539{
2540 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2541 Assert(!pszFile[cchFile]); NOREF(cchFile);
2542 return RTDbgModLineAdd(pThis->hCnt, pszFile, uLineNo, iSeg, off, piOrdinal);
2543}
2544
2545
2546/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByAddr} */
2547static DECLCALLBACK(int) rtDbgModCv_SymbolByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags,
2548 PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo)
2549{
2550 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2551 return RTDbgModSymbolByAddr(pThis->hCnt, iSeg, off, fFlags, poffDisp, pSymInfo);
2552}
2553
2554
2555/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByName} */
2556static DECLCALLBACK(int) rtDbgModCv_SymbolByName(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol,
2557 PRTDBGSYMBOL pSymInfo)
2558{
2559 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2560 Assert(!pszSymbol[cchSymbol]); RT_NOREF_PV(cchSymbol);
2561 return RTDbgModSymbolByName(pThis->hCnt, pszSymbol/*, cchSymbol*/, pSymInfo);
2562}
2563
2564
2565/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByOrdinal} */
2566static DECLCALLBACK(int) rtDbgModCv_SymbolByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo)
2567{
2568 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2569 return RTDbgModSymbolByOrdinal(pThis->hCnt, iOrdinal, pSymInfo);
2570}
2571
2572
2573/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolCount} */
2574static DECLCALLBACK(uint32_t) rtDbgModCv_SymbolCount(PRTDBGMODINT pMod)
2575{
2576 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2577 return RTDbgModSymbolCount(pThis->hCnt);
2578}
2579
2580
2581/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolAdd} */
2582static DECLCALLBACK(int) rtDbgModCv_SymbolAdd(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol,
2583 RTDBGSEGIDX iSeg, RTUINTPTR off, RTUINTPTR cb, uint32_t fFlags,
2584 uint32_t *piOrdinal)
2585{
2586 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2587 Assert(!pszSymbol[cchSymbol]); NOREF(cchSymbol);
2588 return RTDbgModSymbolAdd(pThis->hCnt, pszSymbol, iSeg, off, cb, fFlags, piOrdinal);
2589}
2590
2591
2592/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentByIndex} */
2593static DECLCALLBACK(int) rtDbgModCv_SegmentByIndex(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo)
2594{
2595 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2596 return RTDbgModSegmentByIndex(pThis->hCnt, iSeg, pSegInfo);
2597}
2598
2599
2600/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentCount} */
2601static DECLCALLBACK(RTDBGSEGIDX) rtDbgModCv_SegmentCount(PRTDBGMODINT pMod)
2602{
2603 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2604 return RTDbgModSegmentCount(pThis->hCnt);
2605}
2606
2607
2608/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentAdd} */
2609static DECLCALLBACK(int) rtDbgModCv_SegmentAdd(PRTDBGMODINT pMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName, size_t cchName,
2610 uint32_t fFlags, PRTDBGSEGIDX piSeg)
2611{
2612 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2613 Assert(!pszName[cchName]); NOREF(cchName);
2614 return RTDbgModSegmentAdd(pThis->hCnt, uRva, cb, pszName, fFlags, piSeg);
2615}
2616
2617
2618/** @interface_method_impl{RTDBGMODVTDBG,pfnImageSize} */
2619static DECLCALLBACK(RTUINTPTR) rtDbgModCv_ImageSize(PRTDBGMODINT pMod)
2620{
2621 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2622 if (pThis->cbImage)
2623 return pThis->cbImage;
2624 return RTDbgModImageSize(pThis->hCnt);
2625}
2626
2627
2628/** @interface_method_impl{RTDBGMODVTDBG,pfnRvaToSegOff} */
2629static DECLCALLBACK(RTDBGSEGIDX) rtDbgModCv_RvaToSegOff(PRTDBGMODINT pMod, RTUINTPTR uRva, PRTUINTPTR poffSeg)
2630{
2631 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2632 return RTDbgModRvaToSegOff(pThis->hCnt, uRva, poffSeg);
2633}
2634
2635
2636/** @interface_method_impl{RTDBGMODVTDBG,pfnClose} */
2637static DECLCALLBACK(int) rtDbgModCv_Close(PRTDBGMODINT pMod)
2638{
2639 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2640
2641 RTDbgModRelease(pThis->hCnt);
2642 if (pThis->hFile != NIL_RTFILE)
2643 RTFileClose(pThis->hFile);
2644 RTMemFree(pThis->paDirEnts);
2645 RTStrFree(pThis->pszPdbFilename);
2646 RTVfsRelease(pThis->hVfsPdb);
2647 pThis->hVfsPdb = NIL_RTVFS;
2648 RTMemFree(pThis);
2649
2650 pMod->pvDbgPriv = NULL; /* for internal use */
2651 return VINF_SUCCESS;
2652}
2653
2654
2655/*
2656 *
2657 * Probing code used by rtDbgModCv_TryOpen.
2658 * Probing code used by rtDbgModCv_TryOpen.
2659 *
2660 */
2661
2662
2663
2664/**
2665 * @callback_method_impl{FNRTLDRENUMSEGS, Used to add segments from the image}
2666 */
2667static DECLCALLBACK(int) rtDbgModCvAddSegmentsCallback(RTLDRMOD hLdrMod, PCRTLDRSEG pSeg, void *pvUser)
2668{
2669 PRTDBGMODCV pThis = (PRTDBGMODCV)pvUser;
2670 Log(("Segment %s: LinkAddress=%#llx RVA=%#llx cb=%#llx\n",
2671 pSeg->pszName, (uint64_t)pSeg->LinkAddress, (uint64_t)pSeg->RVA, pSeg->cb));
2672 NOREF(hLdrMod);
2673
2674 /* If the segment doesn't have a mapping, just add a dummy so the indexing
2675 works out correctly (same as for the image). */
2676 if (pSeg->RVA == NIL_RTLDRADDR)
2677 return RTDbgModSegmentAdd(pThis->hCnt, 0, 0, pSeg->pszName, 0 /*fFlags*/, NULL);
2678
2679 RTLDRADDR cb = RT_MAX(pSeg->cb, pSeg->cbMapped);
2680 return RTDbgModSegmentAdd(pThis->hCnt, pSeg->RVA, cb, pSeg->pszName, 0 /*fFlags*/, NULL);
2681}
2682
2683
2684/**
2685 * Helper for rounding the section size up to the start of the start of the
2686 * next section (e.g. RTLDRSEG::cbMapped approximation).
2687 */
2688DECLINLINE(uint32_t) rtDbgModCvAdjustSectionSizeByNext(PCIMAGE_SECTION_HEADER paShs, uint32_t cShs,
2689 uint32_t iCur, uint32_t cbMapped)
2690{
2691 size_t iNext = iCur + 1;
2692 while (iNext < cShs && (paShs[iNext].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
2693 iNext++;
2694 if (iNext < cShs)
2695 {
2696 uint32_t cbAvailable = paShs[iNext].VirtualAddress - paShs[iCur].VirtualAddress;
2697 Assert(cbMapped <= cbAvailable);
2698 return cbAvailable;
2699 }
2700 return cbMapped;
2701}
2702
2703
2704/**
2705 * Copies the sections over from the DBG file.
2706 *
2707 * Called if we don't have an associated executable image.
2708 *
2709 * @returns IPRT status code.
2710 * @param pThis The CV module instance.
2711 * @param paShs The section headers.
2712 * @param cShs The number of section headers.
2713 * @param cbSectionAlign The section alignment
2714 * (IMAGE_SEPARATE_DEBUG_HEADER::SectionAlignment,
2715 * IMAGE_OPTIONAL_HEADER32::SectionAlignment, ++).
2716 * @param cbImage The image size (if not availble, UINT32_MAX).
2717 * @param pszFilename The filename (for logging).
2718 */
2719static int rtDbgModCvAddSegmentsFromSectHdrs(PRTDBGMODCV pThis, PCIMAGE_SECTION_HEADER paShs, uint32_t cShs,
2720 uint32_t cbSectionAlign, uint32_t cbImage, const char *pszFilename)
2721{
2722 RT_NOREF_PV(pszFilename);
2723 Assert(RT_IS_POWER_OF_TWO(cbSectionAlign));
2724
2725 /*
2726 * Do some basic validation of the section headers.
2727 */
2728 int rc = VINF_SUCCESS;
2729 uint32_t cbHeaders = 0;
2730 uint32_t uRvaPrev = 0;
2731 for (uint32_t i = 0; i < cShs; i++)
2732 {
2733 Log3(("RTDbgModCv: Section #%02u %#010x LB %#010x %.*s\n",
2734 i, paShs[i].VirtualAddress, paShs[i].Misc.VirtualSize, sizeof(paShs[i].Name), paShs[i].Name));
2735
2736 if (paShs[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD)
2737 continue;
2738
2739 if (paShs[i].VirtualAddress < uRvaPrev)
2740 {
2741 Log(("RTDbgModCv: %s: Overlap or sorting error, VirtualAddress=%#x uRvaPrev=%#x - section #%d '%.*s'!!!\n",
2742 pszFilename, paShs[i].VirtualAddress, uRvaPrev, i, sizeof(paShs[i].Name), paShs[i].Name));
2743 rc = VERR_CV_BAD_FORMAT;
2744 }
2745 else if ( paShs[i].VirtualAddress > cbImage
2746 || paShs[i].Misc.VirtualSize > cbImage
2747 || paShs[i].VirtualAddress + paShs[i].Misc.VirtualSize > cbImage)
2748 {
2749 Log(("RTDbgModCv: %s: VirtualAddress=%#x VirtualSize=%#x (total %x) - beyond image size (%#x) - section #%d '%.*s'!!!\n",
2750 pszFilename, paShs[i].VirtualAddress, paShs[i].Misc.VirtualSize,
2751 paShs[i].VirtualAddress + paShs[i].Misc.VirtualSize,
2752 pThis->cbImage, i, sizeof(paShs[i].Name), paShs[i].Name));
2753 rc = VERR_CV_BAD_FORMAT;
2754 }
2755 else if (paShs[i].VirtualAddress & (cbSectionAlign - 1))
2756 {
2757 Log(("RTDbgModCv: %s: VirtualAddress=%#x misaligned (%#x) - section #%d '%.*s'!!!\n",
2758 pszFilename, paShs[i].VirtualAddress, cbSectionAlign, i, sizeof(paShs[i].Name), paShs[i].Name));
2759 rc = VERR_CV_BAD_FORMAT;
2760 }
2761 else if (paShs[i].Characteristics & IMAGE_SCN_ALIGN_MASK)
2762 {
2763 uint32_t const cbAlign = RT_BIT_32((paShs[i].Characteristics & IMAGE_SCN_ALIGN_MASK) >> IMAGE_SCN_ALIGN_SHIFT);
2764 if (RT_ALIGN_32(paShs[i].VirtualAddress, cbAlign) != paShs[i].VirtualAddress)
2765 {
2766 Log(("RTDbgModCv: %s: VirtualAddress=%#x misaligned by flags (%#x) - section #%d '%.*s'!!!\n",
2767 pszFilename, paShs[i].VirtualAddress, cbAlign, i, sizeof(paShs[i].Name), paShs[i].Name));
2768 rc = VERR_CV_BAD_FORMAT;
2769 }
2770 }
2771
2772 if (uRvaPrev == 0)
2773 cbHeaders = paShs[i].VirtualAddress;
2774 uRvaPrev = paShs[i].VirtualAddress + paShs[i].Misc.VirtualSize;
2775 }
2776 if (uRvaPrev == 0)
2777 {
2778 Log(("RTDbgModCv: %s: No loadable sections.\n", pszFilename));
2779 rc = VERR_CV_BAD_FORMAT;
2780 }
2781 if (cbHeaders == 0)
2782 {
2783 Log(("RTDbgModCv: %s: No space for PE headers.\n", pszFilename));
2784 rc = VERR_CV_BAD_FORMAT;
2785 }
2786 if (RT_SUCCESS(rc))
2787 {
2788 /*
2789 * Add sections.
2790 */
2791 rc = RTDbgModSegmentAdd(pThis->hCnt, 0, cbHeaders, "NtHdrs", 0 /*fFlags*/, NULL);
2792 if (RT_SUCCESS(rc))
2793 {
2794 for (uint32_t i = 0; RT_SUCCESS(rc) && i < cShs; i++)
2795 {
2796 char szName[sizeof(paShs[i].Name) + 1];
2797 memcpy(szName, paShs[i].Name, sizeof(paShs[i].Name));
2798 szName[sizeof(szName) - 1] = '\0';
2799 RTStrStripR(szName);
2800
2801 uint32_t uRva = paShs[i].VirtualAddress;
2802 uint32_t cbMapped;
2803 if (!(paShs[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
2804 cbMapped = rtDbgModCvAdjustSectionSizeByNext(paShs, cShs, i, paShs[i].Misc.VirtualSize);
2805 else
2806 uRva = cbMapped = 0;
2807 rc = RTDbgModSegmentAdd(pThis->hCnt, paShs[i].VirtualAddress, cbMapped, szName, 0 /*fFlags*/, NULL);
2808 if (RT_FAILURE(rc))
2809 {
2810 Log(("RTDbgModCv: RTDbgModSegmentAdd failed on #%u: uRva=%#RX32 cbMapped=%#RX32 Flags=%#RX32 '%s' -> %Rrc\n",
2811 i + 1, uRva, cbMapped, szName, paShs[i].Characteristics, rc));
2812 break;
2813 }
2814
2815 }
2816 if (RT_SUCCESS(rc))
2817 pThis->fHaveLoadedSegments = true;
2818 }
2819 else
2820 Log(("RTDbgModCv: RTDbgModSegmentAdd on 'NtHdrs': cbHeaders=%#RX32 - %Rrc\n", cbHeaders, rc));
2821 }
2822 return rc;
2823}
2824
2825
2826/**
2827 * Copies the sections over from the DBG file.
2828 *
2829 * Called if we don't have an associated executable image.
2830 *
2831 * @returns IPRT status code.
2832 * @param pThis The CV module instance.
2833 * @param pDbgHdr The DBG file header.
2834 * @param pszFilename The filename (for logging).
2835 */
2836static int rtDbgModCvAddSegmentsFromDbg(PRTDBGMODCV pThis, PCIMAGE_SEPARATE_DEBUG_HEADER pDbgHdr, const char *pszFilename)
2837{
2838 RT_NOREF_PV(pszFilename);
2839
2840 /*
2841 * Validate the header fields a little.
2842 */
2843 if ( pDbgHdr->NumberOfSections < 1
2844 || pDbgHdr->NumberOfSections > 4096)
2845 {
2846 Log(("RTDbgModCv: Bad NumberOfSections: %d\n", pDbgHdr->NumberOfSections));
2847 return VERR_CV_BAD_FORMAT;
2848 }
2849
2850 /*
2851 * Read the section table.
2852 */
2853 size_t const cbShs = pDbgHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
2854 PIMAGE_SECTION_HEADER const paShs = (PIMAGE_SECTION_HEADER)RTMemTmpAlloc(cbShs);
2855 if (paShs)
2856 {
2857 int rc = RTFileReadAt(pThis->hFile, sizeof(*pDbgHdr), paShs, cbShs, NULL);
2858 if (RT_SUCCESS(rc))
2859 {
2860 /*
2861 * Process them.
2862 */
2863 rc = rtDbgModCvAddSegmentsFromSectHdrs(pThis, paShs, pDbgHdr->NumberOfSections, pDbgHdr->SectionAlignment,
2864 pDbgHdr->SizeOfImage, pszFilename);
2865 }
2866 RTMemTmpFree(paShs);
2867 return rc;
2868 }
2869 return VERR_NO_TMP_MEMORY;
2870}
2871
2872
2873/**
2874 * Instantiates the CV/COFF reader.
2875 *
2876 * @returns IPRT status code
2877 * @param pDbgMod The debug module instance.
2878 * @param enmFileType The type of input file.
2879 * @param hFile The file handle, NIL_RTFILE of image.
2880 * @param ppThis Where to return the reader instance.
2881 */
2882static int rtDbgModCvCreateInstance(PRTDBGMODINT pDbgMod, RTCVFILETYPE enmFileType, RTFILE hFile, PRTDBGMODCV *ppThis)
2883{
2884 /*
2885 * Do we already have an instance? Happens if we find multiple debug
2886 * formats we support.
2887 */
2888 PRTDBGMODCV pThis = (PRTDBGMODCV)pDbgMod->pvDbgPriv;
2889 if (pThis)
2890 {
2891 Assert(pThis->enmType == enmFileType);
2892 Assert(pThis->hFile == hFile);
2893 Assert(pThis->pMod == pDbgMod);
2894 *ppThis = pThis;
2895 return VINF_SUCCESS;
2896 }
2897
2898 /*
2899 * Create a new instance.
2900 */
2901 pThis = (PRTDBGMODCV)RTMemAllocZ(sizeof(RTDBGMODCV));
2902 if (!pThis)
2903 return VERR_NO_MEMORY;
2904 int rc = RTDbgModCreate(&pThis->hCnt, pDbgMod->pszName, 0 /*cbSeg*/, 0 /*fFlags*/);
2905 if (RT_SUCCESS(rc))
2906 {
2907 pDbgMod->pvDbgPriv = pThis;
2908 pThis->enmType = enmFileType;
2909 pThis->hFile = hFile;
2910 pThis->pMod = pDbgMod;
2911 pThis->offBase = UINT32_MAX;
2912 pThis->offCoffDbgInfo = UINT32_MAX;
2913 pThis->hVfsPdb = NIL_RTVFS;
2914 *ppThis = pThis;
2915 return VINF_SUCCESS;
2916 }
2917 RTMemFree(pThis);
2918 return rc;
2919}
2920
2921
2922/**
2923 * Common part of the COFF probing.
2924 *
2925 * @returns status code.
2926 * @param pDbgMod The debug module instance. On success pvDbgPriv
2927 * will point to a valid RTDBGMODCV.
2928 * @param enmFileType The kind of file this is we're probing.
2929 * @param hFile The file with debug info in it.
2930 * @param off The offset where to expect CV debug info.
2931 * @param cb The number of bytes of debug info.
2932 * @param pszFilename The path to the file (for logging).
2933 */
2934static int rtDbgModCvProbeCoff(PRTDBGMODINT pDbgMod, RTCVFILETYPE enmFileType, RTFILE hFile,
2935 uint32_t off, uint32_t cb, const char *pszFilename)
2936{
2937 RT_NOREF_PV(pszFilename);
2938
2939 /*
2940 * Check that there is sufficient data for a header, then read it.
2941 */
2942 if (cb < sizeof(IMAGE_COFF_SYMBOLS_HEADER))
2943 {
2944 Log(("RTDbgModCv: Not enough room for COFF header.\n"));
2945 return VERR_BAD_EXE_FORMAT;
2946 }
2947 if (cb >= UINT32_C(128) * _1M)
2948 {
2949 Log(("RTDbgModCv: COFF debug information is to large (%'u bytes), max is 128MB\n", cb));
2950 return VERR_BAD_EXE_FORMAT;
2951 }
2952
2953 int rc;
2954 IMAGE_COFF_SYMBOLS_HEADER Hdr;
2955 if (hFile == NIL_RTFILE)
2956 rc = pDbgMod->pImgVt->pfnReadAt(pDbgMod, UINT32_MAX, off, &Hdr, sizeof(Hdr));
2957 else
2958 rc = RTFileReadAt(hFile, off, &Hdr, sizeof(Hdr), NULL);
2959 if (RT_FAILURE(rc))
2960 {
2961 Log(("RTDbgModCv: Error reading COFF header: %Rrc\n", rc));
2962 return rc;
2963 }
2964
2965 Log2(("RTDbgModCv: Found COFF debug info header at %#x (LB %#x) in %s\n", off, cb, pszFilename));
2966 Log2((" NumberOfSymbols = %#010x\n", Hdr.NumberOfSymbols));
2967 Log2((" LvaToFirstSymbol = %#010x\n", Hdr.LvaToFirstSymbol));
2968 Log2((" NumberOfLinenumbers = %#010x\n", Hdr.NumberOfLinenumbers));
2969 Log2((" LvaToFirstLinenumber = %#010x\n", Hdr.LvaToFirstLinenumber));
2970 Log2((" RvaToFirstByteOfCode = %#010x\n", Hdr.RvaToFirstByteOfCode));
2971 Log2((" RvaToLastByteOfCode = %#010x\n", Hdr.RvaToLastByteOfCode));
2972 Log2((" RvaToFirstByteOfData = %#010x\n", Hdr.RvaToFirstByteOfData));
2973 Log2((" RvaToLastByteOfData = %#010x\n", Hdr.RvaToLastByteOfData));
2974
2975 /*
2976 * Validate the COFF header.
2977 */
2978 if ( (uint64_t)Hdr.LvaToFirstSymbol + (uint64_t)Hdr.NumberOfSymbols * sizeof(IMAGE_SYMBOL) > cb
2979 || (Hdr.LvaToFirstSymbol < sizeof(Hdr) && Hdr.NumberOfSymbols > 0))
2980 {
2981 Log(("RTDbgModCv: Bad COFF symbol count or/and offset: LvaToFirstSymbol=%#x, NumberOfSymbols=%#x cbCoff=%#x\n",
2982 Hdr.LvaToFirstSymbol, Hdr.NumberOfSymbols, cb));
2983 return VERR_BAD_EXE_FORMAT;
2984 }
2985 if ( (uint64_t)Hdr.LvaToFirstLinenumber + (uint64_t)Hdr.NumberOfLinenumbers * sizeof(IMAGE_LINENUMBER) > cb
2986 || (Hdr.LvaToFirstLinenumber < sizeof(Hdr) && Hdr.NumberOfLinenumbers > 0))
2987 {
2988 Log(("RTDbgModCv: Bad COFF symbol count or/and offset: LvaToFirstSymbol=%#x, NumberOfSymbols=%#x cbCoff=%#x\n",
2989 Hdr.LvaToFirstSymbol, Hdr.NumberOfSymbols, cb));
2990 return VERR_BAD_EXE_FORMAT;
2991 }
2992 if (Hdr.NumberOfSymbols < 2)
2993 {
2994 Log(("RTDbgModCv: The COFF symbol table is too short to be of any worth... (%u syms)\n", Hdr.NumberOfSymbols));
2995 return VERR_NO_DATA;
2996 }
2997
2998 /*
2999 * What we care about looks fine, use it.
3000 */
3001 PRTDBGMODCV pThis;
3002 rc = rtDbgModCvCreateInstance(pDbgMod, enmFileType, hFile, &pThis);
3003 if (RT_SUCCESS(rc))
3004 {
3005 pThis->offCoffDbgInfo = off;
3006 pThis->cbCoffDbgInfo = cb;
3007 pThis->CoffHdr = Hdr;
3008 }
3009
3010 return rc;
3011}
3012
3013/**
3014 * Argument package for rtDbgModCvOpenPdb20Callback via pvUser2.
3015 */
3016typedef struct RTDBGMODCVOPENPDB20CALLBACK
3017{
3018 RTLDRARCH enmArch;
3019 RTFILE hFile;
3020 RTCVFILETYPE enmFileType;
3021 uint32_t uTimestamp;
3022 uint32_t uAge;
3023} RTDBGMODCVOPENPDB20CALLBACK;
3024
3025/** @callback_method_impl{FNRTDBGCFGOPEN,
3026 * For opening PDB linked in windows 2000 area DBG files.}
3027 */
3028static DECLCALLBACK(int) rtDbgModCvOpenPdb20Callback(RTDBGCFG hDbgCfg, const char *pszFilename, void *pvUser1, void *pvUser2)
3029{
3030 PRTDBGMODINT const pDbgMod = (PRTDBGMODINT)pvUser1;
3031 RTDBGMODCVOPENPDB20CALLBACK * const pArgs = (RTDBGMODCVOPENPDB20CALLBACK *)pvUser2;
3032 RT_NOREF(hDbgCfg);
3033
3034 /*
3035 * Open the file as a VFS and check that the timestamp and age matches
3036 * what we're looking for.
3037 */
3038 Log2(("rtDbgModCvOpenPdb20Callback: Trying '%s'...\n", pszFilename));
3039 RTVFSFILE hVfsFilePdb = NIL_RTVFSFILE;
3040 int rc = RTVfsFileOpenNormal(pszFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hVfsFilePdb);
3041 if (RT_SUCCESS(rc))
3042 {
3043 RTVFS hVfsPdb = NIL_RTVFS;
3044#ifdef LOG_ENABLED
3045 RTERRINFOSTATIC ErrInfo;
3046 rc = RTFsPdbVolOpen(hVfsFilePdb, 0, &hVfsPdb, RTErrInfoInitStatic(&ErrInfo));
3047#else
3048 rc = RTFsPdbVolOpen(hVfsFilePdb, 0, &hVfsPdb, NULL /*pErrInfo*/);
3049#endif
3050 RTVfsFileRelease(hVfsFilePdb);
3051 if (RT_SUCCESS(rc))
3052 {
3053 /*
3054 * The timestamp + age is available as a string.
3055 */
3056 size_t cbRet;
3057 char szTimestampAndAge[64] = {0};
3058 rc = RTVfsQueryLabel(hVfsPdb, false /*RTVFSQIEX_VOL_LABEL*/,
3059 szTimestampAndAge, sizeof(szTimestampAndAge) - 1, &cbRet);
3060 AssertRC(rc);
3061 if (RT_SUCCESS(rc))
3062 {
3063 char szExpect[64];
3064 RTStrPrintf(szExpect, sizeof(szExpect), "%08X%x", pArgs->uTimestamp, pArgs->uAge);
3065 if (RTStrICmpAscii(szTimestampAndAge, szExpect) == 0)
3066 {
3067 /** @todo check ARCH. It's usually in the DBI header. */
3068
3069 /*
3070 * Okay, we're good.
3071 */
3072 PRTDBGMODCV pThis;
3073 rc = rtDbgModCvCreateInstance(pDbgMod, pArgs->enmFileType, pArgs->hFile, &pThis);
3074 if (RT_SUCCESS(rc))
3075 {
3076 pThis->u32CvMagic = RTCVHDR_MAGIC_NB10;
3077 pThis->offBase = UINT32_MAX;
3078 pThis->cbDbgInfo = 0;
3079 pThis->offDir = 0;
3080 pThis->pszPdbFilename = RTStrDup(pszFilename);
3081 if (pThis->pszPdbFilename)
3082 {
3083 pThis->hVfsPdb = hVfsPdb;
3084 return VINF_CALLBACK_RETURN;
3085 }
3086 }
3087 }
3088 else
3089 Log2(("RTFsPdbVolOpen: timestamp+age mismatch: %s, expected %s (for '%s')\n",
3090 szTimestampAndAge, szExpect, pszFilename));
3091 }
3092 }
3093 else
3094 Log2(("RTFsPdbVolOpen: RTVfsFileOpenNormal '%s' failed: %Rrc%#RTeim\n", pszFilename, rc, &ErrInfo.Core));
3095 }
3096 else
3097 Log2(("rtDbgModCvOpenPdb20Callback: RTVfsFileOpenNormal '%s' failed: %Rrc\n", pszFilename, rc));
3098 return rc;
3099}
3100
3101
3102/**
3103 * Common part of the CodeView probing.
3104 *
3105 * @returns status code.
3106 * @param pDbgMod The debug module instance. On success pvDbgPriv
3107 * will point to a valid RTDBGMODCV.
3108 * @param pCvHdr The CodeView base header.
3109 * @param enmFileType The kind of file this is we're probing.
3110 * @param hFile The file with debug info in it.
3111 * @param off The offset where to expect CV debug info.
3112 * @param cb The number of bytes of debug info.
3113 * @param enmArch The desired image architecture.
3114 * @param cbImage The image size, if available.
3115 * @param pszFilename The path to the file (for logging).
3116 * @param hDbgCfg For dealing with external PDB found in windows
3117 * 2000 area DBG files.
3118 */
3119static int rtDbgModCvProbeCommon(PRTDBGMODINT pDbgMod, PRTCVHDR pCvHdr, RTCVFILETYPE enmFileType, RTFILE hFile,
3120 uint32_t off, uint32_t cb, RTLDRARCH enmArch, size_t cbImage,
3121 const char *pszFilename, RTDBGCFG hDbgCfg)
3122{
3123 int rc = VERR_DBG_NO_MATCHING_INTERPRETER;
3124 RT_NOREF_PV(enmArch); RT_NOREF_PV(pszFilename);
3125
3126 /* Is a codeview format we (wish to) support? */
3127 if ( pCvHdr->u32Magic == RTCVHDR_MAGIC_NB11
3128 || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB09
3129 || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB08
3130 || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB05
3131 || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB04
3132 || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB02
3133 || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB00
3134 )
3135 {
3136 /* We're assuming it's a base header, so the offset must be within
3137 the area defined by the debug info we got from the loader. */
3138 if (pCvHdr->off < cb && pCvHdr->off >= sizeof(*pCvHdr))
3139 {
3140 Log(("RTDbgModCv: Found %c%c%c%c at %#x - size %#x, directory at %#x. file type %d\n",
3141 RT_BYTE1(pCvHdr->u32Magic), RT_BYTE2(pCvHdr->u32Magic), RT_BYTE3(pCvHdr->u32Magic), RT_BYTE4(pCvHdr->u32Magic),
3142 off, cb, pCvHdr->off, enmFileType));
3143
3144 /*
3145 * Create a module instance, if not already done.
3146 */
3147 PRTDBGMODCV pThis;
3148 rc = rtDbgModCvCreateInstance(pDbgMod, enmFileType, hFile, &pThis);
3149 if (RT_SUCCESS(rc))
3150 {
3151 pThis->u32CvMagic = pCvHdr->u32Magic;
3152 pThis->offBase = off;
3153 pThis->cbDbgInfo = cb;
3154 pThis->offDir = pCvHdr->off;
3155 return VINF_SUCCESS;
3156 }
3157 }
3158 }
3159 /*
3160 * The NB10 is used to link to a PDB file.
3161 * This is the case with the windows 2000 DBG files.
3162 */
3163 else if (pCvHdr->u32Magic == RTCVHDR_MAGIC_NB10)
3164 {
3165 if (pCvHdr->off == 0)
3166 {
3167 if (cb > RT_UOFFSETOF(CVPDB20INFO, szPdbFilename) && cb < _64K)
3168 {
3169 /*
3170 * Read it into memory and validate it.
3171 */
3172 PCVPDB20INFO pInfo = (PCVPDB20INFO)RTMemTmpAlloc(cb);
3173 if (pInfo)
3174 {
3175 int rc2 = RTFileReadAt(hFile, off, pInfo, cb, NULL);
3176 if (RT_SUCCESS(rc2))
3177 {
3178 uint32_t const cbPdbFilename = cb - RT_UOFFSETOF(CVPDB20INFO, szPdbFilename);
3179 rc2 = RTStrValidateEncodingEx((char const *)&pInfo->szPdbFilename[0], cbPdbFilename,
3180 RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
3181 if (RT_SUCCESS(rc2))
3182 {
3183 Log2(("RTDbgModCv: Found external PDB (v2.0): TS=%#010RX32 uAge=%x '%.*s'\n", pInfo->uTimestamp,
3184 pInfo->uAge, cb - RT_UOFFSETOF(CVPDB20INFO, szPdbFilename), &pInfo->szPdbFilename[0]));
3185 if ( hDbgCfg != NIL_RTDBGCFG
3186 && enmFileType == RTCVFILETYPE_DBG)
3187 {
3188 /*
3189 * Try open it. Need to figure the image size first if
3190 * we can, though not actually used by the function.
3191 *
3192 * The callback will create an instance if a PDB is found,
3193 * but it won't actually read it. That's done later by the
3194 * RTDbgModCv_TryOpen code.
3195 */
3196 RTDBGMODCVOPENPDB20CALLBACK Args = { enmArch, hFile, enmFileType, pInfo->uTimestamp, pInfo->uAge };
3197 rc = RTDbgCfgOpenPdb20(hDbgCfg, (const char *)&pInfo->szPdbFilename[0],
3198 (uint32_t)cbImage, pInfo->uTimestamp, pInfo->uAge,
3199 rtDbgModCvOpenPdb20Callback, pDbgMod, &Args);
3200 Log(("RTDbgModCv: RTDbgCfgOpenPdb20 returns %Rrc\n", rc));
3201 }
3202 }
3203 else
3204 Log(("RTDbgModCv: Found external PDB (v2.0) w/ invalid filename encoding: TS=%#010RX32 uAge=%x %.*Rhxs rc2=%Rrc\n",
3205 pInfo->uTimestamp, pInfo->uAge, cbPdbFilename, &pInfo->szPdbFilename[0], rc2));
3206 }
3207 else
3208 Log(("RTDbgModCv: NB10 read error - %Rrc\n", rc2));
3209 RTMemTmpFree(pInfo);
3210 }
3211 else
3212 rc = VERR_NO_TMP_MEMORY;
3213 }
3214 else
3215 Log(("RTDbgModCv: NB10 with bogus size: %#x\n", cb));
3216 }
3217 else
3218 Log(("RTDbgModCv: NB10 with non-zero offset!\n"));
3219 }
3220 return rc;
3221}
3222
3223
3224/**
3225 * Arguments passed to rtDbgModCvEnumCallback
3226 */
3227typedef struct RTDBGMODCVENUMCALLBACKARGS
3228{
3229 PRTDBGMODINT pDbgMod;
3230 RTDBGCFG hDbgCfg;
3231} RTDBGMODCVENUMCALLBACKARGS;
3232
3233
3234/** @callback_method_impl{FNRTLDRENUMDBG} */
3235static DECLCALLBACK(int) rtDbgModCvEnumCallback(RTLDRMOD hLdrMod, PCRTLDRDBGINFO pDbgInfo, void *pvUser)
3236{
3237 RTDBGMODCVENUMCALLBACKARGS * const pArgs = (RTDBGMODCVENUMCALLBACKARGS *)pvUser;
3238 PRTDBGMODINT const pDbgMod = pArgs->pDbgMod;
3239 Assert(!pDbgMod->pvDbgPriv);
3240 RT_NOREF_PV(hLdrMod);
3241
3242 /* Skip external files, RTDbgMod will deal with those
3243 via RTDBGMODINT::pszDbgFile. */
3244 if (pDbgInfo->pszExtFile)
3245 return VINF_SUCCESS;
3246
3247 /* We only handle the codeview sections. */
3248 if (pDbgInfo->enmType == RTLDRDBGINFOTYPE_CODEVIEW)
3249 {
3250 /* Read the specified header and check if we like it. */
3251 RTCVHDR CvHdr;
3252 int rc = pDbgMod->pImgVt->pfnReadAt(pDbgMod, pDbgInfo->iDbgInfo, pDbgInfo->offFile, &CvHdr, sizeof(CvHdr));
3253 if (RT_SUCCESS(rc))
3254 rc = rtDbgModCvProbeCommon(pDbgMod, &CvHdr, RTCVFILETYPE_IMAGE, NIL_RTFILE, pDbgInfo->offFile, pDbgInfo->cb,
3255 pDbgMod->pImgVt->pfnGetArch(pDbgMod), pDbgMod->pImgVt->pfnImageSize(pDbgMod),
3256 pDbgMod->pszImgFile, pArgs->hDbgCfg);
3257 }
3258 else if (pDbgInfo->enmType == RTLDRDBGINFOTYPE_COFF)
3259 {
3260 /* Join paths with the DBG code. */
3261 rtDbgModCvProbeCoff(pDbgMod, RTCVFILETYPE_IMAGE, NIL_RTFILE, pDbgInfo->offFile, pDbgInfo->cb, pDbgMod->pszImgFile);
3262 }
3263
3264 return VINF_SUCCESS;
3265}
3266
3267
3268/**
3269 * Part two of the external file probing.
3270 *
3271 * @returns status code.
3272 * @param pThis The debug module instance. On success pvDbgPriv
3273 * will point to a valid RTDBGMODCV.
3274 * @param enmFileType The kind of file this is we're probing.
3275 * @param hFile The file with debug info in it.
3276 * @param off The offset where to expect CV debug info.
3277 * @param cb The number of bytes of debug info.
3278 * @param enmArch The desired image architecture.
3279 * @param cbImage The image size (for PDB lookup, see hDbgCfg).
3280 * @param pszFilename The path to the file (for logging).
3281 * @param hDbgCfg Optional debug config handle for locating PDB
3282 * linked to by NB10 records (in DBG and
3283 * elsewhere).
3284 */
3285static int rtDbgModCvProbeFile2(PRTDBGMODINT pThis, RTCVFILETYPE enmFileType, RTFILE hFile, uint32_t off, uint32_t cb,
3286 RTLDRARCH enmArch, size_t cbImage, const char *pszFilename, RTDBGCFG hDbgCfg)
3287{
3288 RTCVHDR CvHdr;
3289 int rc = RTFileReadAt(hFile, off, &CvHdr, sizeof(CvHdr), NULL);
3290 if (RT_SUCCESS(rc))
3291 rc = rtDbgModCvProbeCommon(pThis, &CvHdr, enmFileType, hFile, off, cb, enmArch, cbImage, pszFilename, hDbgCfg);
3292 return rc;
3293}
3294
3295#ifdef LOG_ENABLED
3296/** Translate PE debug directory type value to string. */
3297static const char *dbgPeDebugTypeName(uint32_t uType)
3298{
3299 switch (uType)
3300 {
3301 case IMAGE_DEBUG_TYPE_UNKNOWN: return "UNKNOWN";
3302 case IMAGE_DEBUG_TYPE_COFF: return "COFF";
3303 case IMAGE_DEBUG_TYPE_CODEVIEW: return "CODEVIEW";
3304 case IMAGE_DEBUG_TYPE_FPO: return "FPO";
3305 case IMAGE_DEBUG_TYPE_MISC: return "MISC";
3306 case IMAGE_DEBUG_TYPE_EXCEPTION: return "EXCEPTION";
3307 case IMAGE_DEBUG_TYPE_FIXUP: return "FIXUP";
3308 case IMAGE_DEBUG_TYPE_OMAP_TO_SRC: return "OMAP_TO_SRC";
3309 case IMAGE_DEBUG_TYPE_OMAP_FROM_SRC: return "OMAP_FROM_SRC";
3310 case IMAGE_DEBUG_TYPE_BORLAND: return "BORLAND";
3311 case IMAGE_DEBUG_TYPE_RESERVED10: return "RESERVED10";
3312 case IMAGE_DEBUG_TYPE_CLSID: return "CLSID";
3313 case IMAGE_DEBUG_TYPE_VC_FEATURE: return "VC_FEATURE";
3314 case IMAGE_DEBUG_TYPE_POGO: return "POGO";
3315 case IMAGE_DEBUG_TYPE_ILTCG: return "ILTCG";
3316 case IMAGE_DEBUG_TYPE_MPX: return "MPX";
3317 case IMAGE_DEBUG_TYPE_REPRO: return "REPRO";
3318 }
3319 return "??";
3320}
3321#endif
3322
3323/**
3324 * Probes an external file for CodeView information.
3325 *
3326 * @returns status code.
3327 * @param pDbgMod The debug module instance. On success pvDbgPriv
3328 * will point to a valid RTDBGMODCV.
3329 * @param pszFilename The path to the file to probe.
3330 * @param enmArch The desired image architecture.
3331 * @param hDbgCfg Optional debug config handle for locating PDB
3332 * linked to by NB10 records (in DBG and
3333 * elsewhere).
3334 */
3335static int rtDbgModCvProbeFile(PRTDBGMODINT pDbgMod, const char *pszFilename, RTLDRARCH enmArch, RTDBGCFG hDbgCfg)
3336{
3337 RTFILE hFile;
3338 int rc = RTFileOpen(&hFile, pszFilename, RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN);
3339 if (RT_FAILURE(rc))
3340 return rc;
3341
3342 /*
3343 * Check for .DBG file
3344 */
3345 IMAGE_SEPARATE_DEBUG_HEADER DbgHdr;
3346 rc = RTFileReadAt(hFile, 0, &DbgHdr, sizeof(DbgHdr), NULL);
3347 if ( RT_SUCCESS(rc)
3348 && DbgHdr.Signature == IMAGE_SEPARATE_DEBUG_SIGNATURE)
3349 {
3350 Log2(("RTDbgModCv: Found separate debug header in %s:\n", pszFilename));
3351 Log2((" Flags = %#x\n", DbgHdr.Flags));
3352 Log2((" Machine = %#x\n", DbgHdr.Machine));
3353 Log2((" Characteristics = %#x\n", DbgHdr.Characteristics));
3354 Log2((" TimeDateStamp = %#x\n", DbgHdr.TimeDateStamp));
3355 Log2((" CheckSum = %#x\n", DbgHdr.CheckSum));
3356 Log2((" ImageBase = %#x\n", DbgHdr.ImageBase));
3357 Log2((" SizeOfImage = %#x\n", DbgHdr.SizeOfImage));
3358 Log2((" NumberOfSections = %#x\n", DbgHdr.NumberOfSections));
3359 Log2((" ExportedNamesSize = %#x\n", DbgHdr.ExportedNamesSize));
3360 Log2((" DebugDirectorySize = %#x\n", DbgHdr.DebugDirectorySize));
3361 Log2((" SectionAlignment = %#x\n", DbgHdr.SectionAlignment));
3362
3363 /*
3364 * Match up the architecture if specified.
3365 */
3366 switch (enmArch)
3367 {
3368 case RTLDRARCH_X86_32:
3369 if (DbgHdr.Machine != IMAGE_FILE_MACHINE_I386)
3370 rc = VERR_LDR_ARCH_MISMATCH;
3371 break;
3372 case RTLDRARCH_AMD64:
3373 if (DbgHdr.Machine != IMAGE_FILE_MACHINE_AMD64)
3374 rc = VERR_LDR_ARCH_MISMATCH;
3375 break;
3376
3377 default:
3378 RT_FALL_THROUGH();
3379 case RTLDRARCH_HOST:
3380 AssertFailedBreak();
3381 case RTLDRARCH_WHATEVER:
3382 break;
3383 }
3384 if (RT_FAILURE(rc))
3385 {
3386 RTFileClose(hFile);
3387 return rc;
3388 }
3389
3390 /*
3391 * Probe for readable debug info in the debug directory.
3392 */
3393 uint32_t offDbgDir = sizeof(DbgHdr)
3394 + DbgHdr.NumberOfSections * sizeof(IMAGE_SECTION_HEADER)
3395 + DbgHdr.ExportedNamesSize;
3396
3397 uint32_t cEntries = DbgHdr.DebugDirectorySize / sizeof(IMAGE_DEBUG_DIRECTORY);
3398 for (uint32_t i = 0; i < cEntries; i++, offDbgDir += sizeof(IMAGE_DEBUG_DIRECTORY))
3399 {
3400 IMAGE_DEBUG_DIRECTORY DbgDir;
3401 rc = RTFileReadAt(hFile, offDbgDir, &DbgDir, sizeof(DbgDir), NULL);
3402 if (RT_FAILURE(rc))
3403 break;
3404 Log2(("DbgDir[%u]: Type=%#x Char=%#x TS=%#010x Ver=%#x.%#x Size=%#010x Addr=%#010x Ptr=%#010x %s\n",
3405 i, DbgDir.Type, DbgDir.Characteristics, DbgDir.TimeDateStamp, DbgDir.MajorVersion, DbgDir.MinorVersion,
3406 DbgDir.SizeOfData, DbgDir.AddressOfRawData, DbgDir.PointerToRawData, dbgPeDebugTypeName(DbgDir.Type)));
3407 if (DbgDir.Type == IMAGE_DEBUG_TYPE_CODEVIEW)
3408 rc = rtDbgModCvProbeFile2(pDbgMod, RTCVFILETYPE_DBG, hFile,
3409 DbgDir.PointerToRawData, DbgDir.SizeOfData,
3410 enmArch, DbgHdr.SizeOfImage, pszFilename, hDbgCfg);
3411 else if (DbgDir.Type == IMAGE_DEBUG_TYPE_COFF)
3412 rc = rtDbgModCvProbeCoff(pDbgMod, RTCVFILETYPE_DBG, hFile,
3413 DbgDir.PointerToRawData, DbgDir.SizeOfData, pszFilename);
3414 else
3415 continue;
3416 Log2(("DbgDir[%u]: parsing -> %Rrc\n", i, rc));
3417 }
3418
3419#ifdef LOG_ENABLED
3420 /*
3421 * Dump the section table.
3422 */
3423 for (uint32_t i = 0, offSecHdr = sizeof(DbgHdr);
3424 i < DbgHdr.NumberOfSections;
3425 i++, offSecHdr += sizeof(IMAGE_SECTION_HEADER))
3426 {
3427 IMAGE_SECTION_HEADER Sh;
3428 rc = RTFileReadAt(hFile, offSecHdr, &Sh, sizeof(Sh), NULL);
3429 if (RT_FAILURE(rc))
3430 break;
3431 Log2(("SHdr[%02u]: VA=%#08RX32 LB %#08RX32 Char=%#010RX32 Ptr=%#08RX32 LB %#08RX32 PtrLines=%#RX32 L %#RX32 PtrReloc=%#RX32 L %#RX32 '%.8s'\n",
3432 i, Sh.VirtualAddress, Sh.Misc.VirtualSize, Sh.Characteristics, Sh.PointerToRawData, Sh.SizeOfRawData,
3433 Sh.PointerToLinenumbers, Sh.NumberOfLinenumbers, Sh.PointerToRelocations, Sh.NumberOfRelocations, &Sh.Name[0]));
3434 }
3435#endif
3436
3437 /*
3438 * If we get down here with an instance, it prooves that we've found
3439 * something, regardless of any errors. Add the sections and such.
3440 */
3441 PRTDBGMODCV pThis = (PRTDBGMODCV)pDbgMod->pvDbgPriv;
3442 if (pThis)
3443 {
3444 pThis->cbImage = DbgHdr.SizeOfImage;
3445 if (pDbgMod->pImgVt)
3446 rc = VINF_SUCCESS;
3447 else
3448 {
3449 rc = rtDbgModCvAddSegmentsFromDbg(pThis, &DbgHdr, pszFilename);
3450 if (RT_FAILURE(rc))
3451 rtDbgModCv_Close(pDbgMod);
3452 }
3453 return rc;
3454 }
3455
3456 /* Failed to find CV or smth, look at the end of the file just to be sure... */
3457 }
3458
3459 /*
3460 * Look for CV tail header.
3461 */
3462 uint64_t cbFile;
3463 rc = RTFileSeek(hFile, -(RTFOFF)sizeof(RTCVHDR), RTFILE_SEEK_END, &cbFile);
3464 if (RT_SUCCESS(rc))
3465 {
3466 cbFile += sizeof(RTCVHDR);
3467 RTCVHDR CvHdr;
3468 rc = RTFileRead(hFile, &CvHdr, sizeof(CvHdr), NULL);
3469 if (RT_SUCCESS(rc))
3470 rc = rtDbgModCvProbeFile2(pDbgMod, RTCVFILETYPE_OTHER_AT_END, hFile,
3471 cbFile - CvHdr.off, CvHdr.off, enmArch,
3472 pDbgMod->pImgVt ? pDbgMod->pImgVt->pfnImageSize(pDbgMod) : 0 /*cbImage */,
3473 pszFilename, hDbgCfg);
3474 }
3475
3476 if (RT_FAILURE(rc))
3477 RTFileClose(hFile);
3478 return rc;
3479}
3480
3481
3482
3483/** @interface_method_impl{RTDBGMODVTDBG,pfnTryOpen} */
3484static DECLCALLBACK(int) rtDbgModCv_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch, RTDBGCFG hDbgCfg)
3485{
3486 /*
3487 * Look for debug info.
3488 */
3489 int rc = VERR_DBG_NO_MATCHING_INTERPRETER;
3490 if (pMod->pszDbgFile)
3491 rc = rtDbgModCvProbeFile(pMod, pMod->pszDbgFile, enmArch, hDbgCfg);
3492
3493 if (!pMod->pvDbgPriv && pMod->pImgVt)
3494 {
3495 RTDBGMODCVENUMCALLBACKARGS Args = { pMod, hDbgCfg };
3496 int rc2 = pMod->pImgVt->pfnEnumDbgInfo(pMod, rtDbgModCvEnumCallback, &Args);
3497 if (RT_FAILURE(rc2))
3498 rc = rc2;
3499
3500 if (!pMod->pvDbgPriv)
3501 {
3502 /* Try the executable in case it has a NBxx tail header. */
3503 rc2 = rtDbgModCvProbeFile(pMod, pMod->pszImgFile, enmArch, hDbgCfg);
3504 if (RT_FAILURE(rc2) && (RT_SUCCESS(rc) || rc == VERR_DBG_NO_MATCHING_INTERPRETER))
3505 rc = rc2;
3506 }
3507 }
3508
3509 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
3510 if (!pThis)
3511 return RT_SUCCESS_NP(rc) ? VERR_DBG_NO_MATCHING_INTERPRETER : rc;
3512 Assert( pThis->offBase != UINT32_MAX
3513 || pThis->offCoffDbgInfo != UINT32_MAX
3514 || (pThis->pszPdbFilename != NULL && pThis->hVfsPdb != NIL_RTVFS && pThis->enmType == RTCVFILETYPE_DBG));
3515
3516 /*
3517 * Load the debug info.
3518 */
3519 if (pMod->pImgVt)
3520 {
3521 rc = pMod->pImgVt->pfnEnumSegments(pMod, rtDbgModCvAddSegmentsCallback, pThis);
3522 pThis->fHaveLoadedSegments = true;
3523 }
3524 if (RT_SUCCESS(rc) && pThis->offBase != UINT32_MAX)
3525 rc = rtDbgModCvLoadCodeViewInfo(pThis);
3526 if (RT_SUCCESS(rc) && pThis->offCoffDbgInfo != UINT32_MAX)
3527 rc = rtDbgModCvLoadCoffInfo(pThis);
3528 if (RT_SUCCESS(rc) && pThis->hVfsPdb != NIL_RTVFS)
3529 {
3530 rc = rtDbgModPdb_CommonOpenWorker(pThis, pThis->pszPdbFilename, pThis->hVfsPdb, hDbgCfg);
3531 RTVfsRelease(pThis->hVfsPdb);
3532 pThis->hVfsPdb = NIL_RTVFS;
3533 }
3534 if (RT_SUCCESS(rc))
3535 {
3536 Log(("RTDbgCv: Successfully loaded debug info\n"));
3537 return VINF_SUCCESS;
3538 }
3539
3540 Log(("RTDbgCv: Debug info load error %Rrc\n", rc));
3541 rtDbgModCv_Close(pMod);
3542 RT_NOREF(hDbgCfg);
3543 return rc;
3544}
3545
3546
3547
3548
3549
3550/** Virtual function table for the CodeView debug info reader. */
3551DECL_HIDDEN_CONST(RTDBGMODVTDBG) const g_rtDbgModVtDbgCodeView =
3552{
3553 /*.u32Magic = */ RTDBGMODVTDBG_MAGIC,
3554 /*.fSupports = */ RT_DBGTYPE_CODEVIEW,
3555 /*.pszName = */ "codeview",
3556 /*.pfnTryOpen = */ rtDbgModCv_TryOpen,
3557 /*.pfnClose = */ rtDbgModCv_Close,
3558
3559 /*.pfnRvaToSegOff = */ rtDbgModCv_RvaToSegOff,
3560 /*.pfnImageSize = */ rtDbgModCv_ImageSize,
3561
3562 /*.pfnSegmentAdd = */ rtDbgModCv_SegmentAdd,
3563 /*.pfnSegmentCount = */ rtDbgModCv_SegmentCount,
3564 /*.pfnSegmentByIndex = */ rtDbgModCv_SegmentByIndex,
3565
3566 /*.pfnSymbolAdd = */ rtDbgModCv_SymbolAdd,
3567 /*.pfnSymbolCount = */ rtDbgModCv_SymbolCount,
3568 /*.pfnSymbolByOrdinal = */ rtDbgModCv_SymbolByOrdinal,
3569 /*.pfnSymbolByName = */ rtDbgModCv_SymbolByName,
3570 /*.pfnSymbolByAddr = */ rtDbgModCv_SymbolByAddr,
3571
3572 /*.pfnLineAdd = */ rtDbgModCv_LineAdd,
3573 /*.pfnLineCount = */ rtDbgModCv_LineCount,
3574 /*.pfnLineByOrdinal = */ rtDbgModCv_LineByOrdinal,
3575 /*.pfnLineByAddr = */ rtDbgModCv_LineByAddr,
3576
3577 /*.pfnUnwindFrame = */ rtDbgModCv_UnwindFrame,
3578
3579 /*.u32EndMagic = */ RTDBGMODVTDBG_MAGIC
3580};
3581
3582
3583/*
3584 *
3585 * PDB Debug module implementation.
3586 * PDB Debug module implementation.
3587 * PDB Debug module implementation.
3588 *
3589 */
3590
3591
3592/** @interface_method_impl{RTDBGMODVTDBG,pfnUnwindFrame} */
3593static DECLCALLBACK(int) rtDbgModPdb_UnwindFrame(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState)
3594{
3595 RT_NOREF(pMod, iSeg, off, pState);
3596 return VERR_DBG_NO_UNWIND_INFO;
3597}
3598
3599
3600/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByAddr} */
3601static DECLCALLBACK(int) rtDbgModPdb_LineByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off,
3602 PRTINTPTR poffDisp, PRTDBGLINE pLineInfo)
3603{
3604 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
3605 return RTDbgModLineByAddr(pThis->hCnt, iSeg, off, poffDisp, pLineInfo);
3606}
3607
3608
3609/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByOrdinal} */
3610static DECLCALLBACK(int) rtDbgModPdb_LineByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo)
3611{
3612 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
3613 return RTDbgModLineByOrdinal(pThis->hCnt, iOrdinal, pLineInfo);
3614}
3615
3616
3617/** @interface_method_impl{RTDBGMODVTDBG,pfnLineCount} */
3618static DECLCALLBACK(uint32_t) rtDbgModPdb_LineCount(PRTDBGMODINT pMod)
3619{
3620 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
3621 return RTDbgModLineCount(pThis->hCnt);
3622}
3623
3624
3625/** @interface_method_impl{RTDBGMODVTDBG,pfnLineAdd} */
3626static DECLCALLBACK(int) rtDbgModPdb_LineAdd(PRTDBGMODINT pMod, const char *pszFile, size_t cchFile, uint32_t uLineNo,
3627 uint32_t iSeg, RTUINTPTR off, uint32_t *piOrdinal)
3628{
3629 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
3630 Assert(!pszFile[cchFile]); NOREF(cchFile);
3631 return RTDbgModLineAdd(pThis->hCnt, pszFile, uLineNo, iSeg, off, piOrdinal);
3632}
3633
3634
3635/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByAddr} */
3636static DECLCALLBACK(int) rtDbgModPdb_SymbolByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags,
3637 PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo)
3638{
3639 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
3640 return RTDbgModSymbolByAddr(pThis->hCnt, iSeg, off, fFlags, poffDisp, pSymInfo);
3641}
3642
3643
3644/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByName} */
3645static DECLCALLBACK(int) rtDbgModPdb_SymbolByName(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol,
3646 PRTDBGSYMBOL pSymInfo)
3647{
3648 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
3649 Assert(!pszSymbol[cchSymbol]); RT_NOREF_PV(cchSymbol);
3650 return RTDbgModSymbolByName(pThis->hCnt, pszSymbol/*, cchSymbol*/, pSymInfo);
3651}
3652
3653
3654/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByOrdinal} */
3655static DECLCALLBACK(int) rtDbgModPdb_SymbolByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo)
3656{
3657 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
3658 return RTDbgModSymbolByOrdinal(pThis->hCnt, iOrdinal, pSymInfo);
3659}
3660
3661
3662/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolCount} */
3663static DECLCALLBACK(uint32_t) rtDbgModPdb_SymbolCount(PRTDBGMODINT pMod)
3664{
3665 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
3666 return RTDbgModSymbolCount(pThis->hCnt);
3667}
3668
3669
3670/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolAdd} */
3671static DECLCALLBACK(int) rtDbgModPdb_SymbolAdd(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol,
3672 RTDBGSEGIDX iSeg, RTUINTPTR off, RTUINTPTR cb, uint32_t fFlags,
3673 uint32_t *piOrdinal)
3674{
3675 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
3676 Assert(!pszSymbol[cchSymbol]); NOREF(cchSymbol);
3677 return RTDbgModSymbolAdd(pThis->hCnt, pszSymbol, iSeg, off, cb, fFlags, piOrdinal);
3678}
3679
3680
3681/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentByIndex} */
3682static DECLCALLBACK(int) rtDbgModPdb_SegmentByIndex(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo)
3683{
3684 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
3685 return RTDbgModSegmentByIndex(pThis->hCnt, iSeg, pSegInfo);
3686}
3687
3688
3689/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentCount} */
3690static DECLCALLBACK(RTDBGSEGIDX) rtDbgModPdb_SegmentCount(PRTDBGMODINT pMod)
3691{
3692 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
3693 return RTDbgModSegmentCount(pThis->hCnt);
3694}
3695
3696
3697/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentAdd} */
3698static DECLCALLBACK(int) rtDbgModPdb_SegmentAdd(PRTDBGMODINT pMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName,
3699 size_t cchName, uint32_t fFlags, PRTDBGSEGIDX piSeg)
3700{
3701 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
3702 Assert(!pszName[cchName]); NOREF(cchName);
3703 return RTDbgModSegmentAdd(pThis->hCnt, uRva, cb, pszName, fFlags, piSeg);
3704}
3705
3706
3707/** @interface_method_impl{RTDBGMODVTDBG,pfnImageSize} */
3708static DECLCALLBACK(RTUINTPTR) rtDbgModPdb_ImageSize(PRTDBGMODINT pMod)
3709{
3710 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
3711 if (pThis->cbImage)
3712 return pThis->cbImage;
3713 return RTDbgModImageSize(pThis->hCnt);
3714}
3715
3716
3717/** @interface_method_impl{RTDBGMODVTDBG,pfnRvaToSegOff} */
3718static DECLCALLBACK(RTDBGSEGIDX) rtDbgModPdb_RvaToSegOff(PRTDBGMODINT pMod, RTUINTPTR uRva, PRTUINTPTR poffSeg)
3719{
3720 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
3721 return RTDbgModRvaToSegOff(pThis->hCnt, uRva, poffSeg);
3722}
3723
3724
3725/** @interface_method_impl{RTDBGMODVTDBG,pfnClose} */
3726static DECLCALLBACK(int) rtDbgModPdb_Close(PRTDBGMODINT pMod)
3727{
3728 return rtDbgModCv_Close(pMod);
3729}
3730
3731
3732/*
3733 *
3734 * Probing code used by rtDbgModPdb_TryOpen.
3735 * Probing code used by rtDbgModPdb_TryOpen.
3736 *
3737 */
3738
3739static int rtDbgModPdb_ScanSymRecs(PRTDBGMODCV pThis, RTVFS hVfsPdb)
3740{
3741 RTVFSFILE hVfsFileSymRecs = NIL_RTVFSFILE;
3742 int rc = RTVfsFileOpen(hVfsPdb, "symbol-records", RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hVfsFileSymRecs);
3743 if (RT_SUCCESS(rc))
3744 {
3745 void *pvSymRecs;
3746 size_t cbSymRecs;
3747 rc = RTVfsFileReadAll(hVfsFileSymRecs, &pvSymRecs, &cbSymRecs);
3748 RTVfsFileRelease(hVfsFileSymRecs);
3749 if (RT_SUCCESS(rc))
3750 {
3751 Log3(("rtDbgModPdb_ScanSymRecs: %p LB %#x\n", pvSymRecs, cbSymRecs));
3752 rc = rtDbgModCvSsProcessV4PlusSymTab(pThis, pvSymRecs, cbSymRecs, 0 /*fFlags*/);
3753
3754 RTVfsFileReadAllFree(pvSymRecs, cbSymRecs);
3755 }
3756 else
3757 Log(("rtDbgModPdb_ScanSymRecs: RTVfsFileReadAll failed - %Rrc\n", rc));
3758 }
3759 return rc;
3760}
3761
3762
3763static int rtDbgModPdb_LoadSegMap(PRTDBGMODCV pThis, RTVFS hVfsPdb)
3764{
3765 /*
3766 * Open the DBI section map and read it into a regular heap block just like
3767 * rtDbgModCvReadAtAlloc does for the other code path.
3768 */
3769 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
3770 int rc = RTVfsFileOpen(hVfsPdb, "dbi-section-map", RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hVfsFile);
3771 if (RT_SUCCESS(rc))
3772 {
3773 uint64_t cbSegMap;
3774 rc = RTVfsFileQuerySize(hVfsFile, &cbSegMap);
3775 AssertRC(rc);
3776 if (RT_SUCCESS(rc))
3777 {
3778 if ( cbSegMap >= RT_UOFFSETOF(RTCVSEGMAP, aDescs)
3779 && cbSegMap <= RT_UOFFSETOF_DYN(RTCVSEGMAP, aDescs[4096])) /* random upper limit */
3780 {
3781 pThis->pSegMap = (PRTCVSEGMAP)RTMemAlloc((size_t)cbSegMap);
3782 if (pThis->pSegMap)
3783 {
3784 rc = RTVfsFileReadAt(hVfsFile, 0, pThis->pSegMap, cbSegMap, NULL);
3785 if (RT_SUCCESS(rc))
3786 {
3787 /*
3788 * Join paths with the codeview module reader and process the map.
3789 */
3790 rc = rtDbgModCvProcessSegmentMap(pThis);
3791
3792 }
3793 else
3794 Log(("rtDbgModPdb_LoadSegMap: Read error: %Rrc\n", rc));
3795 }
3796 else
3797 rc = VERR_NO_MEMORY;
3798 }
3799 else
3800 {
3801 Log(("rtDbgModPdb_LoadSegMap: Bogus section map size: %#RX64\n", cbSegMap));
3802 rc = VERR_CV_BAD_FORMAT;
3803 }
3804 }
3805 RTVfsFileRelease(hVfsFile);
3806 }
3807 else
3808 {
3809 Log(("rtDbgModPdb_LoadSegMap: Failed to open dbi-section-map: %Rrc\n", rc));
3810 if (rc == VERR_PATH_NOT_FOUND || rc == VERR_FILE_NOT_FOUND)
3811 rc = VINF_SUCCESS;
3812 }
3813 return rc;
3814}
3815
3816
3817/**
3818 * Helper for rtDbgModPdb_TryOpen that loads the section/segments into the
3819 * container.
3820 */
3821static int rtDbgModPdb_LoadSections(PRTDBGMODCV pThis, PRTDBGMODINT pDbgMod, RTVFS hVfsPdb, const char *pszFilename)
3822{
3823 /*
3824 * Use the info from the image if present.
3825 */
3826 int rc;
3827 if (pDbgMod->pImgVt)
3828 rc = pDbgMod->pImgVt->pfnEnumSegments(pDbgMod, rtDbgModCvAddSegmentsCallback, pThis);
3829 /*
3830 * Otherwise use the copy of the section table from the PDB.
3831 */
3832 else
3833 {
3834 RTVFSFILE hVfsFileSHdrs;
3835 rc = RTVfsFileOpen(hVfsPdb, "image-section-headers", RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hVfsFileSHdrs);
3836 if (RT_SUCCESS(rc))
3837 {
3838 PIMAGE_SECTION_HEADER paShs = NULL;
3839 size_t cbSHdrs = 0;
3840 rc = RTVfsFileReadAll(hVfsFileSHdrs, (void **)&paShs, &cbSHdrs);
3841 RTVfsFileRelease(hVfsFileSHdrs);
3842 if (RT_SUCCESS(rc))
3843 {
3844 rc = rtDbgModCvAddSegmentsFromSectHdrs(pThis, paShs, (uint32_t)(cbSHdrs / sizeof(IMAGE_SECTION_HEADER)),
3845 1 /* cbSectAlign - only for validation */,
3846 pThis->cbImage ? (uint32_t)pThis->cbImage : UINT32_MAX /* only for validation */,
3847 pszFilename);
3848 RTVfsFileReadAllFree(paShs, cbSHdrs);
3849 }
3850 else
3851 Log(("rtDbgModPdb_LoadSections: RTVfsFileReadAll failed - %Rrc\n", rc));
3852 }
3853 else
3854 Log2(("rtDbgModPdb_TryOpen: Failed to open 'image-section-headers' in '%s' -> %Rrc\n", pszFilename, rc));
3855 }
3856 RT_NOREF(pszFilename);
3857 pThis->fHaveLoadedSegments = true;
3858 return rc;
3859}
3860
3861
3862/**
3863 * Common worker for rtDbgModPdb_TryOpen and rtDbgModCv_TryOpen (for DBG+PDB).
3864 */
3865static int rtDbgModPdb_CommonOpenWorker(PRTDBGMODCV pThis, const char *pszFilename, RTVFS hVfsPdb, RTDBGCFG hDbgCfg)
3866{
3867 RT_NOREF(hDbgCfg, pszFilename);
3868
3869 /*
3870 * We need section headers to successfully deal with the section (segment)
3871 * map and symbol records. If there is an associated EXE or DBG file, they
3872 * will have segment information we can enumerate or load. In the DBG case,
3873 * it's already done by the time we get here. Otherwise, newer PDB files
3874 * will include a copy of the section header and we can use that of we got
3875 * no EXE or DBG files.
3876 */
3877 int rc = VINF_SUCCESS;
3878 if (!pThis->fHaveLoadedSegments)
3879 rc = rtDbgModPdb_LoadSections(pThis, pThis->pMod, hVfsPdb, pszFilename);
3880 if (RT_SUCCESS(rc))
3881 {
3882 /*
3883 * Load the section/segment map as we need to know how absolute segments
3884 * and such. This should definitely be done after loading sections.
3885 * (Unfortuantely, the segement map doesn't not provide sufficient
3886 * information to replace the section headers if we cannot find them.)
3887 */
3888 rc = rtDbgModPdb_LoadSegMap(pThis, hVfsPdb);
3889 if (RT_SUCCESS(rc))
3890 {
3891 /*
3892 * Scan symbol records for symbol addresses and anything else we find
3893 * interesting.
3894 */
3895 rc = rtDbgModPdb_ScanSymRecs(pThis, hVfsPdb);
3896 if (RT_SUCCESS(rc))
3897 {
3898 /*
3899 * We're good.
3900 */
3901 /** @todo load source files and line numbers if present. */
3902 /** @todo load unwind information. */
3903 Log(("rtDbgModPdb_TryOpen: Succeeded\n"));
3904 return VINF_SUCCESS;
3905 }
3906 Log(("rtDbgModPdb_TryOpen: rtDbgModPdb_ScanSymRecs failed - %Rrc\n", rc));
3907 }
3908 else
3909 Log(("rtDbgModPdb_TryOpen: rtDbgModPdb_LoadSegMap failed on '%s' -> %Rrc\n", pszFilename, rc));
3910 }
3911 else
3912 Log(("rtDbgModPdb_TryOpen: rtDbgModPdb_LoadSections failed on '%s' -> %Rrc\n", pszFilename, rc));
3913 return rc;
3914}
3915
3916
3917/** @interface_method_impl{RTDBGMODVTDBG,pfnTryOpen} */
3918static DECLCALLBACK(int) rtDbgModPdb_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch, RTDBGCFG hDbgCfg)
3919{
3920 /*
3921 * This only works for external files for now.
3922 */
3923 if (!pMod->pszDbgFile)
3924 return VERR_DBG_NO_MATCHING_INTERPRETER;
3925
3926 /*
3927 * Open the PDB as a VFS.
3928 */
3929 RTVFSFILE hVfsFilePdb = NIL_RTVFSFILE;
3930 int rc = RTVfsFileOpenNormal(pMod->pszDbgFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hVfsFilePdb);
3931 if (RT_SUCCESS(rc))
3932 {
3933 RTVFS hVfsPdb = NIL_RTVFS;
3934#ifdef LOG_ENABLED
3935 RTERRINFOSTATIC ErrInfo;
3936 rc = RTFsPdbVolOpen(hVfsFilePdb, 0, &hVfsPdb, RTErrInfoInitStatic(&ErrInfo));
3937#else
3938 rc = RTFsPdbVolOpen(hVfsFilePdb, 0, &hVfsPdb, NULL /*pErrInfo*/);
3939#endif
3940 RTVfsFileRelease(hVfsFilePdb);
3941 if (RT_SUCCESS(rc))
3942 {
3943 /** @todo check enmArch. It's part of the new DBI header. */
3944 RT_NOREF(enmArch);
3945
3946 /*
3947 * Create an instance so we can share the next bits with the DBG+PDB
3948 * code path in rtDbgModCv_TryOpen. We need to be able to check if
3949 * section headers have been loaded among other things.
3950 */
3951 PRTDBGMODCV pThis;
3952 rc = rtDbgModCvCreateInstance(pMod, RTCVFILETYPE_PDB, NIL_RTFILE, &pThis);
3953 if (RT_SUCCESS(rc))
3954 {
3955 rc = rtDbgModPdb_CommonOpenWorker(pThis, pMod->pszDbgFile, hVfsPdb, hDbgCfg);
3956 if (RT_FAILURE(rc))
3957 rtDbgModCv_Close(pMod);
3958 }
3959 RTVfsRelease(hVfsPdb);
3960 }
3961 else
3962 Log2(("rtDbgModPdb_TryOpen: RTFsPdbVolOpen '%s' -> %Rrc%#RTeim\n", pMod->pszDbgFile, rc, &ErrInfo.Core));
3963 }
3964 else
3965 Log2(("rtDbgModPdb_TryOpen: RTVfsFileOpenNormal '%s' -> %Rrc\n", pMod->pszDbgFile, rc));
3966 return rc;
3967}
3968
3969
3970
3971/** Virtual function table for the PDB debug info reader. */
3972DECL_HIDDEN_CONST(RTDBGMODVTDBG) const g_rtDbgModVtDbgPdb =
3973{
3974 /*.u32Magic = */ RTDBGMODVTDBG_MAGIC,
3975 /*.fSupports = */ RT_DBGTYPE_CODEVIEW,
3976 /*.pszName = */ "pdb",
3977 /*.pfnTryOpen = */ rtDbgModPdb_TryOpen,
3978 /*.pfnClose = */ rtDbgModPdb_Close,
3979
3980 /*.pfnRvaToSegOff = */ rtDbgModPdb_RvaToSegOff,
3981 /*.pfnImageSize = */ rtDbgModPdb_ImageSize,
3982
3983 /*.pfnSegmentAdd = */ rtDbgModPdb_SegmentAdd,
3984 /*.pfnSegmentCount = */ rtDbgModPdb_SegmentCount,
3985 /*.pfnSegmentByIndex = */ rtDbgModPdb_SegmentByIndex,
3986
3987 /*.pfnSymbolAdd = */ rtDbgModPdb_SymbolAdd,
3988 /*.pfnSymbolCount = */ rtDbgModPdb_SymbolCount,
3989 /*.pfnSymbolByOrdinal = */ rtDbgModPdb_SymbolByOrdinal,
3990 /*.pfnSymbolByName = */ rtDbgModPdb_SymbolByName,
3991 /*.pfnSymbolByAddr = */ rtDbgModPdb_SymbolByAddr,
3992
3993 /*.pfnLineAdd = */ rtDbgModPdb_LineAdd,
3994 /*.pfnLineCount = */ rtDbgModPdb_LineCount,
3995 /*.pfnLineByOrdinal = */ rtDbgModPdb_LineByOrdinal,
3996 /*.pfnLineByAddr = */ rtDbgModPdb_LineByAddr,
3997
3998 /*.pfnUnwindFrame = */ rtDbgModPdb_UnwindFrame,
3999
4000 /*.u32EndMagic = */ RTDBGMODVTDBG_MAGIC
4001};
4002
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use