VirtualBox

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

Last change on this file since 105508 was 105508, checked in by vboxsync, 4 months ago

VBoxDbg: Extended the dumpimage command, creating standalone tool of it (VBoxDumpImage). bugref:10727

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