VirtualBox

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

Last change on this file since 59973 was 59973, checked in by vboxsync, 9 years ago

dbgmodecodeview.cpp: committed too much

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 112.0 KB
Line 
1/* $Id: dbgmodcodeview.cpp 59973 2016-03-09 20:07:42Z 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-2015 Oracle Corporation
23 *
24 * This file is part of VirtualBox Open Source Edition (OSE), as
25 * available from http://www.virtualbox.org. This file is free software;
26 * you can redistribute it and/or modify it under the terms of the GNU
27 * General Public License (GPL) as published by the Free Software
28 * Foundation, in version 2 as it comes in the "COPYING" file of the
29 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
30 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
31 *
32 * The contents of this file may alternatively be used under the terms
33 * of the Common Development and Distribution License Version 1.0
34 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
35 * VirtualBox OSE distribution, in which case the provisions of the
36 * CDDL are applicable instead of those of the GPL.
37 *
38 * You may elect to license modified versions of this file under the
39 * terms and conditions of either the GPL or the CDDL or both.
40 */
41
42
43/*********************************************************************************************************************************
44* Header Files *
45*********************************************************************************************************************************/
46#define LOG_GROUP RTLOGGROUP_DBG
47#include <iprt/dbg.h>
48#include "internal/iprt.h"
49
50#include <iprt/alloca.h>
51#include <iprt/asm.h>
52#include <iprt/assert.h>
53#include <iprt/err.h>
54#include <iprt/file.h>
55#include <iprt/log.h>
56#include <iprt/mem.h>
57#include <iprt/param.h>
58#include <iprt/path.h>
59#include <iprt/string.h>
60#include <iprt/strcache.h>
61#include "internal/dbgmod.h"
62#include "internal/magics.h"
63
64#include <iprt/formats/codeview.h>
65#include <iprt/formats/pecoff.h>
66
67
68/*********************************************************************************************************************************
69* Structures and Typedefs *
70*********************************************************************************************************************************/
71/**
72 * Directory sorting order.
73 */
74typedef enum RTCVDIRORDER
75{
76 RTCVDIRORDER_INVALID = 0,
77 /** Ordered by module. */
78 RTCVDIRORDER_BY_MOD,
79 /** Ordered by module, but 0 modules at the end. */
80 RTCVDIRORDER_BY_MOD_0,
81 /** Ordered by section, with global modules at the end. */
82 RTCVDIRORDER_BY_SST_MOD
83} RTCVDIRORDER;
84
85
86/**
87 * File type.
88 */
89typedef enum RTCVFILETYPE
90{
91 RTCVFILETYPE_INVALID = 0,
92 /** Executable image. */
93 RTCVFILETYPE_IMAGE,
94 /** A DBG-file with a IMAGE_SEPARATE_DEBUG_HEADER. */
95 RTCVFILETYPE_DBG,
96 /** A PDB file. */
97 RTCVFILETYPE_PDB,
98 /** Some other kind of file with CV at the end. */
99 RTCVFILETYPE_OTHER_AT_END,
100 /** The end of the valid values. */
101 RTCVFILETYPE_END,
102 /** Type blowup. */
103 RTCVFILETYPE_32BIT_HACK = 0x7fffffff
104} RTCVFILETYPE;
105
106
107/**
108 * CodeView debug info reader instance.
109 */
110typedef struct RTDBGMODCV
111{
112 /** Using a container for managing the debug info. */
113 RTDBGMOD hCnt;
114
115 /** @name Codeview details
116 * @{ */
117 /** The code view magic (used as format indicator). */
118 uint32_t u32CvMagic;
119 /** The offset of the CV debug info in the file. */
120 uint32_t offBase;
121 /** The size of the CV debug info. */
122 uint32_t cbDbgInfo;
123 /** The offset of the subsection directory (relative to offBase). */
124 uint32_t offDir;
125 /** The directory order. */
126 RTCVDIRORDER enmDirOrder;
127 /** @} */
128
129 /** @name COFF details.
130 * @{ */
131 /** Offset of the COFF header. */
132 uint32_t offCoffDbgInfo;
133 /** The size of the COFF debug info. */
134 uint32_t cbCoffDbgInfo;
135 /** The COFF debug info header. */
136 IMAGE_COFF_SYMBOLS_HEADER CoffHdr;
137 /** @} */
138
139 /** The file type. */
140 RTCVFILETYPE enmType;
141 /** The file handle (if external). */
142 RTFILE hFile;
143 /** Pointer to the module (no reference retained). */
144 PRTDBGMODINT pMod;
145
146 /** The image size, if we know it. This is 0 if we don't know it. */
147 uint32_t cbImage;
148
149 /** Indicates that we've loaded segments intot he container already. */
150 bool fHaveLoadedSegments;
151 /** Alternative address translation method for DOS frames. */
152 bool fHaveDosFrames;
153
154 /** @name Codeview Parsing state.
155 * @{ */
156 /** Number of directory entries. */
157 uint32_t cDirEnts;
158 /** The directory (converted to 32-bit). */
159 PRTCVDIRENT32 paDirEnts;
160 /** Current debugging style when parsing modules. */
161 uint16_t uCurStyle;
162 /** Current debugging style version (HLL only). */
163 uint16_t uCurStyleVer;
164
165 /** The segment map (if present). */
166 PRTCVSEGMAP pSegMap;
167 /** Segment names. */
168 char *pszzSegNames;
169 /** The size of the segment names. */
170 uint32_t cbSegNames;
171
172 /** @} */
173
174} RTDBGMODCV;
175/** Pointer to a codeview debug info reader instance. */
176typedef RTDBGMODCV *PRTDBGMODCV;
177/** Pointer to a const codeview debug info reader instance. */
178typedef RTDBGMODCV *PCRTDBGMODCV;
179
180
181
182/**
183 * Subsection callback.
184 *
185 * @returns IPRT status code.
186 * @param pThis The CodeView debug info reader instance.
187 * @param pvSubSect Pointer to the subsection data.
188 * @param cbSubSect The size of the subsection data.
189 * @param pDirEnt The directory entry.
190 */
191typedef DECLCALLBACK(int) FNDBGMODCVSUBSECTCALLBACK(PRTDBGMODCV pThis, void const *pvSubSect, size_t cbSubSect,
192 PCRTCVDIRENT32 pDirEnt);
193/** Pointer to a subsection callback. */
194typedef FNDBGMODCVSUBSECTCALLBACK *PFNDBGMODCVSUBSECTCALLBACK;
195
196
197
198/*********************************************************************************************************************************
199* Defined Constants And Macros *
200*********************************************************************************************************************************/
201/** Light weight assert + return w/ fixed status code. */
202#define RTDBGMODCV_CHECK_RET_BF(a_Expr, a_LogArgs) \
203 do { \
204 if (!(a_Expr)) \
205 { \
206 Log(("RTDbgCv: Check failed on line %d: " #a_Expr "\n", __LINE__)); \
207 Log(a_LogArgs); \
208 /*AssertFailed();*/ \
209 return VERR_CV_BAD_FORMAT; \
210 } \
211 } while (0)
212
213
214/** Light weight assert + return w/ fixed status code. */
215#define RTDBGMODCV_CHECK_NOMSG_RET_BF(a_Expr) \
216 do { \
217 if (!(a_Expr)) \
218 { \
219 Log(("RTDbgCv: Check failed on line %d: " #a_Expr "\n", __LINE__)); \
220 /*AssertFailed();*/ \
221 return VERR_CV_BAD_FORMAT; \
222 } \
223 } while (0)
224
225
226
227
228
229/**
230 * Reads CodeView information.
231 *
232 * @returns IPRT status code.
233 * @param pThis The CodeView reader instance.
234 * @param off The offset to start reading at, relative to the
235 * CodeView base header.
236 * @param pvBuf The buffer to read into.
237 * @param cb How many bytes to read.
238 */
239static int rtDbgModCvReadAt(PRTDBGMODCV pThis, uint32_t off, void *pvBuf, size_t cb)
240{
241 int rc;
242 if (pThis->hFile == NIL_RTFILE)
243 rc = pThis->pMod->pImgVt->pfnReadAt(pThis->pMod, UINT32_MAX, off + pThis->offBase, pvBuf, cb);
244 else
245 rc = RTFileReadAt(pThis->hFile, off + pThis->offBase, pvBuf, cb, NULL);
246 return rc;
247}
248
249
250/**
251 * Reads CodeView information into an allocated buffer.
252 *
253 * @returns IPRT status code.
254 * @param pThis The CodeView reader instance.
255 * @param off The offset to start reading at, relative to the
256 * CodeView base header.
257 * @param ppvBuf Where to return the allocated buffer on success.
258 * @param cb How many bytes to read.
259 */
260static int rtDbgModCvReadAtAlloc(PRTDBGMODCV pThis, uint32_t off, void **ppvBuf, size_t cb)
261{
262 int rc;
263 void *pvBuf = *ppvBuf = RTMemAlloc(cb);
264 if (pvBuf)
265 {
266 if (pThis->hFile == NIL_RTFILE)
267 rc = pThis->pMod->pImgVt->pfnReadAt(pThis->pMod, UINT32_MAX, off + pThis->offBase, pvBuf, cb);
268 else
269 rc = RTFileReadAt(pThis->hFile, off + pThis->offBase, pvBuf, cb, NULL);
270 if (RT_SUCCESS(rc))
271 return VINF_SUCCESS;
272
273 RTMemFree(pvBuf);
274 *ppvBuf = NULL;
275 }
276 else
277 rc = VERR_NO_MEMORY;
278 return rc;
279}
280
281
282/**
283 * Gets a name string for a subsection type.
284 *
285 * @returns Section name (read only).
286 * @param uSubSectType The subsection type.
287 */
288static const char *rtDbgModCvGetSubSectionName(uint16_t uSubSectType)
289{
290 switch (uSubSectType)
291 {
292 case kCvSst_OldModule: return "sstOldModule";
293 case kCvSst_OldPublic: return "sstOldPublic";
294 case kCvSst_OldTypes: return "sstOldTypes";
295 case kCvSst_OldSymbols: return "sstOldSymbols";
296 case kCvSst_OldSrcLines: return "sstOldSrcLines";
297 case kCvSst_OldLibraries: return "sstOldLibraries";
298 case kCvSst_OldImports: return "sstOldImports";
299 case kCvSst_OldCompacted: return "sstOldCompacted";
300 case kCvSst_OldSrcLnSeg: return "sstOldSrcLnSeg";
301 case kCvSst_OldSrcLines3: return "sstOldSrcLines3";
302
303 case kCvSst_Module: return "sstModule";
304 case kCvSst_Types: return "sstTypes";
305 case kCvSst_Public: return "sstPublic";
306 case kCvSst_PublicSym: return "sstPublicSym";
307 case kCvSst_Symbols: return "sstSymbols";
308 case kCvSst_AlignSym: return "sstAlignSym";
309 case kCvSst_SrcLnSeg: return "sstSrcLnSeg";
310 case kCvSst_SrcModule: return "sstSrcModule";
311 case kCvSst_Libraries: return "sstLibraries";
312 case kCvSst_GlobalSym: return "sstGlobalSym";
313 case kCvSst_GlobalPub: return "sstGlobalPub";
314 case kCvSst_GlobalTypes: return "sstGlobalTypes";
315 case kCvSst_MPC: return "sstMPC";
316 case kCvSst_SegMap: return "sstSegMap";
317 case kCvSst_SegName: return "sstSegName";
318 case kCvSst_PreComp: return "sstPreComp";
319 case kCvSst_PreCompMap: return "sstPreCompMap";
320 case kCvSst_OffsetMap16: return "sstOffsetMap16";
321 case kCvSst_OffsetMap32: return "sstOffsetMap32";
322 case kCvSst_FileIndex: return "sstFileIndex";
323 case kCvSst_StaticSym: return "sstStaticSym";
324 }
325 static char s_sz[32];
326 RTStrPrintf(s_sz, sizeof(s_sz), "Unknown%#x", uSubSectType);
327 return s_sz;
328}
329
330
331/**
332 * Gets a name string for a symbol type.
333 *
334 * @returns symbol type name (read only).
335 * @param enmSymType The symbol type to name.
336 */
337static const char *rtDbgModCvSsSymTypeName(RTCVSYMTYPE enmSymType)
338{
339 switch (enmSymType)
340 {
341#define CASE_RET_STR(Name) case kCvSymType_##Name: return #Name;
342 CASE_RET_STR(Compile);
343 CASE_RET_STR(Register);
344 CASE_RET_STR(Constant);
345 CASE_RET_STR(UDT);
346 CASE_RET_STR(SSearch);
347 CASE_RET_STR(End);
348 CASE_RET_STR(Skip);
349 CASE_RET_STR(CVReserve);
350 CASE_RET_STR(ObjName);
351 CASE_RET_STR(EndArg);
352 CASE_RET_STR(CobolUDT);
353 CASE_RET_STR(ManyReg);
354 CASE_RET_STR(Return);
355 CASE_RET_STR(EntryThis);
356 CASE_RET_STR(BpRel16);
357 CASE_RET_STR(LData16);
358 CASE_RET_STR(GData16);
359 CASE_RET_STR(Pub16);
360 CASE_RET_STR(LProc16);
361 CASE_RET_STR(GProc16);
362 CASE_RET_STR(Thunk16);
363 CASE_RET_STR(BLock16);
364 CASE_RET_STR(With16);
365 CASE_RET_STR(Label16);
366 CASE_RET_STR(CExModel16);
367 CASE_RET_STR(VftPath16);
368 CASE_RET_STR(RegRel16);
369 CASE_RET_STR(BpRel32);
370 CASE_RET_STR(LData32);
371 CASE_RET_STR(GData32);
372 CASE_RET_STR(Pub32);
373 CASE_RET_STR(LProc32);
374 CASE_RET_STR(GProc32);
375 CASE_RET_STR(Thunk32);
376 CASE_RET_STR(Block32);
377 CASE_RET_STR(With32);
378 CASE_RET_STR(Label32);
379 CASE_RET_STR(CExModel32);
380 CASE_RET_STR(VftPath32);
381 CASE_RET_STR(RegRel32);
382 CASE_RET_STR(LThread32);
383 CASE_RET_STR(GThread32);
384 CASE_RET_STR(LProcMips);
385 CASE_RET_STR(GProcMips);
386 CASE_RET_STR(ProcRef);
387 CASE_RET_STR(DataRef);
388 CASE_RET_STR(Align);
389 CASE_RET_STR(LProcRef);
390 CASE_RET_STR(V2_Register);
391 CASE_RET_STR(V2_Constant);
392 CASE_RET_STR(V2_Udt);
393 CASE_RET_STR(V2_CobolUdt);
394 CASE_RET_STR(V2_ManyReg);
395 CASE_RET_STR(V2_BpRel);
396 CASE_RET_STR(V2_LData);
397 CASE_RET_STR(V2_GData);
398 CASE_RET_STR(V2_Pub);
399 CASE_RET_STR(V2_LProc);
400 CASE_RET_STR(V2_GProc);
401 CASE_RET_STR(V2_VftTable);
402 CASE_RET_STR(V2_RegRel);
403 CASE_RET_STR(V2_LThread);
404 CASE_RET_STR(V2_GThread);
405 CASE_RET_STR(V2_Unknown_1010);
406 CASE_RET_STR(V2_Unknown_1011);
407 CASE_RET_STR(V2_FrameInfo);
408 CASE_RET_STR(V2_Compliand);
409 CASE_RET_STR(V3_Compliand);
410 CASE_RET_STR(V3_Thunk);
411 CASE_RET_STR(V3_Block);
412 CASE_RET_STR(V3_Unknown_1104);
413 CASE_RET_STR(V3_Label);
414 CASE_RET_STR(V3_Register);
415 CASE_RET_STR(V3_Constant);
416 CASE_RET_STR(V3_Udt);
417 CASE_RET_STR(V3_Unknown_1109);
418 CASE_RET_STR(V3_Unknown_110a);
419 CASE_RET_STR(V3_BpRel);
420 CASE_RET_STR(V3_LData);
421 CASE_RET_STR(V3_GData);
422 CASE_RET_STR(V3_Pub);
423 CASE_RET_STR(V3_LProc);
424 CASE_RET_STR(V3_GProc);
425 CASE_RET_STR(V3_RegRel);
426 CASE_RET_STR(V3_LThread);
427 CASE_RET_STR(V3_GThread);
428 CASE_RET_STR(V3_Unknown_1114);
429 CASE_RET_STR(V3_Unknown_1115);
430 CASE_RET_STR(V3_MSTool);
431 CASE_RET_STR(V3_PubFunc1);
432 CASE_RET_STR(V3_PubFunc2);
433 CASE_RET_STR(V3_SectInfo);
434 CASE_RET_STR(V3_SubSectInfo);
435 CASE_RET_STR(V3_Entrypoint);
436 CASE_RET_STR(V3_Unknown_1139);
437 CASE_RET_STR(V3_SecuCookie);
438 CASE_RET_STR(V3_Unknown_113b);
439 CASE_RET_STR(V3_MsToolInfo);
440 CASE_RET_STR(V3_MsToolEnv);
441 CASE_RET_STR(VS2013_Local);
442 CASE_RET_STR(VS2013_FpOff);
443 CASE_RET_STR(VS2013_LProc32);
444 CASE_RET_STR(VS2013_GProc32);
445#undef CASE_RET_STR
446 case kCvSymType_EndOfValues: break;
447 }
448 return "<unknown type>";
449}
450
451
452/**
453 * Adds a string to the g_hDbgModStrCache after sanitizing it.
454 *
455 * IPRT only deals with UTF-8 strings, so the string will be forced to UTF-8
456 * encoding. Also, codeview generally have length prefixed
457 *
458 * @returns String cached copy of the string.
459 * @param pch The string to copy to the cache.
460 * @param cch The length of the string. RTSTR_MAX if zero
461 * terminated.
462 */
463static const char *rtDbgModCvAddSanitizedStringToCache(const char *pch, size_t cch)
464{
465 /*
466 * If the string is valid UTF-8 and or the right length, we're good.
467 * This is usually the case.
468 */
469 const char *pszRet;
470 int rc;
471 if (cch != RTSTR_MAX)
472 rc = RTStrValidateEncodingEx(pch, cch, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
473 else
474 rc = RTStrValidateEncodingEx(pch, cch, 0);
475 if (RT_SUCCESS(rc))
476 pszRet = RTStrCacheEnterN(g_hDbgModStrCache, pch, cch);
477 else
478 {
479 /*
480 * Need to sanitize the string, so make a copy of it.
481 */
482 char *pszCopy = (char *)RTMemDupEx(pch, cch, 1);
483 AssertPtrReturn(pszCopy, NULL);
484
485 /* Deal with anyembedded zero chars. */
486 char *psz = RTStrEnd(pszCopy, cch);
487 while (psz)
488 {
489 *psz = '_';
490 psz = RTStrEnd(psz, cch - (psz - pszCopy));
491 }
492
493 /* Force valid UTF-8 encoding. */
494 size_t cchTmp = RTStrPurgeEncoding(pszCopy);
495 NOREF(cchTmp); Assert(cchTmp == cch);
496
497 /* Enter it into the cache and free the temp copy. */
498 pszRet = RTStrCacheEnterN(g_hDbgModStrCache, pszCopy, cch);
499 RTMemFree(pszCopy);
500 }
501 return pszRet;
502}
503
504
505/**
506 * Translates a codeview segment and offset into our segment layout.
507 *
508 * @returns
509 * @param pThis .
510 * @param piSeg .
511 * @param poff .
512 */
513DECLINLINE(int) rtDbgModCvAdjustSegAndOffset(PRTDBGMODCV pThis, uint32_t *piSeg, uint64_t *poff)
514{
515 uint32_t iSeg = *piSeg;
516 if (iSeg == 0)
517 iSeg = RTDBGSEGIDX_ABS;
518 else if (pThis->pSegMap)
519 {
520 if (pThis->fHaveDosFrames)
521 {
522 if ( iSeg > pThis->pSegMap->Hdr.cSegs
523 || iSeg == 0)
524 return VERR_CV_BAD_FORMAT;
525 if (*poff <= pThis->pSegMap->aDescs[iSeg - 1].cb + pThis->pSegMap->aDescs[iSeg - 1].off)
526 *poff -= pThis->pSegMap->aDescs[iSeg - 1].off;
527 else
528 {
529 /* Workaround for VGABIOS where _DATA symbols like vgafont8 are
530 reported in the VGAROM segment. */
531 uint64_t uAddrSym = *poff + ((uint32_t)pThis->pSegMap->aDescs[iSeg - 1].iFrame << 4);
532 uint16_t j = pThis->pSegMap->Hdr.cSegs;
533 while (j-- > 0)
534 {
535 uint64_t uAddrFirst = (uint64_t)pThis->pSegMap->aDescs[j].off
536 + ((uint32_t)pThis->pSegMap->aDescs[j].iFrame << 4);
537 if (uAddrSym - uAddrFirst < pThis->pSegMap->aDescs[j].cb)
538 {
539 Log(("CV addr fix: %04x:%08x -> %04x:%08x\n", iSeg, *poff, j + 1, uAddrSym - uAddrFirst));
540 *poff = uAddrSym - uAddrFirst;
541 iSeg = j + 1;
542 break;
543 }
544 }
545 if (j == UINT16_MAX)
546 return VERR_CV_BAD_FORMAT;
547 }
548 }
549 else
550 {
551 if ( iSeg > pThis->pSegMap->Hdr.cSegs
552 || iSeg == 0
553 || *poff > pThis->pSegMap->aDescs[iSeg - 1].cb)
554 return VERR_CV_BAD_FORMAT;
555 *poff += pThis->pSegMap->aDescs[iSeg - 1].off;
556 }
557 if (pThis->pSegMap->aDescs[iSeg - 1].fFlags & RTCVSEGMAPDESC_F_ABS)
558 iSeg = RTDBGSEGIDX_ABS;
559 else
560 iSeg = pThis->pSegMap->aDescs[iSeg - 1].iGroup;
561 }
562 *piSeg = iSeg;
563 return VINF_SUCCESS;
564}
565
566
567/**
568 * Adds a symbol to the container.
569 *
570 * @returns IPRT status code
571 * @param pThis The CodeView debug info reader instance.
572 * @param iSeg Segment number.
573 * @param off Offset into the segment
574 * @param pchName The symbol name (not necessarily terminated).
575 * @param cchName The symbol name length.
576 * @param fFlags Flags reserved for future exploits, MBZ.
577 * @param cbSym Symbol size, 0 if not avaiable.
578 */
579static int rtDbgModCvAddSymbol(PRTDBGMODCV pThis, uint32_t iSeg, uint64_t off, const char *pchName,
580 uint32_t cchName, uint32_t fFlags, uint32_t cbSym)
581{
582 const char *pszName = rtDbgModCvAddSanitizedStringToCache(pchName, cchName);
583 int rc;
584 if (pszName)
585 {
586#if 1
587 Log2(("CV Sym: %04x:%08x %.*s\n", iSeg, off, cchName, pchName));
588 rc = rtDbgModCvAdjustSegAndOffset(pThis, &iSeg, &off);
589 if (RT_SUCCESS(rc))
590 {
591 rc = RTDbgModSymbolAdd(pThis->hCnt, pszName, iSeg, off, cbSym, 0 /*fFlags*/, NULL);
592
593 /* Simple duplicate symbol mangling, just to get more details. */
594 if (rc == VERR_DBG_DUPLICATE_SYMBOL && cchName < _2K)
595 {
596 char szTmpName[_2K + 96];
597 memcpy(szTmpName, pszName, cchName);
598 szTmpName[cchName] = '_';
599 for (uint32_t i = 1; i < 32; i++)
600 {
601 RTStrFormatU32(&szTmpName[cchName + 1], 80, i, 10, 0, 0, 0);
602 rc = RTDbgModSymbolAdd(pThis->hCnt, szTmpName, iSeg, off, cbSym, 0 /*fFlags*/, NULL);
603 if (rc != VERR_DBG_DUPLICATE_SYMBOL)
604 break;
605 }
606
607 }
608
609 Log(("Symbol: %04x:%08x %.*s [%Rrc]\n", iSeg, off, cchName, pchName, rc));
610 if (rc == VERR_DBG_ADDRESS_CONFLICT || rc == VERR_DBG_DUPLICATE_SYMBOL)
611 rc = VINF_SUCCESS;
612 }
613 else
614 Log(("Invalid segment index/offset %#06x:%08x for symbol %.*s\n", iSeg, off, cchName, pchName));
615
616#else
617 Log(("Symbol: %04x:%08x %.*s\n", iSeg, off, cchName, pchName));
618 rc = VINF_SUCCESS;
619#endif
620 RTStrCacheRelease(g_hDbgModStrCache, pszName);
621 }
622 else
623 rc = VERR_NO_STR_MEMORY;
624 return rc;
625}
626
627
628/**
629 * Validates the a zero terminated string.
630 *
631 * @returns String length if valid, UINT16_MAX if invalid.
632 * @param pszString The string to validate.
633 * @param pvRec The pointer to the record containing the string.
634 * @param cbRec The record length.
635 */
636static uint16_t rtDbgModCvValidateZeroString(const char *pszString, void const *pvRec, uint16_t cbRec)
637{
638 size_t offStrMember = (uintptr_t)pszString - (uintptr_t)pvRec;
639 AssertReturn(offStrMember < _1K, UINT16_MAX);
640 AssertReturn(offStrMember <= cbRec, UINT16_MAX);
641 cbRec -= (uint16_t)offStrMember;
642
643 const char *pchEnd = RTStrEnd(pszString, cbRec);
644 AssertReturn(pchEnd, UINT16_MAX);
645
646 int rc = RTStrValidateEncoding(pszString);
647 AssertRCReturn(rc, UINT16_MAX);
648
649 return (uint16_t)(pchEnd - pszString);
650}
651
652
653/**
654 * Parses a CV4 symbol table, adding symbols to the container.
655 *
656 * @returns IPRT status code
657 * @param pThis The CodeView debug info reader instance.
658 * @param pvSymTab The symbol table.
659 * @param cbSymTab The size of the symbol table.
660 * @param fFlags Flags reserved for future exploits, MBZ.
661 */
662static int rtDbgModCvSsProcessV4PlusSymTab(PRTDBGMODCV pThis, void const *pvSymTab, size_t cbSymTab, uint32_t fFlags)
663{
664 int rc = VINF_SUCCESS;
665 RTCPTRUNION uCursor;
666 uCursor.pv = pvSymTab;
667
668 while (cbSymTab > 0 && RT_SUCCESS(rc))
669 {
670 uint8_t const * const pbRecStart = uCursor.pu8;
671 uint16_t cbRec = *uCursor.pu16++;
672 if (cbRec >= 2)
673 {
674 uint16_t uSymType = *uCursor.pu16++;
675
676 Log3((" %p: uSymType=%#06x LB %#x %s\n",
677 pbRecStart - (uint8_t *)pvSymTab, uSymType, cbRec, rtDbgModCvSsSymTypeName((RTCVSYMTYPE)uSymType)));
678 RTDBGMODCV_CHECK_RET_BF(cbRec >= 2 && cbRec <= cbSymTab, ("cbRec=%#x cbSymTab=%#x\n", cbRec, cbSymTab));
679
680 switch (uSymType)
681 {
682 case kCvSymType_LData16:
683 case kCvSymType_GData16:
684 case kCvSymType_Pub16:
685 {
686 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec > 2 + 2+2+2+1);
687 uint16_t off = *uCursor.pu16++;
688 uint16_t iSeg = *uCursor.pu16++;
689 /*uint16_t iType =*/ *uCursor.pu16++;
690 uint8_t cchName = *uCursor.pu8++;
691 RTDBGMODCV_CHECK_NOMSG_RET_BF(cchName > 0);
692 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= 2 + 2+2+2+1 + cchName);
693
694 rc = rtDbgModCvAddSymbol(pThis, iSeg, off, uCursor.pch, cchName, 0, 0);
695 break;
696 }
697
698 case kCvSymType_LData32:
699 case kCvSymType_GData32:
700 case kCvSymType_Pub32:
701 {
702 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec > 2 + 4+2+2+1);
703 uint32_t off = *uCursor.pu32++;
704 uint16_t iSeg = *uCursor.pu16++;
705 /*uint16_t iType =*/ *uCursor.pu16++;
706 uint8_t cchName = *uCursor.pu8++;
707 RTDBGMODCV_CHECK_NOMSG_RET_BF(cchName > 0);
708 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= 2 + 4+2+2+1 + cchName);
709
710 rc = rtDbgModCvAddSymbol(pThis, iSeg, off, uCursor.pch, cchName, 0, 0);
711 break;
712 }
713
714 /** @todo add GProc and LProc so we can gather sizes as well as just symbols. */
715
716 case kCvSymType_V3_Label:
717 {
718 PCRTCVSYMV3LABEL pLabel = (PCRTCVSYMV3LABEL)uCursor.pv;
719 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= sizeof(*pLabel));
720 uint16_t cchName = rtDbgModCvValidateZeroString(pLabel->szName, pLabel, cbRec);
721 if (cchName != UINT16_MAX && cchName > 0)
722 rc = rtDbgModCvAddSymbol(pThis, pLabel->iSection, pLabel->offSection, pLabel->szName, cchName, 0, 0);
723 else
724 Log3((" cchName=%#x sec:off=%#x:%#x %.*Rhxs\n",
725 cchName, pLabel->iSection, pLabel->offSection, cbRec, pLabel));
726 break;
727 }
728
729 case kCvSymType_V3_LData:
730 case kCvSymType_V3_GData:
731 case kCvSymType_V3_Pub:
732 {
733 PCRTCVSYMV3TYPEDNAME pData = (PCRTCVSYMV3TYPEDNAME)uCursor.pv;
734 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= sizeof(*pData));
735 uint16_t cchName = rtDbgModCvValidateZeroString(pData->szName, pData, cbRec);
736 if (cchName != UINT16_MAX && cchName > 0)
737 rc = rtDbgModCvAddSymbol(pThis, pData->iSection, pData->offSection, pData->szName, cchName, 0, 0);
738 else
739 Log3((" cchName=%#x sec:off=%#x:%#x idType=%#x %.*Rhxs\n",
740 cchName, pData->iSection, pData->offSection, pData->idType, cbRec, pData));
741 break;
742 }
743
744 case kCvSymType_V3_LProc:
745 case kCvSymType_V3_GProc:
746 {
747 PCRTCVSYMV3PROC pProc = (PCRTCVSYMV3PROC)uCursor.pv;
748 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbRec >= sizeof(*pProc));
749 uint16_t cchName = rtDbgModCvValidateZeroString(pProc->szName, pProc, cbRec);
750 if (cchName != UINT16_MAX && cchName > 0)
751 rc = rtDbgModCvAddSymbol(pThis, pProc->iSection, pProc->offSection, pProc->szName, cchName,
752 0, pProc->cbProc);
753 else
754 Log3((" cchName=%#x sec:off=%#x:%#x LB %#x\n",
755 cchName, pProc->iSection, pProc->offSection, pProc->cbProc));
756 break;
757 }
758
759 }
760 }
761 /*else: shorter records can be used for alignment, I guess. */
762
763 /* next */
764 uCursor.pu8 = pbRecStart + cbRec + 2;
765 cbSymTab -= cbRec + 2;
766 }
767 return rc;
768}
769
770
771/**
772 * Parses a CV8 symbol table, adding symbols to the container.
773 *
774 * @returns IPRT status code
775 * @param pThis The CodeView debug info reader instance.
776 * @param pvSymTab The symbol table.
777 * @param cbSymTab The size of the symbol table.
778 * @param fFlags Flags reserved for future exploits, MBZ.
779 */
780static int rtDbgModCvSsProcessV8SymTab(PRTDBGMODCV pThis, void const *pvSymTab, size_t cbSymTab, uint32_t fFlags)
781{
782 int rc = VINF_SUCCESS;
783 RTCPTRUNION uCursor;
784 uCursor.pv = pvSymTab;
785
786 for (;;)
787 {
788 RTDBGMODCV_CHECK_RET_BF(cbSymTab > sizeof(RTCV8SYMBOLSBLOCK), ("cbSymTab=%zu\n", cbSymTab));
789 PCRTCV8SYMBOLSBLOCK pBlockHdr = (PCRTCV8SYMBOLSBLOCK)uCursor.pv;
790 Log3((" %p: uType=%#04x LB %#x\n", (uint8_t *)pBlockHdr - (uint8_t *)pvSymTab, pBlockHdr->uType, pBlockHdr->cb));
791 RTDBGMODCV_CHECK_RET_BF(pBlockHdr->cb <= cbSymTab - sizeof(RTCV8SYMBOLSBLOCK),
792 ("cb=%#u cbSymTab=%zu\n", pBlockHdr->cb, cbSymTab));
793
794 switch (pBlockHdr->uType)
795 {
796 case RTCV8SYMBLOCK_TYPE_SYMBOLS:
797 rc = rtDbgModCvSsProcessV4PlusSymTab(pThis, pBlockHdr + 1, pBlockHdr->cb, fFlags);
798 break;
799
800 case RTCV8SYMBLOCK_TYPE_SRC_STR:
801 /** @todo would have to cache the string table as the line numbers using it
802 * may be in a different .debug$S section and wlinking will therefore
803 * issue two sstSymbols entries for the module. */
804 break;
805
806 case RTCV8SYMBLOCK_TYPE_SECT_LINES:
807 break;
808
809 case RTCV8SYMBLOCK_TYPE_SRC_INFO:
810 /* Not something we currently care about. Could be useful later
811 for checking if a source file has changed. */
812 break;
813 default:
814 Log(("rtDbgModCvSsProcessV8SymTab: Unknown block type %#x (LB %#x)\n", pBlockHdr->uType, pBlockHdr->cb));
815 break;
816 }
817 uint32_t cbAligned = RT_ALIGN_32(sizeof(*pBlockHdr) + pBlockHdr->cb, 4);
818 if (RT_SUCCESS(rc) && cbSymTab > cbAligned)
819 {
820 uCursor.pu8 += cbAligned;
821 cbSymTab -= cbAligned;
822 }
823 else
824 break;
825 }
826 return rc;
827}
828
829
830/** @callback_method_impl{FNDBGMODCVSUBSECTCALLBACK,
831 * Parses kCvSst_GlobalPub\, kCvSst_GlobalSym and kCvSst_StaticSym subsections\,
832 * adding symbols it finds to the container.} */
833static DECLCALLBACK(int)
834rtDbgModCvSs_GlobalPub_GlobalSym_StaticSym(PRTDBGMODCV pThis, void const *pvSubSect, size_t cbSubSect, PCRTCVDIRENT32 pDirEnt)
835{
836 PCRTCVGLOBALSYMTABHDR pHdr = (PCRTCVGLOBALSYMTABHDR)pvSubSect;
837
838 /*
839 * Quick data validation.
840 */
841 Log2(("RTDbgModCv: %s: uSymHash=%#x uAddrHash=%#x cbSymbols=%#x cbSymHash=%#x cbAddrHash=%#x\n",
842 rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType), pHdr->uSymHash,
843 pHdr->uAddrHash, pHdr->cbSymbols, pHdr->cbSymHash, pHdr->cbAddrHash));
844 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSubSect >= sizeof(RTCVGLOBALSYMTABHDR));
845 RTDBGMODCV_CHECK_NOMSG_RET_BF((uint64_t)pHdr->cbSymbols + pHdr->cbSymHash + pHdr->cbAddrHash <= cbSubSect - sizeof(*pHdr));
846 RTDBGMODCV_CHECK_NOMSG_RET_BF(pHdr->uSymHash < 0x20);
847 RTDBGMODCV_CHECK_NOMSG_RET_BF(pHdr->uAddrHash < 0x20);
848 if (!pHdr->cbSymbols)
849 return VINF_SUCCESS;
850
851 /*
852 * Parse the symbols.
853 */
854 return rtDbgModCvSsProcessV4PlusSymTab(pThis, pHdr + 1, pHdr->cbSymbols, 0);
855}
856
857
858/** @callback_method_impl{FNDBGMODCVSUBSECTCALLBACK,
859 * Parses kCvSst_Module subsection\, storing the debugging style in pThis.} */
860static DECLCALLBACK(int)
861rtDbgModCvSs_Module(PRTDBGMODCV pThis, void const *pvSubSect, size_t cbSubSect, PCRTCVDIRENT32 pDirEnt)
862{
863 RTCPTRUNION uCursor;
864 uCursor.pv = pvSubSect;
865 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSubSect >= 2 + 2 + 2 + 2 + 0 + 1);
866 uint16_t iOverlay = *uCursor.pu16++;
867 uint16_t iLib = *uCursor.pu16++;
868 uint16_t cSegs = *uCursor.pu16++;
869 pThis->uCurStyle = *uCursor.pu16++;
870 if (pThis->uCurStyle == 0)
871 pThis->uCurStyle = RT_MAKE_U16('C', 'V');
872 pThis->uCurStyleVer = 0;
873 uint8_t cchName = uCursor.pu8[cSegs * 12];
874 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSubSect >= 2 + 2 + 2 + 2 + cSegs * 12U + 1 + cchName);
875
876 const char *pchName = (const char *)&uCursor.pu8[cSegs * 12 + 1];
877 Log2(("RTDbgModCv: Module: iOverlay=%#x iLib=%#x cSegs=%#x Style=%c%c (%#x) %.*s\n", iOverlay, iLib, cSegs,
878 RT_BYTE1(pThis->uCurStyle), RT_BYTE2(pThis->uCurStyle), pThis->uCurStyle, cchName, pchName));
879 RTDBGMODCV_CHECK_NOMSG_RET_BF(pThis->uCurStyle == RT_MAKE_U16('C', 'V'));
880
881 PCRTCVMODSEGINFO32 paSegs = (PCRTCVMODSEGINFO32)uCursor.pv;
882 for (uint16_t iSeg = 0; iSeg < cSegs; iSeg++)
883 Log2((" #%02u: %04x:%08x LB %08x\n", iSeg, paSegs[iSeg].iSeg, paSegs[iSeg].off, paSegs[iSeg].cb));
884
885 return VINF_SUCCESS;
886}
887
888
889/** @callback_method_impl{FNDBGMODCVSUBSECTCALLBACK,
890 * Parses kCvSst_Symbols\, kCvSst_PublicSym and kCvSst_AlignSym subsections\,
891 * adding symbols it finds to the container.} */
892static DECLCALLBACK(int)
893rtDbgModCvSs_Symbols_PublicSym_AlignSym(PRTDBGMODCV pThis, void const *pvSubSect, size_t cbSubSect, PCRTCVDIRENT32 pDirEnt)
894{
895 RTDBGMODCV_CHECK_NOMSG_RET_BF(pThis->uCurStyle == RT_MAKE_U16('C', 'V'));
896 RTDBGMODCV_CHECK_NOMSG_RET_BF(cbSubSect >= 8);
897
898 uint32_t u32Signature = *(uint32_t const *)pvSubSect;
899 RTDBGMODCV_CHECK_RET_BF(u32Signature == RTCVSYMBOLS_SIGNATURE_CV4 || u32Signature == RTCVSYMBOLS_SIGNATURE_CV8,
900 ("%#x, expected %#x\n", u32Signature, RTCVSYMBOLS_SIGNATURE_CV4));
901 if (u32Signature == RTCVSYMBOLS_SIGNATURE_CV8)
902 return rtDbgModCvSsProcessV8SymTab(pThis, (uint8_t const *)pvSubSect + 4, cbSubSect - 4, 0);
903 return rtDbgModCvSsProcessV4PlusSymTab(pThis, (uint8_t const *)pvSubSect + 4, cbSubSect - 4, 0);
904}
905
906
907/** @callback_method_impl{FNDBGMODCVSUBSECTCALLBACK,
908 * Parses kCvSst_SrcModule adding line numbers it finds to the container.}
909 */
910static DECLCALLBACK(int)
911rtDbgModCvSs_SrcModule(PRTDBGMODCV pThis, void const *pvSubSect, size_t cbSubSect, PCRTCVDIRENT32 pDirEnt)
912{
913 Log(("rtDbgModCvSs_SrcModule: uCurStyle=%#x\n%.*Rhxd\n", pThis->uCurStyle, cbSubSect, pvSubSect));
914
915 /* Check the header. */
916 PCRTCVSRCMODULE pHdr = (PCRTCVSRCMODULE)pvSubSect;
917 AssertReturn(cbSubSect >= RT_UOFFSETOF(RTCVSRCMODULE, aoffSrcFiles), VERR_CV_BAD_FORMAT);
918 size_t cbHdr = sizeof(RTCVSRCMODULE)
919 + pHdr->cFiles * sizeof(uint32_t)
920 + pHdr->cSegs * sizeof(uint32_t) * 2
921 + pHdr->cSegs * sizeof(uint16_t);
922 Log2(("RTDbgModCv: SrcModule: cFiles=%u cSegs=%u\n", pHdr->cFiles, pHdr->cFiles));
923 RTDBGMODCV_CHECK_RET_BF(cbSubSect >= cbHdr, ("cbSubSect=%#x cbHdr=%zx\n", cbSubSect, cbHdr));
924 if (LogIs2Enabled())
925 {
926 for (uint32_t i = 0; i < pHdr->cFiles; i++)
927 Log2(("RTDbgModCv: source file #%u: %#x\n", i, pHdr->aoffSrcFiles[i]));
928 PCRTCVSRCRANGE paSegRanges = (PCRTCVSRCRANGE)&pHdr->aoffSrcFiles[pHdr->cFiles];
929 uint16_t const *paidxSegs = (uint16_t const *)&paSegRanges[pHdr->cSegs];
930 for (uint32_t i = 0; i < pHdr->cSegs; i++)
931 Log2(("RTDbgModCv: seg #%u: %#010x-%#010x\n", paidxSegs[i], paSegRanges[i].offStart, paSegRanges[i].offEnd));
932 }
933
934 /*
935 * Work over the source files.
936 */
937 for (uint32_t i = 0; i < pHdr->cFiles; i++)
938 {
939 uint32_t const offSrcFile = pHdr->aoffSrcFiles[i];
940 RTDBGMODCV_CHECK_RET_BF(cbSubSect - RT_UOFFSETOF(RTCVSRCFILE, aoffSrcLines) >= offSrcFile,
941 ("cbSubSect=%#x (- %#x) aoffSrcFiles[%u]=%#x\n",
942 cbSubSect, RT_UOFFSETOF(RTCVSRCFILE, aoffSrcLines), i, offSrcFile));
943 PCRTCVSRCFILE pSrcFile = (PCRTCVSRCFILE)((uint8_t const *)pvSubSect + offSrcFile);
944 size_t cbSrcFileHdr = RT_UOFFSETOF(RTCVSRCFILE, aoffSrcLines[pSrcFile->cSegs])
945 + sizeof(RTCVSRCRANGE) * pSrcFile->cSegs
946 + sizeof(uint8_t);
947 RTDBGMODCV_CHECK_RET_BF(cbSubSect >= offSrcFile + cbSrcFileHdr && cbSubSect > cbSrcFileHdr,
948 ("cbSubSect=%#x aoffSrcFiles[%u]=%#x cbSrcFileHdr=%#x\n", cbSubSect, offSrcFile, i, cbSrcFileHdr));
949 PCRTCVSRCRANGE paSegRanges = (PCRTCVSRCRANGE)&pSrcFile->aoffSrcLines[pSrcFile->cSegs];
950 uint8_t const *pcchName = (uint8_t const *)&paSegRanges[pSrcFile->cSegs]; /** @todo TIS NB09 docs say 16-bit length... */
951 const char *pchName = (const char *)(pcchName + 1);
952 RTDBGMODCV_CHECK_RET_BF(cbSubSect >= offSrcFile + cbSrcFileHdr + *pcchName,
953 ("cbSubSect=%#x offSrcFile=%#x cbSubSect=%#x *pcchName=%#x\n",
954 cbSubSect, offSrcFile, cbSubSect, *pcchName));
955 Log2(("RTDbgModCv: source file #%u/%#x: cSegs=%#x '%.*s'\n", i, offSrcFile, pSrcFile->cSegs, *pcchName, pchName));
956 const char *pszName = rtDbgModCvAddSanitizedStringToCache(pchName, *pcchName);
957
958 /*
959 * Work the segments this source file contributes code to.
960 */
961 for (uint32_t iSeg = 0; iSeg < pSrcFile->cSegs; iSeg++)
962 {
963 uint32_t const offSrcLine = pSrcFile->aoffSrcLines[iSeg];
964 RTDBGMODCV_CHECK_RET_BF(cbSubSect - RT_UOFFSETOF(RTCVSRCLINE, aoffLines) >= offSrcLine,
965 ("cbSubSect=%#x (- %#x) aoffSrcFiles[%u]=%#x\n",
966 cbSubSect, RT_UOFFSETOF(RTCVSRCLINE, aoffLines), iSeg, offSrcLine));
967 PCRTCVSRCLINE pSrcLine = (PCRTCVSRCLINE)((uint8_t const *)pvSubSect + offSrcLine);
968 size_t cbSrcLine = RT_UOFFSETOF(RTCVSRCLINE, aoffLines[pSrcLine->cPairs])
969 + pSrcLine->cPairs * sizeof(uint16_t);
970 RTDBGMODCV_CHECK_RET_BF(cbSubSect >= offSrcLine + cbSrcLine,
971 ("cbSubSect=%#x aoffSrcFiles[%u]=%#x cbSrcLine=%#x\n",
972 cbSubSect, iSeg, offSrcLine, cbSrcLine));
973 uint16_t const *paiLines = (uint16_t const *)&pSrcLine->aoffLines[pSrcLine->cPairs];
974 Log2(("RTDbgModCv: seg #%u, %u pairs (off %#x)\n", pSrcLine->idxSeg, pSrcLine->cPairs, offSrcLine));
975 for (uint32_t iPair = 0; iPair < pSrcLine->cPairs; iPair++)
976 {
977
978 uint32_t idxSeg = pSrcLine->idxSeg;
979 uint64_t off = pSrcLine->aoffLines[iPair];
980 int rc = rtDbgModCvAdjustSegAndOffset(pThis, &idxSeg, &off);
981 if (RT_SUCCESS(rc))
982 rc = RTDbgModLineAdd(pThis->hCnt, pszName, paiLines[iPair], idxSeg, off, NULL);
983 if (RT_SUCCESS(rc))
984 Log3(("RTDbgModCv: %#x:%#010llx %0u\n", idxSeg, off, paiLines[iPair]));
985 /* Note! Wlink produces the sstSrcModule subsections from LINNUM records, however the
986 CVGenLines() function assumes there is only one segment contributing to the
987 line numbers. So, when we do assembly that jumps between segments, it emits
988 the wrong addresses for some line numbers and we end up here, typically with
989 VERR_DBG_ADDRESS_CONFLICT. */
990 else
991 Log(( "RTDbgModCv: %#x:%#010llx %0u - rc=%Rrc!! (org: idxSeg=%#x off=%#x)\n",
992 idxSeg, off, paiLines[iPair], rc, pSrcLine->idxSeg, pSrcLine->aoffLines[iPair]));
993 }
994 }
995 }
996
997 return VINF_SUCCESS;
998}
999
1000
1001static int rtDbgModCvLoadSegmentMap(PRTDBGMODCV pThis)
1002{
1003 /*
1004 * Search for the segment map and segment names. They will be at the end of the directory.
1005 */
1006 uint32_t iSegMap = UINT32_MAX;
1007 uint32_t iSegNames = UINT32_MAX;
1008 uint32_t i = pThis->cDirEnts;
1009 while (i-- > 0)
1010 {
1011 if ( pThis->paDirEnts[i].iMod != 0xffff
1012 && pThis->paDirEnts[i].iMod != 0x0000)
1013 break;
1014 if (pThis->paDirEnts[i].uSubSectType == kCvSst_SegMap)
1015 iSegMap = i;
1016 else if (pThis->paDirEnts[i].uSubSectType == kCvSst_SegName)
1017 iSegNames = i;
1018 }
1019 if (iSegMap == UINT32_MAX)
1020 {
1021 Log(("RTDbgModCv: No segment map present, using segment indexes as is then...\n"));
1022 return VINF_SUCCESS;
1023 }
1024 RTDBGMODCV_CHECK_RET_BF(pThis->paDirEnts[iSegMap].cb >= sizeof(RTCVSEGMAPHDR),
1025 ("Bad sstSegMap entry: cb=%#x\n", pThis->paDirEnts[iSegMap].cb));
1026 RTDBGMODCV_CHECK_NOMSG_RET_BF(iSegNames == UINT32_MAX || pThis->paDirEnts[iSegNames].cb > 0);
1027
1028 /*
1029 * Read them into memory.
1030 */
1031 int rc = rtDbgModCvReadAtAlloc(pThis, pThis->paDirEnts[iSegMap].off, (void **)&pThis->pSegMap,
1032 pThis->paDirEnts[iSegMap].cb);
1033 if (iSegNames != UINT32_MAX && RT_SUCCESS(rc))
1034 {
1035 pThis->cbSegNames = pThis->paDirEnts[iSegNames].cb;
1036 rc = rtDbgModCvReadAtAlloc(pThis, pThis->paDirEnts[iSegNames].off, (void **)&pThis->pszzSegNames,
1037 pThis->paDirEnts[iSegNames].cb);
1038 }
1039 if (RT_FAILURE(rc))
1040 return rc;
1041 RTDBGMODCV_CHECK_NOMSG_RET_BF(!pThis->pszzSegNames || !pThis->pszzSegNames[pThis->cbSegNames - 1]); /* must be terminated */
1042
1043 /* Use local pointers to avoid lots of indirection and typing. */
1044 PCRTCVSEGMAPHDR pHdr = &pThis->pSegMap->Hdr;
1045 PRTCVSEGMAPDESC paDescs = &pThis->pSegMap->aDescs[0];
1046
1047 /*
1048 * If there are only logical segments, assume a direct mapping.
1049 * PE images, like the NT4 kernel, does it like this.
1050 */
1051 bool const fNoGroups = pHdr->cSegs == pHdr->cLogSegs;
1052
1053 /*
1054 * The PE image has an extra section/segment for the headers, the others
1055 * doesn't. PE images doesn't have DOS frames. So, figure the image type now.
1056 */
1057 RTLDRFMT enmImgFmt = RTLDRFMT_INVALID;
1058 if (pThis->pMod->pImgVt)
1059 enmImgFmt = pThis->pMod->pImgVt->pfnGetFormat(pThis->pMod);
1060
1061 /*
1062 * Validate and display it all.
1063 */
1064 Log2(("RTDbgModCv: SegMap: cSegs=%#x cLogSegs=%#x (cbSegNames=%#x)\n", pHdr->cSegs, pHdr->cLogSegs, pThis->cbSegNames));
1065 RTDBGMODCV_CHECK_RET_BF(pThis->paDirEnts[iSegMap].cb >= sizeof(*pHdr) + pHdr->cSegs * sizeof(paDescs[0]),
1066 ("SegMap is out of bounds: cbSubSect=%#x cSegs=%#x\n", pThis->paDirEnts[iSegMap].cb, pHdr->cSegs));
1067 RTDBGMODCV_CHECK_NOMSG_RET_BF(pHdr->cSegs >= pHdr->cLogSegs);
1068
1069 Log2(("Logical segment descriptors: %u\n", pHdr->cLogSegs));
1070
1071 bool fHaveDosFrames = false;
1072 for (i = 0; i < pHdr->cSegs; i++)
1073 {
1074 if (i == pHdr->cLogSegs)
1075 Log2(("Group/Physical descriptors: %u\n", pHdr->cSegs - pHdr->cLogSegs));
1076 uint16_t idx = i < pHdr->cLogSegs ? i : i - pHdr->cLogSegs;
1077 char szFlags[16];
1078 memset(szFlags, '-', sizeof(szFlags));
1079 if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_READ)
1080 szFlags[0] = 'R';
1081 if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_WRITE)
1082 szFlags[1] = 'W';
1083 if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_EXECUTE)
1084 szFlags[2] = 'X';
1085 if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_32BIT)
1086 szFlags[3] = '3', szFlags[4] = '2';
1087 if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_SEL)
1088 szFlags[5] = 'S';
1089 if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_ABS)
1090 szFlags[6] = 'A';
1091 if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_GROUP)
1092 szFlags[7] = 'G';
1093 szFlags[8] = '\0';
1094 if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_RESERVED)
1095 szFlags[8] = '!', szFlags[9] = '\0';
1096 Log2((" #%02u: %#010x LB %#010x flags=%#06x ovl=%#06x group=%#06x frame=%#06x iSegName=%#06x iClassName=%#06x %s\n",
1097 idx, paDescs[i].off, paDescs[i].cb, paDescs[i].fFlags, paDescs[i].iOverlay, paDescs[i].iGroup,
1098 paDescs[i].iFrame, paDescs[i].offSegName, paDescs[i].offClassName, szFlags));
1099
1100 RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].offSegName == UINT16_MAX || paDescs[i].offSegName < pThis->cbSegNames);
1101 RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].offClassName == UINT16_MAX || paDescs[i].offClassName < pThis->cbSegNames);
1102 const char *pszName = paDescs[i].offSegName != UINT16_MAX
1103 ? pThis->pszzSegNames + paDescs[i].offSegName
1104 : NULL;
1105 const char *pszClass = paDescs[i].offClassName != UINT16_MAX
1106 ? pThis->pszzSegNames + paDescs[i].offClassName
1107 : NULL;
1108 if (pszName || pszClass)
1109 Log2((" pszName=%s pszClass=%s\n", pszName, pszClass));
1110
1111 /* Validate the group link. */
1112 RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].iGroup == 0 || !(paDescs[i].fFlags & RTCVSEGMAPDESC_F_GROUP));
1113 RTDBGMODCV_CHECK_NOMSG_RET_BF( paDescs[i].iGroup == 0
1114 || ( paDescs[i].iGroup >= pHdr->cLogSegs
1115 && paDescs[i].iGroup < pHdr->cSegs));
1116 RTDBGMODCV_CHECK_NOMSG_RET_BF( paDescs[i].iGroup == 0
1117 || (paDescs[paDescs[i].iGroup].fFlags & RTCVSEGMAPDESC_F_GROUP));
1118 RTDBGMODCV_CHECK_NOMSG_RET_BF(!(paDescs[i].fFlags & RTCVSEGMAPDESC_F_GROUP) || paDescs[i].off == 0); /* assumed below */
1119
1120 if (fNoGroups)
1121 {
1122 RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].iGroup == 0);
1123 if ( !fHaveDosFrames
1124 && paDescs[i].iFrame != 0
1125 && (paDescs[i].fFlags & (RTCVSEGMAPDESC_F_SEL | RTCVSEGMAPDESC_F_ABS))
1126 && paDescs[i].iOverlay == 0
1127 && enmImgFmt != RTLDRFMT_PE
1128 && pThis->enmType != RTCVFILETYPE_DBG)
1129 fHaveDosFrames = true; /* BIOS, only groups with frames. */
1130 }
1131 }
1132
1133 /*
1134 * Further valiations based on fHaveDosFrames or not.
1135 */
1136 if (fNoGroups)
1137 {
1138 if (fHaveDosFrames)
1139 for (i = 0; i < pHdr->cSegs; i++)
1140 {
1141 RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].iOverlay == 0);
1142 RTDBGMODCV_CHECK_NOMSG_RET_BF( (paDescs[i].fFlags & (RTCVSEGMAPDESC_F_SEL | RTCVSEGMAPDESC_F_ABS))
1143 == RTCVSEGMAPDESC_F_SEL
1144 || (paDescs[i].fFlags & (RTCVSEGMAPDESC_F_SEL | RTCVSEGMAPDESC_F_ABS))
1145 == RTCVSEGMAPDESC_F_ABS);
1146 RTDBGMODCV_CHECK_NOMSG_RET_BF(!(paDescs[i].fFlags & RTCVSEGMAPDESC_F_ABS));
1147 }
1148 else
1149 for (i = 0; i < pHdr->cSegs; i++)
1150 RTDBGMODCV_CHECK_NOMSG_RET_BF(paDescs[i].off == 0);
1151 }
1152
1153 /*
1154 * Modify the groups index to be the loader segment index instead, also
1155 * add the segments to the container if we haven't done that already.
1156 */
1157
1158 /* Guess work: Group can be implicit if used. Observed Visual C++ v1.5,
1159 omitting the CODE group. */
1160 const char *pszGroup0 = NULL;
1161 uint64_t cbGroup0 = 0;
1162 if (!fNoGroups && !fHaveDosFrames)
1163 {
1164 for (i = 0; i < pHdr->cSegs; i++)
1165 if ( !(paDescs[i].fFlags & (RTCVSEGMAPDESC_F_GROUP | RTCVSEGMAPDESC_F_ABS))
1166 && paDescs[i].iGroup == 0)
1167 {
1168 if (pszGroup0 == NULL && paDescs[i].offClassName != UINT16_MAX)
1169 pszGroup0 = pThis->pszzSegNames + paDescs[i].offClassName;
1170 uint64_t offEnd = (uint64_t)paDescs[i].off + paDescs[i].cb;
1171 if (offEnd > cbGroup0)
1172 cbGroup0 = offEnd;
1173 }
1174 }
1175
1176 /* Add the segments.
1177 Note! The RVAs derived from this exercise are all wrong. :-/
1178 Note! We don't have an image loader, so we cannot add any fake sections. */
1179 /** @todo Try see if we can figure something out from the frame value later. */
1180 if (!pThis->fHaveLoadedSegments)
1181 {
1182 uint16_t iSeg = 0;
1183 if (!fHaveDosFrames)
1184 {
1185 Assert(!pThis->pMod->pImgVt); Assert(pThis->enmType != RTCVFILETYPE_DBG);
1186 uint64_t uRva = 0;
1187 if (cbGroup0 && !fNoGroups)
1188 {
1189 rc = RTDbgModSegmentAdd(pThis->hCnt, 0, cbGroup0, pszGroup0 ? pszGroup0 : "Seg00", 0 /*fFlags*/, NULL);
1190 uRva += cbGroup0;
1191 iSeg++;
1192 }
1193
1194 for (i = 0; RT_SUCCESS(rc) && i < pHdr->cSegs; i++)
1195 if ((paDescs[i].fFlags & RTCVSEGMAPDESC_F_GROUP) || fNoGroups)
1196 {
1197 char szName[16];
1198 char *pszName = szName;
1199 if (paDescs[i].offSegName != UINT16_MAX)
1200 pszName = pThis->pszzSegNames + paDescs[i].offSegName;
1201 else
1202 RTStrPrintf(szName, sizeof(szName), "Seg%02u", iSeg);
1203 rc = RTDbgModSegmentAdd(pThis->hCnt, uRva, paDescs[i].cb, pszName, 0 /*fFlags*/, NULL);
1204 uRva += paDescs[i].cb;
1205 iSeg++;
1206 }
1207 }
1208 else
1209 {
1210 /* The map is not sorted by RVA, very annoying, but I'm countering
1211 by being lazy and slow about it. :-) Btw. this is the BIOS case. */
1212 Assert(fNoGroups);
1213#if 1 /** @todo need more inputs */
1214
1215 /* Figure image base address. */
1216 uint64_t uImageBase = UINT64_MAX;
1217 for (i = 0; RT_SUCCESS(rc) && i < pHdr->cSegs; i++)
1218 {
1219 uint64_t uAddr = (uint64_t)paDescs[i].off + ((uint32_t)paDescs[i].iFrame << 4);
1220 if (uAddr < uImageBase)
1221 uImageBase = uAddr;
1222 }
1223
1224 /* Add the segments. */
1225 uint64_t uMinAddr = uImageBase;
1226 for (i = 0; RT_SUCCESS(rc) && i < pHdr->cSegs; i++)
1227 {
1228 /* Figure out the next one. */
1229 uint16_t cOverlaps = 0;
1230 uint16_t iBest = UINT16_MAX;
1231 uint64_t uBestAddr = UINT64_MAX;
1232 for (uint16_t j = 0; j < pHdr->cSegs; j++)
1233 {
1234 uint64_t uAddr = (uint64_t)paDescs[j].off + ((uint32_t)paDescs[j].iFrame << 4);
1235 if (uAddr >= uMinAddr && uAddr < uBestAddr)
1236 {
1237 uBestAddr = uAddr;
1238 iBest = j;
1239 }
1240 else if (uAddr == uBestAddr)
1241 {
1242 cOverlaps++;
1243 if (paDescs[j].cb > paDescs[iBest].cb)
1244 {
1245 uBestAddr = uAddr;
1246 iBest = j;
1247 }
1248 }
1249 }
1250 if (iBest == UINT16_MAX && RT_SUCCESS(rc))
1251 {
1252 rc = VERR_CV_IPE;
1253 break;
1254 }
1255
1256 /* Add it. */
1257 char szName[16];
1258 char *pszName = szName;
1259 if (paDescs[iBest].offSegName != UINT16_MAX)
1260 pszName = pThis->pszzSegNames + paDescs[iBest].offSegName;
1261 else
1262 RTStrPrintf(szName, sizeof(szName), "Seg%02u", iSeg);
1263 RTDBGSEGIDX idxDbgSeg = NIL_RTDBGSEGIDX;
1264 rc = RTDbgModSegmentAdd(pThis->hCnt, uBestAddr - uImageBase, paDescs[iBest].cb, pszName, 0 /*fFlags*/, &idxDbgSeg);
1265 Log(("CV: %#010x LB %#010x %s uRVA=%#010x iBest=%u cOverlaps=%u [idxDbgSeg=%#x iSeg=%#x]\n",
1266 uBestAddr, paDescs[iBest].cb, szName, uBestAddr - uImageBase, iBest, cOverlaps, idxDbgSeg, idxDbgSeg));
1267
1268 /* Update translations. */
1269 paDescs[iBest].iGroup = iSeg;
1270 if (cOverlaps > 0)
1271 {
1272 for (uint16_t j = 0; j < pHdr->cSegs; j++)
1273 if ((uint64_t)paDescs[j].off + ((uint32_t)paDescs[j].iFrame << 4) == uBestAddr)
1274 paDescs[iBest].iGroup = iSeg;
1275 i += cOverlaps;
1276 }
1277
1278 /* Advance. */
1279 uMinAddr = uBestAddr + 1;
1280 iSeg++;
1281 }
1282
1283 pThis->fHaveDosFrames = true;
1284#else
1285 uint32_t iFrameFirst = UINT32_MAX;
1286 uint16_t iSeg = 0;
1287 uint32_t iFrameMin = 0;
1288 do
1289 {
1290 /* Find next frame. */
1291 uint32_t iFrame = UINT32_MAX;
1292 for (uint16_t j = 0; j < pHdr->cSegs; j++)
1293 if (paDescs[j].iFrame >= iFrameMin && paDescs[j].iFrame < iFrame)
1294 iFrame = paDescs[j].iFrame;
1295 if (iFrame == UINT32_MAX)
1296 break;
1297
1298 /* Figure the frame span. */
1299 uint32_t offFirst = UINT32_MAX;
1300 uint64_t offEnd = 0;
1301 for (uint16_t j = 0; j < pHdr->cSegs; j++)
1302 if (paDescs[j].iFrame == iFrame)
1303 {
1304 uint64_t offThisEnd = paDescs[j].off + paDescs[j].cb;
1305 if (offThisEnd > offEnd)
1306 offEnd = offThisEnd;
1307 if (paDescs[j].off < offFirst)
1308 offFirst = paDescs[j].off;
1309 }
1310
1311 if (offFirst < offEnd)
1312 {
1313 /* Add it. */
1314 char szName[16];
1315 RTStrPrintf(szName, sizeof(szName), "Frame_%04x", iFrame);
1316 Log(("CV: %s offEnd=%#x offFirst=%#x\n", szName, offEnd, offFirst));
1317 if (iFrameFirst == UINT32_MAX)
1318 iFrameFirst = iFrame;
1319 rc = RTDbgModSegmentAdd(pThis->hCnt, (iFrame - iFrameFirst) << 4, offEnd, szName, 0 /*fFlags*/, NULL);
1320
1321 /* Translation updates. */
1322 for (uint16_t j = 0; j < pHdr->cSegs; j++)
1323 if (paDescs[j].iFrame == iFrame)
1324 {
1325 paDescs[j].iGroup = iSeg;
1326 paDescs[j].off = 0;
1327 paDescs[j].cb = offEnd > UINT32_MAX ? UINT32_MAX : (uint32_t)offEnd;
1328 }
1329
1330 iSeg++;
1331 }
1332
1333 iFrameMin = iFrame + 1;
1334 } while (RT_SUCCESS(rc));
1335#endif
1336 }
1337
1338 if (RT_FAILURE(rc))
1339 {
1340 Log(("RTDbgModCv: %Rrc while adding segments from SegMap\n", rc));
1341 return rc;
1342 }
1343
1344 pThis->fHaveLoadedSegments = true;
1345
1346 /* Skip the stuff below if we have DOS frames since we did it all above. */
1347 if (fHaveDosFrames)
1348 return VINF_SUCCESS;
1349 }
1350
1351 /* Pass one: Fixate the group segment indexes. */
1352 uint16_t iSeg0 = enmImgFmt == RTLDRFMT_PE || pThis->enmType == RTCVFILETYPE_DBG ? 1 : 0;
1353 uint16_t iSeg = iSeg0 + (cbGroup0 > 0); /** @todo probably wrong... */
1354 for (i = 0; i < pHdr->cSegs; i++)
1355 if (paDescs[i].fFlags & RTCVSEGMAPDESC_F_ABS)
1356 paDescs[i].iGroup = (uint16_t)RTDBGSEGIDX_ABS;
1357 else if ((paDescs[i].fFlags & RTCVSEGMAPDESC_F_GROUP) || fNoGroups)
1358 paDescs[i].iGroup = iSeg++;
1359
1360 /* Pass two: Resolve group references in to segment indexes. */
1361 Log2(("Mapped segments (both kinds):\n"));
1362 for (i = 0; i < pHdr->cSegs; i++)
1363 {
1364 if (!fNoGroups && !(paDescs[i].fFlags & (RTCVSEGMAPDESC_F_GROUP | RTCVSEGMAPDESC_F_ABS)))
1365 paDescs[i].iGroup = paDescs[i].iGroup == 0 ? iSeg0 : paDescs[paDescs[i].iGroup].iGroup;
1366
1367 Log2((" #%02u: %#010x LB %#010x -> %#06x (flags=%#06x ovl=%#06x frame=%#06x)\n",
1368 i, paDescs[i].off, paDescs[i].cb, paDescs[i].iGroup,
1369 paDescs[i].fFlags, paDescs[i].iOverlay, paDescs[i].iFrame));
1370 }
1371
1372 return VINF_SUCCESS;
1373}
1374
1375
1376/**
1377 * Loads the directory into memory (RTDBGMODCV::paDirEnts and
1378 * RTDBGMODCV::cDirEnts).
1379 *
1380 * Converting old format version into the newer format to simplifying the code
1381 * using the directory.
1382 *
1383 *
1384 * @returns IPRT status code. (May leave with paDirEnts allocated on failure.)
1385 * @param pThis The CV reader instance.
1386 */
1387static int rtDbgModCvLoadDirectory(PRTDBGMODCV pThis)
1388{
1389 /*
1390 * Read in the CV directory.
1391 */
1392 int rc;
1393 if ( pThis->u32CvMagic == RTCVHDR_MAGIC_NB00
1394 || pThis->u32CvMagic == RTCVHDR_MAGIC_NB02)
1395 {
1396 /*
1397 * 16-bit type.
1398 */
1399 RTCVDIRHDR16 DirHdr;
1400 rc = rtDbgModCvReadAt(pThis, pThis->offDir, &DirHdr, sizeof(DirHdr));
1401 if (RT_SUCCESS(rc))
1402 {
1403 if (DirHdr.cEntries > 2 && DirHdr.cEntries < _64K - 32U)
1404 {
1405 pThis->cDirEnts = DirHdr.cEntries;
1406 pThis->paDirEnts = (PRTCVDIRENT32)RTMemAlloc(DirHdr.cEntries * sizeof(pThis->paDirEnts[0]));
1407 if (pThis->paDirEnts)
1408 {
1409 rc = rtDbgModCvReadAt(pThis, pThis->offDir + sizeof(DirHdr),
1410 pThis->paDirEnts, DirHdr.cEntries * sizeof(RTCVDIRENT16));
1411 if (RT_SUCCESS(rc))
1412 {
1413 /* Convert the entries (from the end). */
1414 uint32_t cLeft = DirHdr.cEntries;
1415 RTCVDIRENT32 volatile *pDst = pThis->paDirEnts + cLeft;
1416 RTCVDIRENT16 volatile *pSrc = (RTCVDIRENT16 volatile *)pThis->paDirEnts + cLeft;
1417 while (cLeft--)
1418 {
1419 pDst--;
1420 pSrc--;
1421
1422 pDst->cb = pSrc->cb;
1423 pDst->off = RT_MAKE_U32(pSrc->offLow, pSrc->offHigh);
1424 pDst->iMod = pSrc->iMod;
1425 pDst->uSubSectType = pSrc->uSubSectType;
1426 }
1427 }
1428 }
1429 else
1430 rc = VERR_NO_MEMORY;
1431 }
1432 else
1433 {
1434 Log(("Old CV directory count is out of considered valid range: %#x\n", DirHdr.cEntries));
1435 rc = VERR_CV_BAD_FORMAT;
1436 }
1437 }
1438 }
1439 else
1440 {
1441 /*
1442 * 32-bit type (reading too much for NB04 is no problem).
1443 */
1444 RTCVDIRHDR32EX DirHdr;
1445 rc = rtDbgModCvReadAt(pThis, pThis->offDir, &DirHdr, sizeof(DirHdr));
1446 if (RT_SUCCESS(rc))
1447 {
1448 if ( DirHdr.Core.cbHdr != sizeof(DirHdr.Core)
1449 && DirHdr.Core.cbHdr != sizeof(DirHdr))
1450 {
1451 Log(("Unexpected CV directory size: %#x\n", DirHdr.Core.cbHdr));
1452 rc = VERR_CV_BAD_FORMAT;
1453 }
1454 if ( DirHdr.Core.cbHdr == sizeof(DirHdr)
1455 && ( DirHdr.offNextDir != 0
1456 || DirHdr.fFlags != 0) )
1457 {
1458 Log(("Extended CV directory headers fields are not zero: fFlags=%#x offNextDir=%#x\n",
1459 DirHdr.fFlags, DirHdr.offNextDir));
1460 rc = VERR_CV_BAD_FORMAT;
1461 }
1462 if (DirHdr.Core.cbEntry != sizeof(RTCVDIRENT32))
1463 {
1464 Log(("Unexpected CV directory entry size: %#x (expected %#x)\n", DirHdr.Core.cbEntry, sizeof(RTCVDIRENT32)));
1465 rc = VERR_CV_BAD_FORMAT;
1466 }
1467 if (DirHdr.Core.cEntries < 2 || DirHdr.Core.cEntries >= _512K)
1468 {
1469 Log(("CV directory count is out of considered valid range: %#x\n", DirHdr.Core.cEntries));
1470 rc = VERR_CV_BAD_FORMAT;
1471 }
1472 if (RT_SUCCESS(rc))
1473 {
1474 pThis->cDirEnts = DirHdr.Core.cEntries;
1475 pThis->paDirEnts = (PRTCVDIRENT32)RTMemAlloc(DirHdr.Core.cEntries * sizeof(pThis->paDirEnts[0]));
1476 if (pThis->paDirEnts)
1477 rc = rtDbgModCvReadAt(pThis, pThis->offDir + DirHdr.Core.cbHdr,
1478 pThis->paDirEnts, DirHdr.Core.cEntries * sizeof(RTCVDIRENT32));
1479 else
1480 rc = VERR_NO_MEMORY;
1481 }
1482 }
1483 }
1484
1485 if (RT_SUCCESS(rc))
1486 {
1487 /*
1488 * Basic info validation and determining the directory ordering.
1489 */
1490 bool fWatcom = 0;
1491 uint16_t cGlobalMods = 0;
1492 uint16_t cNormalMods = 0;
1493 uint16_t iModLast = 0;
1494 uint32_t const cbDbgInfo = pThis->cbDbgInfo;
1495 uint32_t const cDirEnts = pThis->cDirEnts;
1496 Log2(("RTDbgModCv: %u (%#x) directory entries:\n", cDirEnts, cDirEnts));
1497 for (uint32_t i = 0; i < cDirEnts; i++)
1498 {
1499 PCRTCVDIRENT32 pDirEnt = &pThis->paDirEnts[i];
1500 Log2((" #%04u mod=%#06x sst=%#06x at %#010x LB %#07x %s\n",
1501 i, pDirEnt->iMod, pDirEnt->uSubSectType, pDirEnt->off, pDirEnt->cb,
1502 rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType)));
1503
1504 if ( pDirEnt->off >= cbDbgInfo
1505 || pDirEnt->cb >= cbDbgInfo
1506 || pDirEnt->off + pDirEnt->cb > cbDbgInfo)
1507 {
1508 Log(("CV directory entry #%u is out of bounds: %#x LB %#x, max %#x\n", i, pDirEnt->off, pDirEnt->cb, cbDbgInfo));
1509 rc = VERR_CV_BAD_FORMAT;
1510 }
1511 if ( pDirEnt->iMod == 0
1512 && pThis->u32CvMagic != RTCVHDR_MAGIC_NB04
1513 && pThis->u32CvMagic != RTCVHDR_MAGIC_NB02
1514 && pThis->u32CvMagic != RTCVHDR_MAGIC_NB00)
1515 {
1516 Log(("CV directory entry #%u uses module index 0 (uSubSectType=%#x)\n", i, pDirEnt->uSubSectType));
1517 rc = VERR_CV_BAD_FORMAT;
1518 }
1519 if (pDirEnt->iMod == 0 || pDirEnt->iMod == 0xffff)
1520 cGlobalMods++;
1521 else
1522 {
1523 if (pDirEnt->iMod > iModLast)
1524 {
1525 if ( pDirEnt->uSubSectType != kCvSst_Module
1526 && pDirEnt->uSubSectType != kCvSst_OldModule)
1527 {
1528 Log(("CV directory entry #%u: expected module subsection first, found %s (%#x)\n",
1529 i, rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType), pDirEnt->uSubSectType));
1530 rc = VERR_CV_BAD_FORMAT;
1531 }
1532 if (pDirEnt->iMod != iModLast + 1)
1533 {
1534 Log(("CV directory entry #%u: skips from mod %#x to %#x modules\n", i, iModLast, pDirEnt->iMod));
1535 rc = VERR_CV_BAD_FORMAT;
1536 }
1537 iModLast = pDirEnt->iMod;
1538 }
1539 else if (pDirEnt->iMod < iModLast)
1540 fWatcom = true;
1541 cNormalMods++;
1542 }
1543 }
1544 if (cGlobalMods == 0)
1545 {
1546 Log(("CV directory contains no global modules\n"));
1547 rc = VERR_CV_BAD_FORMAT;
1548 }
1549 if (RT_SUCCESS(rc))
1550 {
1551 if (fWatcom)
1552 pThis->enmDirOrder = RTCVDIRORDER_BY_SST_MOD;
1553 else if (pThis->paDirEnts[0].iMod == 0)
1554 pThis->enmDirOrder = RTCVDIRORDER_BY_MOD_0;
1555 else
1556 pThis->enmDirOrder = RTCVDIRORDER_BY_MOD;
1557 Log(("CV dir stats: %u total, %u normal, %u special, iModLast=%#x (%u), enmDirOrder=%d\n",
1558 cDirEnts, cNormalMods, cGlobalMods, iModLast, iModLast, pThis->enmDirOrder));
1559
1560
1561 /*
1562 * Validate the directory ordering.
1563 */
1564 uint16_t i = 0;
1565
1566 /* Old style with special modules up front. */
1567 if (pThis->enmDirOrder == RTCVDIRORDER_BY_MOD_0)
1568 while (i < cGlobalMods)
1569 {
1570 if (pThis->paDirEnts[i].iMod != 0)
1571 {
1572 Log(("CV directory entry #%u: Expected iMod=%x instead of %x\n", i, 0, pThis->paDirEnts[i].iMod));
1573 rc = VERR_CV_BAD_FORMAT;
1574 }
1575 i++;
1576 }
1577
1578 /* Normal modules. */
1579 if (pThis->enmDirOrder != RTCVDIRORDER_BY_SST_MOD)
1580 {
1581 uint16_t iEndNormalMods = cNormalMods + (pThis->enmDirOrder == RTCVDIRORDER_BY_MOD_0 ? cGlobalMods : 0);
1582 while (i < iEndNormalMods)
1583 {
1584 if (pThis->paDirEnts[i].iMod == 0 || pThis->paDirEnts[i].iMod == 0xffff)
1585 {
1586 Log(("CV directory entry #%u: Unexpected global module entry.\n", i));
1587 rc = VERR_CV_BAD_FORMAT;
1588 }
1589 i++;
1590 }
1591 }
1592 else
1593 {
1594 uint32_t fSeen = RT_BIT_32(kCvSst_Module - kCvSst_Module)
1595 | RT_BIT_32(kCvSst_Libraries - kCvSst_Module)
1596 | RT_BIT_32(kCvSst_GlobalSym - kCvSst_Module)
1597 | RT_BIT_32(kCvSst_GlobalPub - kCvSst_Module)
1598 | RT_BIT_32(kCvSst_GlobalTypes - kCvSst_Module)
1599 | RT_BIT_32(kCvSst_SegName - kCvSst_Module)
1600 | RT_BIT_32(kCvSst_SegMap - kCvSst_Module)
1601 | RT_BIT_32(kCvSst_StaticSym - kCvSst_Module)
1602 | RT_BIT_32(kCvSst_FileIndex - kCvSst_Module)
1603 | RT_BIT_32(kCvSst_MPC - kCvSst_Module);
1604 uint16_t iMod = 0;
1605 uint16_t uSst = kCvSst_Module;
1606 while (i < cNormalMods)
1607 {
1608 PCRTCVDIRENT32 pDirEnt = &pThis->paDirEnts[i];
1609 if ( pDirEnt->iMod > iMod
1610 || pDirEnt->iMod == iMod) /* wlink subjected to MSVC 2010 /Z7 files with multiple .debug$S. */
1611 {
1612 if (pDirEnt->uSubSectType != uSst)
1613 {
1614 Log(("CV directory entry #%u: Expected %s (%#x), found %s (%#x).\n",
1615 i, rtDbgModCvGetSubSectionName(uSst), uSst,
1616 rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType), pDirEnt->uSubSectType));
1617 rc = VERR_CV_BAD_FORMAT;
1618 }
1619 }
1620 else
1621 {
1622 uint32_t iBit = pDirEnt->uSubSectType - kCvSst_Module;
1623 if (iBit >= 32U || (fSeen & RT_BIT_32(iBit)))
1624 {
1625 Log(("CV directory entry #%u: SST %s (%#x) has already been seen or is for globals.\n",
1626 i, rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType), pDirEnt->uSubSectType));
1627 rc = VERR_CV_BAD_FORMAT;
1628 }
1629 fSeen |= RT_BIT_32(iBit);
1630 }
1631
1632 uSst = pDirEnt->uSubSectType;
1633 iMod = pDirEnt->iMod;
1634 i++;
1635 }
1636 }
1637
1638 /* New style with special modules at the end. */
1639 if (pThis->enmDirOrder != RTCVDIRORDER_BY_MOD_0)
1640 while (i < cDirEnts)
1641 {
1642 if (pThis->paDirEnts[i].iMod != 0 && pThis->paDirEnts[i].iMod != 0xffff)
1643 {
1644 Log(("CV directory entry #%u: Expected global module entry, not %#x.\n", i,
1645 pThis->paDirEnts[i].iMod));
1646 rc = VERR_CV_BAD_FORMAT;
1647 }
1648 i++;
1649 }
1650 }
1651
1652 }
1653
1654 return rc;
1655}
1656
1657
1658static int rtDbgModCvLoadCodeViewInfo(PRTDBGMODCV pThis)
1659{
1660 /*
1661 * Load the directory, the segment map (if any) and then scan for segments
1662 * if necessary.
1663 */
1664 int rc = rtDbgModCvLoadDirectory(pThis);
1665 if (RT_SUCCESS(rc))
1666 rc = rtDbgModCvLoadSegmentMap(pThis);
1667 if (RT_SUCCESS(rc) && !pThis->fHaveLoadedSegments)
1668 {
1669 rc = VERR_CV_TODO; /** @todo Scan anything containing address, in particular sstSegMap and sstModule,
1670 * and reconstruct the segments from that information. */
1671 pThis->cbImage = 0x1000;
1672 rc = VINF_SUCCESS;
1673 }
1674
1675 /*
1676 * Process the directory.
1677 */
1678 for (uint32_t i = 0; RT_SUCCESS(rc) && i < pThis->cDirEnts; i++)
1679 {
1680 PCRTCVDIRENT32 pDirEnt = &pThis->paDirEnts[i];
1681 Log3(("Processing subsection %#u %s\n", i, rtDbgModCvGetSubSectionName(pDirEnt->uSubSectType)));
1682 PFNDBGMODCVSUBSECTCALLBACK pfnCallback = NULL;
1683 switch (pDirEnt->uSubSectType)
1684 {
1685 case kCvSst_GlobalPub:
1686 case kCvSst_GlobalSym:
1687 case kCvSst_StaticSym:
1688 pfnCallback = rtDbgModCvSs_GlobalPub_GlobalSym_StaticSym;
1689 break;
1690 case kCvSst_Module:
1691 pfnCallback = rtDbgModCvSs_Module;
1692 break;
1693 case kCvSst_PublicSym:
1694 case kCvSst_Symbols:
1695 case kCvSst_AlignSym:
1696 pfnCallback = rtDbgModCvSs_Symbols_PublicSym_AlignSym;
1697 break;
1698
1699 case kCvSst_OldModule:
1700 case kCvSst_OldPublic:
1701 case kCvSst_OldTypes:
1702 case kCvSst_OldSymbols:
1703 case kCvSst_OldSrcLines:
1704 case kCvSst_OldLibraries:
1705 case kCvSst_OldImports:
1706 case kCvSst_OldCompacted:
1707 case kCvSst_OldSrcLnSeg:
1708 case kCvSst_OldSrcLines3:
1709 /** @todo implement more. */
1710 break;
1711
1712 case kCvSst_Types:
1713 case kCvSst_Public:
1714 case kCvSst_SrcLnSeg:
1715 /** @todo implement more. */
1716 break;
1717 case kCvSst_SrcModule:
1718 pfnCallback = rtDbgModCvSs_SrcModule;
1719 break;
1720 case kCvSst_Libraries:
1721 case kCvSst_GlobalTypes:
1722 case kCvSst_MPC:
1723 case kCvSst_PreComp:
1724 case kCvSst_PreCompMap:
1725 case kCvSst_OffsetMap16:
1726 case kCvSst_OffsetMap32:
1727 case kCvSst_FileIndex:
1728
1729 default:
1730 /** @todo implement more. */
1731 break;
1732
1733 /* Skip because we've already processed them: */
1734 case kCvSst_SegMap:
1735 case kCvSst_SegName:
1736 pfnCallback = NULL;
1737 break;
1738 }
1739
1740 if (pfnCallback)
1741 {
1742 void *pvSubSect;
1743 rc = rtDbgModCvReadAtAlloc(pThis, pDirEnt->off, &pvSubSect, pDirEnt->cb);
1744 if (RT_SUCCESS(rc))
1745 {
1746 rc = pfnCallback(pThis, pvSubSect, pDirEnt->cb, pDirEnt);
1747 RTMemFree(pvSubSect);
1748 }
1749 }
1750 }
1751
1752 return rc;
1753}
1754
1755
1756/*
1757 *
1758 * COFF Debug Info Parsing.
1759 * COFF Debug Info Parsing.
1760 * COFF Debug Info Parsing.
1761 *
1762 */
1763
1764static const char *rtDbgModCvGetCoffStorageClassName(uint8_t bStorageClass)
1765{
1766 switch (bStorageClass)
1767 {
1768 case IMAGE_SYM_CLASS_END_OF_FUNCTION: return "END_OF_FUNCTION";
1769 case IMAGE_SYM_CLASS_NULL: return "NULL";
1770 case IMAGE_SYM_CLASS_AUTOMATIC: return "AUTOMATIC";
1771 case IMAGE_SYM_CLASS_EXTERNAL: return "EXTERNAL";
1772 case IMAGE_SYM_CLASS_STATIC: return "STATIC";
1773 case IMAGE_SYM_CLASS_REGISTER: return "REGISTER";
1774 case IMAGE_SYM_CLASS_EXTERNAL_DEF: return "EXTERNAL_DEF";
1775 case IMAGE_SYM_CLASS_LABEL: return "LABEL";
1776 case IMAGE_SYM_CLASS_UNDEFINED_LABEL: return "UNDEFINED_LABEL";
1777 case IMAGE_SYM_CLASS_MEMBER_OF_STRUCT: return "MEMBER_OF_STRUCT";
1778 case IMAGE_SYM_CLASS_ARGUMENT: return "ARGUMENT";
1779 case IMAGE_SYM_CLASS_STRUCT_TAG: return "STRUCT_TAG";
1780 case IMAGE_SYM_CLASS_MEMBER_OF_UNION: return "MEMBER_OF_UNION";
1781 case IMAGE_SYM_CLASS_UNION_TAG: return "UNION_TAG";
1782 case IMAGE_SYM_CLASS_TYPE_DEFINITION: return "TYPE_DEFINITION";
1783 case IMAGE_SYM_CLASS_UNDEFINED_STATIC: return "UNDEFINED_STATIC";
1784 case IMAGE_SYM_CLASS_ENUM_TAG: return "ENUM_TAG";
1785 case IMAGE_SYM_CLASS_MEMBER_OF_ENUM: return "MEMBER_OF_ENUM";
1786 case IMAGE_SYM_CLASS_REGISTER_PARAM: return "REGISTER_PARAM";
1787 case IMAGE_SYM_CLASS_BIT_FIELD: return "BIT_FIELD";
1788 case IMAGE_SYM_CLASS_FAR_EXTERNAL: return "FAR_EXTERNAL";
1789 case IMAGE_SYM_CLASS_BLOCK: return "BLOCK";
1790 case IMAGE_SYM_CLASS_FUNCTION: return "FUNCTION";
1791 case IMAGE_SYM_CLASS_END_OF_STRUCT: return "END_OF_STRUCT";
1792 case IMAGE_SYM_CLASS_FILE: return "FILE";
1793 case IMAGE_SYM_CLASS_SECTION: return "SECTION";
1794 case IMAGE_SYM_CLASS_WEAK_EXTERNAL: return "WEAK_EXTERNAL";
1795 case IMAGE_SYM_CLASS_CLR_TOKEN: return "CLR_TOKEN";
1796 }
1797
1798 static char s_szName[32];
1799 RTStrPrintf(s_szName, sizeof(s_szName), "Unknown%#04x", bStorageClass);
1800 return s_szName;
1801}
1802
1803
1804/**
1805 * Adds a chunk of COFF line numbers.
1806 *
1807 * @param pThis The COFF/CodeView reader instance.
1808 * @param pszFile The source file name.
1809 * @param iSection The section number.
1810 * @param paLines Pointer to the first line number table entry.
1811 * @param cLines The number of line number table entries to add.
1812 */
1813static void rtDbgModCvAddCoffLineNumbers(PRTDBGMODCV pThis, const char *pszFile, uint32_t iSection,
1814 PCIMAGE_LINENUMBER paLines, uint32_t cLines)
1815{
1816 Log4(("Adding %u line numbers in section #%u for %s\n", cLines, iSection, pszFile));
1817 PCIMAGE_LINENUMBER pCur = paLines;
1818 while (cLines-- > 0)
1819 {
1820 if (pCur->Linenumber)
1821 {
1822 int rc = RTDbgModLineAdd(pThis->hCnt, pszFile, pCur->Linenumber, RTDBGSEGIDX_RVA, pCur->Type.VirtualAddress, NULL);
1823 Log4((" %#010x: %u [%Rrc]\n", pCur->Type.VirtualAddress, pCur->Linenumber, rc));
1824 }
1825 pCur++;
1826 }
1827}
1828
1829
1830/**
1831 * Adds a COFF symbol.
1832 *
1833 * @returns IPRT status (ignored)
1834 * @param pThis The COFF/CodeView reader instance.
1835 * @param idxSeg IPRT RVA or ABS segment index indicator.
1836 * @param uValue The symbol value.
1837 * @param pszName The symbol name.
1838 */
1839static int rtDbgModCvAddCoffSymbol(PRTDBGMODCV pThis, uint32_t idxSeg, uint32_t uValue, const char *pszName)
1840{
1841 int rc = RTDbgModSymbolAdd(pThis->hCnt, pszName, idxSeg, uValue, 0, 0 /*fFlags*/, NULL);
1842 Log(("Symbol: %s:%08x %s [%Rrc]\n", idxSeg == RTDBGSEGIDX_RVA ? "rva" : "abs", uValue, pszName, rc));
1843 if (rc == VERR_DBG_ADDRESS_CONFLICT || rc == VERR_DBG_DUPLICATE_SYMBOL)
1844 rc = VINF_SUCCESS;
1845 return rc;
1846}
1847
1848
1849/**
1850 * Processes the COFF symbol table.
1851 *
1852 * @returns IPRT status code
1853 * @param pThis The COFF/CodeView reader instance.
1854 * @param paSymbols Pointer to the symbol table.
1855 * @param cSymbols The number of entries in the symbol table.
1856 * @param paLines Pointer to the line number table.
1857 * @param cLines The number of entires in the line number table.
1858 * @param pszzStrTab Pointer to the string table.
1859 * @param cbStrTab Size of the string table.
1860 */
1861static int rtDbgModCvProcessCoffSymbolTable(PRTDBGMODCV pThis,
1862 PCIMAGE_SYMBOL paSymbols, uint32_t cSymbols,
1863 PCIMAGE_LINENUMBER paLines, uint32_t cLines,
1864 const char *pszzStrTab, uint32_t cbStrTab)
1865{
1866 Log3(("Processing COFF symbol table with %#x symbols\n", cSymbols));
1867
1868 /*
1869 * Making some bold assumption that the line numbers for the section in
1870 * the file are allocated sequentially, we do multiple passes until we've
1871 * gathered them all.
1872 */
1873 int rc = VINF_SUCCESS;
1874 uint32_t cSections = 1;
1875 uint32_t iLineSect = 1;
1876 uint32_t iLine = 0;
1877 do
1878 {
1879 /*
1880 * Process the symbols.
1881 */
1882 char szShort[9];
1883 char szFile[RTPATH_MAX];
1884 uint32_t iSymbol = 0;
1885 szFile[0] = '\0';
1886 szShort[8] = '\0'; /* avoid having to terminate it all the time. */
1887
1888 while (iSymbol < cSymbols && RT_SUCCESS(rc))
1889 {
1890 /* Copy the symbol in and hope it works around the misalignment
1891 issues everywhere. */
1892 IMAGE_SYMBOL Sym;
1893 memcpy(&Sym, &paSymbols[iSymbol], sizeof(Sym));
1894 RTDBGMODCV_CHECK_NOMSG_RET_BF(Sym.NumberOfAuxSymbols < cSymbols);
1895
1896 /* Calc a zero terminated symbol name. */
1897 const char *pszName;
1898 if (Sym.N.Name.Short)
1899 pszName = (const char *)memcpy(szShort, &Sym.N, 8);
1900 else
1901 {
1902 RTDBGMODCV_CHECK_NOMSG_RET_BF(Sym.N.Name.Long < cbStrTab);
1903 pszName = pszzStrTab + Sym.N.Name.Long;
1904 }
1905
1906 /* Only log stuff and count sections the in the first pass.*/
1907 if (iLineSect == 1)
1908 {
1909 Log3(("%04x: s=%#06x v=%#010x t=%#06x a=%#04x c=%#04x (%s) name='%s'\n",
1910 iSymbol, Sym.SectionNumber, Sym.Value, Sym.Type, Sym.NumberOfAuxSymbols,
1911 Sym.StorageClass, rtDbgModCvGetCoffStorageClassName(Sym.StorageClass), pszName));
1912 if ((int16_t)cSections <= Sym.SectionNumber && Sym.SectionNumber > 0)
1913 cSections = Sym.SectionNumber + 1;
1914 }
1915
1916 /*
1917 * Use storage class to pick what we need (which isn't much because,
1918 * MS only provides a very restricted set of symbols).
1919 */
1920 IMAGE_AUX_SYMBOL Aux;
1921 switch (Sym.StorageClass)
1922 {
1923 case IMAGE_SYM_CLASS_NULL:
1924 /* a NOP */
1925 break;
1926
1927 case IMAGE_SYM_CLASS_FILE:
1928 {
1929 /* Change the current file name (for line numbers). Pretend
1930 ANSI and ISO-8859-1 are similar enough for out purposes... */
1931 RTDBGMODCV_CHECK_NOMSG_RET_BF(Sym.NumberOfAuxSymbols > 0);
1932 const char *pszFile = (const char *)&paSymbols[iSymbol + 1];
1933 char *pszDst = szFile;
1934 rc = RTLatin1ToUtf8Ex(pszFile, Sym.NumberOfAuxSymbols * sizeof(IMAGE_SYMBOL), &pszDst, sizeof(szFile), NULL);
1935 if (RT_FAILURE(rc))
1936 Log(("Error converting COFF filename: %Rrc\n", rc));
1937 else if (iLineSect == 1)
1938 Log3((" filename='%s'\n", szFile));
1939 break;
1940 }
1941
1942 case IMAGE_SYM_CLASS_STATIC:
1943 if ( Sym.NumberOfAuxSymbols == 1
1944 && ( iLineSect == 1
1945 || Sym.SectionNumber == (int32_t)iLineSect) )
1946 {
1947 memcpy(&Aux, &paSymbols[iSymbol + 1], sizeof(Aux));
1948 if (iLineSect == 1)
1949 Log3((" section: cb=%#010x #relocs=%#06x #lines=%#06x csum=%#x num=%#x sel=%x rvd=%u\n",
1950 Aux.Section.Length, Aux.Section.NumberOfRelocations,
1951 Aux.Section.NumberOfLinenumbers,
1952 Aux.Section.CheckSum,
1953 RT_MAKE_U32(Aux.Section.Number, Aux.Section.HighNumber),
1954 Aux.Section.Selection,
1955 Aux.Section.bReserved));
1956 if ( Sym.SectionNumber == (int32_t)iLineSect
1957 && Aux.Section.NumberOfLinenumbers > 0)
1958 {
1959 uint32_t cLinesToAdd = RT_MIN(Aux.Section.NumberOfLinenumbers, cLines - iLine);
1960 if (iLine < cLines && szFile[0])
1961 rtDbgModCvAddCoffLineNumbers(pThis, szFile, iLineSect, &paLines[iLine], cLinesToAdd);
1962 iLine += cLinesToAdd;
1963 }
1964 }
1965 /* Not so sure about the quality here, but might be useful. */
1966 else if ( iLineSect == 1
1967 && Sym.NumberOfAuxSymbols == 0
1968 && Sym.SectionNumber != IMAGE_SYM_UNDEFINED
1969 && Sym.SectionNumber != IMAGE_SYM_ABSOLUTE
1970 && Sym.SectionNumber != IMAGE_SYM_DEBUG
1971 && Sym.Value > 0
1972 && *pszName)
1973 rtDbgModCvAddCoffSymbol(pThis, RTDBGSEGIDX_RVA, Sym.Value, pszName);
1974 break;
1975
1976 case IMAGE_SYM_CLASS_EXTERNAL:
1977 /* Add functions (first pass only). */
1978 if ( iLineSect == 1
1979 && (ISFCN(Sym.Type) || Sym.Type == 0)
1980 && Sym.NumberOfAuxSymbols == 0
1981 && *pszName )
1982 {
1983 if (Sym.SectionNumber == IMAGE_SYM_ABSOLUTE)
1984 rtDbgModCvAddCoffSymbol(pThis, RTDBGSEGIDX_ABS, Sym.Value, pszName);
1985 else if ( Sym.SectionNumber != IMAGE_SYM_UNDEFINED
1986 && Sym.SectionNumber != IMAGE_SYM_DEBUG)
1987 rtDbgModCvAddCoffSymbol(pThis, RTDBGSEGIDX_RVA, Sym.Value, pszName);
1988 }
1989 break;
1990
1991 case IMAGE_SYM_CLASS_FUNCTION:
1992 /* Not sure this is really used. */
1993 break;
1994
1995 case IMAGE_SYM_CLASS_END_OF_FUNCTION:
1996 case IMAGE_SYM_CLASS_AUTOMATIC:
1997 case IMAGE_SYM_CLASS_REGISTER:
1998 case IMAGE_SYM_CLASS_EXTERNAL_DEF:
1999 case IMAGE_SYM_CLASS_LABEL:
2000 case IMAGE_SYM_CLASS_UNDEFINED_LABEL:
2001 case IMAGE_SYM_CLASS_MEMBER_OF_STRUCT:
2002 case IMAGE_SYM_CLASS_ARGUMENT:
2003 case IMAGE_SYM_CLASS_STRUCT_TAG:
2004 case IMAGE_SYM_CLASS_MEMBER_OF_UNION:
2005 case IMAGE_SYM_CLASS_UNION_TAG:
2006 case IMAGE_SYM_CLASS_TYPE_DEFINITION:
2007 case IMAGE_SYM_CLASS_UNDEFINED_STATIC:
2008 case IMAGE_SYM_CLASS_ENUM_TAG:
2009 case IMAGE_SYM_CLASS_MEMBER_OF_ENUM:
2010 case IMAGE_SYM_CLASS_REGISTER_PARAM:
2011 case IMAGE_SYM_CLASS_BIT_FIELD:
2012 case IMAGE_SYM_CLASS_FAR_EXTERNAL:
2013 case IMAGE_SYM_CLASS_BLOCK:
2014 case IMAGE_SYM_CLASS_END_OF_STRUCT:
2015 case IMAGE_SYM_CLASS_SECTION:
2016 case IMAGE_SYM_CLASS_WEAK_EXTERNAL:
2017 case IMAGE_SYM_CLASS_CLR_TOKEN:
2018 /* Not used by MS, I think. */
2019 break;
2020
2021 default:
2022 Log(("RTDbgCv: Unexpected COFF storage class %#x (%u)\n", Sym.StorageClass, Sym.StorageClass));
2023 break;
2024 }
2025
2026 /* next symbol */
2027 iSymbol += 1 + Sym.NumberOfAuxSymbols;
2028 }
2029
2030 /* Next section with line numbers. */
2031 iLineSect++;
2032 } while (iLine < cLines && iLineSect < cSections && RT_SUCCESS(rc));
2033
2034 return rc;
2035}
2036
2037
2038/**
2039 * Loads COFF debug information into the container.
2040 *
2041 * @returns IPRT status code.
2042 * @param pThis The COFF/CodeView debug reader instance.
2043 */
2044static int rtDbgModCvLoadCoffInfo(PRTDBGMODCV pThis)
2045{
2046 /*
2047 * Read the whole section into memory.
2048 * Note! Cannot use rtDbgModCvReadAt or rtDbgModCvReadAtAlloc here.
2049 */
2050 int rc;
2051 uint8_t *pbDbgSect = (uint8_t *)RTMemAlloc(pThis->cbCoffDbgInfo);
2052 if (pbDbgSect)
2053 {
2054 if (pThis->hFile == NIL_RTFILE)
2055 rc = pThis->pMod->pImgVt->pfnReadAt(pThis->pMod, UINT32_MAX, pThis->offCoffDbgInfo, pbDbgSect, pThis->cbCoffDbgInfo);
2056 else
2057 rc = RTFileReadAt(pThis->hFile, pThis->offCoffDbgInfo, pbDbgSect, pThis->cbCoffDbgInfo, NULL);
2058 if (RT_SUCCESS(rc))
2059 {
2060 /* The string table follows after the symbol table. */
2061 const char *pszzStrTab = (const char *)( pbDbgSect
2062 + pThis->CoffHdr.LvaToFirstSymbol
2063 + pThis->CoffHdr.NumberOfSymbols * sizeof(IMAGE_SYMBOL));
2064 uint32_t cbStrTab = (uint32_t)((uintptr_t)(pbDbgSect + pThis->cbCoffDbgInfo) - (uintptr_t)pszzStrTab);
2065 /** @todo The symbol table starts with a size. Read it and checking. Also verify
2066 * that the symtab ends with a terminator character. */
2067
2068 rc = rtDbgModCvProcessCoffSymbolTable(pThis,
2069 (PCIMAGE_SYMBOL)(pbDbgSect + pThis->CoffHdr.LvaToFirstSymbol),
2070 pThis->CoffHdr.NumberOfSymbols,
2071 (PCIMAGE_LINENUMBER)(pbDbgSect + pThis->CoffHdr.LvaToFirstLinenumber),
2072 pThis->CoffHdr.NumberOfLinenumbers,
2073 pszzStrTab, cbStrTab);
2074 }
2075 RTMemFree(pbDbgSect);
2076 }
2077 else
2078 rc = VERR_NO_MEMORY;
2079 return rc;
2080}
2081
2082
2083
2084
2085
2086
2087/*
2088 *
2089 * CodeView Debug module implementation.
2090 * CodeView Debug module implementation.
2091 * CodeView Debug module implementation.
2092 *
2093 */
2094
2095
2096/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByAddr} */
2097static DECLCALLBACK(int) rtDbgModCv_LineByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off,
2098 PRTINTPTR poffDisp, PRTDBGLINE pLineInfo)
2099{
2100 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2101 return RTDbgModLineByAddr(pThis->hCnt, iSeg, off, poffDisp, pLineInfo);
2102}
2103
2104
2105/** @interface_method_impl{RTDBGMODVTDBG,pfnLineByOrdinal} */
2106static DECLCALLBACK(int) rtDbgModCv_LineByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGLINE pLineInfo)
2107{
2108 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2109 return RTDbgModLineByOrdinal(pThis->hCnt, iOrdinal, pLineInfo);
2110}
2111
2112
2113/** @interface_method_impl{RTDBGMODVTDBG,pfnLineCount} */
2114static DECLCALLBACK(uint32_t) rtDbgModCv_LineCount(PRTDBGMODINT pMod)
2115{
2116 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2117 return RTDbgModLineCount(pThis->hCnt);
2118}
2119
2120
2121/** @interface_method_impl{RTDBGMODVTDBG,pfnLineAdd} */
2122static DECLCALLBACK(int) rtDbgModCv_LineAdd(PRTDBGMODINT pMod, const char *pszFile, size_t cchFile, uint32_t uLineNo,
2123 uint32_t iSeg, RTUINTPTR off, uint32_t *piOrdinal)
2124{
2125 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2126 Assert(!pszFile[cchFile]); NOREF(cchFile);
2127 return RTDbgModLineAdd(pThis->hCnt, pszFile, uLineNo, iSeg, off, piOrdinal);
2128}
2129
2130
2131/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByAddr} */
2132static DECLCALLBACK(int) rtDbgModCv_SymbolByAddr(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, RTUINTPTR off, uint32_t fFlags,
2133 PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo)
2134{
2135 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2136 return RTDbgModSymbolByAddr(pThis->hCnt, iSeg, off, fFlags, poffDisp, pSymInfo);
2137}
2138
2139
2140/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByName} */
2141static DECLCALLBACK(int) rtDbgModCv_SymbolByName(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol,
2142 PRTDBGSYMBOL pSymInfo)
2143{
2144 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2145 Assert(!pszSymbol[cchSymbol]);
2146 return RTDbgModSymbolByName(pThis->hCnt, pszSymbol/*, cchSymbol*/, pSymInfo);
2147}
2148
2149
2150/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolByOrdinal} */
2151static DECLCALLBACK(int) rtDbgModCv_SymbolByOrdinal(PRTDBGMODINT pMod, uint32_t iOrdinal, PRTDBGSYMBOL pSymInfo)
2152{
2153 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2154 return RTDbgModSymbolByOrdinal(pThis->hCnt, iOrdinal, pSymInfo);
2155}
2156
2157
2158/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolCount} */
2159static DECLCALLBACK(uint32_t) rtDbgModCv_SymbolCount(PRTDBGMODINT pMod)
2160{
2161 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2162 return RTDbgModSymbolCount(pThis->hCnt);
2163}
2164
2165
2166/** @interface_method_impl{RTDBGMODVTDBG,pfnSymbolAdd} */
2167static DECLCALLBACK(int) rtDbgModCv_SymbolAdd(PRTDBGMODINT pMod, const char *pszSymbol, size_t cchSymbol,
2168 RTDBGSEGIDX iSeg, RTUINTPTR off, RTUINTPTR cb, uint32_t fFlags,
2169 uint32_t *piOrdinal)
2170{
2171 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2172 Assert(!pszSymbol[cchSymbol]); NOREF(cchSymbol);
2173 return RTDbgModSymbolAdd(pThis->hCnt, pszSymbol, iSeg, off, cb, fFlags, piOrdinal);
2174}
2175
2176
2177/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentByIndex} */
2178static DECLCALLBACK(int) rtDbgModCv_SegmentByIndex(PRTDBGMODINT pMod, RTDBGSEGIDX iSeg, PRTDBGSEGMENT pSegInfo)
2179{
2180 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2181 return RTDbgModSegmentByIndex(pThis->hCnt, iSeg, pSegInfo);
2182}
2183
2184
2185/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentCount} */
2186static DECLCALLBACK(RTDBGSEGIDX) rtDbgModCv_SegmentCount(PRTDBGMODINT pMod)
2187{
2188 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2189 return RTDbgModSegmentCount(pThis->hCnt);
2190}
2191
2192
2193/** @interface_method_impl{RTDBGMODVTDBG,pfnSegmentAdd} */
2194static DECLCALLBACK(int) rtDbgModCv_SegmentAdd(PRTDBGMODINT pMod, RTUINTPTR uRva, RTUINTPTR cb, const char *pszName, size_t cchName,
2195 uint32_t fFlags, PRTDBGSEGIDX piSeg)
2196{
2197 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2198 Assert(!pszName[cchName]); NOREF(cchName);
2199 return RTDbgModSegmentAdd(pThis->hCnt, uRva, cb, pszName, fFlags, piSeg);
2200}
2201
2202
2203/** @interface_method_impl{RTDBGMODVTDBG,pfnImageSize} */
2204static DECLCALLBACK(RTUINTPTR) rtDbgModCv_ImageSize(PRTDBGMODINT pMod)
2205{
2206 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2207 if (pThis->cbImage)
2208 return pThis->cbImage;
2209 return RTDbgModImageSize(pThis->hCnt);
2210}
2211
2212
2213/** @interface_method_impl{RTDBGMODVTDBG,pfnRvaToSegOff} */
2214static DECLCALLBACK(RTDBGSEGIDX) rtDbgModCv_RvaToSegOff(PRTDBGMODINT pMod, RTUINTPTR uRva, PRTUINTPTR poffSeg)
2215{
2216 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2217 return RTDbgModRvaToSegOff(pThis->hCnt, uRva, poffSeg);
2218}
2219
2220
2221/** @interface_method_impl{RTDBGMODVTDBG,pfnClose} */
2222static DECLCALLBACK(int) rtDbgModCv_Close(PRTDBGMODINT pMod)
2223{
2224 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2225
2226 RTDbgModRelease(pThis->hCnt);
2227 if (pThis->hFile != NIL_RTFILE)
2228 RTFileClose(pThis->hFile);
2229 RTMemFree(pThis->paDirEnts);
2230 RTMemFree(pThis);
2231
2232 pMod->pvDbgPriv = NULL; /* for internal use */
2233 return VINF_SUCCESS;
2234}
2235
2236
2237/*
2238 *
2239 * Probing code used by rtDbgModCv_TryOpen.
2240 * Probing code used by rtDbgModCv_TryOpen.
2241 *
2242 */
2243
2244
2245
2246/**
2247 * @callback_method_impl{FNRTLDRENUMSEGS, Used to add segments from the image}
2248 */
2249static DECLCALLBACK(int) rtDbgModCvAddSegmentsCallback(RTLDRMOD hLdrMod, PCRTLDRSEG pSeg, void *pvUser)
2250{
2251 PRTDBGMODCV pThis = (PRTDBGMODCV)pvUser;
2252 Log(("Segment %s: LinkAddress=%#llx RVA=%#llx cb=%#llx\n",
2253 pSeg->pszName, (uint64_t)pSeg->LinkAddress, (uint64_t)pSeg->RVA, pSeg->cb));
2254 NOREF(hLdrMod);
2255
2256 /* If the segment doesn't have a mapping, just add a dummy so the indexing
2257 works out correctly (same as for the image). */
2258 if (pSeg->RVA == NIL_RTLDRADDR)
2259 return RTDbgModSegmentAdd(pThis->hCnt, 0, 0, pSeg->pszName, 0 /*fFlags*/, NULL);
2260
2261 RTLDRADDR cb = RT_MAX(pSeg->cb, pSeg->cbMapped);
2262 return RTDbgModSegmentAdd(pThis->hCnt, pSeg->RVA, cb, pSeg->pszName, 0 /*fFlags*/, NULL);
2263}
2264
2265
2266/**
2267 * Copies the sections over from the DBG file.
2268 *
2269 * Called if we don't have an associated executable image.
2270 *
2271 * @returns IPRT status code.
2272 * @param pThis The CV module instance.
2273 * @param pDbgHdr The DBG file header.
2274 * @param pszFilename The filename (for logging).
2275 */
2276static int rtDbgModCvAddSegmentsFromDbg(PRTDBGMODCV pThis, PCIMAGE_SEPARATE_DEBUG_HEADER pDbgHdr, const char *pszFilename)
2277{
2278 /*
2279 * Validate the header fields a little.
2280 */
2281 if ( pDbgHdr->NumberOfSections < 1
2282 || pDbgHdr->NumberOfSections > 4096)
2283 {
2284 Log(("RTDbgModCv: Bad NumberOfSections: %d\n", pDbgHdr->NumberOfSections));
2285 return VERR_CV_BAD_FORMAT;
2286 }
2287 if (!RT_IS_POWER_OF_TWO(pDbgHdr->SectionAlignment))
2288 {
2289 Log(("RTDbgModCv: Bad SectionAlignment: %#x\n", pDbgHdr->SectionAlignment));
2290 return VERR_CV_BAD_FORMAT;
2291 }
2292
2293 /*
2294 * Read the section table.
2295 */
2296 size_t cbShs = pDbgHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
2297 PIMAGE_SECTION_HEADER paShs = (PIMAGE_SECTION_HEADER)RTMemAlloc(cbShs);
2298 if (!paShs)
2299 return VERR_NO_MEMORY;
2300 int rc = RTFileReadAt(pThis->hFile, sizeof(*pDbgHdr), paShs, cbShs, NULL);
2301 if (RT_SUCCESS(rc))
2302 {
2303 /*
2304 * Do some basic validation.
2305 */
2306 uint32_t cbHeaders = 0;
2307 uint32_t uRvaPrev = 0;
2308 for (uint32_t i = 0; i < pDbgHdr->NumberOfSections; i++)
2309 {
2310 Log3(("RTDbgModCv: Section #%02u %#010x LB %#010x %.*s\n",
2311 i, paShs[i].VirtualAddress, paShs[i].Misc.VirtualSize, sizeof(paShs[i].Name), paShs[i].Name));
2312
2313 if (paShs[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD)
2314 continue;
2315
2316 if (paShs[i].VirtualAddress < uRvaPrev)
2317 {
2318 Log(("RTDbgModCv: %s: Overlap or soring error, VirtualAddress=%#x uRvaPrev=%#x - section #%d '%.*s'!!!\n",
2319 pszFilename, paShs[i].VirtualAddress, uRvaPrev, i, sizeof(paShs[i].Name), paShs[i].Name));
2320 rc = VERR_CV_BAD_FORMAT;
2321 }
2322 else if ( paShs[i].VirtualAddress > pDbgHdr->SizeOfImage
2323 || paShs[i].Misc.VirtualSize > pDbgHdr->SizeOfImage
2324 || paShs[i].VirtualAddress + paShs[i].Misc.VirtualSize > pDbgHdr->SizeOfImage)
2325 {
2326 Log(("RTDbgModCv: %s: VirtualAddress=%#x VirtualSize=%#x (total %x) - beyond image size (%#x) - section #%d '%.*s'!!!\n",
2327 pszFilename, paShs[i].VirtualAddress, paShs[i].Misc.VirtualSize,
2328 paShs[i].VirtualAddress + paShs[i].Misc.VirtualSize,
2329 pThis->cbImage, i, sizeof(paShs[i].Name), paShs[i].Name));
2330 rc = VERR_CV_BAD_FORMAT;
2331 }
2332 else if (paShs[i].VirtualAddress & (pDbgHdr->SectionAlignment - 1))
2333 {
2334 Log(("RTDbgModCv: %s: VirtualAddress=%#x misaligned (%#x) - section #%d '%.*s'!!!\n",
2335 pszFilename, paShs[i].VirtualAddress, pDbgHdr->SectionAlignment, i, sizeof(paShs[i].Name), paShs[i].Name));
2336 rc = VERR_CV_BAD_FORMAT;
2337 }
2338 else
2339 {
2340 if (uRvaPrev == 0)
2341 cbHeaders = paShs[i].VirtualAddress;
2342 uRvaPrev = paShs[i].VirtualAddress + paShs[i].Misc.VirtualSize;
2343 continue;
2344 }
2345 }
2346 if (RT_SUCCESS(rc) && uRvaPrev == 0)
2347 {
2348 Log(("RTDbgModCv: %s: No loadable sections.\n", pszFilename));
2349 rc = VERR_CV_BAD_FORMAT;
2350 }
2351 if (RT_SUCCESS(rc) && cbHeaders == 0)
2352 {
2353 Log(("RTDbgModCv: %s: No space for PE headers.\n", pszFilename));
2354 rc = VERR_CV_BAD_FORMAT;
2355 }
2356 if (RT_SUCCESS(rc))
2357 {
2358 /*
2359 * Add sections.
2360 */
2361 rc = RTDbgModSegmentAdd(pThis->hCnt, 0, cbHeaders, "NtHdrs", 0 /*fFlags*/, NULL);
2362 for (uint32_t i = 0; RT_SUCCESS(rc) && i < pDbgHdr->NumberOfSections; i++)
2363 {
2364 char szName[sizeof(paShs[i].Name) + 1];
2365 memcpy(szName, paShs[i].Name, sizeof(paShs[i].Name));
2366 szName[sizeof(szName) - 1] = '\0';
2367
2368 if (paShs[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD)
2369 rc = RTDbgModSegmentAdd(pThis->hCnt, 0, 0, szName, 0 /*fFlags*/, NULL);
2370 else
2371 rc = RTDbgModSegmentAdd(pThis->hCnt, paShs[i].VirtualAddress, paShs[i].Misc.VirtualSize, szName,
2372 0 /*fFlags*/, NULL);
2373 }
2374 if (RT_SUCCESS(rc))
2375 pThis->fHaveLoadedSegments = true;
2376 }
2377 }
2378
2379 RTMemFree(paShs);
2380 return rc;
2381}
2382
2383
2384/**
2385 * Instantiates the CV/COFF reader.
2386 *
2387 * @returns IPRT status code
2388 * @param pDbgMod The debug module instance.
2389 * @param enmFileType The type of input file.
2390 * @param hFile The file handle, NIL_RTFILE of image.
2391 * @param ppThis Where to return the reader instance.
2392 */
2393static int rtDbgModCvCreateInstance(PRTDBGMODINT pDbgMod, RTCVFILETYPE enmFileType, RTFILE hFile, PRTDBGMODCV *ppThis)
2394{
2395 /*
2396 * Do we already have an instance? Happens if we find multiple debug
2397 * formats we support.
2398 */
2399 PRTDBGMODCV pThis = (PRTDBGMODCV)pDbgMod->pvDbgPriv;
2400 if (pThis)
2401 {
2402 Assert(pThis->enmType == enmFileType);
2403 Assert(pThis->hFile == hFile);
2404 Assert(pThis->pMod == pDbgMod);
2405 *ppThis = pThis;
2406 return VINF_SUCCESS;
2407 }
2408
2409 /*
2410 * Create a new instance.
2411 */
2412 pThis = (PRTDBGMODCV)RTMemAllocZ(sizeof(RTDBGMODCV));
2413 if (!pThis)
2414 return VERR_NO_MEMORY;
2415 int rc = RTDbgModCreate(&pThis->hCnt, pDbgMod->pszName, 0 /*cbSeg*/, 0 /*fFlags*/);
2416 if (RT_SUCCESS(rc))
2417 {
2418 pDbgMod->pvDbgPriv = pThis;
2419 pThis->enmType = enmFileType;
2420 pThis->hFile = hFile;
2421 pThis->pMod = pDbgMod;
2422 pThis->offBase = UINT32_MAX;
2423 pThis->offCoffDbgInfo = UINT32_MAX;
2424 *ppThis = pThis;
2425 return VINF_SUCCESS;
2426 }
2427 RTMemFree(pThis);
2428 return rc;
2429}
2430
2431
2432/**
2433 * Common part of the COFF probing.
2434 *
2435 * @returns status code.
2436 * @param pDbgMod The debug module instance. On success pvDbgPriv
2437 * will point to a valid RTDBGMODCV.
2438 * @param enmFileType The kind of file this is we're probing.
2439 * @param hFile The file with debug info in it.
2440 * @param off The offset where to expect CV debug info.
2441 * @param cb The number of bytes of debug info.
2442 * @param pszFilename The path to the file (for logging).
2443 */
2444static int rtDbgModCvProbeCoff(PRTDBGMODINT pDbgMod, RTCVFILETYPE enmFileType, RTFILE hFile,
2445 uint32_t off, uint32_t cb, const char *pszFilename)
2446{
2447 /*
2448 * Check that there is sufficient data for a header, then read it.
2449 */
2450 if (cb < sizeof(IMAGE_COFF_SYMBOLS_HEADER))
2451 {
2452 Log(("RTDbgModCv: Not enough room for COFF header.\n"));
2453 return VERR_BAD_EXE_FORMAT;
2454 }
2455 if (cb >= UINT32_C(128) * _1M)
2456 {
2457 Log(("RTDbgModCv: COFF debug information is to large (%'u bytes), max is 128MB\n", cb));
2458 return VERR_BAD_EXE_FORMAT;
2459 }
2460
2461 int rc;
2462 IMAGE_COFF_SYMBOLS_HEADER Hdr;
2463 if (hFile == NIL_RTFILE)
2464 rc = pDbgMod->pImgVt->pfnReadAt(pDbgMod, UINT32_MAX, off, &Hdr, sizeof(Hdr));
2465 else
2466 rc = RTFileReadAt(hFile, off, &Hdr, sizeof(Hdr), NULL);
2467 if (RT_FAILURE(rc))
2468 {
2469 Log(("RTDbgModCv: Error reading COFF header: %Rrc\n", rc));
2470 return rc;
2471 }
2472
2473 Log2(("RTDbgModCv: Found COFF debug info header at %#x (LB %#x) in %s\n", off, cb, pszFilename));
2474 Log2((" NumberOfSymbols = %#010x\n", Hdr.NumberOfSymbols));
2475 Log2((" LvaToFirstSymbol = %#010x\n", Hdr.LvaToFirstSymbol));
2476 Log2((" NumberOfLinenumbers = %#010x\n", Hdr.NumberOfLinenumbers));
2477 Log2((" LvaToFirstLinenumber = %#010x\n", Hdr.LvaToFirstLinenumber));
2478 Log2((" RvaToFirstByteOfCode = %#010x\n", Hdr.RvaToFirstByteOfCode));
2479 Log2((" RvaToLastByteOfCode = %#010x\n", Hdr.RvaToLastByteOfCode));
2480 Log2((" RvaToFirstByteOfData = %#010x\n", Hdr.RvaToFirstByteOfData));
2481 Log2((" RvaToLastByteOfData = %#010x\n", Hdr.RvaToLastByteOfData));
2482
2483 /*
2484 * Validate the COFF header.
2485 */
2486 if ( (uint64_t)Hdr.LvaToFirstSymbol + (uint64_t)Hdr.NumberOfSymbols * sizeof(IMAGE_SYMBOL) > cb
2487 || (Hdr.LvaToFirstSymbol < sizeof(Hdr) && Hdr.NumberOfSymbols > 0))
2488 {
2489 Log(("RTDbgModCv: Bad COFF symbol count or/and offset: LvaToFirstSymbol=%#x, NumberOfSymbols=%#x cbCoff=%#x\n",
2490 Hdr.LvaToFirstSymbol, Hdr.NumberOfSymbols, cb));
2491 return VERR_BAD_EXE_FORMAT;
2492 }
2493 if ( (uint64_t)Hdr.LvaToFirstLinenumber + (uint64_t)Hdr.NumberOfLinenumbers * sizeof(IMAGE_LINENUMBER) > cb
2494 || (Hdr.LvaToFirstLinenumber < sizeof(Hdr) && Hdr.NumberOfLinenumbers > 0))
2495 {
2496 Log(("RTDbgModCv: Bad COFF symbol count or/and offset: LvaToFirstSymbol=%#x, NumberOfSymbols=%#x cbCoff=%#x\n",
2497 Hdr.LvaToFirstSymbol, Hdr.NumberOfSymbols, cb));
2498 return VERR_BAD_EXE_FORMAT;
2499 }
2500 if (Hdr.NumberOfSymbols < 2)
2501 {
2502 Log(("RTDbgModCv: The COFF symbol table is too short to be of any worth... (%u syms)\n", Hdr.NumberOfSymbols));
2503 return VERR_NO_DATA;
2504 }
2505
2506 /*
2507 * What we care about looks fine, use it.
2508 */
2509 PRTDBGMODCV pThis;
2510 rc = rtDbgModCvCreateInstance(pDbgMod, enmFileType, hFile, &pThis);
2511 if (RT_SUCCESS(rc))
2512 {
2513 pThis->offCoffDbgInfo = off;
2514 pThis->cbCoffDbgInfo = cb;
2515 pThis->CoffHdr = Hdr;
2516 }
2517
2518 return rc;
2519}
2520
2521
2522/**
2523 * Common part of the CodeView probing.
2524 *
2525 * @returns status code.
2526 * @param pDbgMod The debug module instance. On success pvDbgPriv
2527 * will point to a valid RTDBGMODCV.
2528 * @param pCvHdr The CodeView base header.
2529 * @param enmFileType The kind of file this is we're probing.
2530 * @param hFile The file with debug info in it.
2531 * @param off The offset where to expect CV debug info.
2532 * @param cb The number of bytes of debug info.
2533 * @param enmArch The desired image architecture.
2534 * @param pszFilename The path to the file (for logging).
2535 */
2536static int rtDbgModCvProbeCommon(PRTDBGMODINT pDbgMod, PRTCVHDR pCvHdr, RTCVFILETYPE enmFileType, RTFILE hFile,
2537 uint32_t off, uint32_t cb, RTLDRARCH enmArch, const char *pszFilename)
2538{
2539 int rc = VERR_DBG_NO_MATCHING_INTERPRETER;
2540
2541 /* Is a codeview format we (wish to) support? */
2542 if ( pCvHdr->u32Magic == RTCVHDR_MAGIC_NB11
2543 || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB09
2544 || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB08
2545 || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB05
2546 || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB04
2547 || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB02
2548 || pCvHdr->u32Magic == RTCVHDR_MAGIC_NB00
2549 )
2550 {
2551 /* We're assuming it's a base header, so the offset must be within
2552 the area defined by the debug info we got from the loader. */
2553 if (pCvHdr->off < cb && pCvHdr->off >= sizeof(*pCvHdr))
2554 {
2555 Log(("RTDbgModCv: Found %c%c%c%c at %#RTfoff - size %#x, directory at %#x. file type %d\n",
2556 RT_BYTE1(pCvHdr->u32Magic), RT_BYTE2(pCvHdr->u32Magic), RT_BYTE3(pCvHdr->u32Magic), RT_BYTE4(pCvHdr->u32Magic),
2557 off, cb, pCvHdr->off, enmFileType));
2558
2559 /*
2560 * Create a module instance, if not already done.
2561 */
2562 PRTDBGMODCV pThis;
2563 rc = rtDbgModCvCreateInstance(pDbgMod, enmFileType, hFile, &pThis);
2564 if (RT_SUCCESS(rc))
2565 {
2566 pThis->u32CvMagic = pCvHdr->u32Magic;
2567 pThis->offBase = off;
2568 pThis->cbDbgInfo = cb;
2569 pThis->offDir = pCvHdr->off;
2570 return VINF_SUCCESS;
2571 }
2572 }
2573 }
2574
2575 return rc;
2576}
2577
2578
2579/** @callback_method_impl{FNRTLDRENUMDBG} */
2580static DECLCALLBACK(int) rtDbgModCvEnumCallback(RTLDRMOD hLdrMod, PCRTLDRDBGINFO pDbgInfo, void *pvUser)
2581{
2582 PRTDBGMODINT pDbgMod = (PRTDBGMODINT)pvUser;
2583 Assert(!pDbgMod->pvDbgPriv);
2584
2585 /* Skip external files, RTDbgMod will deal with those
2586 via RTDBGMODINT::pszDbgFile. */
2587 if (pDbgInfo->pszExtFile)
2588 return VINF_SUCCESS;
2589
2590 /* We only handle the codeview sections. */
2591 if (pDbgInfo->enmType == RTLDRDBGINFOTYPE_CODEVIEW)
2592 {
2593 /* Read the specified header and check if we like it. */
2594 RTCVHDR CvHdr;
2595 int rc = pDbgMod->pImgVt->pfnReadAt(pDbgMod, pDbgInfo->iDbgInfo, pDbgInfo->offFile, &CvHdr, sizeof(CvHdr));
2596 if (RT_SUCCESS(rc))
2597 rc = rtDbgModCvProbeCommon(pDbgMod, &CvHdr, RTCVFILETYPE_IMAGE, NIL_RTFILE, pDbgInfo->offFile, pDbgInfo->cb,
2598 pDbgMod->pImgVt->pfnGetArch(pDbgMod), pDbgMod->pszImgFile);
2599 }
2600 else if (pDbgInfo->enmType == RTLDRDBGINFOTYPE_COFF)
2601 {
2602 /* Join paths with the DBG code. */
2603 rtDbgModCvProbeCoff(pDbgMod, RTCVFILETYPE_IMAGE, NIL_RTFILE, pDbgInfo->offFile, pDbgInfo->cb, pDbgMod->pszImgFile);
2604 }
2605
2606 return VINF_SUCCESS;
2607}
2608
2609
2610/**
2611 * Part two of the external file probing.
2612 *
2613 * @returns status code.
2614 * @param pThis The debug module instance. On success pvDbgPriv
2615 * will point to a valid RTDBGMODCV.
2616 * @param enmFileType The kind of file this is we're probing.
2617 * @param hFile The file with debug info in it.
2618 * @param off The offset where to expect CV debug info.
2619 * @param cb The number of bytes of debug info.
2620 * @param enmArch The desired image architecture.
2621 * @param pszFilename The path to the file (for logging).
2622 */
2623static int rtDbgModCvProbeFile2(PRTDBGMODINT pThis, RTCVFILETYPE enmFileType, RTFILE hFile, uint32_t off, uint32_t cb,
2624 RTLDRARCH enmArch, const char *pszFilename)
2625{
2626 RTCVHDR CvHdr;
2627 int rc = RTFileReadAt(hFile, off, &CvHdr, sizeof(CvHdr), NULL);
2628 if (RT_SUCCESS(rc))
2629 rc = rtDbgModCvProbeCommon(pThis, &CvHdr, enmFileType, hFile, off, cb, enmArch, pszFilename);
2630 return rc;
2631}
2632
2633
2634/**
2635 * Probes an external file for CodeView information.
2636 *
2637 * @returns status code.
2638 * @param pDbgMod The debug module instance. On success pvDbgPriv
2639 * will point to a valid RTDBGMODCV.
2640 * @param pszFilename The path to the file to probe.
2641 * @param enmArch The desired image architecture.
2642 */
2643static int rtDbgModCvProbeFile(PRTDBGMODINT pDbgMod, const char *pszFilename, RTLDRARCH enmArch)
2644{
2645 RTFILE hFile;
2646 int rc = RTFileOpen(&hFile, pszFilename, RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN);
2647 if (RT_FAILURE(rc))
2648 return rc;
2649
2650 /*
2651 * Check for .DBG file
2652 */
2653 IMAGE_SEPARATE_DEBUG_HEADER DbgHdr;
2654 rc = RTFileReadAt(hFile, 0, &DbgHdr, sizeof(DbgHdr), NULL);
2655 if ( RT_SUCCESS(rc)
2656 && DbgHdr.Signature == IMAGE_SEPARATE_DEBUG_SIGNATURE)
2657 {
2658 Log2(("RTDbgModCv: Found separate debug header in %s:\n", pszFilename));
2659 Log2((" Flags = %#x\n", DbgHdr.Flags));
2660 Log2((" Machine = %#x\n", DbgHdr.Machine));
2661 Log2((" Characteristics = %#x\n", DbgHdr.Characteristics));
2662 Log2((" TimeDateStamp = %#x\n", DbgHdr.TimeDateStamp));
2663 Log2((" CheckSum = %#x\n", DbgHdr.CheckSum));
2664 Log2((" ImageBase = %#x\n", DbgHdr.ImageBase));
2665 Log2((" SizeOfImage = %#x\n", DbgHdr.SizeOfImage));
2666 Log2((" NumberOfSections = %#x\n", DbgHdr.NumberOfSections));
2667 Log2((" ExportedNamesSize = %#x\n", DbgHdr.ExportedNamesSize));
2668 Log2((" DebugDirectorySize = %#x\n", DbgHdr.DebugDirectorySize));
2669 Log2((" SectionAlignment = %#x\n", DbgHdr.SectionAlignment));
2670
2671 /*
2672 * Match up the architecture if specified.
2673 */
2674 switch (enmArch)
2675 {
2676 case RTLDRARCH_X86_32:
2677 if (DbgHdr.Machine != IMAGE_FILE_MACHINE_I386)
2678 rc = VERR_LDR_ARCH_MISMATCH;
2679 break;
2680 case RTLDRARCH_AMD64:
2681 if (DbgHdr.Machine != IMAGE_FILE_MACHINE_AMD64)
2682 rc = VERR_LDR_ARCH_MISMATCH;
2683 break;
2684
2685 default:
2686 case RTLDRARCH_HOST:
2687 AssertFailed();
2688 case RTLDRARCH_WHATEVER:
2689 break;
2690 }
2691 if (RT_FAILURE(rc))
2692 {
2693 RTFileClose(hFile);
2694 return rc;
2695 }
2696
2697 /*
2698 * Probe for readable debug info in the debug directory.
2699 */
2700 uint32_t offDbgDir = sizeof(DbgHdr)
2701 + DbgHdr.NumberOfSections * sizeof(IMAGE_SECTION_HEADER)
2702 + DbgHdr.ExportedNamesSize;
2703
2704 uint32_t cEntries = DbgHdr.DebugDirectorySize / sizeof(IMAGE_DEBUG_DIRECTORY);
2705 for (uint32_t i = 0; i < cEntries; i++, offDbgDir += sizeof(IMAGE_DEBUG_DIRECTORY))
2706 {
2707 IMAGE_DEBUG_DIRECTORY DbgDir;
2708 rc = RTFileReadAt(hFile, offDbgDir, &DbgDir, sizeof(DbgDir), NULL);
2709 if (RT_FAILURE(rc))
2710 break;
2711 if (DbgDir.Type == IMAGE_DEBUG_TYPE_CODEVIEW)
2712 rc = rtDbgModCvProbeFile2(pDbgMod, RTCVFILETYPE_DBG, hFile,
2713 DbgDir.PointerToRawData, DbgDir.SizeOfData,
2714 enmArch, pszFilename);
2715 else if (DbgDir.Type == IMAGE_DEBUG_TYPE_COFF)
2716 rc = rtDbgModCvProbeCoff(pDbgMod, RTCVFILETYPE_DBG, hFile,
2717 DbgDir.PointerToRawData, DbgDir.SizeOfData, pszFilename);
2718 }
2719
2720 /*
2721 * If we get down here with an instance, it prooves that we've found
2722 * something, regardless of any errors. Add the sections and such.
2723 */
2724 PRTDBGMODCV pThis = (PRTDBGMODCV)pDbgMod->pvDbgPriv;
2725 if (pThis)
2726 {
2727 pThis->cbImage = DbgHdr.SizeOfImage;
2728 if (pDbgMod->pImgVt)
2729 rc = VINF_SUCCESS;
2730 else
2731 {
2732 rc = rtDbgModCvAddSegmentsFromDbg(pThis, &DbgHdr, pszFilename);
2733 if (RT_FAILURE(rc))
2734 rtDbgModCv_Close(pDbgMod);
2735 }
2736 return rc;
2737 }
2738
2739 /* Failed to find CV or smth, look at the end of the file just to be sure... */
2740 }
2741
2742 /*
2743 * Look for CV tail header.
2744 */
2745 uint64_t cbFile;
2746 rc = RTFileSeek(hFile, -(RTFOFF)sizeof(RTCVHDR), RTFILE_SEEK_END, &cbFile);
2747 if (RT_SUCCESS(rc))
2748 {
2749 cbFile += sizeof(RTCVHDR);
2750 RTCVHDR CvHdr;
2751 rc = RTFileRead(hFile, &CvHdr, sizeof(CvHdr), NULL);
2752 if (RT_SUCCESS(rc))
2753 rc = rtDbgModCvProbeFile2(pDbgMod, RTCVFILETYPE_OTHER_AT_END, hFile,
2754 cbFile - CvHdr.off, CvHdr.off, enmArch, pszFilename);
2755 }
2756
2757 if (RT_FAILURE(rc))
2758 RTFileClose(hFile);
2759 return rc;
2760}
2761
2762
2763
2764/** @interface_method_impl{RTDBGMODVTDBG,pfnTryOpen} */
2765static DECLCALLBACK(int) rtDbgModCv_TryOpen(PRTDBGMODINT pMod, RTLDRARCH enmArch)
2766{
2767 /*
2768 * Look for debug info.
2769 */
2770 int rc = VERR_DBG_NO_MATCHING_INTERPRETER;
2771 if (pMod->pszDbgFile)
2772 rc = rtDbgModCvProbeFile(pMod, pMod->pszDbgFile, enmArch);
2773
2774 if (!pMod->pvDbgPriv && pMod->pImgVt)
2775 {
2776 int rc2 = pMod->pImgVt->pfnEnumDbgInfo(pMod, rtDbgModCvEnumCallback, pMod);
2777 if (RT_FAILURE(rc2))
2778 rc = rc2;
2779
2780 if (!pMod->pvDbgPriv)
2781 {
2782 /* Try the executable in case it has a NBxx tail header. */
2783 rc2 = rtDbgModCvProbeFile(pMod, pMod->pszImgFile, enmArch);
2784 if (RT_FAILURE(rc2) && (RT_SUCCESS(rc) || rc == VERR_DBG_NO_MATCHING_INTERPRETER))
2785 rc = rc2;
2786 }
2787 }
2788
2789 PRTDBGMODCV pThis = (PRTDBGMODCV)pMod->pvDbgPriv;
2790 if (!pThis)
2791 return RT_SUCCESS_NP(rc) ? VERR_DBG_NO_MATCHING_INTERPRETER : rc;
2792 Assert(pThis->offBase != UINT32_MAX || pThis->offCoffDbgInfo != UINT32_MAX);
2793
2794 /*
2795 * Load the debug info.
2796 */
2797 if (pMod->pImgVt)
2798 {
2799 rc = pMod->pImgVt->pfnEnumSegments(pMod, rtDbgModCvAddSegmentsCallback, pThis);
2800 pThis->fHaveLoadedSegments = true;
2801 }
2802 if (RT_SUCCESS(rc) && pThis->offBase != UINT32_MAX)
2803 rc = rtDbgModCvLoadCodeViewInfo(pThis);
2804 if (RT_SUCCESS(rc) && pThis->offCoffDbgInfo != UINT32_MAX)
2805 rc = rtDbgModCvLoadCoffInfo(pThis);
2806 if (RT_SUCCESS(rc))
2807 {
2808 Log(("RTDbgCv: Successfully loaded debug info\n"));
2809 return VINF_SUCCESS;
2810 }
2811
2812 Log(("RTDbgCv: Debug info load error %Rrc\n", rc));
2813 rtDbgModCv_Close(pMod);
2814 return rc;
2815}
2816
2817
2818
2819
2820
2821/** Virtual function table for the CodeView debug info reader. */
2822DECL_HIDDEN_CONST(RTDBGMODVTDBG) const g_rtDbgModVtDbgCodeView =
2823{
2824 /*.u32Magic = */ RTDBGMODVTDBG_MAGIC,
2825 /*.fSupports = */ RT_DBGTYPE_CODEVIEW,
2826 /*.pszName = */ "codeview",
2827 /*.pfnTryOpen = */ rtDbgModCv_TryOpen,
2828 /*.pfnClose = */ rtDbgModCv_Close,
2829
2830 /*.pfnRvaToSegOff = */ rtDbgModCv_RvaToSegOff,
2831 /*.pfnImageSize = */ rtDbgModCv_ImageSize,
2832
2833 /*.pfnSegmentAdd = */ rtDbgModCv_SegmentAdd,
2834 /*.pfnSegmentCount = */ rtDbgModCv_SegmentCount,
2835 /*.pfnSegmentByIndex = */ rtDbgModCv_SegmentByIndex,
2836
2837 /*.pfnSymbolAdd = */ rtDbgModCv_SymbolAdd,
2838 /*.pfnSymbolCount = */ rtDbgModCv_SymbolCount,
2839 /*.pfnSymbolByOrdinal = */ rtDbgModCv_SymbolByOrdinal,
2840 /*.pfnSymbolByName = */ rtDbgModCv_SymbolByName,
2841 /*.pfnSymbolByAddr = */ rtDbgModCv_SymbolByAddr,
2842
2843 /*.pfnLineAdd = */ rtDbgModCv_LineAdd,
2844 /*.pfnLineCount = */ rtDbgModCv_LineCount,
2845 /*.pfnLineByOrdinal = */ rtDbgModCv_LineByOrdinal,
2846 /*.pfnLineByAddr = */ rtDbgModCv_LineByAddr,
2847
2848 /*.u32EndMagic = */ RTDBGMODVTDBG_MAGIC
2849};
2850
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette