VirtualBox

source: vbox/trunk/src/VBox/Debugger/DBGCDumpImage.cpp

Last change on this file was 105536, checked in by vboxsync, 7 weeks ago

VBoxDbg: Added a ntrbtree command for dumping the red-black trees in Windows 8 and later. bugref:10727

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 86.6 KB
Line 
1/* $Id: DBGCDumpImage.cpp 105536 2024-07-30 01:08:28Z vboxsync $ */
2/** @file
3 * DBGC - Debugger Console, Native Commands.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_DBGC
33#include <VBox/dbg.h>
34#include <VBox/vmm/dbgf.h>
35#include <VBox/param.h>
36#include <iprt/errcore.h>
37#include <VBox/log.h>
38
39#include <iprt/assert.h>
40#include <iprt/ctype.h>
41#include <iprt/dir.h>
42#include <iprt/env.h>
43#include <iprt/ldr.h>
44#include <iprt/mem.h>
45#include <iprt/path.h>
46#include <iprt/string.h>
47#include <iprt/time.h>
48#ifdef DBGC_DUMP_IMAGE_TOOL
49# include <iprt/buildconfig.h>
50# include <iprt/message.h>
51# include <iprt/file.h>
52# include <iprt/getopt.h>
53# include <iprt/initterm.h>
54# include <iprt/process.h>
55# include <iprt/stream.h>
56# include <iprt/vfs.h>
57#endif
58#include <iprt/formats/mz.h>
59#include <iprt/formats/pecoff.h>
60#include <iprt/formats/elf32.h>
61#include <iprt/formats/elf64.h>
62#include <iprt/formats/codeview.h>
63#include <iprt/formats/mach-o.h>
64
65#include "DBGCInternal.h"
66
67
68/*********************************************************************************************************************************
69* Structures and Typedefs *
70*********************************************************************************************************************************/
71#ifdef DBGC_DUMP_IMAGE_TOOL
72/** Command helper state for the image dumper tool. */
73typedef struct CMDHLPSTATE
74{
75 DBGCCMDHLP Core;
76 /** The exit code for the tool. */
77 RTEXITCODE rcExit;
78 /** The current input file. */
79 RTVFSFILE hVfsFile;
80} CMDHLPSTATE;
81typedef CMDHLPSTATE *PCMDHLPSTATE;
82#endif
83
84
85/** Helper for translating flags. */
86typedef struct
87{
88 uint32_t fFlag;
89 const char *pszNm;
90} DBGCDUMPFLAGENTRY;
91#define FLENT(a_Define) { a_Define, #a_Define }
92
93
94
95/*********************************************************************************************************************************
96* DebugImageCmd *
97*********************************************************************************************************************************/
98
99#define DUMPIMAGE_SELECT_HEADERS RT_BIT_64(0)
100#define DUMPIMAGE_SELECT_SECTIONS RT_BIT_64(1)
101#define DUMPIMAGE_SELECT_EXPORTS RT_BIT_64(2)
102#define DUMPIMAGE_SELECT_IMPORTS RT_BIT_64(3)
103#define DUMPIMAGE_SELECT_TLS RT_BIT_64(4)
104#define DUMPIMAGE_SELECT_LOAD_CONFIG RT_BIT_64(5)
105#define DUMPIMAGE_SELECT_RESOURCES RT_BIT_64(6)
106#define DUMPIMAGE_SELECT_FIXUP RT_BIT_64(7)
107#define DUMPIMAGE_SELECT_DEBUG RT_BIT_64(8)
108#define DUMPIMAGE_SELECT_EVERYTHING UINT64_MAX
109#define DUMPIMAGE_SELECT_DEFAULT DUMPIMAGE_SELECT_EVERYTHING
110
111class DumpImageCmd
112{
113public:
114 /** Pointer to the command helpers. */
115 PDBGCCMDHLP const m_pCmdHlp;
116 /** The command descriptor (for failing the command). */
117 PCDBGCCMD const m_pCmd;
118 /** The command exit code. */
119 RTEXITCODE m_rcExit;
120 /** The first failure code. */
121 int m_rc;
122
123 /** Current number of targets. */
124 unsigned m_cTargets;
125 /** The name of what's being dumped (for error messages). */
126 const char *m_pszName;
127#ifndef DBGC_DUMP_IMAGE_TOOL
128 /** Debugger: Pointer to the image base address variable. */
129 PCDBGCVAR m_pImageBase;
130#else
131 /** Command line tool: The file we're dumping. */
132 RTVFSFILE m_hVfsFile;
133#endif
134
135public:
136 /** What to dump (DUMPIMAGE_SELECT_XXX). */
137 uint64_t m_fSelection;
138
139private:
140 DumpImageCmd();
141
142public:
143 DumpImageCmd(PDBGCCMDHLP a_pCmdHlp, PCDBGCCMD a_pCmd)
144 : m_pCmdHlp(a_pCmdHlp)
145 , m_pCmd(a_pCmd)
146 , m_rcExit(RTEXITCODE_SUCCESS)
147 , m_rc(VINF_SUCCESS)
148 , m_cTargets(0)
149 , m_pszName(NULL)
150#ifndef DBGC_DUMP_IMAGE_TOOL
151 , m_pImageBase(NULL)
152#else
153 , m_hVfsFile(NIL_RTVFSFILE)
154#endif
155 , m_fSelection(DUMPIMAGE_SELECT_DEFAULT)
156 {
157 }
158
159 ~DumpImageCmd()
160 {
161 clearTarget();
162 }
163
164 /** @name Methods not requiring any target.
165 * @{ */
166
167 void myPrintfV(const char *pszFormat, va_list va) const RT_NOEXCEPT
168 {
169#ifndef DBGC_DUMP_IMAGE_TOOL
170 m_pCmdHlp->pfnPrintfV(m_pCmdHlp, NULL, pszFormat, va);
171#else
172 RTPrintfV(pszFormat, va);
173#endif
174 }
175
176 void myPrintf(const char *pszFormat, ...) const RT_NOEXCEPT
177 {
178 va_list va;
179 va_start(va, pszFormat);
180#ifndef DBGC_DUMP_IMAGE_TOOL
181 m_pCmdHlp->pfnPrintfV(m_pCmdHlp, NULL, pszFormat, va);
182#else
183 RTPrintfV(pszFormat, va);
184#endif
185 va_end(va);
186 }
187
188 int myErrorV(const char *pszFormat, va_list va) RT_NOEXCEPT
189 {
190 int rc;
191 if (m_pszName)
192 {
193 va_list vaCopy;
194 va_copy(vaCopy, va);
195#ifndef DBGC_DUMP_IMAGE_TOOL
196 rc = DBGCCmdHlpFail(m_pCmdHlp, m_pCmd, "%s: %N", m_pszName, pszFormat, &vaCopy);
197#else
198 RTMsgError("%s: %N", m_pszName, pszFormat, &vaCopy);
199#endif
200 va_end(va);
201 }
202 else
203#ifndef DBGC_DUMP_IMAGE_TOOL
204 rc = m_pCmdHlp->pfnFailV(m_pCmdHlp, m_pCmd, pszFormat, va);
205#else
206 RTMsgErrorV(pszFormat, va);
207 rc = VERR_GENERAL_FAILURE;
208#endif
209
210 m_rcExit = RTEXITCODE_FAILURE;
211 if (m_rc == VINF_SUCCESS)
212 m_rc = rc;
213 return rc;
214 }
215
216 int myError(const char *pszFormat, ...) RT_NOEXCEPT
217 {
218 va_list va;
219 va_start(va, pszFormat);
220 int rc = myErrorV(pszFormat, va);
221 va_end(va);
222 return rc;
223 }
224
225 int myErrorV(int rc, const char *pszFormat, va_list va) RT_NOEXCEPT
226 {
227 if (m_pszName)
228 {
229 va_list vaCopy;
230 va_copy(vaCopy, va);
231#ifndef DBGC_DUMP_IMAGE_TOOL
232 rc = DBGCCmdHlpFailRc(m_pCmdHlp, m_pCmd, rc, "%s: %N", m_pszName, pszFormat, &vaCopy);
233#else
234 RTMsgError("%s: %N: %Rrc", m_pszName, pszFormat, &vaCopy, rc);
235#endif
236 va_end(vaCopy);
237 }
238 else
239 {
240#ifndef DBGC_DUMP_IMAGE_TOOL
241 rc = m_pCmdHlp->pfnFailRcV(m_pCmdHlp, m_pCmd, rc, pszFormat, va);
242#else
243 va_list vaCopy;
244 va_copy(vaCopy, va);
245 RTMsgError("%N: %Rrc", pszFormat, &vaCopy, rc);
246 va_end(vaCopy);
247#endif
248 }
249
250 m_rcExit = RTEXITCODE_FAILURE;
251 if (m_rc == VINF_SUCCESS)
252 m_rc = rc;
253 return rc;
254 }
255
256 int myError(int rc, const char *pszFormat, ...) RT_NOEXCEPT
257 {
258 va_list va;
259 va_start(va, pszFormat);
260 rc = myErrorV(rc, pszFormat, va);
261 va_end(va);
262 return rc;
263 }
264
265 int mySyntax(const char *pszFormat, ...) RT_NOEXCEPT
266 {
267 m_rcExit = RTEXITCODE_SYNTAX;
268 va_list va;
269 va_start(va, pszFormat);
270#ifndef DBGC_DUMP_IMAGE_TOOL
271 int rc = DBGCCmdHlpFail(m_pCmdHlp, m_pCmd, "syntax: %N", pszFormat, &va);
272#else
273 RTMsgSyntaxV(pszFormat, va);
274 int const rc = VERR_GENERAL_FAILURE;
275#endif
276 va_end(va);
277
278 m_rcExit = RTEXITCODE_SYNTAX;
279 if (m_rc == VINF_SUCCESS)
280 m_rc = rc;
281 return rc;
282 }
283
284 void setFailure(int rc) RT_NOEXCEPT
285 {
286 m_rcExit = RTEXITCODE_FAILURE;
287 if (m_rc == VINF_SUCCESS)
288 m_rc = rc;
289 }
290
291 RTEXITCODE getExitCode() const RT_NOEXCEPT
292 {
293 return m_rcExit;
294 }
295
296 int getStatus() const RT_NOEXCEPT
297 {
298 return m_rc;
299 }
300
301private:
302 int parseSelection(const char *pszSelection, uint64_t *pfSel)
303 {
304 static const struct { const char *psz; size_t cch; uint64_t fSel; } s_aMnemonics[] =
305 {
306 { RT_STR_TUPLE("h"), DUMPIMAGE_SELECT_HEADERS },
307 { RT_STR_TUPLE("hd"), DUMPIMAGE_SELECT_HEADERS },
308 { RT_STR_TUPLE("hdr"), DUMPIMAGE_SELECT_HEADERS },
309 { RT_STR_TUPLE("header"), DUMPIMAGE_SELECT_HEADERS },
310 { RT_STR_TUPLE("headers"), DUMPIMAGE_SELECT_HEADERS },
311 { RT_STR_TUPLE("s"), DUMPIMAGE_SELECT_SECTIONS },
312 { RT_STR_TUPLE("se"), DUMPIMAGE_SELECT_SECTIONS },
313 { RT_STR_TUPLE("sec"), DUMPIMAGE_SELECT_SECTIONS },
314 { RT_STR_TUPLE("section"), DUMPIMAGE_SELECT_SECTIONS },
315 { RT_STR_TUPLE("sections"), DUMPIMAGE_SELECT_SECTIONS },
316 { RT_STR_TUPLE("d"), DUMPIMAGE_SELECT_DEBUG },
317 { RT_STR_TUPLE("db"), DUMPIMAGE_SELECT_DEBUG },
318 { RT_STR_TUPLE("dg"), DUMPIMAGE_SELECT_DEBUG },
319 { RT_STR_TUPLE("dbg"), DUMPIMAGE_SELECT_DEBUG },
320 { RT_STR_TUPLE("dbginfo"), DUMPIMAGE_SELECT_DEBUG },
321 { RT_STR_TUPLE("debug"), DUMPIMAGE_SELECT_DEBUG },
322 { RT_STR_TUPLE("f"), DUMPIMAGE_SELECT_FIXUP },
323 { RT_STR_TUPLE("fx"), DUMPIMAGE_SELECT_FIXUP },
324 { RT_STR_TUPLE("fix"), DUMPIMAGE_SELECT_FIXUP },
325 { RT_STR_TUPLE("fixup"), DUMPIMAGE_SELECT_FIXUP },
326 { RT_STR_TUPLE("fixups"), DUMPIMAGE_SELECT_FIXUP },
327 { RT_STR_TUPLE("e"), DUMPIMAGE_SELECT_EXPORTS },
328 { RT_STR_TUPLE("ex"), DUMPIMAGE_SELECT_EXPORTS },
329 { RT_STR_TUPLE("exp"), DUMPIMAGE_SELECT_EXPORTS },
330 { RT_STR_TUPLE("export"), DUMPIMAGE_SELECT_EXPORTS },
331 { RT_STR_TUPLE("exports"), DUMPIMAGE_SELECT_EXPORTS },
332 { RT_STR_TUPLE("i"), DUMPIMAGE_SELECT_IMPORTS },
333 { RT_STR_TUPLE("im"), DUMPIMAGE_SELECT_IMPORTS },
334 { RT_STR_TUPLE("imp"), DUMPIMAGE_SELECT_IMPORTS },
335 { RT_STR_TUPLE("import"), DUMPIMAGE_SELECT_IMPORTS },
336 { RT_STR_TUPLE("imports"), DUMPIMAGE_SELECT_IMPORTS },
337 { RT_STR_TUPLE("l"), DUMPIMAGE_SELECT_LOAD_CONFIG },
338 { RT_STR_TUPLE("lc"), DUMPIMAGE_SELECT_LOAD_CONFIG },
339 { RT_STR_TUPLE("lcfg"), DUMPIMAGE_SELECT_LOAD_CONFIG },
340 { RT_STR_TUPLE("loadcfg"), DUMPIMAGE_SELECT_LOAD_CONFIG },
341 { RT_STR_TUPLE("rc"), DUMPIMAGE_SELECT_RESOURCES },
342 { RT_STR_TUPLE("rcs"), DUMPIMAGE_SELECT_RESOURCES },
343 { RT_STR_TUPLE("rcsrc"), DUMPIMAGE_SELECT_RESOURCES },
344 { RT_STR_TUPLE("resource"), DUMPIMAGE_SELECT_RESOURCES },
345 { RT_STR_TUPLE("resources"), DUMPIMAGE_SELECT_RESOURCES },
346 { RT_STR_TUPLE("t"), DUMPIMAGE_SELECT_TLS },
347 { RT_STR_TUPLE("tls"), DUMPIMAGE_SELECT_TLS },
348 /* masks: */
349 { RT_STR_TUPLE("all"), DUMPIMAGE_SELECT_EVERYTHING },
350 { RT_STR_TUPLE("everything"), DUMPIMAGE_SELECT_EVERYTHING },
351 { RT_STR_TUPLE("def"), DUMPIMAGE_SELECT_DEFAULT },
352 { RT_STR_TUPLE("default"), DUMPIMAGE_SELECT_DEFAULT },
353 };
354
355 *pfSel = 0;
356 char ch;
357 do
358 {
359 /* Skip leading spaces and commas. */
360 while ((ch = *pszSelection) != '\0' && (RT_C_IS_BLANK(ch) || ch == ','))
361 pszSelection++;
362
363 /* Find the end of the selection mnemonic. */
364 size_t cch = 0;
365 while (ch != '\0' && ch != ',' && !RT_C_IS_BLANK(ch))
366 ch = pszSelection[++cch];
367 if (!cch)
368 {
369 if (*pfSel)
370 break;
371 mySyntax("No selection");
372 return VERR_INVALID_PARAMETER;
373 }
374
375 /* Look it up. */
376 uint32_t i;
377 for (i = 0; i < RT_ELEMENTS(s_aMnemonics); i++)
378 if (cch == s_aMnemonics[i].cch && memcmp(s_aMnemonics[i].psz, pszSelection, cch) == 0)
379 {
380 *pfSel = s_aMnemonics[i].fSel;
381 break;
382 }
383 if (i >= RT_ELEMENTS(s_aMnemonics))
384 {
385 mySyntax("Unknown selection '%.*s'", cch, pszSelection);
386 return VERR_INVALID_PARAMETER;
387 }
388 } while (ch != '\0');
389 return VINF_SUCCESS;
390 }
391
392public:
393 int optSelectionInclude(const char *pszSelection) RT_NOEXCEPT
394 {
395 uint64_t fSel = 0;
396 int rc = parseSelection(pszSelection, &fSel);
397 if (RT_SUCCESS(rc))
398 m_fSelection |= fSel;
399 return rc;
400 }
401
402 int optSelectionOnly(const char *pszSelection) RT_NOEXCEPT
403 {
404 uint64_t fSel = 0;
405 int rc = parseSelection(pszSelection, &fSel);
406 if (RT_SUCCESS(rc))
407 {
408 if (m_fSelection == DUMPIMAGE_SELECT_DEFAULT)
409 m_fSelection = 0;
410 m_fSelection |= fSel;
411 }
412 return rc;
413 }
414
415 int optSelectionSkip(const char *pszSelection) RT_NOEXCEPT
416 {
417 uint64_t fSel = 0;
418 int rc = parseSelection(pszSelection, &fSel);
419 if (RT_SUCCESS(rc))
420 m_fSelection &= ~fSel;
421 return rc;
422
423 }
424
425 /** @} */
426
427
428 /** @name Methods working on a target.
429 * @{ */
430
431#ifndef DBGC_DUMP_IMAGE_TOOL
432 void setTarget(const char *a_pszName, PCDBGCVAR a_pImageBase) RT_NOEXCEPT
433#else
434 void setTarget(const char *a_pszName, RTVFSFILE a_hVfsFile) RT_NOEXCEPT
435#endif
436 {
437 m_cTargets += 1;
438 m_pszName = a_pszName;
439#ifndef DBGC_DUMP_IMAGE_TOOL
440 m_pImageBase = a_pImageBase;
441#else
442 m_hVfsFile = a_hVfsFile;
443#endif
444 }
445
446 void clearTarget() RT_NOEXCEPT
447 {
448 m_pszName = NULL;
449#ifndef DBGC_DUMP_IMAGE_TOOL
450 m_pImageBase = NULL;
451#else
452 RTVfsFileRelease(m_hVfsFile);
453 m_hVfsFile = NIL_RTVFSFILE;
454#endif
455 }
456
457 bool isFirstTarget() const RT_NOEXCEPT
458 {
459 return m_cTargets == 1;
460 }
461
462 /**
463 * Early read function.
464 *
465 * This kind of works on file offsets, though we all knows that it really
466 * depends on whether the stuff being dumped is in-memory or a file. However,
467 * in the latter case we do not have the ability to do any RVA translation, thus
468 * the input is treated as file offsets.
469 */
470 int readAt(size_t off, void *pvDst, size_t cbToRead, size_t *pcbRead) RT_NOEXCEPT
471 {
472 RT_BZERO(pvDst, cbToRead);
473 if (pcbRead)
474 *pcbRead = 0;
475/** @todo introduce a buffer here? */
476#ifndef DBGC_DUMP_IMAGE_TOOL
477 DBGCVAR AddrToReadAt;
478 int rc = DBGCCmdHlpEval(m_pCmdHlp, &AddrToReadAt, "%DV + %#zx", m_pImageBase, off);
479 if (RT_SUCCESS(rc))
480 {
481 rc = DBGCCmdHlpMemRead(m_pCmdHlp, pvDst, cbToRead, &AddrToReadAt, pcbRead);
482 if (RT_SUCCESS(rc))
483 return VINF_SUCCESS;
484 return myError(rc, "Failed to read %zu bytes at offset %Dv", cbToRead, &AddrToReadAt);
485 }
486 return myError(rc, "Failed to calculate address %Dv + #%zx for %#zx byte read", m_pImageBase, off, cbToRead);
487
488#else /* DBGC_DUMP_IMAGE_TOOL */
489 int rc = RTVfsFileReadAt(m_hVfsFile, off, pvDst, cbToRead, pcbRead);
490 if (RT_SUCCESS(rc))
491 return VINF_SUCCESS;
492 return myError(rc, "Failed to read %zu bytes at offset %#zx", cbToRead, off);
493#endif /* DBGC_DUMP_IMAGE_TOOL */
494 }
495
496 int dumpImage(const char *pszImageBaseAddr) RT_NOEXCEPT;
497
498 /** @} */
499};
500
501
502/** Stringifies a 32-bit flag value. */
503static void dbgcDumpImageFlags32(DumpImageCmd *pCmd, uint32_t fFlags, DBGCDUMPFLAGENTRY const *paEntries, size_t cEntries)
504{
505 for (size_t i = 0; i < cEntries; i++)
506 if (fFlags & paEntries[i].fFlag)
507 pCmd->myPrintf(" %s", paEntries[i].pszNm);
508}
509
510
511/*********************************************************************************************************************************
512* DumpImageBase *
513*********************************************************************************************************************************/
514/**
515 * Base class for the dumpers.
516 */
517class DumpImageBase
518{
519protected:
520 DumpImageCmd *m_pCmd;
521
522private:
523 /** The Image base address. */
524 uint64_t m_uImageBaseAddr;
525protected:
526 /** The full formatted address width. */
527 uint8_t m_cchAddr;
528private:
529 /** The formatted address value width. */
530 uint8_t m_cchAddrValue;
531 /** The address prefix length. */
532 uint8_t m_cchAddrPfx;
533 /** The address prefix. */
534 char m_szAddrPfx[16 - 3];
535
536private:
537 DumpImageBase();
538
539 void setupAddrFormatting(const char *a_pszImageBaseAddr) RT_NOEXCEPT
540 {
541 /*
542 * Expected inputs: %%12345678, %123456789abcdef, 0x12345678, 0008:12345678
543 *
544 * So, work backwards till be find the start of the address/offset value
545 * component, and treat what comes first as a prefix.
546 */
547 size_t const cch = strlen(a_pszImageBaseAddr);
548 size_t cchAddrPfx = cch;
549 while (cchAddrPfx > 0 && RT_C_IS_XDIGIT(a_pszImageBaseAddr[cchAddrPfx - 1]))
550 cchAddrPfx--;
551
552 size_t cchLeadingZeros = 0;
553 while (a_pszImageBaseAddr[cchAddrPfx + cchLeadingZeros] == '0')
554 cchLeadingZeros++;
555
556 int rc = RTStrToUInt64Full(&a_pszImageBaseAddr[cchAddrPfx], 16, &m_uImageBaseAddr);
557 AssertRCSuccess(rc);
558 m_cchAddrValue = (uint8_t)(cch - cchAddrPfx);
559 Assert(m_cchAddrValue == cch - cchAddrPfx);
560 if (m_cchAddrValue > 8 && cchLeadingZeros > 1)
561 m_cchAddrValue = RT_ALIGN_T(m_cchAddrValue - (uint8_t)(cchLeadingZeros - 1), 2, uint8_t);
562
563 AssertStmt(cchAddrPfx < sizeof(m_szAddrPfx), cchAddrPfx = sizeof(m_szAddrPfx) - 1);
564 memcpy(m_szAddrPfx, a_pszImageBaseAddr, cchAddrPfx);
565 m_szAddrPfx[cchAddrPfx] = '\0';
566 m_cchAddrPfx = (uint8_t)cchAddrPfx;
567
568 m_cchAddr = m_cchAddrPfx + m_cchAddrValue;
569 }
570
571public:
572 DumpImageBase(DumpImageCmd *a_pCmd, const char *a_pszImageBaseAddr) RT_NOEXCEPT
573 : m_pCmd(a_pCmd)
574 , m_uImageBaseAddr(0)
575 , m_cchAddr(0)
576 , m_cchAddrValue(12)
577 , m_cchAddrPfx(2)
578 , m_szAddrPfx("0x")
579 {
580 setupAddrFormatting(a_pszImageBaseAddr);
581 }
582
583 virtual ~DumpImageBase() { }
584
585 virtual size_t rvaToFileOffset(size_t uRva) const RT_NOEXCEPT = 0;
586 virtual size_t getEndRva(bool a_fAligned = true) const RT_NOEXCEPT = 0;
587
588 char *rvaToStringWithAddr(size_t uRva, char *pszDst, size_t cbDst, bool fWide = false) const RT_NOEXCEPT
589 {
590 if (!fWide)
591 RTStrPrintf(pszDst, cbDst, "%#09zx/%s%0*RX64", uRva, m_szAddrPfx, m_cchAddrValue, m_uImageBaseAddr + uRva);
592 else
593 RTStrPrintf(pszDst, cbDst, "%#09zx / %s%0*RX64", uRva, m_szAddrPfx, m_cchAddrValue, m_uImageBaseAddr + uRva);
594 return pszDst;
595 }
596
597 void myPrintf(const char *pszFormat, ...) const RT_NOEXCEPT
598 {
599 va_list va;
600 va_start(va, pszFormat);
601 m_pCmd->myPrintfV(pszFormat, va);
602 va_end(va);
603 }
604
605 void myPrintHeader(size_t uRva, const char *pszFormat, ...) const RT_NOEXCEPT
606 {
607 char szTmp[64];
608 char szLine[128];
609 va_list va;
610 va_start(va, pszFormat);
611 size_t const cchLine = RTStrPrintf(szLine, sizeof(szLine), "%s - %N",
612 rvaToStringWithAddr(uRva, szTmp, sizeof(szTmp), true), pszFormat, &va);
613 va_end(va);
614 myPrintf("\n"
615 "%s\n"
616 "%.*s====\n",
617 szLine,
618 cchLine, "===============================================================================");
619 }
620
621 int myError(const char *pszFormat, ...) const RT_NOEXCEPT
622 {
623 va_list va;
624 va_start(va, pszFormat);
625 int rc = m_pCmd->myErrorV(pszFormat, va);
626 va_end(va);
627 return rc;
628 }
629
630 int myError(int rc, const char *pszFormat, ...) const RT_NOEXCEPT
631 {
632 va_list va;
633 va_start(va, pszFormat);
634 rc = m_pCmd->myErrorV(rc, pszFormat, va);
635 va_end(va);
636 return rc;
637 }
638
639 int readBytesAtRva(size_t uRva, void *pvBuf, size_t cbToRead, size_t *pcbRead = NULL) RT_NOEXCEPT
640 {
641#ifndef DBGC_DUMP_IMAGE_TOOL
642 /* RVA and offset is the same in this context. */
643 return m_pCmd->readAt(uRva, pvBuf, cbToRead, pcbRead);
644#else
645 size_t const offFile = rvaToFileOffset(uRva);
646 if (offFile != ~(size_t)0)
647 return m_pCmd->readAt(offFile, pvBuf, cbToRead, pcbRead);
648 return myError(VERR_READ_ERROR, "Failed to convert RVA %#zx to file offset for %zu byte read!", uRva, cbToRead);
649#endif
650 }
651};
652
653
654/**
655 * Buffered reading by relative virtual address (RVA).
656 */
657class DumpImageBufferedReader
658{
659private:
660 /** Static sized buffer. */
661 uint8_t m_abBufFixed[4096];
662 /** Pointer to m_abBufFixed if that's sufficient, otherwise heap buffer. */
663 uint8_t *m_pbBuf;
664 /** The size of the buffer m_pbBuf points at. */
665 size_t m_cbBufAlloc;
666 /** Number of valid bytes in the buffer. */
667 size_t m_cbBuf;
668 /** The RVA of the first buffer byte, maximum value if empty. */
669 size_t m_uRvaBuf;
670 /** Pointer to the image dumper. */
671 DumpImageBase *m_pImage;
672
673 int loadBuffer(size_t uRva) RT_NOEXCEPT
674 {
675 /* Check that the RVA is within the image. */
676 size_t const cbMaxRva = m_pImage->getEndRva();
677 if (uRva >= cbMaxRva)
678 return VERR_EOF;
679
680 /* Adjust the RVA if we're reading beyond the end of the image. */
681 if (uRva + m_cbBufAlloc > RT_ALIGN_Z(cbMaxRva, 8))
682 uRva = m_cbBufAlloc < RT_ALIGN_Z(cbMaxRva, 8) ? RT_ALIGN_Z(cbMaxRva, 8) - m_cbBufAlloc : 0;
683
684 /* Do the read. In case of failure readBytesAtRva will zero the buffer. */
685 m_uRvaBuf = uRva;
686 m_cbBuf = 0;
687 return m_pImage->readBytesAtRva(uRva, m_pbBuf, RT_MIN(cbMaxRva - uRva, m_cbBufAlloc), &m_cbBuf);
688 }
689
690 /** Resizes the buffer if the current one can't hold @a cbNeeded bytes. */
691 int ensureBufferSpace(size_t cbNeeded) RT_NOEXCEPT
692 {
693 if (cbNeeded > m_cbBufAlloc)
694 {
695 cbNeeded = RT_ALIGN_Z(cbNeeded, 512);
696 void *pvNew = RTMemTmpAllocZ(cbNeeded);
697 if (!pvNew)
698 return m_pImage->myError(VERR_NO_TMP_MEMORY, "Failed to allocate %zu (%#zx) bytes", cbNeeded, cbNeeded);
699 memcpy(pvNew, m_pbBuf, RT_MIN(m_cbBuf, m_cbBufAlloc));
700
701 if (m_pbBuf != &m_abBufFixed[0])
702 RTMemTmpFree(m_pbBuf);
703 m_pbBuf = (uint8_t *)pvNew;
704 m_cbBufAlloc = cbNeeded;
705 }
706 return VINF_SUCCESS;
707 }
708
709 DumpImageBufferedReader();
710
711public:
712 DumpImageBufferedReader(DumpImageBase *a_pImage) RT_NOEXCEPT
713 : m_pbBuf(&m_abBufFixed[0])
714 , m_cbBufAlloc(sizeof(m_abBufFixed))
715 , m_cbBuf(0)
716 , m_uRvaBuf(~(size_t)0)
717 , m_pImage(a_pImage)
718 {
719 RT_ZERO(m_abBufFixed);
720 }
721
722 /** Copy constructor. */
723 DumpImageBufferedReader(DumpImageBufferedReader const &a_rThat) RT_NOEXCEPT
724 : m_pbBuf(&m_abBufFixed[0])
725 , m_cbBufAlloc(sizeof(m_abBufFixed))
726 , m_cbBuf(RT_MIN(a_rThat.m_cbBuf, sizeof(m_abBufFixed)))
727 , m_uRvaBuf(a_rThat.m_uRvaBuf)
728 , m_pImage(a_rThat.m_pImage)
729 {
730 memcpy(m_abBufFixed, a_rThat.m_pbBuf, m_cbBuf);
731 if (m_cbBuf < sizeof(m_abBufFixed))
732 RT_BZERO(&m_abBufFixed[m_cbBuf], sizeof(m_abBufFixed) - m_cbBuf);
733 }
734
735 ~DumpImageBufferedReader() RT_NOEXCEPT
736 {
737 if (m_pbBuf != &m_abBufFixed[0])
738 RTMemTmpFree(m_pbBuf);
739 m_pbBuf = NULL;
740 }
741
742 /**
743 * Reads @a cbToRead bytes at @a uRva into @a pvDst.
744 *
745 * The buffer is entirely zeroed before reading anything, so it's okay to ignore
746 * the status code.
747 */
748 int readBytes(size_t uRva, void *pvDst, size_t cbToRead) RT_NOEXCEPT
749 {
750 RT_BZERO(pvDst, cbToRead);
751
752 while (cbToRead)
753 {
754 /*
755 * Is the start of the request overlapping with the buffer?
756 */
757 if (uRva >= m_uRvaBuf)
758 {
759 size_t const offBuf = uRva - m_uRvaBuf;
760 if (offBuf < m_cbBuf)
761 {
762 size_t const cbThisRead = RT_MIN(m_cbBuf - offBuf, cbToRead);
763 memcpy(pvDst, &m_pbBuf[offBuf], cbThisRead);
764 if (cbToRead <= cbThisRead)
765 return VINF_SUCCESS;
766 uRva += cbThisRead;
767 cbToRead -= cbThisRead;
768 pvDst = (uint8_t *)pvDst + cbThisRead;
769 }
770 }
771
772 /*
773 * Fill buffer.
774 */
775 int rc = loadBuffer(uRva);
776 if (RT_FAILURE(rc))
777 return rc;
778 }
779 return VINF_SUCCESS;
780 }
781
782 /**
783 * Ensures @a cbItem at @a uRva is in the buffer and returns a pointer to it.
784 *
785 * The returned pointer is only valid till the next call to the reader instance.
786 *
787 * @returns NULL if failed to load the range into the buffer.
788 * @note Extra buffer space will be allocated if @a cbItem is larger than the
789 * internal buffer.
790 */
791 uint8_t const *bufferedBytes(size_t uRva, size_t cbItem) RT_NOEXCEPT
792 {
793 /* Do we need to load the item into the buffer? */
794 if ( uRva < m_uRvaBuf
795 || uRva + cbItem > m_uRvaBuf + m_cbBuf)
796 {
797 int rc = ensureBufferSpace(cbItem);
798 if (RT_SUCCESS(rc))
799 rc = loadBuffer(uRva);
800 if (RT_FAILURE(rc))
801 return NULL;
802 }
803
804 Assert(uRva >= m_uRvaBuf && uRva + cbItem <= m_uRvaBuf + m_cbBuf);
805 return &m_pbBuf[uRva - m_uRvaBuf];
806 }
807
808 /**
809 * Gets a buffered zero terminated string at @a uRva.
810 *
811 * @note The implied max length is the size of the internal buffer. No extra
812 * space will be allocated if the string doesn't terminate within the
813 * buffer size.
814 */
815 const char *bufferedString(size_t uRva) RT_NOEXCEPT
816 {
817 /* Do we need to reload the buffer? */
818 if ( uRva < m_uRvaBuf
819 || uRva >= m_uRvaBuf + m_cbBuf
820 || ( uRva != m_uRvaBuf
821 && !memchr(&m_pbBuf[uRva - m_uRvaBuf], '\0', m_cbBufAlloc - (uRva - m_uRvaBuf))))
822 {
823 int rc = loadBuffer(uRva);
824 AssertRCReturn(rc, NULL);
825 }
826
827 /* The RVA is within the buffer now, just check that the string ends
828 before the end of the buffer. */
829 Assert(uRva >= m_uRvaBuf && uRva < m_uRvaBuf + m_cbBuf);
830 size_t const offString = uRva - m_uRvaBuf;
831 const char * const pszString = (const char *)&m_pbBuf[offString];
832 AssertReturn(memchr(pszString, '\0', m_cbBufAlloc - offString), NULL);
833 return pszString;
834 }
835
836 /**
837 * Gets a simple integer value, with default in case of failure.
838 */
839 template<typename IntType>
840 IntType bufferedInt(size_t uRva, IntType Default = 0) RT_NOEXCEPT
841 {
842 AssertCompile(sizeof(IntType) <= 8);
843 AssertReturn(uRva < uRva + sizeof(IntType), Default);
844
845 /* Do we need to reload the buffer? */
846 if ( uRva < m_uRvaBuf
847 || uRva + sizeof(IntType) > m_uRvaBuf + m_cbBuf)
848 {
849 int rc = loadBuffer(uRva);
850 AssertRCReturn(rc, Default);
851 }
852
853 /* The RVA is within the buffer now. */
854 Assert(uRva >= m_uRvaBuf && uRva + sizeof(IntType) <= m_uRvaBuf + m_cbBuf);
855 return *(IntType *)&m_pbBuf[uRva - m_uRvaBuf];
856 }
857
858};
859
860
861/*********************************************************************************************************************************
862* PE *
863*********************************************************************************************************************************/
864
865/**
866 * PE dumper class.
867 */
868class DumpImagePe : public DumpImageBase
869{
870public:
871 /** Pointer to the file header. */
872 PCIMAGE_FILE_HEADER m_pFileHdr;
873 /** Pointer to the NT headers. */
874 union
875 {
876 PCIMAGE_NT_HEADERS32 pNt32;
877 PCIMAGE_NT_HEADERS64 pNt64;
878 void *pv;
879 } u;
880 /** The PE header RVA / file offset. */
881 uint32_t m_offPeHdr;
882 /** Section table RVA / file offset. */
883 uint32_t m_offShdrs;
884 /** Pointer to the section headers. */
885 PCIMAGE_SECTION_HEADER m_paShdrs;
886 /** Number of section headers. */
887 unsigned m_cShdrs;
888 /** Number of RVA and sizes (data directory entries). */
889 unsigned cDataDir;
890 /** Pointer to the data directory. */
891 PCIMAGE_DATA_DIRECTORY paDataDir;
892
893public:
894 DumpImagePe(DumpImageCmd *a_pCmd, const char *a_pszImageBaseAddr,
895 uint32_t a_offPeHdr, PCIMAGE_FILE_HEADER a_pFileHdr, void *a_pvNtHdrs,
896 uint32_t a_offShdrs, unsigned a_cShdrs, PCIMAGE_SECTION_HEADER a_paShdrs) RT_NOEXCEPT
897 : DumpImageBase(a_pCmd, a_pszImageBaseAddr)
898 , m_pFileHdr(a_pFileHdr)
899 , m_offPeHdr(a_offPeHdr)
900 , m_offShdrs(a_offShdrs)
901 , m_paShdrs(a_paShdrs)
902 , m_cShdrs(a_cShdrs)
903 , cDataDir(0)
904 , paDataDir(NULL)
905 {
906 u.pv = a_pvNtHdrs;
907 if (a_pFileHdr->SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32))
908 {
909 paDataDir = u.pNt32->OptionalHeader.DataDirectory;
910 cDataDir = u.pNt32->OptionalHeader.NumberOfRvaAndSizes;
911 }
912 else
913 {
914 paDataDir = u.pNt64->OptionalHeader.DataDirectory;
915 cDataDir = u.pNt64->OptionalHeader.NumberOfRvaAndSizes;
916 }
917 }
918
919 virtual size_t rvaToFileOffset(size_t uRva) const RT_NOEXCEPT RT_OVERRIDE
920 {
921 AssertReturn(m_paShdrs, uRva);
922 AssertReturn(u.pv, uRva);
923 if (uRva < m_paShdrs[0].VirtualAddress)
924 return uRva;
925 /** @todo handle uninitialized data. needs different return code or smth. */
926 unsigned iSh = m_cShdrs;
927 while (iSh-- > 0)
928 {
929 if (uRva >= m_paShdrs[iSh].VirtualAddress)
930 {
931 size_t offSection = uRva - m_paShdrs[iSh].VirtualAddress;
932 if (offSection < m_paShdrs[iSh].SizeOfRawData)
933 return m_paShdrs[iSh].PointerToRawData + offSection;
934 return ~(size_t)0;
935 }
936 }
937 return ~(size_t)0;
938 }
939
940 virtual size_t getEndRva(bool a_fAligned = true) const RT_NOEXCEPT RT_OVERRIDE
941 {
942 AssertCompileMembersAtSameOffset(IMAGE_NT_HEADERS32, OptionalHeader.SizeOfImage,
943 IMAGE_NT_HEADERS64, OptionalHeader.SizeOfImage);
944 if (a_fAligned)
945 {
946 uint32_t const cbAlignment = u.pNt32->OptionalHeader.SectionAlignment;
947 if (RT_IS_POWER_OF_TWO(cbAlignment))
948 return RT_ALIGN_Z((size_t)u.pNt32->OptionalHeader.SizeOfImage, cbAlignment);
949 }
950 return u.pNt32->OptionalHeader.SizeOfImage;
951 }
952
953
954 /** @name Helpers
955 * @{
956 */
957
958 char *timestampToString(uint32_t uTimestamp, char *pszDst, size_t cbDst) RT_NOEXCEPT
959 {
960 /** @todo detect random numbers and skip formatting them. */
961 RTTIMESPEC TimeSpec;
962 RTTIME Time;
963 RTTimeToStringEx(RTTimeExplode(&Time, RTTimeSpecSetDosSeconds(&TimeSpec, uTimestamp)),
964 pszDst, cbDst, 0 /*cFractionDigits*/);
965 return pszDst;
966 }
967
968 /** @} */
969
970 /** @name Constants naming
971 * @{ */
972
973 static const char *machineToString(uint16_t uMachine) RT_NOEXCEPT
974 {
975 switch (uMachine)
976 {
977 case IMAGE_FILE_MACHINE_I386 : return "I386";
978 case IMAGE_FILE_MACHINE_AMD64 : return "AMD64";
979 case IMAGE_FILE_MACHINE_UNKNOWN : return "UNKNOWN";
980 case IMAGE_FILE_MACHINE_BASIC_16 : return "BASIC_16";
981 case IMAGE_FILE_MACHINE_BASIC_16_TV : return "BASIC_16_TV";
982 case IMAGE_FILE_MACHINE_IAPX16 : return "IAPX16";
983 case IMAGE_FILE_MACHINE_IAPX16_TV : return "IAPX16_TV";
984 //case IMAGE_FILE_MACHINE_IAPX20 : return "IAPX20";
985 //case IMAGE_FILE_MACHINE_IAPX20_TV : return "IAPX20_TV";
986 case IMAGE_FILE_MACHINE_I8086 : return "I8086";
987 case IMAGE_FILE_MACHINE_I8086_TV : return "I8086_TV";
988 case IMAGE_FILE_MACHINE_I286_SMALL : return "I286_SMALL";
989 case IMAGE_FILE_MACHINE_MC68 : return "MC68";
990 //case IMAGE_FILE_MACHINE_MC68_WR : return "MC68_WR";
991 case IMAGE_FILE_MACHINE_MC68_TV : return "MC68_TV";
992 case IMAGE_FILE_MACHINE_MC68_PG : return "MC68_PG";
993 //case IMAGE_FILE_MACHINE_I286_LARGE : return "I286_LARGE";
994 case IMAGE_FILE_MACHINE_U370_WR : return "U370_WR";
995 case IMAGE_FILE_MACHINE_AMDAHL_470_WR: return "AMDAHL_470_WR";
996 case IMAGE_FILE_MACHINE_AMDAHL_470_RO: return "AMDAHL_470_RO";
997 case IMAGE_FILE_MACHINE_U370_RO : return "U370_RO";
998 case IMAGE_FILE_MACHINE_R4000 : return "R4000";
999 case IMAGE_FILE_MACHINE_WCEMIPSV2 : return "WCEMIPSV2";
1000 case IMAGE_FILE_MACHINE_VAX_WR : return "VAX_WR";
1001 case IMAGE_FILE_MACHINE_VAX_RO : return "VAX_RO";
1002 case IMAGE_FILE_MACHINE_SH3 : return "SH3";
1003 case IMAGE_FILE_MACHINE_SH3DSP : return "SH3DSP";
1004 case IMAGE_FILE_MACHINE_SH4 : return "SH4";
1005 case IMAGE_FILE_MACHINE_SH5 : return "SH5";
1006 case IMAGE_FILE_MACHINE_ARM : return "ARM";
1007 case IMAGE_FILE_MACHINE_THUMB : return "THUMB";
1008 case IMAGE_FILE_MACHINE_ARMNT : return "ARMNT";
1009 case IMAGE_FILE_MACHINE_AM33 : return "AM33";
1010 case IMAGE_FILE_MACHINE_POWERPC : return "POWERPC";
1011 case IMAGE_FILE_MACHINE_POWERPCFP : return "POWERPCFP";
1012 case IMAGE_FILE_MACHINE_IA64 : return "IA64";
1013 case IMAGE_FILE_MACHINE_MIPS16 : return "MIPS16";
1014 case IMAGE_FILE_MACHINE_MIPSFPU : return "MIPSFPU";
1015 case IMAGE_FILE_MACHINE_MIPSFPU16 : return "MIPSFPU16";
1016 case IMAGE_FILE_MACHINE_EBC : return "EBC";
1017 case IMAGE_FILE_MACHINE_M32R : return "M32R";
1018 case IMAGE_FILE_MACHINE_ARM64 : return "ARM64";
1019 }
1020 return "??";
1021 }
1022
1023 static const char *dataDirectoryToString(unsigned iDir) RT_NOEXCEPT
1024 {
1025 switch (iDir)
1026 {
1027 case IMAGE_DIRECTORY_ENTRY_EXPORT: return "EXPORT";
1028 case IMAGE_DIRECTORY_ENTRY_IMPORT: return "IMPORT";
1029 case IMAGE_DIRECTORY_ENTRY_RESOURCE: return "RESOURCE";
1030 case IMAGE_DIRECTORY_ENTRY_EXCEPTION: return "EXCEPTION";
1031 case IMAGE_DIRECTORY_ENTRY_SECURITY: return "SECURITY";
1032 case IMAGE_DIRECTORY_ENTRY_BASERELOC: return "BASERELOC";
1033 case IMAGE_DIRECTORY_ENTRY_DEBUG: return "DEBUG";
1034 case IMAGE_DIRECTORY_ENTRY_ARCHITECTURE: return "ARCHITECTURE";
1035 case IMAGE_DIRECTORY_ENTRY_GLOBALPTR: return "GLOBALPTR";
1036 case IMAGE_DIRECTORY_ENTRY_TLS: return "TLS";
1037 case IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG: return "LOAD_CONFIG";
1038 case IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT: return "BOUND_IMPORT";
1039 case IMAGE_DIRECTORY_ENTRY_IAT: return "IAT";
1040 case IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT: return "DELAY_IMPORT";
1041 case IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR: return "COM_DESCRIPTOR";
1042 }
1043 return "??";
1044 }
1045
1046 static const char *debugTypeToString(uint32_t uType, char *pszTmp, size_t cchTmp) RT_NOEXCEPT
1047 {
1048 switch (uType)
1049 {
1050 case IMAGE_DEBUG_TYPE_UNKNOWN: return "UNKNOWN";
1051 case IMAGE_DEBUG_TYPE_COFF: return "COFF";
1052 case IMAGE_DEBUG_TYPE_CODEVIEW: return "CODEVIEW";
1053 case IMAGE_DEBUG_TYPE_FPO: return "FPO";
1054 case IMAGE_DEBUG_TYPE_MISC: return "MISC";
1055 case IMAGE_DEBUG_TYPE_EXCEPTION: return "EXCEPTION";
1056 case IMAGE_DEBUG_TYPE_FIXUP: return "FIXUP";
1057 case IMAGE_DEBUG_TYPE_OMAP_TO_SRC: return "OMAP_TO_SRC";
1058 case IMAGE_DEBUG_TYPE_OMAP_FROM_SRC: return "OMAP_FROM_SRC";
1059 case IMAGE_DEBUG_TYPE_BORLAND: return "BORLAND";
1060 case IMAGE_DEBUG_TYPE_RESERVED10: return "RESERVED10";
1061 case IMAGE_DEBUG_TYPE_CLSID: return "CLSID";
1062 case IMAGE_DEBUG_TYPE_VC_FEATURE: return "VC_FEATURE";
1063 case IMAGE_DEBUG_TYPE_POGO: return "POGO";
1064 case IMAGE_DEBUG_TYPE_ILTCG: return "ILTCG";
1065 case IMAGE_DEBUG_TYPE_MPX: return "MPX";
1066 case IMAGE_DEBUG_TYPE_REPRO: return "REPRO";
1067 }
1068 RTStrPrintf(pszTmp, cchTmp, "%#RX32", uType);
1069 return pszTmp;
1070 }
1071
1072 /** @} */
1073
1074
1075 /** @name Dumpers
1076 * @{
1077 */
1078
1079 int dumpPeHdr(void) RT_NOEXCEPT
1080 {
1081 if (!(m_pCmd->m_fSelection & DUMPIMAGE_SELECT_HEADERS))
1082 return VINF_SUCCESS;
1083 myPrintHeader(m_offPeHdr, "PE & File Header - %s", m_pCmd->m_pszName);
1084
1085 char szTmp[64];
1086 myPrintf("Signature: %#010RX32\n", u.pNt32->Signature);
1087 PCIMAGE_FILE_HEADER const pFileHdr = &u.pNt32->FileHeader;
1088 myPrintf("Machine: %s (%#06RX16)\n", machineToString(pFileHdr->Machine), pFileHdr->Machine);
1089 myPrintf("Number of sections: %#06RX16\n", pFileHdr->NumberOfSections);
1090 myPrintf("Timestamp: %#010RX32\n",
1091 pFileHdr->TimeDateStamp, timestampToString(pFileHdr->TimeDateStamp, szTmp, sizeof(szTmp)));
1092 if (pFileHdr->PointerToSymbolTable || pFileHdr->NumberOfSymbols)
1093 myPrintf("Symbol table: %#010RX32 L %#06RX16\n",
1094 pFileHdr->PointerToSymbolTable, pFileHdr->NumberOfSymbols);
1095 myPrintf("Size of optional header: %#06RX16\n", pFileHdr->SizeOfOptionalHeader);
1096
1097 myPrintf("Characteristics: %#06RX16", pFileHdr->Characteristics);
1098 if (pFileHdr->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) myPrintf(" RELOCS_STRIPPED");
1099 if (pFileHdr->Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) myPrintf(" EXECUTABLE_IMAGE");
1100 if (pFileHdr->Characteristics & IMAGE_FILE_LINE_NUMS_STRIPPED) myPrintf(" LINE_NUMS_STRIPPED");
1101 if (pFileHdr->Characteristics & IMAGE_FILE_LOCAL_SYMS_STRIPPED) myPrintf(" LOCAL_SYMS_STRIPPED");
1102 if (pFileHdr->Characteristics & IMAGE_FILE_AGGRESIVE_WS_TRIM) myPrintf(" AGGRESIVE_WS_TRIM");
1103 if (pFileHdr->Characteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE) myPrintf(" LARGE_ADDRESS_AWARE");
1104 if (pFileHdr->Characteristics & IMAGE_FILE_16BIT_MACHINE) myPrintf(" 16BIT_MACHINE");
1105 if (pFileHdr->Characteristics & IMAGE_FILE_BYTES_REVERSED_LO) myPrintf(" BYTES_REVERSED_LO");
1106 if (pFileHdr->Characteristics & IMAGE_FILE_32BIT_MACHINE) myPrintf(" 32BIT_MACHINE");
1107 if (pFileHdr->Characteristics & IMAGE_FILE_DEBUG_STRIPPED) myPrintf(" DEBUG_STRIPPED");
1108 if (pFileHdr->Characteristics & IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP) myPrintf(" REMOVABLE_RUN_FROM_SWAP");
1109 if (pFileHdr->Characteristics & IMAGE_FILE_NET_RUN_FROM_SWAP) myPrintf(" NET_RUN_FROM_SWAP");
1110 if (pFileHdr->Characteristics & IMAGE_FILE_SYSTEM) myPrintf(" SYSTEM");
1111 if (pFileHdr->Characteristics & IMAGE_FILE_DLL) myPrintf(" DLL");
1112 if (pFileHdr->Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY) myPrintf(" UP_SYSTEM_ONLY");
1113 if (pFileHdr->Characteristics & IMAGE_FILE_BYTES_REVERSED_HI) myPrintf(" BYTES_REVERSED_HI");
1114 myPrintf("\n");
1115 return VINF_SUCCESS;
1116 }
1117
1118 template<typename OptHdrType, bool const a_f32Bit>
1119 int dumpOptHdr(OptHdrType const *pOptHdr, uint32_t uBaseOfData = 0) RT_NOEXCEPT
1120 {
1121 if (!(m_pCmd->m_fSelection & DUMPIMAGE_SELECT_HEADERS))
1122 return VINF_SUCCESS;
1123 myPrintHeader(m_offPeHdr + RT_UOFFSETOF(IMAGE_NT_HEADERS32, OptionalHeader), "Optional Header");
1124
1125 char szTmp[64];
1126 myPrintf("Optional header magic: %#06RX16\n", pOptHdr->Magic);
1127 myPrintf("Linker version: %u.%02u\n", pOptHdr->MajorLinkerVersion, pOptHdr->MinorLinkerVersion);
1128 if (a_f32Bit)
1129 myPrintf("Image base: %#010RX32\n", pOptHdr->ImageBase);
1130 else
1131 myPrintf("Image base: %#018RX64\n", pOptHdr->ImageBase);
1132 myPrintf("Entrypoint: %s\n", rvaToStringWithAddr(pOptHdr->AddressOfEntryPoint, szTmp, sizeof(szTmp)));
1133 myPrintf("Base of code: %s\n", rvaToStringWithAddr(pOptHdr->BaseOfCode, szTmp, sizeof(szTmp)));
1134 if (a_f32Bit)
1135 myPrintf("Base of data: %s\n", rvaToStringWithAddr(uBaseOfData, szTmp, sizeof(szTmp)));
1136 myPrintf("Size of image: %#010RX32\n", pOptHdr->SizeOfImage);
1137 myPrintf("Size of headers: %#010RX32\n", pOptHdr->SizeOfHeaders);
1138 myPrintf("Size of code: %#010RX32\n", pOptHdr->SizeOfCode);
1139 myPrintf("Size of initialized data: %#010RX32\n", pOptHdr->SizeOfInitializedData);
1140 myPrintf("Size of uninitialized data: %#010RX32\n", pOptHdr->SizeOfUninitializedData);
1141 myPrintf("Section alignment: %#010RX32\n", pOptHdr->SectionAlignment);
1142 myPrintf("File alignment: %#010RX32\n", pOptHdr->FileAlignment);
1143 myPrintf("Image version: %u.%02u\n", pOptHdr->MajorImageVersion, pOptHdr->MinorImageVersion);
1144 myPrintf("Operating system version: %u.%02u\n", pOptHdr->MajorOperatingSystemVersion, pOptHdr->MinorOperatingSystemVersion);
1145 myPrintf("Windows version value: %#010RX32\n", pOptHdr->Win32VersionValue);
1146 const char *pszSubSys;
1147 switch (pOptHdr->Subsystem)
1148 {
1149 case IMAGE_SUBSYSTEM_UNKNOWN: pszSubSys = "Unknown"; break;
1150 case IMAGE_SUBSYSTEM_NATIVE: pszSubSys = "Native"; break;
1151 case IMAGE_SUBSYSTEM_WINDOWS_GUI: pszSubSys = "Windows GUI"; break;
1152 case IMAGE_SUBSYSTEM_WINDOWS_CUI: pszSubSys = "Windows char"; break;
1153 case IMAGE_SUBSYSTEM_OS2_GUI: pszSubSys = "OS/2 GUI"; break;
1154 case IMAGE_SUBSYSTEM_OS2_CUI: pszSubSys = "OS/2 char"; break;
1155 case IMAGE_SUBSYSTEM_POSIX_CUI: pszSubSys = "POSIX"; break;
1156 case IMAGE_SUBSYSTEM_NATIVE_WINDOWS: pszSubSys = "Native Windows 9x"; break;
1157 case IMAGE_SUBSYSTEM_WINDOWS_CE_GUI: pszSubSys = "Windows CE GUI"; break;
1158 case IMAGE_SUBSYSTEM_EFI_APPLICATION: pszSubSys = "EFI Application"; break;
1159 case IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER: pszSubSys = "EFI Boot Service Driver"; break;
1160 case IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER: pszSubSys = "EFI Runtime Driver"; break;
1161 case IMAGE_SUBSYSTEM_EFI_ROM: pszSubSys = "EFI ROM"; break;
1162 case IMAGE_SUBSYSTEM_XBOX: pszSubSys = "XBox"; break;
1163 case IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION: pszSubSys = "Windows Boot Application"; break;
1164 default: pszSubSys = "dunno"; break;
1165 }
1166 myPrintf("Subsystem: %s (%#x)\n", pszSubSys, pOptHdr->Subsystem);
1167 myPrintf("Subsystem version: %u.%02u\n", pOptHdr->MajorSubsystemVersion, pOptHdr->MinorSubsystemVersion);
1168 myPrintf("DLL characteristics: %#06RX16\n", pOptHdr->DllCharacteristics);
1169 myPrintf("Loader flags: %#010RX32\n", pOptHdr->LoaderFlags);
1170
1171 myPrintf("File checksum: %#010RX32\n", pOptHdr->CheckSum);
1172 myPrintf("Size of stack reserve: %#010RX64\n", (uint64_t)pOptHdr->SizeOfStackReserve);
1173 myPrintf("Size of stack commit: %#010RX64\n", (uint64_t)pOptHdr->SizeOfStackReserve);
1174 myPrintf("Size of heap reserve: %#010RX64\n", (uint64_t)pOptHdr->SizeOfHeapReserve);
1175 myPrintf("Size of heap commit: %#010RX64\n", (uint64_t)pOptHdr->SizeOfHeapReserve);
1176
1177 myPrintf("Number of data directories: %#010RX32%s\n", pOptHdr->NumberOfRvaAndSizes,
1178 pOptHdr->NumberOfRvaAndSizes <= RT_ELEMENTS(pOptHdr->DataDirectory) ? "" : " - bogus!");
1179
1180 for (uint32_t i = 0; i < RT_ELEMENTS(pOptHdr->DataDirectory); i++)
1181 if (pOptHdr->DataDirectory[i].Size || pOptHdr->DataDirectory[i].VirtualAddress)
1182 {
1183 const char * const pszName = dataDirectoryToString(i);
1184 rvaToStringWithAddr(pOptHdr->DataDirectory[i].VirtualAddress, szTmp, sizeof(szTmp));
1185 if (i == IMAGE_DIRECTORY_ENTRY_SECURITY)
1186 {
1187 size_t const cchWidth = strlen(szTmp);
1188 size_t cch = RTStrPrintf(szTmp, sizeof(szTmp), "%#09RX32 (file off)",
1189 pOptHdr->DataDirectory[i].VirtualAddress);
1190 while (cch < cchWidth)
1191 szTmp[cch++] = ' ';
1192 szTmp[cch] = '\0';
1193 }
1194 myPrintf("DataDirectory[%#x]: %s LB %#07RX32 %s\n", i, szTmp, pOptHdr->DataDirectory[i].Size, pszName);
1195 }
1196 return VINF_SUCCESS;
1197 }
1198
1199 int dumpSectionHdrs(void) RT_NOEXCEPT
1200 {
1201 if (!(m_pCmd->m_fSelection & DUMPIMAGE_SELECT_SECTIONS))
1202 return VINF_SUCCESS;
1203 myPrintHeader(m_offShdrs, "Section Table");
1204
1205 for (unsigned i = 0; i < m_cShdrs; i++)
1206 {
1207 char szTmp[64];
1208 myPrintf("Section[%02u]: %s LB %08RX32 %.8s\n",
1209 i, rvaToStringWithAddr(m_paShdrs[i].VirtualAddress, szTmp, sizeof(szTmp)),
1210 m_paShdrs[i].Misc.VirtualSize, m_paShdrs[i].Name);
1211 }
1212 return VINF_SUCCESS;
1213 }
1214
1215 int dumpExportDir(DumpImageBufferedReader *pBufRdr, uint32_t uRvaData, uint32_t cbData) RT_NOEXCEPT
1216 {
1217 if (!(m_pCmd->m_fSelection & DUMPIMAGE_SELECT_EXPORTS))
1218 return VINF_SUCCESS;
1219 myPrintHeader(uRvaData, "Export Table");
1220
1221 RT_NOREF(cbData);
1222 char szTmp[64];
1223
1224 /* Use dedicated readers for each array, but saving one by using pBufRdr
1225 for function addresses. */
1226 DumpImageBufferedReader NmAddrRdr(*pBufRdr), OrdRdr(*pBufRdr), NameRdr(*pBufRdr);
1227
1228 /*
1229 * Read the entry into memory.
1230 */
1231 IMAGE_EXPORT_DIRECTORY ExpDir;
1232 int rc = pBufRdr->readBytes(uRvaData, &ExpDir, sizeof(ExpDir));
1233 if (RT_FAILURE(rc))
1234 return rc;
1235
1236 /*
1237 * Dump the directory.
1238 */
1239 myPrintf(" Name: %s %s\n",
1240 rvaToStringWithAddr(ExpDir.Name, szTmp, sizeof(szTmp)), NmAddrRdr.bufferedString(ExpDir.Name));
1241 myPrintf(" Address table: %s L %u\n",
1242 rvaToStringWithAddr(ExpDir.AddressOfFunctions, szTmp, sizeof(szTmp)), ExpDir.NumberOfFunctions);
1243 myPrintf(" Name table: %s L %u\n",
1244 rvaToStringWithAddr(ExpDir.AddressOfNames, szTmp, sizeof(szTmp)), ExpDir.NumberOfNames);
1245 myPrintf(" Name index table: %s L ditto\n",
1246 rvaToStringWithAddr(ExpDir.AddressOfNameOrdinals, szTmp, sizeof(szTmp)), ExpDir.NumberOfNames);
1247 myPrintf(" Ordinal base: %u\n", ExpDir.Base);
1248 if (ExpDir.Characteristics)
1249 myPrintf(" Characteristics: %#RX32\n", ExpDir.Characteristics);
1250 if (ExpDir.TimeDateStamp && ExpDir.TimeDateStamp != UINT32_MAX)
1251 myPrintf(" TimeDateStamp: %#RX32 %s\n",
1252 ExpDir.TimeDateStamp, timestampToString(ExpDir.TimeDateStamp, szTmp, sizeof(szTmp)));
1253 if (ExpDir.MajorVersion || ExpDir.MinorVersion)
1254 myPrintf(" Version: %u.%u\n", ExpDir.MajorVersion, ExpDir.MinorVersion);
1255
1256 uint32_t const cExports = ExpDir.NumberOfNames;
1257 if (cExports > _16K)
1258 {
1259 myPrintf(" Exports: Too many addresses! (%#x)\n", cExports);
1260 return VINF_SUCCESS;
1261 }
1262 uint32_t const cNames = ExpDir.NumberOfNames;
1263 if (cNames > _32K)
1264 {
1265 myPrintf(" Exports: Too many names! (%#x)\n", cNames);
1266 return VINF_SUCCESS;
1267 }
1268 if (cExports == 0)
1269 {
1270 myPrintf(" Exports: No exports!\n");
1271 return VINF_SUCCESS;
1272 }
1273
1274 /*
1275 * Read the export addresses and name tables into memory.
1276 */
1277 uint32_t const *pauExportRvas = (uint32_t const *)pBufRdr->bufferedBytes(ExpDir.AddressOfFunctions,
1278 sizeof(pauExportRvas[0])* cExports);
1279 uint16_t const *pau16Ordinals = NULL;
1280 uint32_t const *pauNameRvas = NULL;
1281 bool fOrderedOrdinals = true;
1282 if (cNames)
1283 {
1284 pauNameRvas = (uint32_t const *)NmAddrRdr.bufferedBytes(ExpDir.AddressOfNames, sizeof(pauNameRvas[0]) * cNames);
1285 if (!pauNameRvas)
1286 return VINF_SUCCESS;
1287 pau16Ordinals = (uint16_t const *)OrdRdr.bufferedBytes(ExpDir.AddressOfNameOrdinals,
1288 sizeof(pau16Ordinals[0]) * cNames);
1289 if (!pau16Ordinals)
1290 return VINF_SUCCESS;
1291
1292 /* Check if the name ordinals are ordered. */
1293 uint16_t iPrev = pau16Ordinals[0];
1294 for (uint32_t iOrd = 1; iOrd < cNames; iOrd++)
1295 {
1296 uint16_t const iCur = pau16Ordinals[iOrd];
1297 if (iCur > iPrev)
1298 iPrev = iCur;
1299 else
1300 {
1301 fOrderedOrdinals = false;
1302 break;
1303 }
1304 }
1305
1306 }
1307
1308 /*
1309 * Dump the exports by named exports.
1310 */
1311 static const char s_szAddr[] = "Export RVA/Address";
1312 unsigned cchAddr = (unsigned)strlen(rvaToStringWithAddr(uRvaData, szTmp, sizeof(szTmp)));
1313 cchAddr = RT_MAX(cchAddr, sizeof(s_szAddr) - 1);
1314 myPrintf("\n"
1315 "Ordinal %*s%s%*s Name RVA Name\n"
1316 "------- %*.*s --------- --------------------------------\n",
1317 (cchAddr - sizeof(s_szAddr) + 1) / 2, "", s_szAddr, (cchAddr - sizeof(s_szAddr) + 1 + 1) / 2, "",
1318 cchAddr, cchAddr, "--------------------------------------");
1319
1320 for (uint32_t iExp = 0, iName = 0; iExp < cExports; iExp++)
1321 {
1322 if (cNames > 0)
1323 {
1324 if (fOrderedOrdinals)
1325 {
1326 if (iName < cNames && pau16Ordinals[iName] == iExp)
1327 {
1328 uint32_t const uRvaName = pauNameRvas[iExp];
1329 const char * const pszName = NameRdr.bufferedString(uRvaName);
1330 myPrintf("%7u %s %#09RX32 %s\n", iExp + ExpDir.Base,
1331 rvaToStringWithAddr(pauExportRvas[iExp], szTmp, sizeof(szTmp)),
1332 uRvaName, pszName ? pszName : "");
1333 iName++;
1334 continue;
1335 }
1336 }
1337 else
1338 {
1339 /* Search the entire name ordinal table, not stopping on a hit
1340 as there could in theory be different names for the same entry. */
1341 uint32_t cPrinted = 0;
1342 for (iName = 0; iName < cNames; iName++)
1343 if (pau16Ordinals[iName] == iExp)
1344 {
1345 uint32_t const uRvaName = pauNameRvas[iExp];
1346 const char * const pszName = NameRdr.bufferedString(uRvaName);
1347 myPrintf("%7u %s %#09RX32 %s\n", iExp + ExpDir.Base,
1348 rvaToStringWithAddr(pauExportRvas[iExp], szTmp, sizeof(szTmp)),
1349 uRvaName, pszName ? pszName : "");
1350 cPrinted++;
1351 }
1352 if (cPrinted)
1353 continue;
1354 }
1355 }
1356 /* Ordinal only. */
1357 myPrintf("%7u %s %#09RX32\n", iExp + ExpDir.Base,
1358 rvaToStringWithAddr(pauExportRvas[iExp], szTmp, sizeof(szTmp)), UINT32_MAX);
1359 }
1360 return VINF_SUCCESS;
1361 }
1362
1363 template<typename ThunkType, bool const a_f32Bit, ThunkType const a_fOrdinalConst>
1364 int dumpImportDir(DumpImageBufferedReader *pBufRdr, uint32_t uRvaData, uint32_t cbData) RT_NOEXCEPT
1365 {
1366 if (!(m_pCmd->m_fSelection & DUMPIMAGE_SELECT_IMPORTS))
1367 return VINF_SUCCESS;
1368 myPrintHeader(uRvaData, "Import table");
1369
1370 char szTmp[64];
1371 char szTmp2[64];
1372 size_t const cchRvaWithAddr = strlen(rvaToStringWithAddr(uRvaData, szTmp, sizeof(szTmp)));
1373
1374 /* Use dedicated readers for each array and names */
1375 DumpImageBufferedReader NameRdr(*pBufRdr), Thunk1stRdr(*pBufRdr), ThunkOrgRdr(*pBufRdr);
1376
1377 int rcRet = VINF_SUCCESS;
1378 uint32_t const cEntries = cbData / sizeof(IMAGE_IMPORT_DESCRIPTOR);
1379 for (uint32_t i = 0; i < cEntries; i += 1, uRvaData += sizeof(IMAGE_IMPORT_DESCRIPTOR))
1380 {
1381 /*
1382 * Read the entry into memory.
1383 */
1384 IMAGE_IMPORT_DESCRIPTOR ImpDir;
1385 int rc = pBufRdr->readBytes(uRvaData, &ImpDir, sizeof(ImpDir));
1386 if (RT_FAILURE(rc))
1387 return rc;
1388
1389 if (ImpDir.Name == 0)
1390 continue;
1391
1392 /*
1393 * Dump it.
1394 */
1395 if (i > 0)
1396 myPrintf("\n");
1397 myPrintf(" Entry #: %u\n", i);
1398 myPrintf(" Name: %s - %s\n", rvaToStringWithAddr(ImpDir.Name, szTmp, sizeof(szTmp)),
1399 ImpDir.Name ? NameRdr.bufferedString(ImpDir.Name) : "");
1400 if (ImpDir.TimeDateStamp && ImpDir.TimeDateStamp != UINT32_MAX)
1401 myPrintf(" Timestamp: %#010RX32 %s\n",
1402 ImpDir.TimeDateStamp, timestampToString(ImpDir.TimeDateStamp, szTmp, sizeof(szTmp)));
1403 myPrintf(" First thunk: %s\n", rvaToStringWithAddr(ImpDir.FirstThunk, szTmp, sizeof(szTmp)));
1404 myPrintf(" Original thunk: %s\n", rvaToStringWithAddr(ImpDir.u.OriginalFirstThunk, szTmp, sizeof(szTmp)));
1405 if (ImpDir.ForwarderChain)
1406 myPrintf(" Forwarder chain: %s\n", rvaToStringWithAddr(ImpDir.ForwarderChain, szTmp, sizeof(szTmp)));
1407
1408 /*
1409 * Try process the arrays.
1410 */
1411 static char const s_szDashes[] = "-----------------------------------------------";
1412 static char const s_szHdr1[] = "Thunk RVA/Addr";
1413 uint32_t uRvaNames = ImpDir.u.OriginalFirstThunk;
1414 uint32_t uRvaThunk = ImpDir.FirstThunk;
1415 if (uRvaThunk == 0)
1416 uRvaThunk = uRvaNames;
1417 if (uRvaNames != 0 && uRvaNames != uRvaThunk)
1418 {
1419 static char const s_szHdr2[] = "Thunk";
1420 static char const s_szHdr4[] = "Hint+Name RVA/Addr";
1421 size_t const cchCol1 = RT_MAX(sizeof(s_szHdr1) - 1, cchRvaWithAddr);
1422 size_t const cchCol2 = RT_MAX(sizeof(s_szHdr2) - 1, 2 + sizeof(ThunkType) * 2);
1423 size_t const cchCol4 = RT_MAX(sizeof(s_szHdr4) - 1, cchRvaWithAddr);
1424
1425 myPrintf(" No. %-*s %-*s Ord/Hint %-*s Name\n"
1426 "---- %.*s %.*s -------- %.*s ----------------\n",
1427 cchCol1, s_szHdr1, cchCol2, s_szHdr2, cchCol4, s_szHdr4,
1428 cchCol1, s_szDashes, cchCol2, s_szDashes, cchCol4, s_szDashes);
1429 for (uint32_t iEntry = 0;; iEntry += 1, uRvaThunk += sizeof(ThunkType), uRvaNames += sizeof(ThunkType))
1430 {
1431 ThunkType const uName = ThunkOrgRdr.bufferedInt<ThunkType>(uRvaNames, 0);
1432 ThunkType const uThunk = Thunk1stRdr.bufferedInt<ThunkType>(uRvaThunk, 0);
1433 if (!uName || !uThunk)
1434 break;
1435
1436 if (!(uName & a_fOrdinalConst))
1437 {
1438 uint16_t const uHint = NameRdr.bufferedInt<uint16_t>(uName);
1439 const char * const pszName = NameRdr.bufferedString(uName + 2);
1440 if (a_f32Bit)
1441 myPrintf("%4u: %s %#010RX32 %8RU16 %s %s\n",
1442 iEntry, rvaToStringWithAddr(uRvaThunk, szTmp, sizeof(szTmp)), uThunk, uHint,
1443 rvaToStringWithAddr(uName, szTmp2, sizeof(szTmp2)), pszName);
1444 else
1445 myPrintf("%4u: %s %#018RX64 %8RU16 %s %s\n",
1446 iEntry, rvaToStringWithAddr(uRvaThunk, szTmp, sizeof(szTmp)), uThunk, uHint,
1447 rvaToStringWithAddr(uName, szTmp2, sizeof(szTmp2)), pszName);
1448 }
1449 else
1450 {
1451 if (a_f32Bit)
1452 myPrintf("%4u: %s %#010RX32 %8RU32\n", iEntry,
1453 rvaToStringWithAddr(uRvaThunk, szTmp, sizeof(szTmp)), uThunk, uName & ~a_fOrdinalConst);
1454 else
1455 myPrintf("%4u: %s %#018RX64 %8RU64\n", iEntry,
1456 rvaToStringWithAddr(uRvaThunk, szTmp, sizeof(szTmp)), uThunk, uName & ~a_fOrdinalConst);
1457 }
1458 }
1459 }
1460 /** @todo */
1461 //else if (uRvaThunk)
1462 // for (;;)
1463 // {
1464 // ThunkType const *pThunk = (ThunkType const *)Thunk1stRdr.bufferedBytes(uRvaThunk, sizeof(*pThunk));
1465 // if (!pThunk->u1.AddressOfData == 0)
1466 // break;
1467 // }
1468 }
1469 return rcRet;
1470 }
1471
1472 int dumpDebugDir(DumpImageBufferedReader *pBufRdr, uint32_t uRvaData, uint32_t cbData) RT_NOEXCEPT
1473 {
1474 if (!(m_pCmd->m_fSelection & DUMPIMAGE_SELECT_DEBUG))
1475 return VINF_SUCCESS;
1476 myPrintHeader(uRvaData, "Debug Directory");
1477
1478 int rcRet = VINF_SUCCESS;
1479 uint32_t const cEntries = cbData / sizeof(IMAGE_DEBUG_DIRECTORY);
1480 for (uint32_t i = 0; i < cEntries; i += 1, uRvaData += sizeof(IMAGE_DEBUG_DIRECTORY))
1481 {
1482 /*
1483 * Read the entry into memory.
1484 */
1485 IMAGE_DEBUG_DIRECTORY DbgDir;
1486 int rc = pBufRdr->readBytes(uRvaData, &DbgDir, sizeof(DbgDir));
1487 if (RT_FAILURE(rc))
1488 return rc;
1489
1490 /*
1491 * Dump it.
1492 * (longest type is 13 chars:'OMAP_FROM_SRC')
1493 */
1494 char szTmp[64];
1495 char szTmp2[64];
1496 myPrintf("%u: %s LB %06RX32 %#09RX32 %13s",
1497 i, rvaToStringWithAddr(DbgDir.AddressOfRawData, szTmp, sizeof(szTmp)), DbgDir.SizeOfData,
1498 DbgDir.PointerToRawData,
1499 debugTypeToString(DbgDir.Type, szTmp2, sizeof(szTmp2)));
1500 if (DbgDir.MajorVersion || DbgDir.MinorVersion)
1501 myPrintf(" v%u.%u", DbgDir.MajorVersion, DbgDir.MinorVersion);
1502 if (DbgDir.Characteristics)
1503 myPrintf(" flags=%#RX32", DbgDir.Characteristics);
1504 myPrintf(" %s (%#010RX32)\n", timestampToString(DbgDir.TimeDateStamp, szTmp, sizeof(szTmp)), DbgDir.TimeDateStamp);
1505
1506 union
1507 {
1508 uint8_t abPage[0x1000];
1509 CVPDB20INFO Pdb20;
1510 CVPDB70INFO Pdb70;
1511 IMAGE_DEBUG_MISC Misc;
1512 } uBuf;
1513 RT_ZERO(uBuf);
1514
1515 if (DbgDir.Type == IMAGE_DEBUG_TYPE_CODEVIEW)
1516 {
1517 if ( DbgDir.SizeOfData < sizeof(uBuf)
1518 && DbgDir.SizeOfData > 16
1519 && DbgDir.AddressOfRawData > 0
1520 && RT_SUCCESS(rc))
1521 {
1522 rc = pBufRdr->readBytes(DbgDir.AddressOfRawData, &uBuf, DbgDir.SizeOfData);
1523 if (RT_SUCCESS(rc))
1524 {
1525 if ( uBuf.Pdb20.u32Magic == CVPDB20INFO_MAGIC
1526 && uBuf.Pdb20.offDbgInfo == 0
1527 && DbgDir.SizeOfData > RT_UOFFSETOF(CVPDB20INFO, szPdbFilename) )
1528 myPrintf(" PDB2.0: ts=%08RX32 age=%RX32 %s\n",
1529 uBuf.Pdb20.uTimestamp, uBuf.Pdb20.uAge, uBuf.Pdb20.szPdbFilename);
1530 else if ( uBuf.Pdb20.u32Magic == CVPDB70INFO_MAGIC
1531 && DbgDir.SizeOfData > RT_UOFFSETOF(CVPDB70INFO, szPdbFilename) )
1532 myPrintf(" PDB7.0: %RTuuid age=%u %s\n",
1533 &uBuf.Pdb70.PdbUuid, uBuf.Pdb70.uAge, uBuf.Pdb70.szPdbFilename);
1534 else
1535 myPrintf(" Unknown PDB/codeview magic: %.8Rhxs\n", uBuf.abPage);
1536 }
1537 else
1538 rcRet = rc;
1539 }
1540 }
1541 else if (DbgDir.Type == IMAGE_DEBUG_TYPE_MISC)
1542 {
1543 if ( DbgDir.SizeOfData < sizeof(uBuf)
1544 && DbgDir.SizeOfData > RT_UOFFSETOF(IMAGE_DEBUG_MISC, Data)
1545 && DbgDir.AddressOfRawData > 0)
1546 {
1547 rc = pBufRdr->readBytes(DbgDir.AddressOfRawData, &uBuf, DbgDir.SizeOfData);
1548 if (RT_SUCCESS(rc))
1549 {
1550 if ( uBuf.Misc.DataType == IMAGE_DEBUG_MISC_EXENAME
1551 && uBuf.Misc.Length == DbgDir.SizeOfData)
1552 {
1553 if (!uBuf.Misc.Unicode)
1554 myPrintf(" Misc DBG: ts=%RX32 %s\n", DbgDir.TimeDateStamp, (const char *)&uBuf.Misc.Data[0]);
1555 else
1556 myPrintf(" Misc DBG: ts=%RX32 %ls\n", DbgDir.TimeDateStamp, (PCRTUTF16)&uBuf.Misc.Data[0]);
1557 }
1558 }
1559 else
1560 rcRet = rc;
1561 }
1562 }
1563 }
1564 return rcRet;
1565 }
1566
1567 int dumpDataDirs(DumpImageBufferedReader *pBufRdr, unsigned cDataDirs, PCIMAGE_DATA_DIRECTORY paDataDirs) RT_NOEXCEPT
1568 {
1569 int rcRet = VINF_SUCCESS;
1570 for (unsigned i = 0; i < cDataDirs; i++)
1571 if (paDataDirs[i].Size > 0 && paDataDirs[i].VirtualAddress)
1572 {
1573 int rc;
1574 if ( i == IMAGE_DIRECTORY_ENTRY_EXPORT
1575 && paDataDirs[i].Size >= sizeof(IMAGE_EXPORT_DIRECTORY))
1576 rc = dumpExportDir(pBufRdr, paDataDirs[i].VirtualAddress, paDataDirs[i].Size);
1577 else if ( i == IMAGE_DIRECTORY_ENTRY_IMPORT
1578 && paDataDirs[i].Size >= sizeof(IMAGE_IMPORT_DESCRIPTOR))
1579 {
1580 if (m_pFileHdr->SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32))
1581 rc = dumpImportDir<uint32_t, true, IMAGE_ORDINAL_FLAG32>(pBufRdr, paDataDirs[i].VirtualAddress,
1582 paDataDirs[i].Size);
1583 else
1584 rc = dumpImportDir<uint64_t, false, IMAGE_ORDINAL_FLAG64>(pBufRdr, paDataDirs[i].VirtualAddress,
1585 paDataDirs[i].Size);
1586 }
1587 else if ( i == IMAGE_DIRECTORY_ENTRY_DEBUG
1588 && paDataDirs[i].Size >= sizeof(IMAGE_DEBUG_DIRECTORY))
1589 rc = dumpDebugDir(pBufRdr, paDataDirs[i].VirtualAddress, paDataDirs[i].Size);
1590 else
1591 continue;
1592 if (RT_FAILURE(rc))
1593 rcRet = rc;
1594 }
1595 return rcRet;
1596 }
1597
1598 /** @} */
1599
1600 static int dumpImage(DumpImageCmd *pCmd, const char *pszImageBaseAddr,
1601 uint32_t offPeHdr, PCIMAGE_FILE_HEADER pFileHdr) RT_NOEXCEPT
1602 {
1603 pCmd->myPrintf("%s: PE image - %#x (%s), %u sections\n", pCmd->m_pszName, pFileHdr->Machine,
1604 machineToString(pFileHdr->Machine), pFileHdr->NumberOfSections);
1605
1606 /* Is it a supported optional header size? */
1607 uint8_t cBits;
1608 if (pFileHdr->SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32))
1609 cBits = 32;
1610 else if (pFileHdr->SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER64))
1611 cBits = 64;
1612 else
1613 return pCmd->myError("Unsupported optional header size: %#x", pFileHdr->SizeOfOptionalHeader);
1614
1615 /*
1616 * Allocate memory for all the headers, including section headers, and read them into memory.
1617 */
1618 size_t const offShdrs = pFileHdr->SizeOfOptionalHeader + sizeof(*pFileHdr) + sizeof(uint32_t);
1619 size_t const cbHdrs = offShdrs + pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
1620 if (cbHdrs > _2M)
1621 return pCmd->myError("headers too big: %zu.\n", cbHdrs);
1622
1623 void *pvBuf = RTMemTmpAllocZ(cbHdrs);
1624 if (!pvBuf)
1625 return pCmd->myError("failed to allocate %zu bytes for headers.", cbHdrs);
1626
1627 int rc = pCmd->readAt(offPeHdr, pvBuf, cbHdrs, NULL);
1628 if (RT_SUCCESS(rc))
1629 {
1630 /* Format the image base value from the header if one isn't specified. */
1631 char szTmp[32];
1632 if (!pszImageBaseAddr)
1633 {
1634 if (cBits == 32)
1635 RTStrPrintf(szTmp, sizeof(szTmp), "%#010RX32", ((PIMAGE_NT_HEADERS32)pvBuf)->OptionalHeader.ImageBase);
1636 else
1637 RTStrPrintf(szTmp, sizeof(szTmp), "%#018RX64", ((PIMAGE_NT_HEADERS64)pvBuf)->OptionalHeader.ImageBase);
1638 pszImageBaseAddr = szTmp;
1639 }
1640
1641 /* Finally, instantiate dumper now that we've got the section table
1642 loaded, and let it contiue. */
1643 DumpImagePe This(pCmd, pszImageBaseAddr, offPeHdr, pFileHdr, pvBuf, (uint32_t)offShdrs,
1644 pFileHdr->NumberOfSections, (PCIMAGE_SECTION_HEADER)((uintptr_t)pvBuf + offShdrs));
1645
1646 This.dumpPeHdr();
1647 if (cBits == 32)
1648 rc = This.dumpOptHdr<IMAGE_OPTIONAL_HEADER32, true>(&This.u.pNt32->OptionalHeader,
1649 This.u.pNt32->OptionalHeader.BaseOfData);
1650 else
1651 rc = This.dumpOptHdr<IMAGE_OPTIONAL_HEADER64, false>(&This.u.pNt64->OptionalHeader);
1652
1653 int rc2 = This.dumpSectionHdrs();
1654 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
1655 rc = rc2;
1656
1657 DumpImageBufferedReader BufRdr(&This);
1658 rc2 = This.dumpDataDirs(&BufRdr, This.cDataDir, This.paDataDir);
1659 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
1660 rc = rc2;
1661 }
1662 RTMemTmpFree(pvBuf);
1663 return rc;
1664 }
1665
1666};
1667
1668
1669/*********************************************************************************************************************************
1670* ELF *
1671*********************************************************************************************************************************/
1672
1673static int dbgcDumpImageElf(DumpImageCmd *pCmd)
1674{
1675 pCmd->myPrintf("%s: ELF image dumping not implemented yet.\n", pCmd->m_pszName);
1676 return VINF_SUCCESS;
1677}
1678
1679
1680/*********************************************************************************************************************************
1681* Mach-O *
1682*********************************************************************************************************************************/
1683
1684static const char *dbgcMachoFileType(uint32_t uType)
1685{
1686 switch (uType)
1687 {
1688 case MH_OBJECT: return "MH_OBJECT";
1689 case MH_EXECUTE: return "MH_EXECUTE";
1690 case MH_FVMLIB: return "MH_FVMLIB";
1691 case MH_CORE: return "MH_CORE";
1692 case MH_PRELOAD: return "MH_PRELOAD";
1693 case MH_DYLIB: return "MH_DYLIB";
1694 case MH_DYLINKER: return "MH_DYLINKER";
1695 case MH_BUNDLE: return "MH_BUNDLE";
1696 case MH_DYLIB_STUB: return "MH_DYLIB_STUB";
1697 case MH_DSYM: return "MH_DSYM";
1698 case MH_KEXT_BUNDLE: return "MH_KEXT_BUNDLE";
1699 }
1700 return "??";
1701}
1702
1703
1704static const char *dbgcMachoCpuType(int32_t iType, int32_t iSubType)
1705{
1706 switch (iType)
1707 {
1708 case CPU_TYPE_ANY: return "CPU_TYPE_ANY";
1709 case CPU_TYPE_VAX: return "VAX";
1710 case CPU_TYPE_MC680x0: return "MC680x0";
1711 case CPU_TYPE_X86: return "X86";
1712 case CPU_TYPE_X86_64:
1713 switch (iSubType)
1714 {
1715 case CPU_SUBTYPE_X86_64_ALL: return "X86_64/ALL64";
1716 }
1717 return "X86_64";
1718 case CPU_TYPE_MC98000: return "MC98000";
1719 case CPU_TYPE_HPPA: return "HPPA";
1720 case CPU_TYPE_MC88000: return "MC88000";
1721 case CPU_TYPE_SPARC: return "SPARC";
1722 case CPU_TYPE_I860: return "I860";
1723 case CPU_TYPE_POWERPC: return "POWERPC";
1724 case CPU_TYPE_POWERPC64: return "POWERPC64";
1725
1726 }
1727 return "??";
1728}
1729
1730
1731static const char *dbgcMachoLoadCommand(uint32_t uCmd)
1732{
1733 switch (uCmd)
1734 {
1735 RT_CASE_RET_STR(LC_SEGMENT_32);
1736 RT_CASE_RET_STR(LC_SYMTAB);
1737 RT_CASE_RET_STR(LC_SYMSEG);
1738 RT_CASE_RET_STR(LC_THREAD);
1739 RT_CASE_RET_STR(LC_UNIXTHREAD);
1740 RT_CASE_RET_STR(LC_LOADFVMLIB);
1741 RT_CASE_RET_STR(LC_IDFVMLIB);
1742 RT_CASE_RET_STR(LC_IDENT);
1743 RT_CASE_RET_STR(LC_FVMFILE);
1744 RT_CASE_RET_STR(LC_PREPAGE);
1745 RT_CASE_RET_STR(LC_DYSYMTAB);
1746 RT_CASE_RET_STR(LC_LOAD_DYLIB);
1747 RT_CASE_RET_STR(LC_ID_DYLIB);
1748 RT_CASE_RET_STR(LC_LOAD_DYLINKER);
1749 RT_CASE_RET_STR(LC_ID_DYLINKER);
1750 RT_CASE_RET_STR(LC_PREBOUND_DYLIB);
1751 RT_CASE_RET_STR(LC_ROUTINES);
1752 RT_CASE_RET_STR(LC_SUB_FRAMEWORK);
1753 RT_CASE_RET_STR(LC_SUB_UMBRELLA);
1754 RT_CASE_RET_STR(LC_SUB_CLIENT);
1755 RT_CASE_RET_STR(LC_SUB_LIBRARY);
1756 RT_CASE_RET_STR(LC_TWOLEVEL_HINTS);
1757 RT_CASE_RET_STR(LC_PREBIND_CKSUM);
1758 RT_CASE_RET_STR(LC_LOAD_WEAK_DYLIB);
1759 RT_CASE_RET_STR(LC_SEGMENT_64);
1760 RT_CASE_RET_STR(LC_ROUTINES_64);
1761 RT_CASE_RET_STR(LC_UUID);
1762 RT_CASE_RET_STR(LC_RPATH);
1763 RT_CASE_RET_STR(LC_CODE_SIGNATURE);
1764 RT_CASE_RET_STR(LC_SEGMENT_SPLIT_INFO);
1765 RT_CASE_RET_STR(LC_REEXPORT_DYLIB);
1766 RT_CASE_RET_STR(LC_LAZY_LOAD_DYLIB);
1767 RT_CASE_RET_STR(LC_ENCRYPTION_INFO);
1768 RT_CASE_RET_STR(LC_DYLD_INFO);
1769 RT_CASE_RET_STR(LC_DYLD_INFO_ONLY);
1770 RT_CASE_RET_STR(LC_LOAD_UPWARD_DYLIB);
1771 RT_CASE_RET_STR(LC_VERSION_MIN_MACOSX);
1772 RT_CASE_RET_STR(LC_VERSION_MIN_IPHONEOS);
1773 RT_CASE_RET_STR(LC_FUNCTION_STARTS);
1774 RT_CASE_RET_STR(LC_DYLD_ENVIRONMENT);
1775 RT_CASE_RET_STR(LC_MAIN);
1776 RT_CASE_RET_STR(LC_DATA_IN_CODE);
1777 RT_CASE_RET_STR(LC_SOURCE_VERSION);
1778 RT_CASE_RET_STR(LC_DYLIB_CODE_SIGN_DRS);
1779 RT_CASE_RET_STR(LC_ENCRYPTION_INFO_64);
1780 RT_CASE_RET_STR(LC_LINKER_OPTION);
1781 RT_CASE_RET_STR(LC_LINKER_OPTIMIZATION_HINT);
1782 RT_CASE_RET_STR(LC_VERSION_MIN_TVOS);
1783 RT_CASE_RET_STR(LC_VERSION_MIN_WATCHOS);
1784 RT_CASE_RET_STR(LC_NOTE);
1785 RT_CASE_RET_STR(LC_BUILD_VERSION);
1786 }
1787 return "??";
1788}
1789
1790
1791static const char *dbgcMachoProt(uint32_t fProt)
1792{
1793 switch (fProt)
1794 {
1795 case VM_PROT_NONE: return "---";
1796 case VM_PROT_READ: return "r--";
1797 case VM_PROT_READ | VM_PROT_WRITE: return "rw-";
1798 case VM_PROT_READ | VM_PROT_EXECUTE: return "r-x";
1799 case VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE: return "rwx";
1800 case VM_PROT_WRITE: return "-w-";
1801 case VM_PROT_WRITE | VM_PROT_EXECUTE: return "-wx";
1802 case VM_PROT_EXECUTE: return "-w-";
1803 }
1804 return "???";
1805}
1806
1807
1808static int dbgcDumpImageMachO(DumpImageCmd *pCmd, mach_header_64_t const *pHdr)
1809{
1810#define ENTRY(a_Define) { a_Define, #a_Define }
1811 RT_NOREF_PV(pCmd);
1812
1813 /*
1814 * Header:
1815 */
1816 pCmd->myPrintf("%s: Mach-O image (%s bit) - %s (%u) - %s (%#x / %#x)\n",
1817 pCmd->m_pszName, pHdr->magic == IMAGE_MACHO64_SIGNATURE ? "64" : "32",
1818 dbgcMachoFileType(pHdr->filetype), pHdr->filetype,
1819 dbgcMachoCpuType(pHdr->cputype, pHdr->cpusubtype), pHdr->cputype, pHdr->cpusubtype);
1820
1821 pCmd->myPrintf("%s: Flags: %#x", pCmd->m_pszName, pHdr->flags);
1822 static DBGCDUMPFLAGENTRY const s_aHdrFlags[] =
1823 {
1824 FLENT(MH_NOUNDEFS), FLENT(MH_INCRLINK),
1825 FLENT(MH_DYLDLINK), FLENT(MH_BINDATLOAD),
1826 FLENT(MH_PREBOUND), FLENT(MH_SPLIT_SEGS),
1827 FLENT(MH_LAZY_INIT), FLENT(MH_TWOLEVEL),
1828 FLENT(MH_FORCE_FLAT), FLENT(MH_NOMULTIDEFS),
1829 FLENT(MH_NOFIXPREBINDING), FLENT(MH_PREBINDABLE),
1830 FLENT(MH_ALLMODSBOUND), FLENT(MH_SUBSECTIONS_VIA_SYMBOLS),
1831 FLENT(MH_CANONICAL), FLENT(MH_WEAK_DEFINES),
1832 FLENT(MH_BINDS_TO_WEAK), FLENT(MH_ALLOW_STACK_EXECUTION),
1833 FLENT(MH_ROOT_SAFE), FLENT(MH_SETUID_SAFE),
1834 FLENT(MH_NO_REEXPORTED_DYLIBS), FLENT(MH_PIE),
1835 FLENT(MH_DEAD_STRIPPABLE_DYLIB), FLENT(MH_HAS_TLV_DESCRIPTORS),
1836 FLENT(MH_NO_HEAP_EXECUTION),
1837 };
1838 dbgcDumpImageFlags32(pCmd, pHdr->flags, s_aHdrFlags, RT_ELEMENTS(s_aHdrFlags));
1839 pCmd->myPrintf("\n");
1840 if (pHdr->reserved != 0 && pHdr->magic == IMAGE_MACHO64_SIGNATURE)
1841 pCmd->myPrintf("%s: Reserved header field: %#x\n", pCmd->m_pszName, pHdr->reserved);
1842
1843 /*
1844 * And now the load commands.
1845 */
1846 const uint32_t cCmds = pHdr->ncmds;
1847 const uint32_t cbCmds = pHdr->sizeofcmds;
1848 pCmd->myPrintf("%s: %u load commands covering %#x bytes:\n", pCmd->m_pszName, cCmds, cbCmds);
1849 if (cbCmds > _16M)
1850 return pCmd->myError(VERR_OUT_OF_RANGE, "%s: Commands too big: %#x bytes, max 16MiB", pCmd->m_pszName, cbCmds);
1851
1852
1853 /* Read the commands into a temp buffer: */
1854 const uint32_t cbHdr = pHdr->magic == IMAGE_MACHO64_SIGNATURE ? sizeof(mach_header_64_t) : sizeof(mach_header_32_t);
1855 uint8_t *pbCmds = (uint8_t *)RTMemTmpAllocZ(cbCmds);
1856 if (!pbCmds)
1857 return VERR_NO_TMP_MEMORY;
1858
1859 int rc = pCmd->readAt(cbHdr, pbCmds, cbCmds, NULL);
1860 if (RT_SUCCESS(rc))
1861 {
1862 static const DBGCDUMPFLAGENTRY s_aSegFlags[] =
1863 { FLENT(SG_HIGHVM), FLENT(SG_FVMLIB), FLENT(SG_NORELOC), FLENT(SG_PROTECTED_VERSION_1), };
1864
1865 /*
1866 * Iterate the commands.
1867 */
1868 uint32_t offCmd = 0;
1869 for (uint32_t iCmd = 0; iCmd < cCmds; iCmd++)
1870 {
1871 load_command_t const *pCurCmd = (load_command_t const *)&pbCmds[offCmd];
1872 const uint32_t cbCurCmd = offCmd + sizeof(*pCurCmd) <= cbCmds ? pCurCmd->cmdsize : sizeof(*pCurCmd);
1873 if (offCmd + cbCurCmd > cbCmds)
1874 {
1875 rc = pCmd->myError(VERR_OUT_OF_RANGE,
1876 "%s: Load command #%u (offset %#x + %#x) is out of bounds! cmdsize=%u (%#x) cmd=%u\n",
1877 pCmd->m_pszName, iCmd, offCmd, cbHdr, cbCurCmd, cbCurCmd,
1878 offCmd + RT_UOFFSET_AFTER(load_command_t, cmd) <= cbCmds ? pCurCmd->cmd : UINT32_MAX);
1879 break;
1880 }
1881
1882 pCmd->myPrintf("%s: Load command #%u (offset %#x + %#x): %s (%u) LB %u\n",
1883 pCmd->m_pszName, iCmd, offCmd, cbHdr, dbgcMachoLoadCommand(pCurCmd->cmd), pCurCmd->cmd, cbCurCmd);
1884 switch (pCurCmd->cmd)
1885 {
1886 case LC_SEGMENT_64:
1887 if (cbCurCmd < sizeof(segment_command_64_t))
1888 rc = pCmd->myError(VERR_LDRMACHO_BAD_LOAD_COMMAND, "LC_SEGMENT64 is too short!");
1889 else
1890 {
1891 segment_command_64_t const *pSeg = (segment_command_64_t const *)pCurCmd;
1892 pCmd->myPrintf("%s: vmaddr: %016RX64 LB %08RX64 prot: %s(%x) maxprot: %s(%x) name: %.16s\n",
1893 pCmd->m_pszName, pSeg->vmaddr, pSeg->vmsize, dbgcMachoProt(pSeg->initprot), pSeg->initprot,
1894 dbgcMachoProt(pSeg->maxprot), pSeg->maxprot, pSeg->segname);
1895 pCmd->myPrintf("%s: file: %016RX64 LB %08RX64 sections: %2u flags: %#x",
1896 pCmd->m_pszName, pSeg->fileoff, pSeg->filesize, pSeg->nsects, pSeg->flags);
1897 dbgcDumpImageFlags32(pCmd, pSeg->flags, s_aSegFlags, RT_ELEMENTS(s_aSegFlags));
1898 pCmd->myPrintf("\n");
1899 if ( pSeg->nsects > _64K
1900 || pSeg->nsects * sizeof(section_64_t) + sizeof(pSeg) > cbCurCmd)
1901 rc = pCmd->myError(VERR_LDRMACHO_BAD_LOAD_COMMAND, "LC_SEGMENT64 is too short for all the sections!");
1902 else
1903 {
1904 section_64_t const *paSec = (section_64_t const *)(pSeg + 1);
1905 for (uint32_t iSec = 0; iSec < pSeg->nsects; iSec++)
1906 {
1907 pCmd->myPrintf("%s: Section #%u: %016RX64 LB %08RX64 align: 2**%-2u name: %.16s",
1908 pCmd->m_pszName, iSec, paSec[iSec].addr, paSec[iSec].size, paSec[iSec].align,
1909 paSec[iSec].sectname);
1910 if (strncmp(pSeg->segname, paSec[iSec].segname, sizeof(pSeg->segname)))
1911 pCmd->myPrintf("(in %.16s)", paSec[iSec].segname);
1912 pCmd->myPrintf("\n");
1913
1914 /// @todo Good night!
1915 /// uint32_t offset;
1916 /// uint32_t reloff;
1917 /// uint32_t nreloc;
1918 /// uint32_t flags;
1919 /// /** For S_LAZY_SYMBOL_POINTERS, S_NON_LAZY_SYMBOL_POINTERS and S_SYMBOL_STUBS
1920 /// * this is the index into the indirect symbol table. */
1921 /// uint32_t reserved1;
1922 /// uint32_t reserved2;
1923 /// uint32_t reserved3;
1924 ///
1925 }
1926 }
1927 }
1928 break;
1929 }
1930
1931 /* Advance: */
1932 offCmd += cbCurCmd;
1933 }
1934 }
1935 RTMemTmpFree(pbCmds);
1936 return rc;
1937#undef ENTRY
1938}
1939
1940
1941/**
1942 * Common worker for the dumpimage command and the VBoxDumpImage tool.
1943 */
1944int DumpImageCmd::dumpImage(const char *pszImageBaseAddr) RT_NOEXCEPT
1945{
1946 if (!isFirstTarget())
1947 myPrintf("===================================================================\n"
1948 "\n"
1949 "\n");
1950 union
1951 {
1952 uint8_t ab[0x10];
1953 IMAGE_DOS_HEADER DosHdr;
1954 struct
1955 {
1956 uint32_t u32Magic;
1957 IMAGE_FILE_HEADER FileHdr;
1958 } Nt;
1959 mach_header_64_t MachO64;
1960 } uBuf;
1961 int rc = readAt(0, &uBuf.DosHdr, sizeof(uBuf.DosHdr), NULL);
1962 if (RT_SUCCESS(rc))
1963 {
1964 /*
1965 * MZ.
1966 */
1967 if (uBuf.DosHdr.e_magic == IMAGE_DOS_SIGNATURE)
1968 {
1969 uint32_t offNewHdr = uBuf.DosHdr.e_lfanew;
1970 if (offNewHdr < _256K && offNewHdr >= 16)
1971 {
1972 /* Look for new header. */
1973 rc = readAt(offNewHdr, &uBuf.Nt, sizeof(uBuf.Nt), NULL);
1974 if (RT_SUCCESS(rc))
1975 {
1976 /* PE: */
1977 if (uBuf.Nt.u32Magic == IMAGE_NT_SIGNATURE)
1978 rc = DumpImagePe::dumpImage(this, pszImageBaseAddr, offNewHdr, &uBuf.Nt.FileHdr);
1979 else
1980 return myError(rc, "Unknown new header magic: %.8Rhxs", uBuf.ab);
1981 }
1982 }
1983 else
1984 return myError(rc, "e_lfanew=%#RX32 is out of bounds (16..256K).", offNewHdr);
1985 }
1986 /*
1987 * ELF.
1988 */
1989 else if (uBuf.ab[0] == ELFMAG0 && uBuf.ab[1] == ELFMAG1 && uBuf.ab[2] == ELFMAG2 && uBuf.ab[3] == ELFMAG3)
1990 rc = dbgcDumpImageElf(this);
1991 /*
1992 * Mach-O.
1993 */
1994 else if ( uBuf.MachO64.magic == IMAGE_MACHO64_SIGNATURE
1995 || uBuf.MachO64.magic == IMAGE_MACHO32_SIGNATURE)
1996 rc = dbgcDumpImageMachO(this, &uBuf.MachO64);
1997 /*
1998 * Dunno.
1999 */
2000 else
2001 return myError(rc, "Unknown magic: %.8Rhxs", uBuf.ab);
2002
2003 /* Make 100% sure the failure status is signalled. */
2004 if (RT_FAILURE(rc))
2005 setFailure(rc);
2006 }
2007 else
2008 rc = myError(rc, "Failed to read %zu", sizeof(uBuf.DosHdr));
2009 return rc;
2010}
2011
2012
2013#ifndef DBGC_DUMP_IMAGE_TOOL
2014
2015/**
2016 * @callback_method_impl{FNDBGCCMD, The 'dumpimage' command.}
2017 */
2018DECLCALLBACK(int) dbgcCmdDumpImage(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
2019{
2020 DumpImageCmd Cmd(pCmdHlp, pCmd);
2021 for (unsigned iArg = 0; iArg < cArgs; iArg++)
2022 {
2023 DBGCVAR const ImageBase = paArgs[iArg];
2024 char szImageBaseAddr[64];
2025 DBGCCmdHlpStrPrintf(pCmdHlp, szImageBaseAddr, sizeof(szImageBaseAddr), "%Dv", &ImageBase);
2026 Cmd.setTarget(szImageBaseAddr, &ImageBase);
2027 Cmd.dumpImage(szImageBaseAddr);
2028 Cmd.clearTarget();
2029 }
2030 RT_NOREF(pUVM);
2031 return Cmd.getStatus();
2032}
2033
2034#else /* DBGC_DUMP_IMAGE_TOOL */
2035
2036int main(int argc, char **argv)
2037{
2038 int rc = RTR3InitExe(argc, &argv, 0);
2039 if (RT_FAILURE(rc))
2040 return RTMsgInitFailure(rc);
2041
2042 /*
2043 * Setup image helper code.
2044 */
2045 DumpImageCmd Cmd(NULL, NULL);
2046 char szImageBaseAddr[32] = {0};
2047 //uint64_t fSelect = DUMPIMAGE_SELECT_DEFAULT;
2048
2049 static const RTGETOPTDEF s_aOptions[] =
2050 {
2051 { "--image-base", 'b', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_HEX },
2052 { "--include", 'i', RTGETOPT_REQ_STRING },
2053 { "--only", 'o', RTGETOPT_REQ_STRING },
2054 { "--only", 'O', RTGETOPT_REQ_STRING },
2055 { "--skip", 's', RTGETOPT_REQ_STRING },
2056 { "--skip", 'S', RTGETOPT_REQ_STRING },
2057 };
2058
2059 RTGETOPTSTATE GetState;
2060 rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
2061 AssertRCReturn(rc, RTEXITCODE_FAILURE);
2062
2063 RTGETOPTUNION ValueUnion;
2064 int chOpt;
2065 while ((chOpt = RTGetOpt(&GetState, &ValueUnion)) != 0)
2066 {
2067 switch (chOpt)
2068 {
2069 case 'b':
2070 if (ValueUnion.u64 >= UINT32_MAX - _16M)
2071 RTStrPrintf(szImageBaseAddr, sizeof(szImageBaseAddr), "%#018RX64", ValueUnion.u64);
2072 else
2073 RTStrPrintf(szImageBaseAddr, sizeof(szImageBaseAddr), "%#010RX64", ValueUnion.u64);
2074 break;
2075
2076 case 'i':
2077 rc = Cmd.optSelectionInclude(ValueUnion.psz);
2078 if (RT_FAILURE(rc))
2079 return RTEXITCODE_SYNTAX;
2080 break;
2081
2082 case 'o':
2083 case 'O':
2084 rc = Cmd.optSelectionOnly(ValueUnion.psz);
2085 if (RT_FAILURE(rc))
2086 return RTEXITCODE_SYNTAX;
2087 break;
2088
2089 case 's':
2090 case 'S':
2091 rc = Cmd.optSelectionSkip(ValueUnion.psz);
2092 if (RT_FAILURE(rc))
2093 return RTEXITCODE_SYNTAX;
2094 break;
2095
2096 case 'V':
2097 RTPrintf("%s\n", RTBldCfgRevision());
2098 return RTEXITCODE_SUCCESS;
2099
2100 case 'h':
2101 RTPrintf("usage: %s [options] <file> [file2..]\n", RTProcShortName());
2102 return RTEXITCODE_SUCCESS;
2103
2104 case VINF_GETOPT_NOT_OPTION:
2105 {
2106 RTERRINFOSTATIC ErrInfo;
2107 uint32_t offError = 0;
2108 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
2109 rc = RTVfsChainOpenFile(ValueUnion.psz, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
2110 &hVfsFile, &offError, RTErrInfoInitStatic(&ErrInfo));
2111 if (RT_SUCCESS(rc))
2112 {
2113 Cmd.setTarget(ValueUnion.psz, hVfsFile);
2114 Cmd.dumpImage(szImageBaseAddr[0] ? szImageBaseAddr : NULL);
2115 Cmd.clearTarget();
2116 }
2117 else
2118 {
2119 RTVfsChainMsgErrorExitFailure("RTVfsChainOpenFile", ValueUnion.psz, rc, offError, &ErrInfo.Core);
2120 Cmd.setFailure(rc);
2121 }
2122 break;
2123 }
2124
2125 default:
2126 return RTGetOptPrintError(chOpt, &ValueUnion);
2127 }
2128 }
2129
2130 return Cmd.getExitCode();
2131}
2132
2133#endif /* !DBGC_DUMP_IMAGE_TOOL */
2134
Note: See TracBrowser for help on using the repository browser.

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