VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/DBGFR3ModInMem.cpp@ 100696

Last change on this file since 100696 was 99775, checked in by vboxsync, 19 months ago

*: Mark functions as static if not used outside of a given compilation unit. Enables the compiler to optimize inlining, reduces the symbol tables, exposes unused functions and in some rare cases exposes mismtaches between function declarations and definitions, but most importantly reduces the number of parfait reports for the extern-function-no-forward-declaration category. This should not result in any functional changes, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 44.6 KB
Line 
1/* $Id: DBGFR3ModInMem.cpp 99775 2023-05-12 12:21:58Z vboxsync $ */
2/** @file
3 * DBGFR3ModInMemPe - In memory PE module 'loader'.
4 */
5
6/*
7 * Copyright (C) 2009-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_DBGF
33#include <VBox/vmm/dbgf.h>
34
35#include <VBox/err.h>
36#include <iprt/ctype.h>
37#include <iprt/ldr.h>
38#include <iprt/mem.h>
39#include <iprt/path.h>
40#include <iprt/string.h>
41#include <iprt/sort.h>
42#include <iprt/formats/pecoff.h>
43#include <iprt/formats/mz.h>
44#include <iprt/formats/elf.h>
45#include <iprt/formats/mach-o.h>
46
47
48/*********************************************************************************************************************************
49* Structures and Typedefs *
50*********************************************************************************************************************************/
51/** Entry for mapping file offset to memory location. */
52typedef struct DBGFMODINMEMMAPPING
53{
54 /** The file offset. */
55 uint32_t offFile;
56 /** The file size of this mapping. */
57 uint32_t cbFile;
58 /** The size of this mapping. */
59 uint32_t cbMem;
60 /** The offset to the memory from the start of the image.
61 * @note This can be negative (for mach_kernel). */
62 int32_t offMem;
63} DBGFMODINMEMMAPPING;
64typedef DBGFMODINMEMMAPPING *PDBGFMODINMEMMAPPING;
65typedef DBGFMODINMEMMAPPING const *PCDBGFMODINMEMMAPPING;
66
67/**
68 * Common in-memory reader instance data.
69 */
70typedef struct DBGFMODINMEMRDR
71{
72 /** The VM handle (referenced). */
73 PUVM pUVM;
74 /** The image base. */
75 DBGFADDRESS ImageAddr;
76 /** The file size, based on the offFile and cbFile of the last mapping. */
77 uint32_t cbFile;
78 /** Number of entries in the aMappings table. */
79 uint32_t cMappings;
80 /** Mapping hint. */
81 uint32_t iHint;
82 /** Mapping file offset to memory offsets, ordered by file offset. */
83 DBGFMODINMEMMAPPING aMappings[RT_FLEXIBLE_ARRAY_NESTED];
84} DBGFMODINMEMRDR;
85/** Pointer to the common instance data for an in-memory file reader. */
86typedef DBGFMODINMEMRDR *PDBGFMODINMEMRDR;
87
88/**
89 * The WinNT digger's loader reader instance data.
90 */
91typedef struct DBGFMODPERDR
92{
93 /** The VM handle (referenced). */
94 PUVM pUVM;
95 /** The image base. */
96 DBGFADDRESS ImageAddr;
97 /** The image size. */
98 uint32_t cbImage;
99 /** The file offset of the SizeOfImage field in the optional header if it
100 * needs patching, otherwise set to UINT32_MAX. */
101 uint32_t offSizeOfImage;
102 /** The correct image size. */
103 uint32_t cbCorrectImageSize;
104 /** Number of entries in the aMappings table. */
105 uint32_t cMappings;
106 /** Mapping hint. */
107 uint32_t iHint;
108 /** Mapping file offset to memory offsets, ordered by file offset. */
109 struct
110 {
111 /** The file offset. */
112 uint32_t offFile;
113 /** The size of this mapping. */
114 uint32_t cbMem;
115 /** The offset to the memory from the start of the image. */
116 uint32_t offMem;
117 } aMappings[1];
118} DBGFMODPERDR;
119/** Pointer a WinNT loader reader instance data. */
120typedef DBGFMODPERDR *PDBGFMODPERDR;
121
122/**
123 * Stack buffer.
124 */
125typedef union DBGFMODINMEMBUF
126{
127 uint8_t ab[0x2000];
128 IMAGE_DOS_HEADER DosHdr;
129 IMAGE_NT_HEADERS32 Nt32;
130 IMAGE_NT_HEADERS64 Nt64;
131 mach_header_64 MachoHdr;
132 DBGFMODINMEMMAPPING aMappings[0x2000 / sizeof(DBGFMODINMEMMAPPING)];
133} DBGFMODINMEMBUF;
134/** Pointer to stack buffer. */
135typedef DBGFMODINMEMBUF *PDBGFMODINMEMBUF;
136
137
138
139/**
140 * Normalizes a debug module name.
141 *
142 * @returns Normalized debug module name.
143 * @param pszName The name.
144 * @param pszBuf Buffer to use if work is needed.
145 * @param cbBuf Size of buffer.
146 */
147static const char *dbgfR3ModNormalizeName(const char *pszName, char *pszBuf, size_t cbBuf)
148{
149 /*
150 * Skip to the filename in case someone gave us a full filename path.
151 */
152 pszName = RTPathFilenameEx(pszName, RTPATH_STR_F_STYLE_DOS);
153
154 /*
155 * Is it okay?
156 */
157 size_t cchName = strlen(pszName);
158 size_t off = 0;
159 for (;; off++)
160 {
161 char ch = pszName[off];
162 if (ch == '\0')
163 return pszName;
164 if (!RT_C_IS_ALNUM(ch) && ch != '_')
165 break;
166 }
167
168 /*
169 * It's no okay, so morph it.
170 */
171 if (cchName >= cbBuf)
172 cchName = cbBuf - 1;
173 for (off = 0; off < cchName; off++)
174 {
175 char ch = pszName[off];
176 if (!RT_C_IS_ALNUM(ch))
177 ch = '_';
178 pszBuf[off] = ch;
179 }
180 pszBuf[off] = '\0';
181
182 return pszBuf;
183}
184
185
186/**
187 * @callback_method_impl{PFNRTLDRRDRMEMREAD}
188 */
189static DECLCALLBACK(int) dbgfModInMemCommon_Read(void *pvBuf, size_t cb, size_t off, void *pvUser)
190{
191 PDBGFMODINMEMRDR pThis = (PDBGFMODINMEMRDR)pvUser;
192 uint32_t offFile = (uint32_t)off;
193 AssertReturn(offFile == off, VERR_INVALID_PARAMETER);
194
195 /*
196 * Set i to a mapping that starts at or before the specified offset.
197 * ASSUMING aMappings are sorted by offFile.
198 */
199 uint32_t i = pThis->iHint;
200 if (pThis->aMappings[i].offFile > offFile)
201 {
202 i = pThis->cMappings; /** @todo doesn't need to start from the end here... */
203 while (i-- > 0)
204 if (offFile >= pThis->aMappings[i].offFile)
205 break;
206 pThis->iHint = i;
207 }
208
209 while (cb > 0)
210 {
211 uint32_t offNextMap = i + 1 < pThis->cMappings ? pThis->aMappings[i + 1].offFile
212 : pThis->aMappings[i].offFile + RT_MAX(pThis->aMappings[i].cbFile, pThis->aMappings[i].cbMem);
213 uint32_t offMap = offFile - pThis->aMappings[i].offFile;
214
215 /* Read file bits backed by memory. */
216 if (offMap < pThis->aMappings[i].cbMem)
217 {
218 uint32_t cbToRead = pThis->aMappings[i].cbMem - offMap;
219 if (cbToRead > cb)
220 cbToRead = (uint32_t)cb;
221
222 DBGFADDRESS Addr = pThis->ImageAddr;
223 DBGFR3AddrAdd(&Addr, pThis->aMappings[i].offMem + offMap);
224
225 int rc = DBGFR3MemRead(pThis->pUVM, 0 /*idCpu*/, &Addr, pvBuf, cbToRead);
226 if (RT_FAILURE(rc))
227 return rc;
228
229 /* Done? */
230 if (cbToRead == cb)
231 break;
232
233 offFile += cbToRead;
234 cb -= cbToRead;
235 pvBuf = (char *)pvBuf + cbToRead;
236 }
237
238 /* Mind the gap. */
239 if (offNextMap > offFile)
240 {
241 uint32_t cbZero = offNextMap - offFile;
242 if (cbZero > cb)
243 {
244 RT_BZERO(pvBuf, cb);
245 break;
246 }
247
248 RT_BZERO(pvBuf, cbZero);
249 offFile += cbZero;
250 cb -= cbZero;
251 pvBuf = (char *)pvBuf + cbZero;
252 }
253
254 pThis->iHint = ++i;
255 }
256
257 return VINF_SUCCESS;
258}
259
260
261/**
262 * @callback_method_impl{PFNRTLDRRDRMEMDTOR}
263 */
264static DECLCALLBACK(void) dbgfModInMemCommon_Dtor(void *pvUser, size_t cbImage)
265{
266 PDBGFMODINMEMRDR pThis = (PDBGFMODINMEMRDR)pvUser;
267 RT_NOREF(cbImage);
268
269 VMR3ReleaseUVM(pThis->pUVM);
270 pThis->pUVM = NULL;
271
272 RTMemFree(pThis);
273}
274
275
276/**
277 * @callback_method_impl{FNRTSORTCMP}
278 */
279static DECLCALLBACK(int) dbgfModInMemCompMappings(void const *pvElement1, void const *pvElement2, void *pvUser)
280{
281 RT_NOREF(pvUser);
282 PCDBGFMODINMEMMAPPING pElement1 = (PCDBGFMODINMEMMAPPING)pvElement1;
283 PCDBGFMODINMEMMAPPING pElement2 = (PCDBGFMODINMEMMAPPING)pvElement2;
284 if (pElement1->offFile < pElement2->offFile)
285 return -1;
286 if (pElement1->offFile > pElement2->offFile)
287 return 1;
288 if (pElement1->cbFile < pElement2->cbFile)
289 return -1;
290 if (pElement1->cbFile > pElement2->cbFile)
291 return 1;
292 if (pElement1->offMem < pElement2->offMem)
293 return -1;
294 if (pElement1->offMem > pElement2->offMem)
295 return 1;
296 if (pElement1->cbMem < pElement2->cbMem)
297 return -1;
298 if (pElement1->cbMem > pElement2->cbMem)
299 return 1;
300 return 0;
301}
302
303
304static int dbgfModInMemCommon_Init(PDBGFMODINMEMRDR pThis, PUVM pUVM, PCDBGFADDRESS pImageAddr,PCDBGFMODINMEMMAPPING paMappings,
305 uint32_t cMappings, const char *pszName, RTLDRARCH enmArch,
306 PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
307{
308 /*
309 * Initialize the reader instance.
310 */
311 VMR3RetainUVM(pUVM);
312 pThis->pUVM = pUVM;
313 pThis->ImageAddr = *pImageAddr;
314 pThis->cMappings = cMappings;
315 pThis->iHint = 0;
316 memcpy(pThis->aMappings, paMappings, cMappings * sizeof(pThis->aMappings[0]));
317 RTSortShell(pThis->aMappings, cMappings, sizeof(pThis->aMappings[0]), dbgfModInMemCompMappings, NULL);
318 pThis->cbFile = pThis->aMappings[cMappings - 1].offFile + pThis->aMappings[cMappings - 1].cbFile;
319
320 /*
321 * Call the loader to open it.
322 * Note! destructore is always called.
323 */
324
325 RTLDRMOD hLdrMod;
326 int rc = RTLdrOpenInMemory(pszName, RTLDR_O_FOR_DEBUG, enmArch, pThis->cbFile,
327 dbgfModInMemCommon_Read, dbgfModInMemCommon_Dtor, pThis,
328 &hLdrMod, pErrInfo);
329 if (RT_SUCCESS(rc))
330 *phLdrMod = hLdrMod;
331 else
332 *phLdrMod = NIL_RTLDRMOD;
333 return rc;
334}
335
336
337/**
338 * Handles in-memory ELF images.
339 *
340 * @returns VBox status code.
341 * @param pUVM The user mode VM handle.
342 * @param pImageAddr The image address.
343 * @param fFlags Flags, DBGFMODINMEM_F_XXX.
344 * @param pszName The module name, optional.
345 * @param pszFilename The image filename, optional.
346 * @param enmArch The image arch if we force it, pass
347 * RTLDRARCH_WHATEVER if you don't care.
348 * @param cbImage Image size. Pass 0 if not known.
349 * @param puBuf The header buffer.
350 * @param phDbgMod Where to return the resulting debug module on success.
351 * @param pErrInfo Where to return extended error info on failure.
352 */
353static int dbgfR3ModInMemElf(PUVM pUVM, PCDBGFADDRESS pImageAddr, uint32_t fFlags, const char *pszName, const char *pszFilename,
354 RTLDRARCH enmArch, uint32_t cbImage, PDBGFMODINMEMBUF puBuf,
355 PRTDBGMOD phDbgMod, PRTERRINFO pErrInfo)
356{
357 RT_NOREF(pUVM, fFlags, pszName, pszFilename, enmArch, cbImage, puBuf, phDbgMod);
358 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_INVALID_EXE_SIGNATURE, "Found ELF magic at %RGv", pImageAddr->FlatPtr);
359}
360
361
362/**
363 * Handles in-memory Mach-O images.
364 *
365 * @returns VBox status code.
366 * @param pUVM The user mode VM handle.
367 * @param pImageAddr The image address.
368 * @param fFlags Flags, DBGFMODINMEM_F_XXX.
369 * @param pszName The module name, optional.
370 * @param pszFilename The image filename, optional.
371 * @param enmArch The image arch if we force it, pass
372 * RTLDRARCH_WHATEVER if you don't care.
373 * @param cbImage Image size. Pass 0 if not known.
374 * @param puBuf The header buffer.
375 * @param phDbgMod Where to return the resulting debug module on success.
376 * @param pErrInfo Where to return extended error info on failure.
377 */
378static int dbgfR3ModInMemMachO(PUVM pUVM, PCDBGFADDRESS pImageAddr, uint32_t fFlags, const char *pszName, const char *pszFilename,
379 RTLDRARCH enmArch, uint32_t cbImage, PDBGFMODINMEMBUF puBuf,
380 PRTDBGMOD phDbgMod, PRTERRINFO pErrInfo)
381{
382 RT_NOREF(cbImage, fFlags);
383
384 /*
385 * Match up enmArch.
386 */
387 if (enmArch == RTLDRARCH_AMD64)
388 {
389 if (puBuf->MachoHdr.magic != IMAGE_MACHO64_SIGNATURE)
390 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDR_ARCH_MISMATCH, "Wanted AMD64 but header is not 64-bit");
391 if (puBuf->MachoHdr.cputype != CPU_TYPE_X86_64)
392 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDR_ARCH_MISMATCH, "Wanted AMD64 but cpu type is %#x instead of %#x",
393 puBuf->MachoHdr.cputype, CPU_TYPE_X86_64);
394 }
395 else if (enmArch == RTLDRARCH_X86_32)
396 {
397 if (puBuf->MachoHdr.magic != IMAGE_MACHO32_SIGNATURE)
398 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDR_ARCH_MISMATCH, "Wanted X86_32 but header is not 32-bit");
399 if (puBuf->MachoHdr.cputype != CPU_TYPE_X86)
400 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDR_ARCH_MISMATCH, "Wanted X86_32 but cpu type is %#x instead of %#x",
401 puBuf->MachoHdr.cputype, CPU_TYPE_X86);
402 }
403 else if (enmArch != RTLDRARCH_WHATEVER)
404 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDR_ARCH_MISMATCH, "Unsupported enmArch value %s (%d)",
405 RTLdrArchName(enmArch), enmArch);
406
407 /*
408 * Guess the module name if not specified and make sure it conforms to DBGC expectations.
409 */
410 char szNormalized[128];
411 if (!pszName)
412 {
413 if (pszFilename)
414 pszName = RTPathFilenameEx(pszFilename, RTPATH_STR_F_STYLE_DOS /*whatever*/);
415 if (!pszName)
416 {
417 RTStrPrintf(szNormalized, sizeof(szNormalized), "image_%#llx", (uint64_t)pImageAddr->FlatPtr);
418 pszName = szNormalized;
419 }
420 }
421 if (pszName != szNormalized)
422 pszName = dbgfR3ModNormalizeName(pszName, szNormalized, sizeof(szNormalized));
423
424 /*
425 * Read the load commands into memory, they follow the header. Refuse
426 * if there appear to be too many or too much of these.
427 */
428 uint32_t const cLoadCmds = puBuf->MachoHdr.ncmds;
429 uint32_t const cbLoadCmds = puBuf->MachoHdr.sizeofcmds;
430 if (cLoadCmds > _8K || cLoadCmds < 2)
431 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRMACHO_BAD_HEADER,
432 "ncmds=%u is out of sensible range (2..8192)", cLoadCmds);
433 if (cbLoadCmds > _2M || cbLoadCmds < sizeof(load_command_t) * 2)
434 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRMACHO_BAD_HEADER,
435 "cbLoadCmds=%#x is out of sensible range (8..2MiB)", cbLoadCmds);
436
437 uint8_t *pbLoadCmds = (uint8_t *)RTMemTmpAllocZ(cbLoadCmds);
438 AssertReturn(pbLoadCmds, VERR_NO_TMP_MEMORY);
439
440 uint32_t const cbHdr = puBuf->MachoHdr.magic == IMAGE_MACHO64_SIGNATURE ? sizeof(mach_header_64) : sizeof(mach_header_32);
441 DBGFADDRESS Addr = *pImageAddr;
442 int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, DBGFR3AddrAdd(&Addr, cbHdr), pbLoadCmds, cbLoadCmds);
443 if (RT_SUCCESS(rc))
444 {
445 /*
446 * Scan it for segments so we can tranlate file offsets to virtual
447 * memory locations.
448 */
449 RTUUID Uuid = RTUUID_INITIALIZE_NULL;
450 uint32_t cMappings = 0;
451 uint32_t offCmd = 0;
452 for (uint32_t iCmd = 0; iCmd < cLoadCmds; iCmd++)
453 {
454 load_command_t const *pCurCmd = (load_command_t const *)&pbLoadCmds[offCmd];
455 uint32_t const cbCurCmd = offCmd + sizeof(*pCurCmd) <= cbLoadCmds ? pCurCmd->cmdsize : sizeof(*pCurCmd);
456 if (offCmd + cbCurCmd > cbLoadCmds)
457 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
458 "Load command #%u @ %#x is out of bounds: size %#x, left %#x", iCmd, offCmd, cbCurCmd,
459 cbLoadCmds - offCmd);
460 else if (pCurCmd->cmd == LC_SEGMENT_64)
461 {
462 segment_command_64 const *pSeg = (segment_command_64 const *)pCurCmd;
463 if (cbCurCmd >= sizeof(*pSeg))
464 {
465 if (cMappings >= RT_ELEMENTS(puBuf->aMappings))
466 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_OUT_OF_RANGE, "Too many segments!");
467 else
468 {
469 puBuf->aMappings[cMappings].offFile = pSeg->fileoff;
470 puBuf->aMappings[cMappings].cbFile = pSeg->filesize;
471 puBuf->aMappings[cMappings].offMem = pSeg->vmaddr - pImageAddr->FlatPtr;
472 puBuf->aMappings[cMappings].cbMem = pSeg->vmsize;
473 cMappings++;
474 }
475 }
476 else
477 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
478 "Load command #%u @ %#x is too small for a 64-bit segment: %#x", iCmd, offCmd, cbCurCmd);
479 }
480 else if (pCurCmd->cmd == LC_SEGMENT_32)
481 {
482 segment_command_32 const *pSeg = (segment_command_32 const *)pCurCmd;
483 if (cbCurCmd >= sizeof(*pSeg))
484 {
485 if (cMappings >= RT_ELEMENTS(puBuf->aMappings))
486 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_OUT_OF_RANGE, "Too many segments!");
487 else
488 {
489 puBuf->aMappings[cMappings].offFile = pSeg->fileoff;
490 puBuf->aMappings[cMappings].cbFile = pSeg->filesize;
491 puBuf->aMappings[cMappings].offMem = pSeg->vmaddr - pImageAddr->FlatPtr;
492 puBuf->aMappings[cMappings].cbMem = pSeg->vmsize;
493 cMappings++;
494 }
495 }
496 else
497 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
498 "Load command #%u @ %#x is too small for a 32-bit segment: %#x", iCmd, offCmd, cbCurCmd);
499 }
500 else if (pCurCmd->cmd == LC_UUID && cbCurCmd == sizeof(uuid_command_t))
501 memcpy(&Uuid, ((uuid_command_t const *)pCurCmd)->uuid, sizeof(Uuid));
502
503 if (RT_SUCCESS(rc))
504 offCmd += cbCurCmd;
505 else
506 break;
507 } /* for each command */
508
509 if (RT_SUCCESS(rc))
510 {
511 /*
512 * Create generic loader module instance (pThis is tied to it
513 * come rain come shine).
514 */
515 PDBGFMODINMEMRDR pThis = (PDBGFMODINMEMRDR)RTMemAllocZVar(RT_UOFFSETOF_DYN(DBGFMODINMEMRDR, aMappings[cMappings]));
516 if (pThis)
517 {
518 RTLDRMOD hLdrMod;
519 rc = dbgfModInMemCommon_Init(pThis, pUVM, pImageAddr, puBuf->aMappings, cMappings,
520 pszName, enmArch, &hLdrMod, pErrInfo);
521 if (RT_SUCCESS(rc)) /* Don't bother if we don't have a handle. */
522 {
523 RTDBGMOD hMod;
524 rc = RTDbgModCreateFromMachOImage(&hMod, pszFilename ? pszFilename : pszName, pszName, enmArch,
525 &hLdrMod, 0 /*cbImage*/, 0, NULL, &Uuid, DBGFR3AsGetConfig(pUVM), fFlags);
526 if (RT_SUCCESS(rc))
527 *phDbgMod = hMod;
528 }
529 else
530 hLdrMod = NIL_RTLDRMOD;
531
532#if 0 /** @todo later */
533 if (RT_FAILURE(rc) && !(fFlags & DBGFMODINMEM_F_NO_CONTAINER_FALLBACK))
534 {
535 /*
536 * Fallback is a container module.
537 */
538 rc = RTDbgModCreate(&hMod, pszName, cbImage, 0);
539 if (RT_SUCCESS(rc))
540 {
541 rc = RTDbgModSymbolAdd(hMod, "Headers", 0 /*iSeg*/, 0, cbImage, 0 /*fFlags*/, NULL);
542 AssertRC(rc);
543 }
544 }
545#endif
546 if (hLdrMod != NIL_RTLDRMOD)
547 RTLdrClose(hLdrMod);
548 }
549 else
550 rc = VERR_NO_MEMORY;
551 }
552 }
553 else
554 RTERRINFO_LOG_SET_F(pErrInfo, rc, "Failed to read %#x bytes of load commands", cbLoadCmds);
555 RTMemTmpFree(pbLoadCmds);
556 return rc;
557}
558
559
560/**
561 * @callback_method_impl{PFNRTLDRRDRMEMREAD}
562 */
563static DECLCALLBACK(int) dbgfModInMemPeRdr_Read(void *pvBuf, size_t cb, size_t off, void *pvUser)
564{
565 PDBGFMODPERDR pThis = (PDBGFMODPERDR)pvUser;
566 uint32_t offFile = (uint32_t)off;
567 AssertReturn(offFile == off, VERR_INVALID_PARAMETER);
568
569 uint32_t i = pThis->iHint;
570 if (pThis->aMappings[i].offFile > offFile)
571 {
572 i = pThis->cMappings;
573 while (i-- > 0)
574 if (offFile >= pThis->aMappings[i].offFile)
575 break;
576 pThis->iHint = i;
577 }
578
579 while (cb > 0)
580 {
581 uint32_t offNextMap = i + 1 < pThis->cMappings ? pThis->aMappings[i + 1].offFile : pThis->cbImage;
582 uint32_t offMap = offFile - pThis->aMappings[i].offFile;
583
584 /* Read file bits backed by memory. */
585 if (offMap < pThis->aMappings[i].cbMem)
586 {
587 uint32_t cbToRead = pThis->aMappings[i].cbMem - offMap;
588 if (cbToRead > cb)
589 cbToRead = (uint32_t)cb;
590
591 DBGFADDRESS Addr = pThis->ImageAddr;
592 DBGFR3AddrAdd(&Addr, pThis->aMappings[i].offMem + offMap);
593
594 int rc = DBGFR3MemRead(pThis->pUVM, 0 /*idCpu*/, &Addr, pvBuf, cbToRead);
595 if (RT_FAILURE(rc))
596 return rc;
597
598 /* Apply SizeOfImage patch? */
599 if ( pThis->offSizeOfImage != UINT32_MAX
600 && offFile < pThis->offSizeOfImage + 4
601 && offFile + cbToRead > pThis->offSizeOfImage)
602 {
603 uint32_t SizeOfImage = pThis->cbCorrectImageSize;
604 uint32_t cbPatch = sizeof(SizeOfImage);
605 int32_t offPatch = pThis->offSizeOfImage - offFile;
606 uint8_t *pbPatch = (uint8_t *)pvBuf + offPatch;
607 if (offFile + cbToRead < pThis->offSizeOfImage + cbPatch)
608 cbPatch = offFile + cbToRead - pThis->offSizeOfImage;
609 while (cbPatch-- > 0)
610 {
611 if (offPatch >= 0)
612 *pbPatch = (uint8_t)SizeOfImage;
613 offPatch++;
614 pbPatch++;
615 SizeOfImage >>= 8;
616 }
617 }
618
619 /* Done? */
620 if (cbToRead == cb)
621 break;
622
623 offFile += cbToRead;
624 cb -= cbToRead;
625 pvBuf = (char *)pvBuf + cbToRead;
626 }
627
628 /* Mind the gap. */
629 if (offNextMap > offFile)
630 {
631 uint32_t cbZero = offNextMap - offFile;
632 if (cbZero > cb)
633 {
634 RT_BZERO(pvBuf, cb);
635 break;
636 }
637
638 RT_BZERO(pvBuf, cbZero);
639 offFile += cbZero;
640 cb -= cbZero;
641 pvBuf = (char *)pvBuf + cbZero;
642 }
643
644 pThis->iHint = ++i;
645 }
646
647 return VINF_SUCCESS;
648}
649
650
651/**
652 * @callback_method_impl{PFNRTLDRRDRMEMDTOR}
653 */
654static DECLCALLBACK(void) dbgfModInMemPeRdr_Dtor(void *pvUser, size_t cbImage)
655{
656 PDBGFMODPERDR pThis = (PDBGFMODPERDR)pvUser;
657 RT_NOREF(cbImage);
658
659 VMR3ReleaseUVM(pThis->pUVM);
660 pThis->pUVM = NULL;
661 RTMemFree(pvUser);
662}
663
664
665/**
666 * Checks if the section headers look okay.
667 *
668 * @returns VBox status code.
669 * @param paShdrs Pointer to the section headers.
670 * @param cShdrs Number of headers.
671 * @param cbImage The image size reported by NT.
672 * @param cbImageFromHdr The image size by the linker in the header.
673 * @param uRvaRsrc The RVA of the resource directory. UINT32_MAX if
674 * no resource directory.
675 * @param cbSectAlign The section alignment specified in the header.
676 * @param fNt31 Set if NT 3.1. Needed for chopped off HAL.
677 * @param pcbImageCorrect The corrected image size. This is derived from
678 * cbImage and virtual range of the section tables.
679 *
680 * The problem is that NT may choose to drop the
681 * last pages in images it loads early, starting at
682 * the resource directory. These images will have
683 * a page aligned cbImage.
684 *
685 * @param pErrInfo Where to return more error details.
686 */
687static int dbgfR3ModPeCheckSectHdrsAndImgSize(PCIMAGE_SECTION_HEADER paShdrs, uint32_t cShdrs, uint32_t cbImage,
688 uint32_t cbImageFromHdr, uint32_t uRvaRsrc, uint32_t cbSectAlign,
689 bool fNt31, uint32_t *pcbImageCorrect, PRTERRINFO pErrInfo)
690{
691 *pcbImageCorrect = cbImage;
692
693 for (uint32_t i = 0; i < cShdrs; i++)
694 {
695 if (!paShdrs[i].Name[0])
696 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "Section header #%u has no name", i);
697
698 if (paShdrs[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD)
699 continue;
700
701 /* Tweak to determine the virtual size if the linker didn't set it (NT 3.1). */
702 /** @todo this isn't really perfect. cbImage is kind of wrong... */
703 uint32_t cbVirtual = paShdrs[i].Misc.VirtualSize;
704 if (cbVirtual == 0)
705 {
706 for (uint32_t j = i + 1; j < cShdrs; j++)
707 if ( !(paShdrs[j].Characteristics & IMAGE_SCN_TYPE_NOLOAD)
708 && paShdrs[j].VirtualAddress > paShdrs[i].VirtualAddress)
709 {
710 cbVirtual = paShdrs[j].VirtualAddress - paShdrs[i].VirtualAddress;
711 break;
712 }
713 if (!cbVirtual)
714 {
715 if (paShdrs[i].VirtualAddress < cbImageFromHdr)
716 cbVirtual = cbImageFromHdr - paShdrs[i].VirtualAddress;
717 else if (paShdrs[i].SizeOfRawData > 0)
718 cbVirtual = RT_ALIGN(paShdrs[i].SizeOfRawData, _4K);
719 }
720 }
721
722 /* Check that sizes are within the same range and that both sizes and
723 addresses are within reasonable limits. */
724 if ( RT_ALIGN(cbVirtual, _64K) < RT_ALIGN(paShdrs[i].SizeOfRawData, _64K)
725 || cbVirtual >= _1G
726 || paShdrs[i].SizeOfRawData >= _1G)
727 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
728 "Section header #%u (%.8s) has a VirtualSize=%#x (%#x) and SizeOfRawData=%#x, that's too much data!",
729 i, paShdrs[i].Name, cbVirtual, paShdrs[i].Misc.VirtualSize, paShdrs[i].SizeOfRawData);
730 uint32_t uRvaEnd = paShdrs[i].VirtualAddress + cbVirtual;
731 if (uRvaEnd >= _1G || uRvaEnd < paShdrs[i].VirtualAddress)
732 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
733 "Section header #%u (%.8s) has a VirtualSize=%#x (%#x) and VirtualAddr=%#x, %#x in total, that's too much!",
734 i, paShdrs[i].Name, cbVirtual, paShdrs[i].Misc.VirtualSize, paShdrs[i].VirtualAddress, uRvaEnd);
735
736 /* Check for images chopped off around '.rsrc'. */
737 if ( cbImage < uRvaEnd
738 && uRvaEnd >= uRvaRsrc)
739 cbImage = RT_ALIGN(uRvaEnd, cbSectAlign);
740
741 /* Check that the section is within the image. */
742 if (uRvaEnd > cbImage && fNt31)
743 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
744 "Section header #%u has a virtual address range beyond the image: %#x TO %#x cbImage=%#x",
745 i, paShdrs[i].VirtualAddress, uRvaEnd, cbImage);
746 }
747
748 Assert(*pcbImageCorrect == cbImage || !(*pcbImageCorrect & 0xfff));
749 *pcbImageCorrect = cbImage;
750 return VINF_SUCCESS;
751}
752
753
754/**
755 * Create a loader module for the in-guest-memory PE module.
756 */
757static int dbgfR3ModInMemPeCreateLdrMod(PUVM pUVM, uint32_t fFlags, const char *pszName, PCDBGFADDRESS pImageAddr,
758 uint32_t cbImage, uint32_t cbImageFromHdr, bool f32Bit,
759 uint32_t cShdrs, PCIMAGE_SECTION_HEADER paShdrs, uint32_t cbSectAlign,
760 uint32_t cDataDir, PCIMAGE_DATA_DIRECTORY paDataDir, uint32_t offHdrs,
761 PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
762{
763 /*
764 * Allocate and create a reader instance.
765 */
766 PDBGFMODPERDR pRdr = (PDBGFMODPERDR)RTMemAlloc(RT_UOFFSETOF_DYN(DBGFMODPERDR, aMappings[cShdrs + 2]));
767 if (!pRdr)
768 return VERR_NO_MEMORY;
769
770 VMR3RetainUVM(pUVM);
771 pRdr->pUVM = pUVM;
772 pRdr->ImageAddr = *pImageAddr;
773 pRdr->cbImage = cbImage;
774 pRdr->cbCorrectImageSize = cbImage;
775 pRdr->offSizeOfImage = UINT32_MAX;
776 pRdr->iHint = 0;
777
778 /*
779 * Use the section table to construct a more accurate view of the file/image.
780 */
781 uint32_t uRvaRsrc = UINT32_MAX;
782 if ( cDataDir > IMAGE_DIRECTORY_ENTRY_RESOURCE
783 && paDataDir[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size > 0)
784 uRvaRsrc = paDataDir[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
785
786 int rc = dbgfR3ModPeCheckSectHdrsAndImgSize(paShdrs, cShdrs, cbImage, cbImageFromHdr, uRvaRsrc, cbSectAlign,
787 RT_BOOL(fFlags & DBGFMODINMEM_F_PE_NT31), &pRdr->cbCorrectImageSize, pErrInfo);
788 if (RT_SUCCESS(rc))
789 {
790 pRdr->cMappings = 0;
791
792 for (uint32_t i = 0; i < cShdrs; i++)
793 if ( paShdrs[i].SizeOfRawData > 0
794 && paShdrs[i].PointerToRawData > 0)
795 {
796 uint32_t j = 1;
797 if (!pRdr->cMappings)
798 pRdr->cMappings++;
799 else
800 {
801 while (j < pRdr->cMappings && pRdr->aMappings[j].offFile < paShdrs[i].PointerToRawData)
802 j++;
803 if (j < pRdr->cMappings)
804 memmove(&pRdr->aMappings[j + 1], &pRdr->aMappings[j], (pRdr->cMappings - j) * sizeof(pRdr->aMappings));
805 }
806 pRdr->aMappings[j].offFile = paShdrs[i].PointerToRawData;
807 pRdr->aMappings[j].offMem = paShdrs[i].VirtualAddress;
808 pRdr->aMappings[j].cbMem = i + 1 < cShdrs
809 ? paShdrs[i + 1].VirtualAddress - paShdrs[i].VirtualAddress
810 : paShdrs[i].Misc.VirtualSize;
811 if (j == pRdr->cMappings)
812 pRdr->cbImage = paShdrs[i].PointerToRawData + paShdrs[i].SizeOfRawData;
813 pRdr->cMappings++;
814 }
815
816 /* Insert the mapping of the headers that isn't covered by the section table. */
817 pRdr->aMappings[0].offFile = 0;
818 pRdr->aMappings[0].offMem = 0;
819 pRdr->aMappings[0].cbMem = pRdr->cMappings ? pRdr->aMappings[1].offFile : pRdr->cbImage;
820
821 int j = pRdr->cMappings - 1;
822 while (j-- > 0)
823 {
824 uint32_t cbFile = pRdr->aMappings[j + 1].offFile - pRdr->aMappings[j].offFile;
825 if (pRdr->aMappings[j].cbMem > cbFile)
826 pRdr->aMappings[j].cbMem = cbFile;
827 }
828 }
829 else if (fFlags & DBGFMODINMEM_F_NO_READER_FALLBACK)
830 return rc;
831 else
832 {
833 /*
834 * Fallback, fake identity mapped file data.
835 */
836 pRdr->cMappings = 1;
837 pRdr->aMappings[0].offFile = 0;
838 pRdr->aMappings[0].offMem = 0;
839 pRdr->aMappings[0].cbMem = pRdr->cbImage;
840 }
841
842 /* Enable the SizeOfImage patching if necessary. */
843 if (pRdr->cbCorrectImageSize != cbImage)
844 {
845 Log(("dbgfR3ModInMemPeCreateLdrMod: The image is really %#x bytes long, not %#x as mapped by NT!\n",
846 pRdr->cbCorrectImageSize, cbImage));
847 pRdr->offSizeOfImage = f32Bit
848 ? offHdrs + RT_OFFSETOF(IMAGE_NT_HEADERS32, OptionalHeader.SizeOfImage)
849 : offHdrs + RT_OFFSETOF(IMAGE_NT_HEADERS64, OptionalHeader.SizeOfImage);
850 }
851
852 /*
853 * Call the loader to open the PE image for debugging.
854 * Note! It always calls pfnDtor.
855 */
856 RTLDRMOD hLdrMod;
857 rc = RTLdrOpenInMemory(pszName, RTLDR_O_FOR_DEBUG, RTLDRARCH_WHATEVER, pRdr->cbImage,
858 dbgfModInMemPeRdr_Read, dbgfModInMemPeRdr_Dtor, pRdr,
859 &hLdrMod, pErrInfo);
860 if (RT_SUCCESS(rc))
861 *phLdrMod = hLdrMod;
862 else
863 *phLdrMod = NIL_RTLDRMOD;
864 return rc;
865}
866
867
868/**
869 * Handles in-memory PE images.
870 *
871 * @returns VBox status code.
872 * @param pUVM The user mode VM handle.
873 * @param pImageAddr The image address.
874 * @param fFlags Flags, DBGFMODINMEM_F_XXX.
875 * @param pszName The module name, optional.
876 * @param pszFilename The image filename, optional.
877 * @param enmArch The image arch if we force it, pass
878 * RTLDRARCH_WHATEVER if you don't care.
879 * @param cbImage Image size. Pass 0 if not known.
880 * @param offPeHdrs Offset of the PE header.
881 * @param cbPeHdrsPart1 How read into uBuf at @a offPeHdrs.
882 * @param puBuf The header buffer.
883 * @param phDbgMod Where to return the resulting debug module on success.
884 * @param pErrInfo Where to return extended error info on failure.
885 */
886static int dbgfR3ModInMemPe(PUVM pUVM, PCDBGFADDRESS pImageAddr, uint32_t fFlags, const char *pszName, const char *pszFilename,
887 RTLDRARCH enmArch, uint32_t cbImage, uint32_t offPeHdrs, uint32_t cbPeHdrsPart1,
888 PDBGFMODINMEMBUF puBuf, PRTDBGMOD phDbgMod, PRTERRINFO pErrInfo)
889{
890 /*
891 * Read the optional header and the section table after validating the
892 * info we need from the file header.
893 */
894 /* Check the opt hdr size and number of sections as these are used to determine how much to read next. */
895 if ( puBuf->Nt32.FileHeader.SizeOfOptionalHeader < sizeof(IMAGE_OPTIONAL_HEADER32)
896 || puBuf->Nt32.FileHeader.SizeOfOptionalHeader > sizeof(IMAGE_OPTIONAL_HEADER64) + 128)
897 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "Invalid SizeOfOptionalHeader value: %#RX32",
898 puBuf->Nt32.FileHeader.SizeOfOptionalHeader);
899
900 if ( puBuf->Nt32.FileHeader.NumberOfSections < 1
901 || puBuf->Nt32.FileHeader.NumberOfSections > 190 /* what fits in our 8K buffer */)
902 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "NumberOfSections is out of range: %#RX32 (1..190)",
903 puBuf->Nt32.FileHeader.NumberOfSections);
904
905 /* Read the optional header and section table. */
906 uint32_t const cbHdrs = RT_UOFFSETOF(IMAGE_NT_HEADERS32, OptionalHeader)
907 + puBuf->Nt32.FileHeader.SizeOfOptionalHeader
908 + puBuf->Nt32.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
909 AssertReturn(cbHdrs <= sizeof(*puBuf), RTERRINFO_LOG_SET_F(pErrInfo, VERR_INTERNAL_ERROR_2, "cbHdrs=%#x", cbHdrs));
910
911 DBGFADDRESS PeHdrPart2Addr = *pImageAddr;
912 DBGFR3AddrAdd(&PeHdrPart2Addr, offPeHdrs + cbPeHdrsPart1);
913 int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &PeHdrPart2Addr, &puBuf->ab[cbPeHdrsPart1], cbHdrs - cbPeHdrsPart1);
914 if (RT_FAILURE(rc))
915 return RTERRINFO_LOG_SET_F(pErrInfo, rc,
916 "Failed to read the second part of the PE headers at %RGv (off=%#RX32 + %#RX32): %Rrc",
917 PeHdrPart2Addr.FlatPtr, offPeHdrs, cbPeHdrsPart1, rc);
918
919 /*
920 * Check the image architecture and determine the bitness.
921 */
922 RTLDRARCH enmArchActual;
923 bool f32Bit;
924 switch (puBuf->Nt32.FileHeader.Machine)
925 {
926 case IMAGE_FILE_MACHINE_I386:
927 enmArchActual = RTLDRARCH_X86_32;
928 f32Bit = true;
929 break;
930 case IMAGE_FILE_MACHINE_AMD64:
931 enmArchActual = RTLDRARCH_AMD64;
932 f32Bit = false;
933 break;
934 case IMAGE_FILE_MACHINE_ARM:
935 case IMAGE_FILE_MACHINE_THUMB:
936 case IMAGE_FILE_MACHINE_ARMNT:
937 enmArchActual = RTLDRARCH_ARM32;
938 f32Bit = true;
939 break;
940 case IMAGE_FILE_MACHINE_ARM64:
941 enmArchActual = RTLDRARCH_ARM64;
942 f32Bit = false;
943 break;
944 default:
945 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDR_ARCH_MISMATCH, "Unknown machine: %#x", puBuf->Nt32.FileHeader.Machine);
946 }
947 if ( enmArch != RTLDRARCH_WHATEVER
948 && enmArch != enmArchActual)
949 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDR_ARCH_MISMATCH, "Found %s expected %s",
950 RTLdrArchName(enmArchActual), RTLdrArchName(enmArch));
951
952 /*
953 * Check optional header magic and size.
954 */
955 uint16_t const uOptMagic = f32Bit ? IMAGE_NT_OPTIONAL_HDR32_MAGIC : IMAGE_NT_OPTIONAL_HDR64_MAGIC;
956 if (puBuf->Nt32.OptionalHeader.Magic != uOptMagic)
957 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "Unexpected optional header magic: %#x (expected %#x)",
958 puBuf->Nt32.OptionalHeader.Magic, uOptMagic);
959
960 uint32_t const cDataDir = f32Bit ? puBuf->Nt32.OptionalHeader.NumberOfRvaAndSizes : puBuf->Nt64.OptionalHeader.NumberOfRvaAndSizes;
961 if ( cDataDir <= IMAGE_DIRECTORY_ENTRY_BASERELOC /* a bit random */
962 || cDataDir > 32 /* also random */)
963 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "Unexpected data directory size: %#x", cDataDir);
964
965 uint32_t cbOptHdr = f32Bit ? sizeof(IMAGE_OPTIONAL_HEADER32) : sizeof(IMAGE_OPTIONAL_HEADER64);
966 cbOptHdr -= sizeof(IMAGE_DATA_DIRECTORY) * IMAGE_NUMBEROF_DIRECTORY_ENTRIES;
967 cbOptHdr += sizeof(IMAGE_DATA_DIRECTORY) * cDataDir;
968 if (puBuf->Nt32.FileHeader.SizeOfOptionalHeader != cbOptHdr)
969 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "Unexpected optional header size: %#x (expected %#x)",
970 puBuf->Nt32.FileHeader.SizeOfOptionalHeader, cbOptHdr);
971
972 uint32_t const cbSectAlign = f32Bit ? puBuf->Nt32.OptionalHeader.SectionAlignment : puBuf->Nt64.OptionalHeader.SectionAlignment;
973 PCIMAGE_SECTION_HEADER pSHdrs = (PCIMAGE_SECTION_HEADER)((uintptr_t)&puBuf->Nt32.OptionalHeader + cbOptHdr);
974 PCIMAGE_DATA_DIRECTORY paDataDir = (PCIMAGE_DATA_DIRECTORY)((uintptr_t)pSHdrs - cDataDir * sizeof(IMAGE_DATA_DIRECTORY));
975
976 /*
977 * Establish the image size.
978 */
979 uint32_t cbImageFromHdr = f32Bit ? puBuf->Nt32.OptionalHeader.SizeOfImage : puBuf->Nt64.OptionalHeader.SizeOfImage;
980 if ( !cbImage
981 || (fFlags & DBGFMODINMEM_F_PE_NT31))
982 cbImage = RT_ALIGN(cbImageFromHdr, _4K);
983 else if (RT_ALIGN(cbImageFromHdr, _4K) != RT_ALIGN(cbImage, _4K))
984 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_MISMATCH, "Image size mismatch: input=%#x header=%#x", cbImage, cbImageFromHdr);
985
986 /*
987 * Guess the module name if not specified and make sure it conforms to DBGC expectations.
988 */
989 if (!pszName)
990 {
991 if (pszFilename)
992 pszName = RTPathFilenameEx(pszFilename, RTPATH_STR_F_STYLE_DOS);
993 /** @todo */
994 }
995
996 char szNormalized[128];
997 pszName = dbgfR3ModNormalizeName(pszName, szNormalized, sizeof(szNormalized));
998
999 /*
1000 * Create the module using the in memory image first, falling back on cached image.
1001 */
1002 RTLDRMOD hLdrMod;
1003 rc = dbgfR3ModInMemPeCreateLdrMod(pUVM, fFlags, pszName, pImageAddr, cbImage, cbImageFromHdr, f32Bit,
1004 puBuf->Nt32.FileHeader.NumberOfSections, pSHdrs, cbSectAlign, cDataDir, paDataDir,
1005 offPeHdrs, &hLdrMod, pErrInfo);
1006 if (RT_FAILURE(rc))
1007 hLdrMod = NIL_RTLDRMOD;
1008
1009 RTDBGMOD hMod;
1010 rc = RTDbgModCreateFromPeImage(&hMod, pszFilename, pszName, &hLdrMod, cbImageFromHdr,
1011 puBuf->Nt32.FileHeader.TimeDateStamp, DBGFR3AsGetConfig(pUVM));
1012 if (RT_SUCCESS(rc))
1013 *phDbgMod = hMod;
1014 else if (!(fFlags & DBGFMODINMEM_F_NO_CONTAINER_FALLBACK))
1015 {
1016 /*
1017 * Fallback is a container module.
1018 */
1019 rc = RTDbgModCreate(&hMod, pszName, cbImage, 0);
1020 if (RT_SUCCESS(rc))
1021 {
1022 rc = RTDbgModSymbolAdd(hMod, "Headers", 0 /*iSeg*/, 0, cbImage, 0 /*fFlags*/, NULL);
1023 AssertRC(rc);
1024 }
1025 }
1026 return rc;
1027}
1028
1029
1030
1031/**
1032 * Process a PE image found in guest memory.
1033 *
1034 * @param pUVM The user mode VM handle.
1035 * @param pImageAddr The image address.
1036 * @param fFlags Flags, DBGFMODINMEM_F_XXX.
1037 * @param pszName The module name, optional.
1038 * @param pszFilename The image filename, optional.
1039 * @param enmArch The image arch if we force it, pass
1040 * RTLDRARCH_WHATEVER if you don't care.
1041 * @param cbImage Image size. Pass 0 if not known.
1042 * @param phDbgMod Where to return the resulting debug module on success.
1043 * @param pErrInfo Where to return extended error info on failure.
1044 */
1045VMMR3DECL(int) DBGFR3ModInMem(PUVM pUVM, PCDBGFADDRESS pImageAddr, uint32_t fFlags, const char *pszName, const char *pszFilename,
1046 RTLDRARCH enmArch, uint32_t cbImage, PRTDBGMOD phDbgMod, PRTERRINFO pErrInfo)
1047{
1048 /*
1049 * Validate and adjust.
1050 */
1051 AssertPtrReturn(phDbgMod, VERR_INVALID_POINTER);
1052 *phDbgMod = NIL_RTDBGMOD;
1053 AssertPtrReturn(pImageAddr, VERR_INVALID_POINTER);
1054 AssertMsgReturn(cbImage == 0 || cbImage >= sizeof(IMAGE_NT_HEADERS32) + sizeof(IMAGE_DOS_HEADER),
1055 ("cbImage=%#x\n", cbImage), VERR_INVALID_PARAMETER);
1056 AssertMsgReturn(!(fFlags & ~DBGFMODINMEM_F_VALID_MASK), ("%#x\n", fFlags), VERR_INVALID_FLAGS);
1057 if (enmArch == RTLDRARCH_HOST)
1058 enmArch = RTLdrGetHostArch();
1059
1060 /*
1061 * Look for an image header we can work with.
1062 */
1063 DBGFMODINMEMBUF uBuf;
1064 RT_ZERO(uBuf);
1065
1066 int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, pImageAddr, &uBuf, sizeof(uBuf.DosHdr));
1067 if (RT_FAILURE(rc))
1068 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Failed to read DOS header at %RGv: %Rrc", pImageAddr->FlatPtr, rc);
1069
1070 if (uBuf.ab[0] == ELFMAG0 && uBuf.ab[1] == ELFMAG1 && uBuf.ab[2] == ELFMAG2 && uBuf.ab[3] == ELFMAG3)
1071 return dbgfR3ModInMemElf(pUVM, pImageAddr, fFlags, pszName, pszFilename, enmArch, cbImage, &uBuf, phDbgMod, pErrInfo);
1072
1073 if ( uBuf.MachoHdr.magic == IMAGE_MACHO64_SIGNATURE
1074 || uBuf.MachoHdr.magic == IMAGE_MACHO32_SIGNATURE)
1075 return dbgfR3ModInMemMachO(pUVM, pImageAddr, fFlags, pszName, pszFilename, enmArch, cbImage, &uBuf, phDbgMod, pErrInfo);
1076
1077 uint32_t offNewHdrs;
1078 if (uBuf.DosHdr.e_magic == IMAGE_DOS_SIGNATURE)
1079 {
1080 offNewHdrs = uBuf.DosHdr.e_lfanew;
1081 if ( offNewHdrs < 16
1082 || offNewHdrs > (cbImage ? _2M : cbImage - sizeof(IMAGE_NT_HEADERS32)))
1083 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "e_lfanew value is out of range: %RX32 (16..%u)",
1084 offNewHdrs, (cbImage ? _2M : cbImage - sizeof(IMAGE_NT_HEADERS32)));
1085 }
1086 else if (uBuf.Nt32.Signature == IMAGE_NT_SIGNATURE)
1087 offNewHdrs = 0;
1088 else
1089 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_INVALID_EXE_SIGNATURE, "Unknown image magic at %RGv: %.8Rhxs",
1090 pImageAddr->FlatPtr, uBuf.ab);
1091
1092 /*
1093 * Read the next bit of header, assuming PE so stop at the end of
1094 * the COFF file header.
1095 */
1096 DBGFADDRESS PeHdrAddr = *pImageAddr;
1097 DBGFR3AddrAdd(&PeHdrAddr, offNewHdrs);
1098 uint32_t const cbPeHdrsPart1 = RT_UOFFSETOF(IMAGE_NT_HEADERS32, OptionalHeader);
1099 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &PeHdrAddr, &uBuf, cbPeHdrsPart1);
1100 if (RT_FAILURE(rc))
1101 return RTERRINFO_LOG_SET_F(pErrInfo, rc, "Failed to read PE/LX/NE headers at %RGv (off=%#RX32): %Rrc",
1102 PeHdrAddr.FlatPtr, offNewHdrs, rc);
1103
1104 if (uBuf.Nt32.Signature == IMAGE_NT_SIGNATURE)
1105 return dbgfR3ModInMemPe(pUVM, pImageAddr, fFlags, pszName, pszFilename, enmArch, cbImage, offNewHdrs, cbPeHdrsPart1,
1106 &uBuf, phDbgMod, pErrInfo);
1107
1108 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_INVALID_EXE_SIGNATURE, "No PE/LX/NE header at %RGv (off=%#RX32): %.8Rhxs",
1109 PeHdrAddr.FlatPtr, offNewHdrs, uBuf.ab);
1110}
1111
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