VirtualBox

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

Last change on this file was 106073, checked in by vboxsync, 2 months ago

VBoxDumpImage: Better help

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