diff options
Diffstat (limited to 'src/VBox/Debugger/DBGCDumpImage.cpp')
-rw-r--r-- | src/VBox/Debugger/DBGCDumpImage.cpp | 807 |
1 files changed, 807 insertions, 0 deletions
diff --git a/src/VBox/Debugger/DBGCDumpImage.cpp b/src/VBox/Debugger/DBGCDumpImage.cpp new file mode 100644 index 00000000..73042b80 --- /dev/null +++ b/src/VBox/Debugger/DBGCDumpImage.cpp @@ -0,0 +1,807 @@ +/* $Id: DBGCDumpImage.cpp $ */ +/** @file + * DBGC - Debugger Console, Native Commands. + */ + +/* + * Copyright (C) 2006-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_DBGC +#include <VBox/dbg.h> +#include <VBox/vmm/dbgf.h> +#include <VBox/param.h> +#include <iprt/errcore.h> +#include <VBox/log.h> + +#include <iprt/assert.h> +#include <iprt/ctype.h> +#include <iprt/dir.h> +#include <iprt/env.h> +#include <iprt/ldr.h> +#include <iprt/mem.h> +#include <iprt/path.h> +#include <iprt/string.h> +#include <iprt/formats/mz.h> +#include <iprt/formats/pecoff.h> +#include <iprt/formats/elf32.h> +#include <iprt/formats/elf64.h> +#include <iprt/formats/codeview.h> +#include <iprt/formats/mach-o.h> + +#include "DBGCInternal.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * PE dumper instance. + */ +typedef struct DUMPIMAGEPE +{ + /** Pointer to the image base address variable. */ + PCDBGCVAR pImageBase; + /** Pointer to the file header. */ + PCIMAGE_FILE_HEADER pFileHdr; + /** Pointer to the NT headers. */ + union + { + PCIMAGE_NT_HEADERS32 pNt32; + PCIMAGE_NT_HEADERS64 pNt64; + void *pv; + } u; + /** Pointer to the section headers. */ + PCIMAGE_SECTION_HEADER paShdrs; + /** Number of section headers. */ + unsigned cShdrs; + /** Number of RVA and sizes (data directory entries). */ + unsigned cDataDir; + /** Pointer to the data directory. */ + PCIMAGE_DATA_DIRECTORY paDataDir; + + /** The command descriptor (for failing the command). */ + PCDBGCCMD pCmd; +} DUMPIMAGEPE; +/** Pointer to a PE dumper instance. */ +typedef DUMPIMAGEPE *PDUMPIMAGEPE; + + +/** Helper for translating flags. */ +typedef struct +{ + uint32_t fFlag; + const char *pszNm; +} DBGCDUMPFLAGENTRY; +#define FLENT(a_Define) { a_Define, #a_Define } + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +extern FNDBGCCMD dbgcCmdDumpImage; /* See DBGCCommands.cpp. */ + + +/** Stringifies a 32-bit flag value. */ +static void dbgcDumpImageFlags32(PDBGCCMDHLP pCmdHlp, uint32_t fFlags, DBGCDUMPFLAGENTRY const *paEntries, size_t cEntries) +{ + for (size_t i = 0; i < cEntries; i++) + if (fFlags & paEntries[i].fFlag) + DBGCCmdHlpPrintf(pCmdHlp, " %s", paEntries[i].pszNm); +} + + +/********************************************************************************************************************************* +* PE * +*********************************************************************************************************************************/ + +static const char *dbgcPeMachineName(uint16_t uMachine) +{ + switch (uMachine) + { + case IMAGE_FILE_MACHINE_I386 : return "I386"; + case IMAGE_FILE_MACHINE_AMD64 : return "AMD64"; + case IMAGE_FILE_MACHINE_UNKNOWN : return "UNKNOWN"; + case IMAGE_FILE_MACHINE_BASIC_16 : return "BASIC_16"; + case IMAGE_FILE_MACHINE_BASIC_16_TV : return "BASIC_16_TV"; + case IMAGE_FILE_MACHINE_IAPX16 : return "IAPX16"; + case IMAGE_FILE_MACHINE_IAPX16_TV : return "IAPX16_TV"; + //case IMAGE_FILE_MACHINE_IAPX20 : return "IAPX20"; + //case IMAGE_FILE_MACHINE_IAPX20_TV : return "IAPX20_TV"; + case IMAGE_FILE_MACHINE_I8086 : return "I8086"; + case IMAGE_FILE_MACHINE_I8086_TV : return "I8086_TV"; + case IMAGE_FILE_MACHINE_I286_SMALL : return "I286_SMALL"; + case IMAGE_FILE_MACHINE_MC68 : return "MC68"; + //case IMAGE_FILE_MACHINE_MC68_WR : return "MC68_WR"; + case IMAGE_FILE_MACHINE_MC68_TV : return "MC68_TV"; + case IMAGE_FILE_MACHINE_MC68_PG : return "MC68_PG"; + //case IMAGE_FILE_MACHINE_I286_LARGE : return "I286_LARGE"; + case IMAGE_FILE_MACHINE_U370_WR : return "U370_WR"; + case IMAGE_FILE_MACHINE_AMDAHL_470_WR: return "AMDAHL_470_WR"; + case IMAGE_FILE_MACHINE_AMDAHL_470_RO: return "AMDAHL_470_RO"; + case IMAGE_FILE_MACHINE_U370_RO : return "U370_RO"; + case IMAGE_FILE_MACHINE_R4000 : return "R4000"; + case IMAGE_FILE_MACHINE_WCEMIPSV2 : return "WCEMIPSV2"; + case IMAGE_FILE_MACHINE_VAX_WR : return "VAX_WR"; + case IMAGE_FILE_MACHINE_VAX_RO : return "VAX_RO"; + case IMAGE_FILE_MACHINE_SH3 : return "SH3"; + case IMAGE_FILE_MACHINE_SH3DSP : return "SH3DSP"; + case IMAGE_FILE_MACHINE_SH4 : return "SH4"; + case IMAGE_FILE_MACHINE_SH5 : return "SH5"; + case IMAGE_FILE_MACHINE_ARM : return "ARM"; + case IMAGE_FILE_MACHINE_THUMB : return "THUMB"; + case IMAGE_FILE_MACHINE_ARMNT : return "ARMNT"; + case IMAGE_FILE_MACHINE_AM33 : return "AM33"; + case IMAGE_FILE_MACHINE_POWERPC : return "POWERPC"; + case IMAGE_FILE_MACHINE_POWERPCFP : return "POWERPCFP"; + case IMAGE_FILE_MACHINE_IA64 : return "IA64"; + case IMAGE_FILE_MACHINE_MIPS16 : return "MIPS16"; + case IMAGE_FILE_MACHINE_MIPSFPU : return "MIPSFPU"; + case IMAGE_FILE_MACHINE_MIPSFPU16 : return "MIPSFPU16"; + case IMAGE_FILE_MACHINE_EBC : return "EBC"; + case IMAGE_FILE_MACHINE_M32R : return "M32R"; + case IMAGE_FILE_MACHINE_ARM64 : return "ARM64"; + } + return "??"; +} + + +static const char *dbgcPeDataDirName(unsigned iDir) +{ + switch (iDir) + { + case IMAGE_DIRECTORY_ENTRY_EXPORT: return "EXPORT"; + case IMAGE_DIRECTORY_ENTRY_IMPORT: return "IMPORT"; + case IMAGE_DIRECTORY_ENTRY_RESOURCE: return "RESOURCE"; + case IMAGE_DIRECTORY_ENTRY_EXCEPTION: return "EXCEPTION"; + case IMAGE_DIRECTORY_ENTRY_SECURITY: return "SECURITY"; + case IMAGE_DIRECTORY_ENTRY_BASERELOC: return "BASERELOC"; + case IMAGE_DIRECTORY_ENTRY_DEBUG: return "DEBUG"; + case IMAGE_DIRECTORY_ENTRY_ARCHITECTURE: return "ARCHITECTURE"; + case IMAGE_DIRECTORY_ENTRY_GLOBALPTR: return "GLOBALPTR"; + case IMAGE_DIRECTORY_ENTRY_TLS: return "TLS"; + case IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG: return "LOAD_CONFIG"; + case IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT: return "BOUND_IMPORT"; + case IMAGE_DIRECTORY_ENTRY_IAT: return "IAT"; + case IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT: return "DELAY_IMPORT"; + case IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR: return "COM_DESCRIPTOR"; + } + return "??"; +} + + +static const char *dbgPeDebugTypeName(uint32_t uType) +{ + switch (uType) + { + case IMAGE_DEBUG_TYPE_UNKNOWN: return "UNKNOWN"; + case IMAGE_DEBUG_TYPE_COFF: return "COFF"; + case IMAGE_DEBUG_TYPE_CODEVIEW: return "CODEVIEW"; + case IMAGE_DEBUG_TYPE_FPO: return "FPO"; + case IMAGE_DEBUG_TYPE_MISC: return "MISC"; + case IMAGE_DEBUG_TYPE_EXCEPTION: return "EXCEPTION"; + case IMAGE_DEBUG_TYPE_FIXUP: return "FIXUP"; + case IMAGE_DEBUG_TYPE_OMAP_TO_SRC: return "OMAP_TO_SRC"; + case IMAGE_DEBUG_TYPE_OMAP_FROM_SRC: return "OMAP_FROM_SRC"; + case IMAGE_DEBUG_TYPE_BORLAND: return "BORLAND"; + case IMAGE_DEBUG_TYPE_RESERVED10: return "RESERVED10"; + case IMAGE_DEBUG_TYPE_CLSID: return "CLSID"; + case IMAGE_DEBUG_TYPE_VC_FEATURE: return "VC_FEATURE"; + case IMAGE_DEBUG_TYPE_POGO: return "POGO"; + case IMAGE_DEBUG_TYPE_ILTCG: return "ILTCG"; + case IMAGE_DEBUG_TYPE_MPX: return "MPX"; + case IMAGE_DEBUG_TYPE_REPRO: return "REPRO"; + } + return "??"; +} + + +static int dbgcDumpImagePeDebugDir(PDUMPIMAGEPE pThis, PDBGCCMDHLP pCmdHlp, PCDBGCVAR pDataAddr, uint32_t cbData) +{ + uint32_t cEntries = cbData / sizeof(IMAGE_DEBUG_DIRECTORY); + for (uint32_t i = 0; i < cEntries; i++) + { + /* + * Read the entry into memory. + */ + DBGCVAR DbgDirAddr; + int rc = DBGCCmdHlpEval(pCmdHlp, &DbgDirAddr, "%DV + %#RX32", pDataAddr, i * sizeof(IMAGE_DEBUG_DIRECTORY)); + if (RT_FAILURE(rc)) + return DBGCCmdHlpFailRc(pCmdHlp, pThis->pCmd, rc, "DBGCCmdHlpEval failed on debug entry %u", i); + + IMAGE_DEBUG_DIRECTORY DbgDir; + rc = DBGCCmdHlpMemRead(pCmdHlp, &DbgDir, sizeof(DbgDir), &DbgDirAddr, NULL); + if (RT_FAILURE(rc)) + return DBGCCmdHlpFailRc(pCmdHlp, pThis->pCmd, rc, "Failed to read %zu at %Dv", sizeof(DbgDir), &DbgDirAddr); + + /* + * Dump it. + */ + DBGCVAR DebugDataAddr = *pThis->pImageBase; + rc = DBGCCmdHlpEval(pCmdHlp, &DebugDataAddr, "%DV + %#RX32", pThis->pImageBase, DbgDir.AddressOfRawData); + DBGCCmdHlpPrintf(pCmdHlp, " Debug[%u]: %Dv/%08RX32 LB %06RX32 %u (%s) v%u.%u file=%RX32 ts=%08RX32 fl=%RX32\n", + i, &DebugDataAddr, DbgDir.AddressOfRawData, DbgDir.SizeOfData, DbgDir.Type, + dbgPeDebugTypeName(DbgDir.Type), DbgDir.MajorVersion, DbgDir.MinorVersion, DbgDir.PointerToRawData, + DbgDir.TimeDateStamp, DbgDir.Characteristics); + union + { + uint8_t abPage[0x1000]; + CVPDB20INFO Pdb20; + CVPDB70INFO Pdb70; + IMAGE_DEBUG_MISC Misc; + } uBuf; + RT_ZERO(uBuf); + + if (DbgDir.Type == IMAGE_DEBUG_TYPE_CODEVIEW) + { + if ( DbgDir.SizeOfData < sizeof(uBuf) + && DbgDir.SizeOfData > 16 + && DbgDir.AddressOfRawData > 0 + && RT_SUCCESS(rc)) + { + rc = DBGCCmdHlpMemRead(pCmdHlp, &uBuf, DbgDir.SizeOfData, &DebugDataAddr, NULL); + if (RT_FAILURE(rc)) + return DBGCCmdHlpFailRc(pCmdHlp, pThis->pCmd, rc, "Failed to read %zu at %Dv", + DbgDir.SizeOfData, &DebugDataAddr); + + if ( uBuf.Pdb20.u32Magic == CVPDB20INFO_MAGIC + && uBuf.Pdb20.offDbgInfo == 0 + && DbgDir.SizeOfData > RT_UOFFSETOF(CVPDB20INFO, szPdbFilename) ) + DBGCCmdHlpPrintf(pCmdHlp, " PDB2.0: ts=%08RX32 age=%RX32 %s\n", + uBuf.Pdb20.uTimestamp, uBuf.Pdb20.uAge, uBuf.Pdb20.szPdbFilename); + else if ( uBuf.Pdb20.u32Magic == CVPDB70INFO_MAGIC + && DbgDir.SizeOfData > RT_UOFFSETOF(CVPDB70INFO, szPdbFilename) ) + DBGCCmdHlpPrintf(pCmdHlp, " PDB7.0: %RTuuid age=%u %s\n", + &uBuf.Pdb70.PdbUuid, uBuf.Pdb70.uAge, uBuf.Pdb70.szPdbFilename); + else + DBGCCmdHlpPrintf(pCmdHlp, " Unknown PDB/codeview magic: %.8Rhxs\n", uBuf.abPage); + } + } + else if (DbgDir.Type == IMAGE_DEBUG_TYPE_MISC) + { + if ( DbgDir.SizeOfData < sizeof(uBuf) + && DbgDir.SizeOfData > RT_UOFFSETOF(IMAGE_DEBUG_MISC, Data) + && DbgDir.AddressOfRawData > 0 + && RT_SUCCESS(rc) ) + { + rc = DBGCCmdHlpMemRead(pCmdHlp, &uBuf, DbgDir.SizeOfData, &DebugDataAddr, NULL); + if (RT_FAILURE(rc)) + return DBGCCmdHlpFailRc(pCmdHlp, pThis->pCmd, rc, "Failed to read %zu at %Dv", + DbgDir.SizeOfData, &DebugDataAddr); + + if ( uBuf.Misc.DataType == IMAGE_DEBUG_MISC_EXENAME + && uBuf.Misc.Length == DbgDir.SizeOfData) + { + if (!uBuf.Misc.Unicode) + DBGCCmdHlpPrintf(pCmdHlp, " Misc DBG: ts=%RX32 %s\n", + DbgDir.TimeDateStamp, (const char *)&uBuf.Misc.Data[0]); + else + DBGCCmdHlpPrintf(pCmdHlp, " Misc DBG: ts=%RX32 %ls\n", + DbgDir.TimeDateStamp, (PCRTUTF16)&uBuf.Misc.Data[0]); + } + } + } + } + return VINF_SUCCESS; +} + + +static int dbgcDumpImagePeDataDirs(PDUMPIMAGEPE pThis, PDBGCCMDHLP pCmdHlp, unsigned cDataDirs, PCIMAGE_DATA_DIRECTORY paDataDirs) +{ + int rcRet = VINF_SUCCESS; + for (unsigned i = 0; i < cDataDirs; i++) + { + if (paDataDirs[i].Size || paDataDirs[i].VirtualAddress) + { + DBGCVAR DataAddr = *pThis->pImageBase; + DBGCCmdHlpEval(pCmdHlp, &DataAddr, "%DV + %#RX32", pThis->pImageBase, paDataDirs[i].VirtualAddress); + DBGCCmdHlpPrintf(pCmdHlp, "DataDir[%02u]: %Dv/%08RX32 LB %08RX32 %s\n", + i, &DataAddr, paDataDirs[i].VirtualAddress, paDataDirs[i].Size, dbgcPeDataDirName(i)); + int rc = VINF_SUCCESS; + if ( i == IMAGE_DIRECTORY_ENTRY_DEBUG + && paDataDirs[i].Size >= sizeof(IMAGE_DEBUG_DIRECTORY)) + rc = dbgcDumpImagePeDebugDir(pThis, pCmdHlp, &DataAddr, paDataDirs[i].Size); + if (RT_FAILURE(rc) && RT_SUCCESS(rcRet)) + rcRet = rc; + } + } + return rcRet; +} + + +static int dbgcDumpImagePeSectionHdrs(PDUMPIMAGEPE pThis, PDBGCCMDHLP pCmdHlp, unsigned cShdrs, PCIMAGE_SECTION_HEADER paShdrs) +{ + for (unsigned i = 0; i < cShdrs; i++) + { + DBGCVAR SectAddr = *pThis->pImageBase; + DBGCCmdHlpEval(pCmdHlp, &SectAddr, "%DV + %#RX32", pThis->pImageBase, paShdrs[i].VirtualAddress); + DBGCCmdHlpPrintf(pCmdHlp, "Section[%02u]: %Dv/%08RX32 LB %08RX32 %.8s\n", + i, &SectAddr, paShdrs[i].VirtualAddress, paShdrs[i].Misc.VirtualSize, paShdrs[i].Name); + } + return VINF_SUCCESS; +} + + +static int dbgcDumpImagePeOptHdr32(PDUMPIMAGEPE pThis, PDBGCCMDHLP pCmdHlp, PCIMAGE_NT_HEADERS32 pNtHdrs) +{ + RT_NOREF(pThis, pCmdHlp, pNtHdrs); + return VINF_SUCCESS; +} + +static int dbgcDumpImagePeOptHdr64(PDUMPIMAGEPE pThis, PDBGCCMDHLP pCmdHlp, PCIMAGE_NT_HEADERS64 pNtHdrs) +{ + RT_NOREF(pThis, pCmdHlp, pNtHdrs); + return VINF_SUCCESS; +} + + +static int dbgcDumpImagePe(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PCDBGCVAR pImageBase, + PCDBGCVAR pPeHdrAddr, PCIMAGE_FILE_HEADER pFileHdr) +{ + /* + * Dump file header fields. + */ + DBGCCmdHlpPrintf(pCmdHlp, "%Dv: PE image - %#x (%s), %u sections\n", pImageBase, pFileHdr->Machine, + dbgcPeMachineName(pFileHdr->Machine), pFileHdr->NumberOfSections); + DBGCCmdHlpPrintf(pCmdHlp, "Characteristics: %#06x", pFileHdr->Characteristics); + if (pFileHdr->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) DBGCCmdHlpPrintf(pCmdHlp, " RELOCS_STRIPPED"); + if (pFileHdr->Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) DBGCCmdHlpPrintf(pCmdHlp, " EXECUTABLE_IMAGE"); + if (pFileHdr->Characteristics & IMAGE_FILE_LINE_NUMS_STRIPPED) DBGCCmdHlpPrintf(pCmdHlp, " LINE_NUMS_STRIPPED"); + if (pFileHdr->Characteristics & IMAGE_FILE_LOCAL_SYMS_STRIPPED) DBGCCmdHlpPrintf(pCmdHlp, " LOCAL_SYMS_STRIPPED"); + if (pFileHdr->Characteristics & IMAGE_FILE_AGGRESIVE_WS_TRIM) DBGCCmdHlpPrintf(pCmdHlp, " AGGRESIVE_WS_TRIM"); + if (pFileHdr->Characteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE) DBGCCmdHlpPrintf(pCmdHlp, " LARGE_ADDRESS_AWARE"); + if (pFileHdr->Characteristics & IMAGE_FILE_16BIT_MACHINE) DBGCCmdHlpPrintf(pCmdHlp, " 16BIT_MACHINE"); + if (pFileHdr->Characteristics & IMAGE_FILE_BYTES_REVERSED_LO) DBGCCmdHlpPrintf(pCmdHlp, " BYTES_REVERSED_LO"); + if (pFileHdr->Characteristics & IMAGE_FILE_32BIT_MACHINE) DBGCCmdHlpPrintf(pCmdHlp, " 32BIT_MACHINE"); + if (pFileHdr->Characteristics & IMAGE_FILE_DEBUG_STRIPPED) DBGCCmdHlpPrintf(pCmdHlp, " DEBUG_STRIPPED"); + if (pFileHdr->Characteristics & IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP) DBGCCmdHlpPrintf(pCmdHlp, " REMOVABLE_RUN_FROM_SWAP"); + if (pFileHdr->Characteristics & IMAGE_FILE_NET_RUN_FROM_SWAP) DBGCCmdHlpPrintf(pCmdHlp, " NET_RUN_FROM_SWAP"); + if (pFileHdr->Characteristics & IMAGE_FILE_SYSTEM) DBGCCmdHlpPrintf(pCmdHlp, " SYSTEM"); + if (pFileHdr->Characteristics & IMAGE_FILE_DLL) DBGCCmdHlpPrintf(pCmdHlp, " DLL"); + if (pFileHdr->Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY) DBGCCmdHlpPrintf(pCmdHlp, " UP_SYSTEM_ONLY"); + if (pFileHdr->Characteristics & IMAGE_FILE_BYTES_REVERSED_HI) DBGCCmdHlpPrintf(pCmdHlp, " BYTES_REVERSED_HI"); + DBGCCmdHlpPrintf(pCmdHlp, "\n"); + + /* + * Allocate memory for all the headers, including section headers, and read them into memory. + */ + size_t offSHdrs = pFileHdr->SizeOfOptionalHeader + sizeof(*pFileHdr) + sizeof(uint32_t); + size_t cbHdrs = offSHdrs + pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER); + if (cbHdrs > _2M) + return DBGCCmdHlpFail(pCmdHlp, pCmd, "%Dv: headers too big: %zu.\n", pImageBase, cbHdrs); + + void *pvBuf = RTMemTmpAllocZ(cbHdrs); + if (!pvBuf) + return DBGCCmdHlpFail(pCmdHlp, pCmd, "%Dv: failed to allocate %zu bytes.\n", pImageBase, cbHdrs); + int rc = DBGCCmdHlpMemRead(pCmdHlp, pvBuf, cbHdrs, pPeHdrAddr, NULL); + if (RT_SUCCESS(rc)) + { + DUMPIMAGEPE This; + RT_ZERO(This); + This.pImageBase = pImageBase; + This.pFileHdr = pFileHdr; + This.u.pv = pvBuf; + This.cShdrs = pFileHdr->NumberOfSections; + This.paShdrs = (PCIMAGE_SECTION_HEADER)((uintptr_t)pvBuf + offSHdrs); + This.pCmd = pCmd; + + if (pFileHdr->SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32)) + { + This.paDataDir = This.u.pNt32->OptionalHeader.DataDirectory; + This.cDataDir = This.u.pNt32->OptionalHeader.NumberOfRvaAndSizes; + rc = dbgcDumpImagePeOptHdr32(&This, pCmdHlp, This.u.pNt32); + } + else if (pFileHdr->SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER64)) + { + This.paDataDir = This.u.pNt64->OptionalHeader.DataDirectory; + This.cDataDir = This.u.pNt64->OptionalHeader.NumberOfRvaAndSizes; + rc = dbgcDumpImagePeOptHdr64(&This, pCmdHlp, This.u.pNt64); + } + else + rc = DBGCCmdHlpFail(pCmdHlp, pCmd, "%Dv: Unsupported optional header size: %#x\n", + pImageBase, pFileHdr->SizeOfOptionalHeader); + + int rc2 = dbgcDumpImagePeSectionHdrs(&This, pCmdHlp, This.cShdrs, This.paShdrs); + if (RT_FAILURE(rc2) && RT_SUCCESS(rc)) + rc = rc2; + + rc2 = dbgcDumpImagePeDataDirs(&This, pCmdHlp, This.cDataDir, This.paDataDir); + if (RT_FAILURE(rc2) && RT_SUCCESS(rc)) + rc = rc2; + } + else + rc = DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "%Dv: Failed to read %zu at %Dv", pImageBase, cbHdrs, pPeHdrAddr); + RTMemTmpFree(pvBuf); + return rc; +} + + +/********************************************************************************************************************************* +* ELF * +*********************************************************************************************************************************/ + +static int dbgcDumpImageElf(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PCDBGCVAR pImageBase) +{ + RT_NOREF_PV(pCmd); + DBGCCmdHlpPrintf(pCmdHlp, "%Dv: ELF image dumping not implemented yet.\n", pImageBase); + return VINF_SUCCESS; +} + + +/********************************************************************************************************************************* +* Mach-O * +*********************************************************************************************************************************/ + +static const char *dbgcMachoFileType(uint32_t uType) +{ + switch (uType) + { + case MH_OBJECT: return "MH_OBJECT"; + case MH_EXECUTE: return "MH_EXECUTE"; + case MH_FVMLIB: return "MH_FVMLIB"; + case MH_CORE: return "MH_CORE"; + case MH_PRELOAD: return "MH_PRELOAD"; + case MH_DYLIB: return "MH_DYLIB"; + case MH_DYLINKER: return "MH_DYLINKER"; + case MH_BUNDLE: return "MH_BUNDLE"; + case MH_DYLIB_STUB: return "MH_DYLIB_STUB"; + case MH_DSYM: return "MH_DSYM"; + case MH_KEXT_BUNDLE: return "MH_KEXT_BUNDLE"; + } + return "??"; +} + + +static const char *dbgcMachoCpuType(int32_t iType, int32_t iSubType) +{ + switch (iType) + { + case CPU_TYPE_ANY: return "CPU_TYPE_ANY"; + case CPU_TYPE_VAX: return "VAX"; + case CPU_TYPE_MC680x0: return "MC680x0"; + case CPU_TYPE_X86: return "X86"; + case CPU_TYPE_X86_64: + switch (iSubType) + { + case CPU_SUBTYPE_X86_64_ALL: return "X86_64/ALL64"; + } + return "X86_64"; + case CPU_TYPE_MC98000: return "MC98000"; + case CPU_TYPE_HPPA: return "HPPA"; + case CPU_TYPE_MC88000: return "MC88000"; + case CPU_TYPE_SPARC: return "SPARC"; + case CPU_TYPE_I860: return "I860"; + case CPU_TYPE_POWERPC: return "POWERPC"; + case CPU_TYPE_POWERPC64: return "POWERPC64"; + + } + return "??"; +} + + +static const char *dbgcMachoLoadCommand(uint32_t uCmd) +{ + switch (uCmd) + { + RT_CASE_RET_STR(LC_SEGMENT_32); + RT_CASE_RET_STR(LC_SYMTAB); + RT_CASE_RET_STR(LC_SYMSEG); + RT_CASE_RET_STR(LC_THREAD); + RT_CASE_RET_STR(LC_UNIXTHREAD); + RT_CASE_RET_STR(LC_LOADFVMLIB); + RT_CASE_RET_STR(LC_IDFVMLIB); + RT_CASE_RET_STR(LC_IDENT); + RT_CASE_RET_STR(LC_FVMFILE); + RT_CASE_RET_STR(LC_PREPAGE); + RT_CASE_RET_STR(LC_DYSYMTAB); + RT_CASE_RET_STR(LC_LOAD_DYLIB); + RT_CASE_RET_STR(LC_ID_DYLIB); + RT_CASE_RET_STR(LC_LOAD_DYLINKER); + RT_CASE_RET_STR(LC_ID_DYLINKER); + RT_CASE_RET_STR(LC_PREBOUND_DYLIB); + RT_CASE_RET_STR(LC_ROUTINES); + RT_CASE_RET_STR(LC_SUB_FRAMEWORK); + RT_CASE_RET_STR(LC_SUB_UMBRELLA); + RT_CASE_RET_STR(LC_SUB_CLIENT); + RT_CASE_RET_STR(LC_SUB_LIBRARY); + RT_CASE_RET_STR(LC_TWOLEVEL_HINTS); + RT_CASE_RET_STR(LC_PREBIND_CKSUM); + RT_CASE_RET_STR(LC_LOAD_WEAK_DYLIB); + RT_CASE_RET_STR(LC_SEGMENT_64); + RT_CASE_RET_STR(LC_ROUTINES_64); + RT_CASE_RET_STR(LC_UUID); + RT_CASE_RET_STR(LC_RPATH); + RT_CASE_RET_STR(LC_CODE_SIGNATURE); + RT_CASE_RET_STR(LC_SEGMENT_SPLIT_INFO); + RT_CASE_RET_STR(LC_REEXPORT_DYLIB); + RT_CASE_RET_STR(LC_LAZY_LOAD_DYLIB); + RT_CASE_RET_STR(LC_ENCRYPTION_INFO); + RT_CASE_RET_STR(LC_DYLD_INFO); + RT_CASE_RET_STR(LC_DYLD_INFO_ONLY); + RT_CASE_RET_STR(LC_LOAD_UPWARD_DYLIB); + RT_CASE_RET_STR(LC_VERSION_MIN_MACOSX); + RT_CASE_RET_STR(LC_VERSION_MIN_IPHONEOS); + RT_CASE_RET_STR(LC_FUNCTION_STARTS); + RT_CASE_RET_STR(LC_DYLD_ENVIRONMENT); + RT_CASE_RET_STR(LC_MAIN); + RT_CASE_RET_STR(LC_DATA_IN_CODE); + RT_CASE_RET_STR(LC_SOURCE_VERSION); + RT_CASE_RET_STR(LC_DYLIB_CODE_SIGN_DRS); + RT_CASE_RET_STR(LC_ENCRYPTION_INFO_64); + RT_CASE_RET_STR(LC_LINKER_OPTION); + RT_CASE_RET_STR(LC_LINKER_OPTIMIZATION_HINT); + RT_CASE_RET_STR(LC_VERSION_MIN_TVOS); + RT_CASE_RET_STR(LC_VERSION_MIN_WATCHOS); + RT_CASE_RET_STR(LC_NOTE); + RT_CASE_RET_STR(LC_BUILD_VERSION); + } + return "??"; +} + + +static const char *dbgcMachoProt(uint32_t fProt) +{ + switch (fProt) + { + case VM_PROT_NONE: return "---"; + case VM_PROT_READ: return "r--"; + case VM_PROT_READ | VM_PROT_WRITE: return "rw-"; + case VM_PROT_READ | VM_PROT_EXECUTE: return "r-x"; + case VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE: return "rwx"; + case VM_PROT_WRITE: return "-w-"; + case VM_PROT_WRITE | VM_PROT_EXECUTE: return "-wx"; + case VM_PROT_EXECUTE: return "-w-"; + } + return "???"; +} + + +static int dbgcDumpImageMachO(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PCDBGCVAR pImageBase, mach_header_64_t const *pHdr) +{ +#define ENTRY(a_Define) { a_Define, #a_Define } + RT_NOREF_PV(pCmd); + + /* + * Header: + */ + DBGCCmdHlpPrintf(pCmdHlp, "%Dv: Mach-O image (%s bit) - %s (%u) - %s (%#x / %#x)\n", + pImageBase, pHdr->magic == IMAGE_MACHO64_SIGNATURE ? "64" : "32", + dbgcMachoFileType(pHdr->filetype), pHdr->filetype, + dbgcMachoCpuType(pHdr->cputype, pHdr->cpusubtype), pHdr->cputype, pHdr->cpusubtype); + + DBGCCmdHlpPrintf(pCmdHlp, "%Dv: Flags: %#x", pImageBase, pHdr->flags); + static DBGCDUMPFLAGENTRY const s_aHdrFlags[] = + { + FLENT(MH_NOUNDEFS), FLENT(MH_INCRLINK), + FLENT(MH_DYLDLINK), FLENT(MH_BINDATLOAD), + FLENT(MH_PREBOUND), FLENT(MH_SPLIT_SEGS), + FLENT(MH_LAZY_INIT), FLENT(MH_TWOLEVEL), + FLENT(MH_FORCE_FLAT), FLENT(MH_NOMULTIDEFS), + FLENT(MH_NOFIXPREBINDING), FLENT(MH_PREBINDABLE), + FLENT(MH_ALLMODSBOUND), FLENT(MH_SUBSECTIONS_VIA_SYMBOLS), + FLENT(MH_CANONICAL), FLENT(MH_WEAK_DEFINES), + FLENT(MH_BINDS_TO_WEAK), FLENT(MH_ALLOW_STACK_EXECUTION), + FLENT(MH_ROOT_SAFE), FLENT(MH_SETUID_SAFE), + FLENT(MH_NO_REEXPORTED_DYLIBS), FLENT(MH_PIE), + FLENT(MH_DEAD_STRIPPABLE_DYLIB), FLENT(MH_HAS_TLV_DESCRIPTORS), + FLENT(MH_NO_HEAP_EXECUTION), + }; + dbgcDumpImageFlags32(pCmdHlp, pHdr->flags, s_aHdrFlags, RT_ELEMENTS(s_aHdrFlags)); + DBGCCmdHlpPrintf(pCmdHlp, "\n"); + if (pHdr->reserved != 0 && pHdr->magic == IMAGE_MACHO64_SIGNATURE) + DBGCCmdHlpPrintf(pCmdHlp, "%Dv: Reserved header field: %#x\n", pImageBase, pHdr->reserved); + + /* + * And now the load commands. + */ + const uint32_t cCmds = pHdr->ncmds; + const uint32_t cbCmds = pHdr->sizeofcmds; + DBGCCmdHlpPrintf(pCmdHlp, "%Dv: %u load commands covering %#x bytes:\n", pImageBase, cCmds, cbCmds); + if (cbCmds > _16M) + return DBGCCmdHlpFailRc(pCmdHlp, pCmd, VERR_OUT_OF_RANGE, + "%Dv: Commands too big: %#x bytes, max 16MiB\n", pImageBase, cbCmds); + + /* Calc address of the first command: */ + const uint32_t cbHdr = pHdr->magic == IMAGE_MACHO64_SIGNATURE ? sizeof(mach_header_64_t) : sizeof(mach_header_32_t); + DBGCVAR Addr; + int rc = DBGCCmdHlpEval(pCmdHlp, &Addr, "%DV + %#RX32", pImageBase, cbHdr); + AssertRCReturn(rc, rc); + + /* Read them into a temp buffer: */ + uint8_t *pbCmds = (uint8_t *)RTMemTmpAllocZ(cbCmds); + if (!pbCmds) + return VERR_NO_TMP_MEMORY; + + rc = DBGCCmdHlpMemRead(pCmdHlp, pbCmds, cbCmds, &Addr, NULL); + if (RT_SUCCESS(rc)) + { + static const DBGCDUMPFLAGENTRY s_aSegFlags[] = + { FLENT(SG_HIGHVM), FLENT(SG_FVMLIB), FLENT(SG_NORELOC), FLENT(SG_PROTECTED_VERSION_1), }; + + /* + * Iterate the commands. + */ + uint32_t offCmd = 0; + for (uint32_t iCmd = 0; iCmd < cCmds; iCmd++) + { + load_command_t const *pCurCmd = (load_command_t const *)&pbCmds[offCmd]; + const uint32_t cbCurCmd = offCmd + sizeof(*pCurCmd) <= cbCmds ? pCurCmd->cmdsize : sizeof(*pCurCmd); + if (offCmd + cbCurCmd > cbCmds) + { + rc = DBGCCmdHlpFailRc(pCmdHlp, pCmd, VERR_OUT_OF_RANGE, + "%Dv: Load command #%u (offset %#x + %#x) is out of bounds! cmdsize=%u (%#x) cmd=%u\n", + pImageBase, iCmd, offCmd, cbHdr, cbCurCmd, cbCurCmd, + offCmd + RT_UOFFSET_AFTER(load_command_t, cmd) <= cbCmds ? pCurCmd->cmd : UINT32_MAX); + break; + } + + DBGCCmdHlpPrintf(pCmdHlp, "%Dv: Load command #%u (offset %#x + %#x): %s (%u) LB %u\n", + pImageBase, iCmd, offCmd, cbHdr, dbgcMachoLoadCommand(pCurCmd->cmd), pCurCmd->cmd, cbCurCmd); + switch (pCurCmd->cmd) + { + case LC_SEGMENT_64: + if (cbCurCmd < sizeof(segment_command_64_t)) + rc = DBGCCmdHlpFailRc(pCmdHlp, pCmd, VERR_LDRMACHO_BAD_LOAD_COMMAND, + "%Dv: LC_SEGMENT64 is too short!\n", pImageBase); + else + { + segment_command_64_t const *pSeg = (segment_command_64_t const *)pCurCmd; + DBGCCmdHlpPrintf(pCmdHlp, "%Dv: vmaddr: %016RX64 LB %08RX64 prot: %s(%x) maxprot: %s(%x) name: %.16s\n", + pImageBase, pSeg->vmaddr, pSeg->vmsize, dbgcMachoProt(pSeg->initprot), pSeg->initprot, + dbgcMachoProt(pSeg->maxprot), pSeg->maxprot, pSeg->segname); + DBGCCmdHlpPrintf(pCmdHlp, "%Dv: file: %016RX64 LB %08RX64 sections: %2u flags: %#x", + pImageBase, pSeg->fileoff, pSeg->filesize, pSeg->nsects, pSeg->flags); + dbgcDumpImageFlags32(pCmdHlp, pSeg->flags, s_aSegFlags, RT_ELEMENTS(s_aSegFlags)); + DBGCCmdHlpPrintf(pCmdHlp, "\n"); + if ( pSeg->nsects > _64K + || pSeg->nsects * sizeof(section_64_t) + sizeof(pSeg) > cbCurCmd) + rc = DBGCCmdHlpFailRc(pCmdHlp, pCmd, VERR_LDRMACHO_BAD_LOAD_COMMAND, + "%Dv: LC_SEGMENT64 is too short for all the sections!\n", pImageBase); + else + { + section_64_t const *paSec = (section_64_t const *)(pSeg + 1); + for (uint32_t iSec = 0; iSec < pSeg->nsects; iSec++) + { + DBGCCmdHlpPrintf(pCmdHlp, + "%Dv: Section #%u: %016RX64 LB %08RX64 align: 2**%-2u name: %.16s", + pImageBase, iSec, paSec[iSec].addr, paSec[iSec].size, paSec[iSec].align, + paSec[iSec].sectname); + if (strncmp(pSeg->segname, paSec[iSec].segname, sizeof(pSeg->segname))) + DBGCCmdHlpPrintf(pCmdHlp, "(in %.16s)", paSec[iSec].segname); + DBGCCmdHlpPrintf(pCmdHlp, "\n"); + + /// @todo Good night! + /// uint32_t offset; + /// uint32_t reloff; + /// uint32_t nreloc; + /// uint32_t flags; + /// /** For S_LAZY_SYMBOL_POINTERS, S_NON_LAZY_SYMBOL_POINTERS and S_SYMBOL_STUBS + /// * this is the index into the indirect symbol table. */ + /// uint32_t reserved1; + /// uint32_t reserved2; + /// uint32_t reserved3; + /// + } + } + } + break; + } + + /* Advance: */ + offCmd += cbCurCmd; + } + } + else + rc = DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "%Dv: Error reading load commands %Dv LB %#x\n", + pImageBase, &Addr, cbCmds); + RTMemTmpFree(pbCmds); + return rc; +#undef ENTRY +} + + +/** + * @callback_method_impl{FNDBGCCMD, The 'dumpimage' command.} + */ +DECLCALLBACK(int) dbgcCmdDumpImage(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs) +{ + int rcRet = VINF_SUCCESS; + for (unsigned iArg = 0; iArg < cArgs; iArg++) + { + union + { + uint8_t ab[0x10]; + IMAGE_DOS_HEADER DosHdr; + struct + { + uint32_t u32Magic; + IMAGE_FILE_HEADER FileHdr; + } Nt; + mach_header_64_t MachO64; + } uBuf; + DBGCVAR const ImageBase = paArgs[iArg]; + int rc = DBGCCmdHlpMemRead(pCmdHlp, &uBuf.DosHdr, sizeof(uBuf.DosHdr), &ImageBase, NULL); + if (RT_SUCCESS(rc)) + { + /* + * MZ. + */ + if (uBuf.DosHdr.e_magic == IMAGE_DOS_SIGNATURE) + { + uint32_t offNewHdr = uBuf.DosHdr.e_lfanew; + if (offNewHdr < _256K && offNewHdr >= 16) + { + /* Look for new header. */ + DBGCVAR NewHdrAddr; + rc = DBGCCmdHlpEval(pCmdHlp, &NewHdrAddr, "%DV + %#RX32", &ImageBase, offNewHdr); + if (RT_SUCCESS(rc)) + { + rc = DBGCCmdHlpMemRead(pCmdHlp, &uBuf.Nt, sizeof(uBuf.Nt), &NewHdrAddr, NULL); + if (RT_SUCCESS(rc)) + { + /* PE: */ + if (uBuf.Nt.u32Magic == IMAGE_NT_SIGNATURE) + rc = dbgcDumpImagePe(pCmd, pCmdHlp, &ImageBase, &NewHdrAddr, &uBuf.Nt.FileHdr); + else + rc = DBGCCmdHlpFail(pCmdHlp, pCmd, "%Dv: Unknown new header magic: %.8Rhxs\n", + &ImageBase, uBuf.ab); + } + else + rc = DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "%Dv: Failed to read %zu at %Dv", + &ImageBase, sizeof(uBuf.Nt), &NewHdrAddr); + } + else + rc = DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "%Dv: Failed to calc address of new header", &ImageBase); + } + else + rc = DBGCCmdHlpFail(pCmdHlp, pCmd, "%Dv: MZ header but e_lfanew=%#RX32 is out of bounds (16..256K).\n", + &ImageBase, offNewHdr); + } + /* + * ELF. + */ + else if (uBuf.ab[0] == ELFMAG0 && uBuf.ab[1] == ELFMAG1 && uBuf.ab[2] == ELFMAG2 && uBuf.ab[3] == ELFMAG3) + rc = dbgcDumpImageElf(pCmd, pCmdHlp, &ImageBase); + /* + * Mach-O. + */ + else if ( uBuf.MachO64.magic == IMAGE_MACHO64_SIGNATURE + || uBuf.MachO64.magic == IMAGE_MACHO32_SIGNATURE ) + rc = dbgcDumpImageMachO(pCmd, pCmdHlp, &ImageBase, &uBuf.MachO64); + /* + * Dunno. + */ + else + rc = DBGCCmdHlpFail(pCmdHlp, pCmd, "%Dv: Unknown magic: %.8Rhxs\n", &ImageBase, uBuf.ab); + } + else + rc = DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "%Dv: Failed to read %zu", &ImageBase, sizeof(uBuf.DosHdr)); + if (RT_FAILURE(rc) && RT_SUCCESS(rcRet)) + rcRet = rc; + } + RT_NOREF(pUVM); + return rcRet; +} + |