VirtualBox

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

Last change on this file since 107415 was 107415, checked in by vboxsync, 2 weeks ago

Debugger/DBGCDumpImage.cpp: Fix check, bugref:3409

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