VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/ldr/ldrPE.cpp@ 46269

Last change on this file since 46269 was 46266, checked in by vboxsync, 12 years ago

IPRT: Changed RTLDRSEG::pchName to pszName and make sure it's always set to something. Started on implementing a codeview reader.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 90.8 KB
Line 
1/* $Id: ldrPE.cpp 46266 2013-05-25 19:51:19Z vboxsync $ */
2/** @file
3 * IPRT - Binary Image Loader, Portable Executable (PE).
4 */
5
6/*
7 * Copyright (C) 2006-2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#define LOG_GROUP RTLOGGROUP_LDR
32#include <iprt/ldr.h>
33#include "internal/iprt.h"
34
35#include <iprt/alloc.h>
36#include <iprt/assert.h>
37#include <iprt/log.h>
38#include <iprt/path.h>
39#include <iprt/string.h>
40#include <iprt/err.h>
41#include <iprt/formats/codeview.h>
42#include "internal/ldrPE.h"
43#include "internal/ldr.h"
44
45
46/*******************************************************************************
47* Defined Constants And Macros *
48*******************************************************************************/
49/** Converts rva to a type.
50 * @param pvBits Pointer to base of image bits.
51 * @param rva Relative virtual address.
52 * @param type Type.
53 */
54#define PE_RVA2TYPE(pvBits, rva, type) ((type) ((uintptr_t)pvBits + (uintptr_t)(rva)) )
55
56
57/*******************************************************************************
58* Structures and Typedefs *
59*******************************************************************************/
60/**
61 * The PE loader structure.
62 */
63typedef struct RTLDRMODPE
64{
65 /** Core module structure. */
66 RTLDRMODINTERNAL Core;
67 /** Pointer to internal copy of image bits.
68 * @todo the reader should take care of this. */
69 void *pvBits;
70 /** The offset of the NT headers. */
71 RTFOFF offNtHdrs;
72 /** The offset of the first byte after the section table. */
73 RTFOFF offEndOfHdrs;
74
75 /** The machine type (IMAGE_FILE_HEADER::Machine). */
76 uint16_t u16Machine;
77 /** The file flags (IMAGE_FILE_HEADER::Characteristics). */
78 uint16_t fFile;
79 /** Number of sections (IMAGE_FILE_HEADER::NumberOfSections). */
80 unsigned cSections;
81 /** Pointer to an array of the section headers related to the file. */
82 PIMAGE_SECTION_HEADER paSections;
83
84 /** The RVA of the entry point (IMAGE_OPTIONAL_HEADER32::AddressOfEntryPoint). */
85 RTUINTPTR uEntryPointRVA;
86 /** The base address of the image at link time (IMAGE_OPTIONAL_HEADER32::ImageBase). */
87 RTUINTPTR uImageBase;
88 /** The size of the loaded image (IMAGE_OPTIONAL_HEADER32::SizeOfImage). */
89 uint32_t cbImage;
90 /** Size of the header (IMAGE_OPTIONAL_HEADER32::SizeOfHeaders). */
91 uint32_t cbHeaders;
92 /** The image timestamp. */
93 uint32_t uTimestamp;
94 /** The import data directory entry. */
95 IMAGE_DATA_DIRECTORY ImportDir;
96 /** The base relocation data directory entry. */
97 IMAGE_DATA_DIRECTORY RelocDir;
98 /** The export data directory entry. */
99 IMAGE_DATA_DIRECTORY ExportDir;
100 /** The debug directory entry. */
101 IMAGE_DATA_DIRECTORY DebugDir;
102} RTLDRMODPE, *PRTLDRMODPE;
103
104/**
105 * PE Loader module operations.
106 *
107 * The PE loader has one operation which is a bit different between 32-bit and 64-bit PE images,
108 * and for historical and performance reasons have been split into separate functions. Thus the
109 * PE loader extends the RTLDROPS structure with this one entry.
110 */
111typedef struct RTLDROPSPE
112{
113 /** The usual ops. */
114 RTLDROPS Core;
115
116 /**
117 * Resolves all imports.
118 *
119 * @returns iprt status code.
120 * @param pModPe Pointer to the PE loader module structure.
121 * @param pvBitsR Where to read raw image bits. (optional)
122 * @param pvBitsW Where to store the imports. The size of this buffer is equal or
123 * larger to the value returned by pfnGetImageSize().
124 * @param pfnGetImport The callback function to use to resolve imports (aka unresolved externals).
125 * @param pvUser User argument to pass to the callback.
126 */
127 DECLCALLBACKMEMBER(int, pfnResolveImports)(PRTLDRMODPE pModPe, const void *pvBitsR, void *pvBitsW, PFNRTLDRIMPORT pfnGetImport, void *pvUser);
128
129 /** Dummy entry to make sure we've initialized it all. */
130 RTUINT uDummy;
131} RTLDROPSPE, *PRTLDROPSPE;
132
133
134/*******************************************************************************
135* Internal Functions *
136*******************************************************************************/
137static void rtldrPEConvert32BitOptionalHeaderTo64Bit(PIMAGE_OPTIONAL_HEADER64 pOptHdr);
138static void rtldrPEConvert32BitLoadConfigTo64Bit(PIMAGE_LOAD_CONFIG_DIRECTORY64 pLoadCfg);
139static int rtldrPEApplyFixups(PRTLDRMODPE pModPe, const void *pvBitsR, void *pvBitsW, RTUINTPTR BaseAddress, RTUINTPTR OldBaseAddress);
140
141
142
143/**
144 * Reads a section of a PE image given by RVA + size, using mapped bits if
145 * available or allocating heap memory and reading from the file.
146 *
147 * @returns IPRT status code.
148 * @param pThis Pointer to the PE loader module structure.
149 * @param pvBits Read only bits if available. NULL if not.
150 * @param uRva The RVA to read at.
151 * @param cbMem The number of bytes to read.
152 * @param ppvMem Where to return the memory on success (heap or
153 * inside pvBits).
154 */
155static int rtldrPEReadPartByRva(PRTLDRMODPE pThis, const void *pvBits, uint32_t uRva, uint32_t cbMem, void const **ppvMem)
156{
157 *ppvMem = NULL;
158 if (!cbMem)
159 return VINF_SUCCESS;
160
161 /*
162 * Use bits if we've got some.
163 */
164 if (pvBits)
165 {
166 *ppvMem = (uint8_t const *)pvBits + uRva;
167 return VINF_SUCCESS;
168 }
169 if (pThis->pvBits)
170 {
171 *ppvMem = (uint8_t const *)pThis->pvBits + uRva;
172 return VINF_SUCCESS;
173 }
174
175 /*
176 * Allocate a buffer and read the bits from the file (or whatever).
177 */
178 if (!pThis->Core.pReader)
179 return VERR_ACCESS_DENIED;
180
181 uint8_t *pbMem = (uint8_t *)RTMemAllocZ(cbMem);
182 if (!pbMem)
183 return VERR_NO_MEMORY;
184 *ppvMem = pbMem;
185
186 /* Do the reading on a per section base. */
187 RTFOFF const cbFile = pThis->Core.pReader->pfnSize(pThis->Core.pReader);
188 for (;;)
189 {
190 /* Translate the RVA into a file offset. */
191 uint32_t offFile = uRva;
192 uint32_t cbToRead = cbMem;
193 uint32_t cbToAdv = cbMem;
194
195 if (uRva < pThis->paSections[0].VirtualAddress)
196 {
197 /* Special header section. */
198 cbToRead = pThis->paSections[0].VirtualAddress - uRva;
199 if (cbToRead > cbMem)
200 cbToRead = cbMem;
201 cbToAdv = cbToRead;
202
203 /* The following capping is an approximation. */
204 uint32_t offFirstRawData = RT_ALIGN(pThis->cbHeaders, _4K);
205 if ( pThis->paSections[0].PointerToRawData > 0
206 && pThis->paSections[0].SizeOfRawData > 0)
207 offFirstRawData = pThis->paSections[0].PointerToRawData;
208 if (offFile > offFirstRawData)
209 cbToRead = 0;
210 else if (offFile + cbToRead > offFirstRawData)
211 cbToRead = offFile + cbToRead - offFirstRawData;
212 }
213 else
214 {
215 /* Find the matching section and its mapping size. */
216 uint32_t j = 0;
217 uint32_t cbMapping = 0;
218 while (j < pThis->cSections)
219 {
220 cbMapping = (j + 1 < pThis->cSections ? pThis->paSections[j + 1].VirtualAddress : pThis->cbImage)
221 - pThis->paSections[j].VirtualAddress;
222 if (uRva - pThis->paSections[j].VirtualAddress < cbMapping)
223 break;
224 j++;
225 }
226 if (j >= cbMapping)
227 break; /* This shouldn't happen, just return zeros if it does. */
228
229 /* Adjust the sizes and calc the file offset. */
230 if (cbToAdv > cbMapping)
231 cbToAdv = cbToRead = cbMapping;
232 if ( pThis->paSections[j].PointerToRawData > 0
233 && pThis->paSections[j].SizeOfRawData > 0)
234 {
235 offFile = uRva - pThis->paSections[j].VirtualAddress;
236 if (offFile + cbToRead > pThis->paSections[j].SizeOfRawData)
237 cbToRead = pThis->paSections[j].SizeOfRawData - offFile;
238 offFile += pThis->paSections[j].PointerToRawData;
239 }
240 else
241 {
242 offFile = UINT32_MAX;
243 cbToRead = 0;
244 }
245 }
246
247 /* Perform the read after adjusting a little (paranoia). */
248 if (offFile > cbFile)
249 cbToRead = 0;
250 if (cbToRead)
251 {
252 if ((RTFOFF)offFile + cbToRead > cbFile)
253 cbToRead = cbFile - (RTFOFF)offFile;
254 int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pbMem, cbToRead, offFile);
255 if (RT_FAILURE(rc))
256 {
257 RTMemFree((void *)*ppvMem);
258 *ppvMem = NULL;
259 return rc;
260 }
261 }
262
263 /* Advance */
264 if (cbMem == cbToRead)
265 break;
266 cbMem -= cbToRead;
267 pbMem += cbToRead;
268 uRva += cbToRead;
269 }
270
271 return VINF_SUCCESS;
272}
273
274
275/**
276 * Reads a part of a PE file from the file and into a heap block.
277 *
278 * @returns IRPT status code.
279 * @param pThis Pointer to the PE loader module structure..
280 * @param offFile The file offset.
281 * @param cbMem The number of bytes to read.
282 * @param ppvMem Where to return the heap block with the bytes on
283 * success.
284 */
285static int rtldrPEReadPartFromFile(PRTLDRMODPE pThis, uint32_t offFile, uint32_t cbMem, void const **ppvMem)
286{
287 *ppvMem = NULL;
288 if (!cbMem)
289 return VINF_SUCCESS;
290
291 /*
292 * Allocate a buffer and read the bits from the file (or whatever).
293 */
294 if (!pThis->Core.pReader)
295 return VERR_ACCESS_DENIED;
296
297 uint8_t *pbMem = (uint8_t *)RTMemAlloc(cbMem);
298 if (!pbMem)
299 return VERR_NO_MEMORY;
300
301 int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pbMem, cbMem, offFile);
302 if (RT_FAILURE(rc))
303 {
304 RTMemFree((void *)*ppvMem);
305 return rc;
306 }
307
308 *ppvMem = pbMem;
309 return VINF_SUCCESS;
310}
311
312
313/**
314 * Reads a part of a PE image into memory one way or another.
315 *
316 * Either the RVA or the offFile must be valid. We'll prefer the RVA if
317 * possible.
318 *
319 * @returns IPRT status code.
320 * @param pThis Pointer to the PE loader module structure.
321 * @param pvBits Read only bits if available. NULL if not.
322 * @param uRva The RVA to read at.
323 * @param offFile The file offset.
324 * @param cbMem The number of bytes to read.
325 * @param ppvMem Where to return the memory on success (heap or
326 * inside pvBits).
327 */
328static int rtldrPEReadPart(PRTLDRMODPE pThis, const void *pvBits, RTFOFF offFile, RTLDRADDR uRva,
329 uint32_t cbMem, void const **ppvMem)
330{
331 if (uRva == NIL_RTLDRADDR || uRva > pThis->cbImage)
332 {
333 if (offFile < 0)
334 return VERR_INVALID_PARAMETER;
335 return rtldrPEReadPartFromFile(pThis, offFile, cbMem, ppvMem);
336 }
337 return rtldrPEReadPartByRva(pThis, pvBits, uRva, cbMem, ppvMem);
338}
339
340
341/**
342 * Frees up memory returned by rtldrPEReadPart*.
343 *
344 * @param pThis Pointer to the PE loader module structure..
345 * @param pvBits Read only bits if available. NULL if not..
346 * @param pvMem The memory we were given by the reader method.
347 */
348static void rtldrPEFreePart(PRTLDRMODPE pThis, const void *pvBits, void const *pvMem)
349{
350 if (!pvMem)
351 return;
352
353 if (pvBits && (uintptr_t)pvBits - (uintptr_t)pvMem < pThis->cbImage)
354 return;
355 if (pThis->pvBits && (uintptr_t)pThis->pvBits - (uintptr_t)pvMem < pThis->cbImage)
356 return;
357
358 RTMemFree((void *)pvMem);
359}
360
361
362/** @copydoc RTLDROPS::pfnGetImageSize */
363static DECLCALLBACK(size_t) rtldrPEGetImageSize(PRTLDRMODINTERNAL pMod)
364{
365 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
366 return pModPe->cbImage;
367}
368
369
370/**
371 * Reads the image into memory.
372 *
373 * @returns iprt status code.
374 * @param pModPe The PE module.
375 * @param pvBits Where to store the bits, this buffer is at least pItem->Core.cbImage in size.
376 */
377static int rtldrPEGetBitsNoImportsNorFixups(PRTLDRMODPE pModPe, void *pvBits)
378{
379 /*
380 * Both these checks are related to pfnDone().
381 */
382 PRTLDRREADER pReader = pModPe->Core.pReader;
383 if (!pReader)
384 {
385 AssertMsgFailed(("You've called done!\n"));
386 return VERR_WRONG_ORDER;
387 }
388 if (!pvBits)
389 return VERR_NO_MEMORY;
390
391 /*
392 * Zero everything (could be done per section).
393 */
394 memset(pvBits, 0, pModPe->cbImage);
395
396#ifdef PE_FILE_OFFSET_EQUALS_RVA
397 /*
398 * Read the entire image / file.
399 */
400 const RTFOFF cbRawImage = pReader->pfnSize(pReader)
401 rc = pReader->pfnRead(pReader, pvBits, RT_MIN(pModPe->cbImage, cbRawImage), 0);
402 if (RT_FAILURE(rc))
403 Log(("rtldrPE: %s: Reading %#x bytes at offset %#x failed, %Rrc!!! (the entire image)\n",
404 pReader->pfnLogName(pReader), RT_MIN(pModPe->cbImage, cbRawImage), 0, rc));
405#else
406
407 /*
408 * Read the headers.
409 */
410 int rc = pReader->pfnRead(pReader, pvBits, pModPe->cbHeaders, 0);
411 if (RT_SUCCESS(rc))
412 {
413 /*
414 * Read the sections.
415 */
416 PIMAGE_SECTION_HEADER pSH = pModPe->paSections;
417 for (unsigned cLeft = pModPe->cSections; cLeft > 0; cLeft--, pSH++)
418 if (pSH->SizeOfRawData && pSH->Misc.VirtualSize)
419 {
420 rc = pReader->pfnRead(pReader, (uint8_t *)pvBits + pSH->VirtualAddress, pSH->SizeOfRawData, pSH->PointerToRawData);
421 if (RT_FAILURE(rc))
422 {
423 Log(("rtldrPE: %s: Reading %#x bytes at offset %#x failed, %Rrc - section #%d '%.*s'!!!\n",
424 pReader->pfnLogName(pReader), pSH->SizeOfRawData, pSH->PointerToRawData, rc,
425 pSH - pModPe->paSections, sizeof(pSH->Name), pSH->Name));
426 break;
427 }
428 }
429 }
430 else
431 Log(("rtldrPE: %s: Reading %#x bytes at offset %#x failed, %Rrc!!!\n",
432 pReader->pfnLogName(pReader), pModPe->cbHeaders, 0, rc));
433#endif
434 return rc;
435}
436
437
438/**
439 * Reads the bits into the internal buffer pointed to by PRTLDRMODPE::pvBits.
440 *
441 * @returns iprt status code.
442 * @param pModPe The PE module.
443 */
444static int rtldrPEReadBits(PRTLDRMODPE pModPe)
445{
446 Assert(!pModPe->pvBits);
447 void *pvBitsW = RTMemAllocZ(pModPe->cbImage);
448 if (!pvBitsW)
449 return VERR_NO_MEMORY;
450 int rc = rtldrPEGetBitsNoImportsNorFixups(pModPe, pvBitsW);
451 if (RT_SUCCESS(rc))
452 pModPe->pvBits = pvBitsW;
453 else
454 RTMemFree(pvBitsW);
455 return rc;
456}
457
458
459/** @copydoc RTLDROPS::pfnGetBits */
460static DECLCALLBACK(int) rtldrPEGetBits(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR BaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
461{
462 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
463
464 /*
465 * Read the image.
466 */
467 int rc = rtldrPEGetBitsNoImportsNorFixups(pModPe, pvBits);
468 if (RT_SUCCESS(rc))
469 {
470 /*
471 * Resolve imports.
472 */
473 rc = ((PRTLDROPSPE)pMod->pOps)->pfnResolveImports(pModPe, pvBits, pvBits, pfnGetImport, pvUser);
474 if (RT_SUCCESS(rc))
475 {
476 /*
477 * Apply relocations.
478 */
479 rc = rtldrPEApplyFixups(pModPe, pvBits, pvBits, BaseAddress, pModPe->uImageBase);
480 if (RT_SUCCESS(rc))
481 return rc;
482 AssertMsgFailed(("Failed to apply fixups. rc=%Rrc\n", rc));
483 }
484 else
485 AssertMsgFailed(("Failed to resolve imports. rc=%Rrc\n", rc));
486 }
487 return rc;
488}
489
490
491/** @copydoc RTLDROPSPE::pfnResolveImports */
492static DECLCALLBACK(int) rtldrPEResolveImports32(PRTLDRMODPE pModPe, const void *pvBitsR, void *pvBitsW, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
493{
494 /*
495 * Check if there is actually anything to work on.
496 */
497 if ( !pModPe->ImportDir.VirtualAddress
498 || !pModPe->ImportDir.Size)
499 return 0;
500
501 /*
502 * Walk the IMAGE_IMPORT_DESCRIPTOR table.
503 */
504 int rc = VINF_SUCCESS;
505 PIMAGE_IMPORT_DESCRIPTOR pImps;
506 for (pImps = PE_RVA2TYPE(pvBitsR, pModPe->ImportDir.VirtualAddress, PIMAGE_IMPORT_DESCRIPTOR);
507 !rc && pImps->Name != 0 && pImps->FirstThunk != 0;
508 pImps++)
509 {
510 const char *pszModName = PE_RVA2TYPE(pvBitsR, pImps->Name, const char *);
511 PIMAGE_THUNK_DATA32 pFirstThunk; /* update this. */
512 PIMAGE_THUNK_DATA32 pThunk; /* read from this. */
513 Log3(("RTLdrPE: Import descriptor: %s\n", pszModName));
514 Log4(("RTLdrPE: OriginalFirstThunk = %#RX32\n"
515 "RTLdrPE: TimeDateStamp = %#RX32\n"
516 "RTLdrPE: ForwarderChain = %#RX32\n"
517 "RTLdrPE: Name = %#RX32\n"
518 "RTLdrPE: FirstThunk = %#RX32\n",
519 pImps->u.OriginalFirstThunk, pImps->TimeDateStamp,
520 pImps->ForwarderChain, pImps->Name, pImps->FirstThunk));
521
522 /*
523 * Walk the thunks table(s).
524 */
525 pFirstThunk = PE_RVA2TYPE(pvBitsW, pImps->FirstThunk, PIMAGE_THUNK_DATA32);
526 pThunk = pImps->u.OriginalFirstThunk == 0
527 ? PE_RVA2TYPE(pvBitsR, pImps->FirstThunk, PIMAGE_THUNK_DATA32)
528 : PE_RVA2TYPE(pvBitsR, pImps->u.OriginalFirstThunk, PIMAGE_THUNK_DATA32);
529 while (!rc && pThunk->u1.Ordinal != 0)
530 {
531 RTUINTPTR Value = 0;
532 if (pThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG32)
533 {
534 rc = pfnGetImport(&pModPe->Core, pszModName, NULL, IMAGE_ORDINAL32(pThunk->u1.Ordinal), &Value, pvUser);
535 Log4((RT_SUCCESS(rc) ? "RTLdrPE: %RTptr #%u\n" : "RTLdrPE: %08RX32 #%u rc=%Rrc\n",
536 (uint32_t)Value, IMAGE_ORDINAL32(pThunk->u1.Ordinal), rc));
537 }
538 else if ( pThunk->u1.Ordinal > 0
539 && pThunk->u1.Ordinal < pModPe->cbImage)
540 {
541 rc = pfnGetImport(&pModPe->Core, pszModName, PE_RVA2TYPE(pvBitsR, (char*)(uintptr_t)pThunk->u1.AddressOfData + 2, const char *),
542 ~0, &Value, pvUser);
543 Log4((RT_SUCCESS(rc) ? "RTLdrPE: %RTptr %s\n" : "RTLdrPE: %08RX32 %s rc=%Rrc\n",
544 (uint32_t)Value, PE_RVA2TYPE(pvBitsR, (char*)(uintptr_t)pThunk->u1.AddressOfData + 2, const char *), rc));
545 }
546 else
547 {
548 AssertMsgFailed(("bad import data thunk!\n"));
549 rc = VERR_BAD_EXE_FORMAT;
550 }
551 pFirstThunk->u1.Function = Value;
552 if (pFirstThunk->u1.Function != Value)
553 {
554 AssertMsgFailed(("external symbol address to big!\n"));
555 rc = VERR_ADDRESS_CONFLICT; /** @todo get me a better error status code. */
556 }
557 pThunk++;
558 pFirstThunk++;
559 }
560 }
561
562 return rc;
563}
564
565
566/** @copydoc RTLDROPSPE::pfnResolveImports */
567static DECLCALLBACK(int) rtldrPEResolveImports64(PRTLDRMODPE pModPe, const void *pvBitsR, void *pvBitsW, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
568{
569 /*
570 * Check if there is actually anything to work on.
571 */
572 if ( !pModPe->ImportDir.VirtualAddress
573 || !pModPe->ImportDir.Size)
574 return 0;
575
576 /*
577 * Walk the IMAGE_IMPORT_DESCRIPTOR table.
578 */
579 int rc = VINF_SUCCESS;
580 PIMAGE_IMPORT_DESCRIPTOR pImps;
581 for (pImps = PE_RVA2TYPE(pvBitsR, pModPe->ImportDir.VirtualAddress, PIMAGE_IMPORT_DESCRIPTOR);
582 !rc && pImps->Name != 0 && pImps->FirstThunk != 0;
583 pImps++)
584 {
585 const char * pszModName = PE_RVA2TYPE(pvBitsR, pImps->Name, const char *);
586 PIMAGE_THUNK_DATA64 pFirstThunk; /* update this. */
587 PIMAGE_THUNK_DATA64 pThunk; /* read from this. */
588 Log3(("RTLdrPE: Import descriptor: %s\n", pszModName));
589 Log4(("RTLdrPE: OriginalFirstThunk = %#RX32\n"
590 "RTLdrPE: TimeDateStamp = %#RX32\n"
591 "RTLdrPE: ForwarderChain = %#RX32\n"
592 "RTLdrPE: Name = %#RX32\n"
593 "RTLdrPE: FirstThunk = %#RX32\n",
594 pImps->u.OriginalFirstThunk, pImps->TimeDateStamp,
595 pImps->ForwarderChain, pImps->Name, pImps->FirstThunk));
596
597 /*
598 * Walk the thunks table(s).
599 */
600 pFirstThunk = PE_RVA2TYPE(pvBitsW, pImps->FirstThunk, PIMAGE_THUNK_DATA64);
601 pThunk = pImps->u.OriginalFirstThunk == 0
602 ? PE_RVA2TYPE(pvBitsR, pImps->FirstThunk, PIMAGE_THUNK_DATA64)
603 : PE_RVA2TYPE(pvBitsR, pImps->u.OriginalFirstThunk, PIMAGE_THUNK_DATA64);
604 while (!rc && pThunk->u1.Ordinal != 0)
605 {
606 RTUINTPTR Value = 0;
607 if (pThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG64)
608 {
609 rc = pfnGetImport(&pModPe->Core, pszModName, NULL, (unsigned)IMAGE_ORDINAL64(pThunk->u1.Ordinal), &Value, pvUser);
610 Log4((RT_SUCCESS(rc) ? "RTLdrPE: %016RX64 #%u\n" : "RTLdrPE: %016RX64 #%u rc=%Rrc\n",
611 (uint64_t)Value, (unsigned)IMAGE_ORDINAL64(pThunk->u1.Ordinal), rc));
612 }
613 else if ( pThunk->u1.Ordinal > 0
614 && pThunk->u1.Ordinal < pModPe->cbImage)
615 {
616 /** @todo add validation of the string pointer! */
617 rc = pfnGetImport(&pModPe->Core, pszModName, PE_RVA2TYPE(pvBitsR, (uintptr_t)pThunk->u1.AddressOfData + 2, const char *),
618 ~0, &Value, pvUser);
619 Log4((RT_SUCCESS(rc) ? "RTLdrPE: %016RX64 %s\n" : "RTLdrPE: %016RX64 %s rc=%Rrc\n",
620 (uint64_t)Value, PE_RVA2TYPE(pvBitsR, (uintptr_t)pThunk->u1.AddressOfData + 2, const char *), rc));
621 }
622 else
623 {
624 AssertMsgFailed(("bad import data thunk!\n"));
625 rc = VERR_BAD_EXE_FORMAT;
626 }
627 pFirstThunk->u1.Function = Value;
628 pThunk++;
629 pFirstThunk++;
630 }
631 }
632
633 return rc;
634}
635
636
637/**
638 * Applies fixups.
639 */
640static int rtldrPEApplyFixups(PRTLDRMODPE pModPe, const void *pvBitsR, void *pvBitsW, RTUINTPTR BaseAddress, RTUINTPTR OldBaseAddress)
641{
642 if ( !pModPe->RelocDir.VirtualAddress
643 || !pModPe->RelocDir.Size)
644 return 0;
645
646 /*
647 * Apply delta fixups iterating fixup chunks.
648 */
649 PIMAGE_BASE_RELOCATION pbr = PE_RVA2TYPE(pvBitsR, pModPe->RelocDir.VirtualAddress, PIMAGE_BASE_RELOCATION);
650 PIMAGE_BASE_RELOCATION pBaseRelocs = pbr;
651 unsigned cbBaseRelocs = pModPe->RelocDir.Size;
652 RTUINTPTR uDelta = BaseAddress - OldBaseAddress;
653 Log2(("RTLdrPE: Fixups: uDelta=%#RTptr BaseAddress=%#RTptr OldBaseAddress=%#RTptr\n", uDelta, BaseAddress, OldBaseAddress));
654 Log4(("RTLdrPE: BASERELOC: VirtualAddres=%RX32 Size=%RX32\n", pModPe->RelocDir.VirtualAddress, pModPe->RelocDir.Size));
655 Assert(sizeof(*pbr) == sizeof(uint32_t) * 2);
656
657 while ( (uintptr_t)pbr - (uintptr_t)pBaseRelocs + 8 < cbBaseRelocs /* 8= VirtualAddress and SizeOfBlock members */
658 && pbr->SizeOfBlock >= 8)
659 {
660 uint16_t *pwoffFixup = (uint16_t *)(pbr + 1);
661 uint32_t cRelocations = (pbr->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(uint16_t);
662 Log3(("RTLdrPE: base relocs for %#010x, size %#06x (%d relocs)\n", pbr->VirtualAddress, pbr->SizeOfBlock, cRelocations));
663
664 /* Some bound checking just to be sure it works... */
665 if ((uintptr_t)pbr - (uintptr_t)pBaseRelocs + pbr->SizeOfBlock > cbBaseRelocs)
666 cRelocations = (((uintptr_t)pBaseRelocs + cbBaseRelocs) - (uintptr_t)pbr - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(uint16_t);
667
668 /*
669 * Loop thru the fixups in this chunk.
670 */
671 while (cRelocations != 0)
672 {
673 /*
674 * Common fixup
675 */
676 static const char * const s_apszReloc[16] =
677 {
678 "ABS", "HIGH", "LOW", "HIGHLOW", "HIGHADJ", "MIPS_JMPADDR", "RES6", "RES7",
679 "RES8", "IA64_IMM64", "DIR64", "HIGH3ADJ", "RES12", "RES13", "RES14", "RES15"
680 }; NOREF(s_apszReloc);
681 union
682 {
683 uint16_t *pu16;
684 uint32_t *pu32;
685 uint64_t *pu64;
686 } u;
687 const int offFixup = *pwoffFixup & 0xfff;
688 u.pu32 = PE_RVA2TYPE(pvBitsW, offFixup + pbr->VirtualAddress, uint32_t *);
689 const int fType = *pwoffFixup >> 12;
690 Log4(("RTLdrPE: %08x %s\n", offFixup + pbr->VirtualAddress, s_apszReloc[fType]));
691 switch (fType)
692 {
693 case IMAGE_REL_BASED_HIGHLOW: /* 32-bit, add delta. */
694 *u.pu32 += uDelta;
695 break;
696 case IMAGE_REL_BASED_DIR64: /* 64-bit, add delta. */
697 *u.pu64 += (RTINTPTR)uDelta;
698 break;
699 case IMAGE_REL_BASED_ABSOLUTE: /* Alignment placeholder. */
700 break;
701 /* odd ones */
702 case IMAGE_REL_BASED_LOW: /* 16-bit, add 1st 16-bit part of the delta. */
703 *u.pu16 += (uint16_t)uDelta;
704 break;
705 case IMAGE_REL_BASED_HIGH: /* 16-bit, add 2nd 16-bit part of the delta. */
706 *u.pu16 += (uint16_t)(uDelta >> 16);
707 break;
708 /* never ever seen these next two, and I'm not 100% sure they are correctly implemented here. */
709 case IMAGE_REL_BASED_HIGHADJ:
710 {
711 if (cRelocations <= 1)
712 {
713 AssertMsgFailed(("HIGHADJ missing 2nd record!\n"));
714 return VERR_BAD_EXE_FORMAT;
715 }
716 cRelocations--;
717 pwoffFixup++;
718 int32_t i32 = (uint32_t)(*u.pu16 << 16) | *pwoffFixup;
719 i32 += uDelta;
720 i32 += 0x8000; //??
721 *u.pu16 = (uint16_t)(i32 >> 16);
722 break;
723 }
724 case IMAGE_REL_BASED_HIGH3ADJ:
725 {
726 if (cRelocations <= 2)
727 {
728 AssertMsgFailed(("HIGHADJ3 missing 2nd record!\n"));
729 return VERR_BAD_EXE_FORMAT;
730 }
731 cRelocations -= 2;
732 pwoffFixup++;
733 int64_t i64 = ((uint64_t)*u.pu16 << 32) | *(uint32_t *)pwoffFixup++;
734 i64 += (int64_t)uDelta << 16; //??
735 i64 += 0x80000000;//??
736 *u.pu16 = (uint16_t)(i64 >> 32);
737 break;
738 }
739 default:
740 AssertMsgFailed(("Unknown fixup type %d offset=%#x\n", fType, offFixup));
741 break;
742 }
743
744 /*
745 * Next offset/type
746 */
747 pwoffFixup++;
748 cRelocations--;
749 } /* while loop */
750
751 /*
752 * Next Fixup chunk. (i.e. next page)
753 */
754 pbr = (PIMAGE_BASE_RELOCATION)((uintptr_t)pbr + pbr->SizeOfBlock);
755 } /* while loop */
756
757 return 0;
758}
759
760
761/** @copydoc RTLDROPS::pfnRelocate. */
762static int rtldrPERelocate(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR NewBaseAddress, RTUINTPTR OldBaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
763{
764 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
765
766 /*
767 * Do we have to read the image bits?
768 */
769 if (!pModPe->pvBits)
770 {
771 int rc = rtldrPEReadBits(pModPe);
772 if (RT_FAILURE(rc))
773 return rc;
774 }
775
776 /*
777 * Process imports.
778 */
779 int rc = ((PRTLDROPSPE)pMod->pOps)->pfnResolveImports(pModPe, pModPe->pvBits, pvBits, pfnGetImport, pvUser);
780 if (RT_SUCCESS(rc))
781 {
782 /*
783 * Apply relocations.
784 */
785 rc = rtldrPEApplyFixups(pModPe, pModPe->pvBits, pvBits, NewBaseAddress, OldBaseAddress);
786 AssertRC(rc);
787 }
788 return rc;
789}
790
791
792/** @copydoc RTLDROPS::pfnGetSymbolEx. */
793static DECLCALLBACK(int) rtldrPEGetSymbolEx(PRTLDRMODINTERNAL pMod, const void *pvBits, RTUINTPTR BaseAddress, const char *pszSymbol, RTUINTPTR *pValue)
794{
795 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
796
797 /*
798 * Check if there is actually anything to work on.
799 */
800 if ( !pModPe->ExportDir.VirtualAddress
801 || !pModPe->ExportDir.Size)
802 return VERR_SYMBOL_NOT_FOUND;
803
804 /*
805 * No bits supplied? Do we need to read the bits?
806 */
807 if (!pvBits)
808 {
809 if (!pModPe->pvBits)
810 {
811 int rc = rtldrPEReadBits(pModPe);
812 if (RT_FAILURE(rc))
813 return rc;
814 }
815 pvBits = pModPe->pvBits;
816 }
817
818 PIMAGE_EXPORT_DIRECTORY pExpDir = PE_RVA2TYPE(pvBits, pModPe->ExportDir.VirtualAddress, PIMAGE_EXPORT_DIRECTORY);
819 int iExpOrdinal = 0; /* index into address table. */
820 if ((uintptr_t)pszSymbol <= 0xffff)
821 {
822 /*
823 * Find ordinal export: Simple table lookup.
824 */
825 unsigned uOrdinal = (uintptr_t)pszSymbol & 0xffff;
826 if ( uOrdinal >= pExpDir->Base + RT_MAX(pExpDir->NumberOfNames, pExpDir->NumberOfFunctions)
827 || uOrdinal < pExpDir->Base)
828 return VERR_SYMBOL_NOT_FOUND;
829 iExpOrdinal = uOrdinal - pExpDir->Base;
830 }
831 else
832 {
833 /*
834 * Find Named Export: Do binary search on the name table.
835 */
836 uint32_t *paRVANames = PE_RVA2TYPE(pvBits, pExpDir->AddressOfNames, uint32_t *);
837 uint16_t *paOrdinals = PE_RVA2TYPE(pvBits, pExpDir->AddressOfNameOrdinals, uint16_t *);
838 int iStart = 1;
839 int iEnd = pExpDir->NumberOfNames;
840
841 for (;;)
842 {
843 /* end of search? */
844 if (iStart > iEnd)
845 {
846 #ifdef RT_STRICT
847 /* do a linear search just to verify the correctness of the above algorithm */
848 for (unsigned i = 0; i < pExpDir->NumberOfNames; i++)
849 {
850 AssertMsg(i == 0 || strcmp(PE_RVA2TYPE(pvBits, paRVANames[i], const char *), PE_RVA2TYPE(pvBits, paRVANames[i - 1], const char *)) > 0,
851 ("bug in binary export search!!!\n"));
852 AssertMsg(strcmp(PE_RVA2TYPE(pvBits, paRVANames[i], const char *), pszSymbol) != 0,
853 ("bug in binary export search!!!\n"));
854 }
855 #endif
856 return VERR_SYMBOL_NOT_FOUND;
857 }
858
859 int i = (iEnd - iStart) / 2 + iStart;
860 const char *pszExpName = PE_RVA2TYPE(pvBits, paRVANames[i - 1], const char *);
861 int diff = strcmp(pszExpName, pszSymbol);
862 if (diff > 0) /* pszExpName > pszSymbol: search chunck before i */
863 iEnd = i - 1;
864 else if (diff) /* pszExpName < pszSymbol: search chunk after i */
865 iStart = i + 1;
866 else /* pszExpName == pszSymbol */
867 {
868 iExpOrdinal = paOrdinals[i - 1];
869 break;
870 }
871 } /* binary search thru name table */
872 }
873
874 /*
875 * Found export (iExpOrdinal).
876 */
877 uint32_t * paAddress = PE_RVA2TYPE(pvBits, pExpDir->AddressOfFunctions, uint32_t *);
878 unsigned uRVAExport = paAddress[iExpOrdinal];
879
880 if ( uRVAExport > pModPe->ExportDir.VirtualAddress
881 && uRVAExport < pModPe->ExportDir.VirtualAddress + pModPe->ExportDir.Size)
882 {
883 /* Resolve forwarder. */
884 AssertMsgFailed(("Forwarders are not supported!\n"));
885 return VERR_SYMBOL_NOT_FOUND;
886 }
887
888 /* Get plain export address */
889 *pValue = PE_RVA2TYPE(BaseAddress, uRVAExport, RTUINTPTR);
890
891 return VINF_SUCCESS;
892}
893
894
895/**
896 * Slow version of rtldrPEEnumSymbols that'll work without all of the image
897 * being accessible.
898 *
899 * This is mainly for use in debuggers and similar.
900 */
901static int rtldrPEEnumSymbolsSlow(PRTLDRMODPE pThis, unsigned fFlags, RTUINTPTR BaseAddress,
902 PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
903{
904 /*
905 * We enumerates by ordinal, which means using a slow linear search for
906 * getting any name
907 */
908 PCIMAGE_EXPORT_DIRECTORY pExpDir = NULL;
909 int rc = rtldrPEReadPartByRva(pThis, NULL, pThis->ExportDir.VirtualAddress, pThis->ExportDir.Size,
910 (void const **)&pExpDir);
911 if (RT_FAILURE(rc))
912 return rc;
913 uint32_t const cOrdinals = RT_MAX(pExpDir->NumberOfNames, pExpDir->NumberOfFunctions);
914
915 uint32_t const *paAddress = NULL;
916 rc = rtldrPEReadPartByRva(pThis, NULL, pExpDir->AddressOfFunctions, cOrdinals * sizeof(uint32_t),
917 (void const **)&paAddress);
918 uint32_t const *paRVANames = NULL;
919 if (RT_SUCCESS(rc) && pExpDir->NumberOfNames)
920 rc = rtldrPEReadPartByRva(pThis, NULL, pExpDir->AddressOfNames, pExpDir->NumberOfNames * sizeof(uint32_t),
921 (void const **)&paRVANames);
922 uint16_t const *paOrdinals = NULL;
923 if (RT_SUCCESS(rc) && pExpDir->NumberOfNames)
924 rc = rtldrPEReadPartByRva(pThis, NULL, pExpDir->AddressOfNameOrdinals, pExpDir->NumberOfNames * sizeof(uint16_t),
925 (void const **)&paOrdinals);
926 if (RT_SUCCESS(rc))
927 {
928 uintptr_t uNamePrev = 0;
929 for (uint32_t uOrdinal = 0; uOrdinal < cOrdinals; uOrdinal++)
930 {
931 if (paAddress[uOrdinal] /* needed? */)
932 {
933 /*
934 * Look for name.
935 */
936 uint32_t uRvaName = UINT32_MAX;
937 /* Search from previous + 1 to the end. */
938 unsigned uName = uNamePrev + 1;
939 while (uName < pExpDir->NumberOfNames)
940 {
941 if (paOrdinals[uName] == uOrdinal)
942 {
943 uRvaName = paRVANames[uName];
944 uNamePrev = uName;
945 break;
946 }
947 uName++;
948 }
949 if (uRvaName == UINT32_MAX)
950 {
951 /* Search from start to the previous. */
952 uName = 0;
953 for (uName = 0 ; uName <= uNamePrev; uName++)
954 {
955 if (paOrdinals[uName] == uOrdinal)
956 {
957 uRvaName = paRVANames[uName];
958 uNamePrev = uName;
959 break;
960 }
961 }
962 }
963
964 /*
965 * Get address.
966 */
967 uintptr_t uRVAExport = paAddress[uOrdinal];
968 RTUINTPTR Value;
969 if ( uRVAExport - (uintptr_t)pThis->ExportDir.VirtualAddress
970 < pThis->ExportDir.Size)
971 {
972 if (!(fFlags & RTLDR_ENUM_SYMBOL_FLAGS_NO_FWD))
973 {
974 /* Resolve forwarder. */
975 AssertMsgFailed(("Forwarders are not supported!\n"));
976 }
977 continue;
978 }
979
980 /* Get plain export address */
981 Value = PE_RVA2TYPE(BaseAddress, uRVAExport, RTUINTPTR);
982
983 /* Read in the name if found one. */
984 char szAltName[32];
985 const char *pszName = NULL;
986 if (uRvaName != UINT32_MAX)
987 {
988 uint32_t cbName = 0x1000 - (uRvaName & 0xfff);
989 if (cbName < 10 || cbName > 512)
990 cbName = 128;
991 rc = rtldrPEReadPartByRva(pThis, NULL, uRvaName, cbName, (void const **)&pszName);
992 while (RT_SUCCESS(rc) && RTStrNLen(pszName, cbName) == cbName)
993 {
994 rtldrPEFreePart(pThis, NULL, pszName);
995 pszName = NULL;
996 if (cbName >= _4K)
997 break;
998 cbName += 128;
999 rc = rtldrPEReadPartByRva(pThis, NULL, uRvaName, cbName, (void const **)&pszName);
1000 }
1001 }
1002 if (!pszName)
1003 {
1004 RTStrPrintf(szAltName, sizeof(szAltName), "Ordinal%#x", uOrdinal);
1005 pszName = szAltName;
1006 }
1007
1008 /*
1009 * Call back.
1010 */
1011 rc = pfnCallback(&pThis->Core, pszName, uOrdinal + pExpDir->Base, Value, pvUser);
1012 if (pszName != szAltName && pszName)
1013 rtldrPEFreePart(pThis, NULL, pszName);
1014 if (rc)
1015 break;
1016 }
1017 }
1018 }
1019
1020 rtldrPEFreePart(pThis, NULL, paOrdinals);
1021 rtldrPEFreePart(pThis, NULL, paRVANames);
1022 rtldrPEFreePart(pThis, NULL, paAddress);
1023 rtldrPEFreePart(pThis, NULL, pExpDir);
1024 return rc;
1025
1026}
1027
1028
1029/** @copydoc RTLDROPS::pfnEnumSymbols */
1030static DECLCALLBACK(int) rtldrPEEnumSymbols(PRTLDRMODINTERNAL pMod, unsigned fFlags, const void *pvBits, RTUINTPTR BaseAddress,
1031 PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
1032{
1033 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
1034 NOREF(fFlags); /* ignored ... */
1035
1036 /*
1037 * Check if there is actually anything to work on.
1038 */
1039 if ( !pModPe->ExportDir.VirtualAddress
1040 || !pModPe->ExportDir.Size)
1041 return VERR_SYMBOL_NOT_FOUND;
1042
1043 /*
1044 * No bits supplied? Do we need to read the bits?
1045 */
1046 if (!pvBits)
1047 {
1048 if (!pModPe->pvBits)
1049 {
1050 int rc = rtldrPEReadBits(pModPe);
1051 if (RT_FAILURE(rc))
1052 return rtldrPEEnumSymbolsSlow(pModPe, fFlags, BaseAddress, pfnCallback, pvUser);
1053 }
1054 pvBits = pModPe->pvBits;
1055 }
1056
1057 /*
1058 * We enumerates by ordinal, which means using a slow linear search for
1059 * getting any name
1060 */
1061 PIMAGE_EXPORT_DIRECTORY pExpDir = PE_RVA2TYPE(pvBits, pModPe->ExportDir.VirtualAddress, PIMAGE_EXPORT_DIRECTORY);
1062 uint32_t *paAddress = PE_RVA2TYPE(pvBits, pExpDir->AddressOfFunctions, uint32_t *);
1063 uint32_t *paRVANames = PE_RVA2TYPE(pvBits, pExpDir->AddressOfNames, uint32_t *);
1064 uint16_t *paOrdinals = PE_RVA2TYPE(pvBits, pExpDir->AddressOfNameOrdinals, uint16_t *);
1065 uintptr_t uNamePrev = 0;
1066 unsigned cOrdinals = RT_MAX(pExpDir->NumberOfNames, pExpDir->NumberOfFunctions);
1067 for (unsigned uOrdinal = 0; uOrdinal < cOrdinals; uOrdinal++)
1068 {
1069 if (paAddress[uOrdinal] /* needed? */)
1070 {
1071 /*
1072 * Look for name.
1073 */
1074 const char *pszName = NULL;
1075 /* Search from previous + 1 to the end. */
1076 unsigned uName = uNamePrev + 1;
1077 while (uName < pExpDir->NumberOfNames)
1078 {
1079 if (paOrdinals[uName] == uOrdinal)
1080 {
1081 pszName = PE_RVA2TYPE(pvBits, paRVANames[uName], const char *);
1082 uNamePrev = uName;
1083 break;
1084 }
1085 uName++;
1086 }
1087 if (!pszName)
1088 {
1089 /* Search from start to the previous. */
1090 uName = 0;
1091 for (uName = 0 ; uName <= uNamePrev; uName++)
1092 {
1093 if (paOrdinals[uName] == uOrdinal)
1094 {
1095 pszName = PE_RVA2TYPE(pvBits, paRVANames[uName], const char *);
1096 uNamePrev = uName;
1097 break;
1098 }
1099 }
1100 }
1101
1102 /*
1103 * Get address.
1104 */
1105 uintptr_t uRVAExport = paAddress[uOrdinal];
1106 RTUINTPTR Value;
1107 if ( uRVAExport - (uintptr_t)pModPe->ExportDir.VirtualAddress
1108 < pModPe->ExportDir.Size)
1109 {
1110 if (!(fFlags & RTLDR_ENUM_SYMBOL_FLAGS_NO_FWD))
1111 {
1112 /* Resolve forwarder. */
1113 AssertMsgFailed(("Forwarders are not supported!\n"));
1114 }
1115 continue;
1116 }
1117
1118 /* Get plain export address */
1119 Value = PE_RVA2TYPE(BaseAddress, uRVAExport, RTUINTPTR);
1120
1121 /*
1122 * Call back.
1123 */
1124 int rc = pfnCallback(pMod, pszName, uOrdinal + pExpDir->Base, Value, pvUser);
1125 if (rc)
1126 return rc;
1127 }
1128 }
1129
1130 return VINF_SUCCESS;
1131}
1132
1133
1134/** @copydoc RTLDROPS::pfnEnumDbgInfo. */
1135static DECLCALLBACK(int) rtldrPE_EnumDbgInfo(PRTLDRMODINTERNAL pMod, const void *pvBits,
1136 PFNRTLDRENUMDBG pfnCallback, void *pvUser)
1137{
1138 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
1139 int rc;
1140
1141 /*
1142 * Debug info directory empty?
1143 */
1144 if ( !pModPe->DebugDir.VirtualAddress
1145 || !pModPe->DebugDir.Size)
1146 return VINF_SUCCESS;
1147
1148 /*
1149 * Get the debug directory.
1150 */
1151 if (!pvBits)
1152 pvBits = pModPe->pvBits;
1153
1154 PCIMAGE_DEBUG_DIRECTORY paDbgDir;
1155 int rcRet = rtldrPEReadPartByRva(pModPe, pvBits, pModPe->DebugDir.VirtualAddress, pModPe->DebugDir.Size,
1156 (void const **)&paDbgDir);
1157 if (RT_FAILURE(rcRet))
1158 return rcRet;
1159
1160 /*
1161 * Enumerate the debug directory.
1162 */
1163 uint32_t const cEntries = pModPe->DebugDir.Size / sizeof(paDbgDir[0]);
1164 for (uint32_t i = 0; i < cEntries; i++)
1165 {
1166 if (paDbgDir[i].PointerToRawData < pModPe->offEndOfHdrs)
1167 continue;
1168 if (paDbgDir[i].SizeOfData < 4)
1169 continue;
1170
1171 void const *pvPart = NULL;
1172 char szPath[RTPATH_MAX];
1173 RTLDRDBGINFO DbgInfo;
1174 RT_ZERO(DbgInfo.u);
1175 DbgInfo.iDbgInfo = i;
1176 DbgInfo.offFile = paDbgDir[i].PointerToRawData;
1177 DbgInfo.LinkAddress = paDbgDir[i].AddressOfRawData < pModPe->cbImage
1178 && paDbgDir[i].AddressOfRawData >= pModPe->offEndOfHdrs
1179 ? paDbgDir[i].AddressOfRawData : NIL_RTLDRADDR;
1180 DbgInfo.cb = paDbgDir[i].SizeOfData;
1181 DbgInfo.pszExtFile = NULL;
1182
1183 rc = VINF_SUCCESS;
1184 switch (paDbgDir[i].Type)
1185 {
1186 case IMAGE_DEBUG_TYPE_CODEVIEW:
1187 DbgInfo.enmType = RTLDRDBGINFOTYPE_CODEVIEW;
1188 DbgInfo.u.Cv.uMajorVer = paDbgDir[i].MajorVersion;
1189 DbgInfo.u.Cv.uMinorVer = paDbgDir[i].MinorVersion;
1190 DbgInfo.u.Cv.uTimestamp = paDbgDir[i].TimeDateStamp;
1191 if ( paDbgDir[i].SizeOfData < sizeof(szPath)
1192 && paDbgDir[i].SizeOfData > 16
1193 && ( DbgInfo.LinkAddress != NIL_RTLDRADDR
1194 || DbgInfo.offFile > 0)
1195 )
1196 {
1197 rc = rtldrPEReadPart(pModPe, pvBits, DbgInfo.offFile, DbgInfo.LinkAddress, paDbgDir[i].SizeOfData, &pvPart);
1198 if (RT_SUCCESS(rc))
1199 {
1200 PCCVPDB20INFO pCv20 = (PCCVPDB20INFO)pvPart;
1201 if ( pCv20->u32Magic == CVPDB20INFO_MAGIC
1202 && pCv20->offDbgInfo == 0
1203 && paDbgDir[i].SizeOfData > RT_UOFFSETOF(CVPDB20INFO, szPdbFilename) )
1204 {
1205 DbgInfo.enmType = RTLDRDBGINFOTYPE_CODEVIEW_PDB20;
1206 DbgInfo.u.Pdb20.cbImage = pModPe->cbImage;
1207 DbgInfo.u.Pdb20.uTimestamp = pCv20->uTimestamp;
1208 DbgInfo.u.Pdb20.uAge = pCv20->uAge;
1209 DbgInfo.pszExtFile = (const char *)&pCv20->szPdbFilename[0];
1210 }
1211 else if ( pCv20->u32Magic == CVPDB70INFO_MAGIC
1212 && paDbgDir[i].SizeOfData > RT_UOFFSETOF(CVPDB70INFO, szPdbFilename) )
1213 {
1214 PCCVPDB70INFO pCv70 = (PCCVPDB70INFO)pCv20;
1215 DbgInfo.enmType = RTLDRDBGINFOTYPE_CODEVIEW_PDB70;
1216 DbgInfo.u.Pdb70.cbImage = pModPe->cbImage;
1217 DbgInfo.u.Pdb70.Uuid = pCv70->PdbUuid;
1218 DbgInfo.u.Pdb70.uAge = pCv70->uAge;
1219 DbgInfo.pszExtFile = (const char *)&pCv70->szPdbFilename[0];
1220 }
1221 }
1222 else
1223 rcRet = rc;
1224 }
1225 break;
1226
1227 case IMAGE_DEBUG_TYPE_MISC:
1228 DbgInfo.enmType = RTLDRDBGINFOTYPE_UNKNOWN;
1229 if ( paDbgDir[i].SizeOfData < sizeof(szPath)
1230 && paDbgDir[i].SizeOfData > RT_UOFFSETOF(IMAGE_DEBUG_MISC, Data))
1231 {
1232 DbgInfo.enmType = RTLDRDBGINFOTYPE_CODEVIEW_DBG;
1233 DbgInfo.u.Dbg.cbImage = pModPe->cbImage;
1234 if (DbgInfo.LinkAddress != NIL_RTLDRADDR)
1235 DbgInfo.u.Dbg.uTimestamp = paDbgDir[i].TimeDateStamp;
1236 else
1237 DbgInfo.u.Dbg.uTimestamp = pModPe->uTimestamp; /* NT4 SP1 ntfs.sys hack. Generic? */
1238
1239 rc = rtldrPEReadPart(pModPe, pvBits, DbgInfo.offFile, DbgInfo.LinkAddress, paDbgDir[i].SizeOfData, &pvPart);
1240 if (RT_SUCCESS(rc))
1241 {
1242 PCIMAGE_DEBUG_MISC pMisc = (PCIMAGE_DEBUG_MISC)pvPart;
1243 if ( pMisc->DataType == IMAGE_DEBUG_MISC_EXENAME
1244 && pMisc->Length == paDbgDir[i].SizeOfData)
1245 {
1246 if (!pMisc->Unicode)
1247 DbgInfo.pszExtFile = (const char *)&pMisc->Data[0];
1248 else
1249 {
1250 char *pszPath = szPath;
1251 rc = RTUtf16ToUtf8Ex((PCRTUTF16)&pMisc->Data[0],
1252 (pMisc->Length - RT_OFFSETOF(IMAGE_DEBUG_MISC, Data)) / sizeof(RTUTF16),
1253 &pszPath, sizeof(szPath), NULL);
1254 if (RT_SUCCESS(rc))
1255 DbgInfo.pszExtFile = szPath;
1256 else
1257 rcRet = rc; /* continue without a filename. */
1258 }
1259 }
1260 }
1261 else
1262 rcRet = rc; /* continue without a filename. */
1263 }
1264 break;
1265
1266 default:
1267 DbgInfo.enmType = RTLDRDBGINFOTYPE_UNKNOWN;
1268 break;
1269 }
1270
1271 /* Fix (hack) the file name encoding. We don't have Windows-1252 handy,
1272 so we'll be using Latin-1 as a reasonable approximation.
1273 (I don't think we know exactly which encoding this is anyway, as
1274 it's probably the current ANSI/Windows code page for the process
1275 generating the image anyways.) */
1276 if (DbgInfo.pszExtFile && DbgInfo.pszExtFile != szPath)
1277 {
1278 char *pszPath = szPath;
1279 rc = RTLatin1ToUtf8Ex(DbgInfo.pszExtFile,
1280 paDbgDir[i].SizeOfData - ((uintptr_t)DbgInfo.pszExtFile - (uintptr_t)pvBits),
1281 &pszPath, sizeof(szPath), NULL);
1282 if (RT_FAILURE(rc))
1283 {
1284 rcRet = rc;
1285 DbgInfo.pszExtFile = NULL;
1286 }
1287 }
1288 if (DbgInfo.pszExtFile)
1289 RTPathChangeToUnixSlashes(szPath, true /*fForce*/);
1290
1291 rc = pfnCallback(pMod, &DbgInfo, pvUser);
1292 rtldrPEFreePart(pModPe, pvBits, pvPart);
1293 if (rc != VINF_SUCCESS)
1294 {
1295 rcRet = rc;
1296 break;
1297 }
1298 }
1299
1300 rtldrPEFreePart(pModPe, pvBits, paDbgDir);
1301 return rcRet;
1302}
1303
1304
1305/** @copydoc RTLDROPS::pfnEnumSegments. */
1306static DECLCALLBACK(int) rtldrPE_EnumSegments(PRTLDRMODINTERNAL pMod, PFNRTLDRENUMSEGS pfnCallback, void *pvUser)
1307{
1308 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
1309 RTLDRSEG SegInfo;
1310
1311 /*
1312 * The first section is a fake one covering the headers.
1313 */
1314 SegInfo.pszName = "NtHdrs";
1315 SegInfo.cchName = 6;
1316 SegInfo.SelFlat = 0;
1317 SegInfo.Sel16bit = 0;
1318 SegInfo.fFlags = 0;
1319 SegInfo.fProt = RTMEM_PROT_READ;
1320 SegInfo.Alignment = 1;
1321 SegInfo.LinkAddress = pModPe->uImageBase;
1322 SegInfo.RVA = 0;
1323 SegInfo.offFile = 0;
1324 SegInfo.cb = pModPe->cbHeaders;
1325 SegInfo.cbFile = pModPe->cbHeaders;
1326 SegInfo.cbMapped = pModPe->cbHeaders;
1327 if ((pModPe->paSections[0].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
1328 SegInfo.cbMapped = pModPe->paSections[0].VirtualAddress;
1329 int rc = pfnCallback(pMod, &SegInfo, pvUser);
1330
1331 /*
1332 * Then all the normal sections.
1333 */
1334 PCIMAGE_SECTION_HEADER pSh = pModPe->paSections;
1335 for (uint32_t i = 0; i < pModPe->cSections && rc == VINF_SUCCESS; i++, pSh++)
1336 {
1337 char szName[32];
1338 SegInfo.pszName = (const char *)&pSh->Name[0];
1339 SegInfo.cchName = (uint32_t)RTStrNLen(SegInfo.pszName, sizeof(pSh->Name));
1340 if (SegInfo.cchName >= sizeof(pSh->Name))
1341 {
1342 memcpy(szName, &pSh->Name[0], sizeof(pSh->Name));
1343 szName[sizeof(sizeof(pSh->Name))] = '\0';
1344 SegInfo.pszName = szName;
1345 }
1346 else if (SegInfo.cchName == 0)
1347 {
1348 SegInfo.pszName = szName;
1349 SegInfo.cchName = (uint32_t)RTStrPrintf(szName, sizeof(szName), "UnamedSect%02u", i);
1350 }
1351 SegInfo.SelFlat = 0;
1352 SegInfo.Sel16bit = 0;
1353 SegInfo.fFlags = 0;
1354 SegInfo.fProt = RTMEM_PROT_NONE;
1355 if (pSh->Characteristics & IMAGE_SCN_MEM_READ)
1356 SegInfo.fProt |= RTMEM_PROT_READ;
1357 if (pSh->Characteristics & IMAGE_SCN_MEM_WRITE)
1358 SegInfo.fProt |= RTMEM_PROT_WRITE;
1359 if (pSh->Characteristics & IMAGE_SCN_MEM_EXECUTE)
1360 SegInfo.fProt |= RTMEM_PROT_EXEC;
1361 SegInfo.Alignment = (pSh->Characteristics & IMAGE_SCN_ALIGN_MASK) >> IMAGE_SCN_ALIGN_SHIFT;
1362 if (SegInfo.Alignment > 0)
1363 SegInfo.Alignment = RT_BIT_64(SegInfo.Alignment - 1);
1364 if (pSh->Characteristics & IMAGE_SCN_TYPE_NOLOAD)
1365 {
1366 SegInfo.LinkAddress = NIL_RTLDRADDR;
1367 SegInfo.RVA = NIL_RTLDRADDR;
1368 SegInfo.cbMapped = pSh->Misc.VirtualSize;
1369 }
1370 else
1371 {
1372 SegInfo.LinkAddress = pSh->VirtualAddress + pModPe->uImageBase ;
1373 SegInfo.RVA = pSh->VirtualAddress;
1374 SegInfo.cbMapped = RT_ALIGN(SegInfo.cb, SegInfo.Alignment);
1375 if (i + 1 < pModPe->cSections && !(pSh[1].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
1376 SegInfo.cbMapped = pSh[1].VirtualAddress - pSh->VirtualAddress;
1377 }
1378 SegInfo.cb = pSh->Misc.VirtualSize;
1379 if (pSh->PointerToRawData == 0 || pSh->SizeOfRawData == 0)
1380 {
1381 SegInfo.offFile = -1;
1382 SegInfo.cbFile = 0;
1383 }
1384 else
1385 {
1386 SegInfo.offFile = pSh->PointerToRawData;
1387 SegInfo.cbFile = pSh->SizeOfRawData;
1388 }
1389
1390 rc = pfnCallback(pMod, &SegInfo, pvUser);
1391 }
1392
1393 return rc;
1394}
1395
1396
1397/** @copydoc RTLDROPS::pfnLinkAddressToSegOffset. */
1398static DECLCALLBACK(int) rtldrPE_LinkAddressToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress,
1399 uint32_t *piSeg, PRTLDRADDR poffSeg)
1400{
1401 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
1402
1403 LinkAddress -= pModPe->uImageBase;
1404
1405 /* Special header segment. */
1406 if (LinkAddress < pModPe->paSections[0].VirtualAddress)
1407 {
1408 *piSeg = 0;
1409 *poffSeg = LinkAddress;
1410 return VINF_SUCCESS;
1411 }
1412
1413 /*
1414 * Search the normal sections. (Could do this in binary fashion, they're
1415 * sorted, but too much bother right now.)
1416 */
1417 if (LinkAddress > pModPe->cbImage)
1418 return VERR_LDR_INVALID_LINK_ADDRESS;
1419 uint32_t i = pModPe->cSections;
1420 PCIMAGE_SECTION_HEADER paShs = pModPe->paSections;
1421 while (i-- > 0)
1422 if (!(paShs[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
1423 {
1424 uint32_t uAddr = paShs[i].VirtualAddress;
1425 if (LinkAddress >= uAddr)
1426 {
1427 *poffSeg = LinkAddress - uAddr;
1428 *piSeg = i + 1;
1429 return VINF_SUCCESS;
1430 }
1431 }
1432
1433 return VERR_LDR_INVALID_LINK_ADDRESS;
1434}
1435
1436
1437/** @copydoc RTLDROPS::pfnLinkAddressToRva. */
1438static DECLCALLBACK(int) rtldrPE_LinkAddressToRva(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress, PRTLDRADDR pRva)
1439{
1440 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
1441
1442 LinkAddress -= pModPe->uImageBase;
1443 if (LinkAddress > pModPe->cbImage)
1444 return VERR_LDR_INVALID_LINK_ADDRESS;
1445 *pRva = LinkAddress;
1446
1447 return VINF_SUCCESS;
1448}
1449
1450
1451/** @copydoc RTLDROPS::pfnSegOffsetToRva. */
1452static DECLCALLBACK(int) rtldrPE_SegOffsetToRva(PRTLDRMODINTERNAL pMod, uint32_t iSeg, RTLDRADDR offSeg,
1453 PRTLDRADDR pRva)
1454{
1455 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
1456
1457 if (iSeg > pModPe->cSections)
1458 return VERR_LDR_INVALID_SEG_OFFSET;
1459
1460 /** @todo should validate offSeg here... too lazy right now. */
1461 if (iSeg == 0)
1462 *pRva = offSeg;
1463 else if (pModPe->paSections[iSeg].Characteristics & IMAGE_SCN_TYPE_NOLOAD)
1464 return VERR_LDR_INVALID_SEG_OFFSET;
1465 else
1466 *pRva = offSeg + pModPe->paSections[iSeg].VirtualAddress;
1467 return VINF_SUCCESS;
1468}
1469
1470
1471/** @copydoc RTLDROPS::pfnRvaToSegOffset. */
1472static DECLCALLBACK(int) rtldrPE_RvaToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR Rva,
1473 uint32_t *piSeg, PRTLDRADDR poffSeg)
1474{
1475 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
1476 int rc = rtldrPE_LinkAddressToSegOffset(pMod, Rva + pModPe->uImageBase, piSeg, poffSeg);
1477 if (RT_FAILURE(rc))
1478 rc = VERR_LDR_INVALID_RVA;
1479 return rc;
1480}
1481
1482
1483/** @copydoc RTLDROPS::pfnDone */
1484static DECLCALLBACK(int) rtldrPEDone(PRTLDRMODINTERNAL pMod)
1485{
1486 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
1487 if (pModPe->pvBits)
1488 {
1489 RTMemFree(pModPe->pvBits);
1490 pModPe->pvBits = NULL;
1491 }
1492 return VINF_SUCCESS;
1493}
1494
1495/** @copydoc RTLDROPS::pfnClose */
1496static DECLCALLBACK(int) rtldrPEClose(PRTLDRMODINTERNAL pMod)
1497{
1498 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
1499 if (pModPe->paSections)
1500 {
1501 RTMemFree(pModPe->paSections);
1502 pModPe->paSections = NULL;
1503 }
1504 if (pModPe->pvBits)
1505 {
1506 RTMemFree(pModPe->pvBits);
1507 pModPe->pvBits = NULL;
1508 }
1509 if (pModPe->Core.pReader)
1510 {
1511 int rc = pModPe->Core.pReader->pfnDestroy(pModPe->Core.pReader);
1512 AssertRC(rc);
1513 pModPe->Core.pReader = NULL;
1514 }
1515 return VINF_SUCCESS;
1516}
1517
1518
1519/**
1520 * Operations for a 32-bit PE module.
1521 */
1522static const RTLDROPSPE s_rtldrPE32Ops =
1523{
1524 {
1525 "pe32",
1526 rtldrPEClose,
1527 NULL,
1528 rtldrPEDone,
1529 rtldrPEEnumSymbols,
1530 /* ext */
1531 rtldrPEGetImageSize,
1532 rtldrPEGetBits,
1533 rtldrPERelocate,
1534 rtldrPEGetSymbolEx,
1535 rtldrPE_EnumDbgInfo,
1536 rtldrPE_EnumSegments,
1537 rtldrPE_LinkAddressToSegOffset,
1538 rtldrPE_LinkAddressToRva,
1539 rtldrPE_SegOffsetToRva,
1540 rtldrPE_RvaToSegOffset,
1541 NULL,
1542 42
1543 },
1544 rtldrPEResolveImports32,
1545 42
1546};
1547
1548
1549/**
1550 * Operations for a 64-bit PE module.
1551 */
1552static const RTLDROPSPE s_rtldrPE64Ops =
1553{
1554 {
1555 "pe64",
1556 rtldrPEClose,
1557 NULL,
1558 rtldrPEDone,
1559 rtldrPEEnumSymbols,
1560 /* ext */
1561 rtldrPEGetImageSize,
1562 rtldrPEGetBits,
1563 rtldrPERelocate,
1564 rtldrPEGetSymbolEx,
1565 rtldrPE_EnumDbgInfo,
1566 rtldrPE_EnumSegments,
1567 rtldrPE_LinkAddressToSegOffset,
1568 rtldrPE_LinkAddressToRva,
1569 rtldrPE_SegOffsetToRva,
1570 rtldrPE_RvaToSegOffset,
1571 NULL,
1572 42
1573 },
1574 rtldrPEResolveImports64,
1575 42
1576};
1577
1578
1579/**
1580 * Converts the optional header from 32 bit to 64 bit.
1581 * This is a rather simple task, if you start from the right end.
1582 *
1583 * @param pOptHdr On input this is a PIMAGE_OPTIONAL_HEADER32.
1584 * On output this will be a PIMAGE_OPTIONAL_HEADER64.
1585 */
1586static void rtldrPEConvert32BitOptionalHeaderTo64Bit(PIMAGE_OPTIONAL_HEADER64 pOptHdr)
1587{
1588 /*
1589 * volatile everywhere! Trying to prevent the compiler being a smarta$$ and reorder stuff.
1590 */
1591 IMAGE_OPTIONAL_HEADER32 volatile *pOptHdr32 = (IMAGE_OPTIONAL_HEADER32 volatile *)pOptHdr;
1592 IMAGE_OPTIONAL_HEADER64 volatile *pOptHdr64 = pOptHdr;
1593
1594 /* from LoaderFlags and out the difference is 4 * 32-bits. */
1595 Assert(RT_OFFSETOF(IMAGE_OPTIONAL_HEADER32, LoaderFlags) + 16 == RT_OFFSETOF(IMAGE_OPTIONAL_HEADER64, LoaderFlags));
1596 Assert( RT_OFFSETOF(IMAGE_OPTIONAL_HEADER32, DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]) + 16
1597 == RT_OFFSETOF(IMAGE_OPTIONAL_HEADER64, DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]));
1598 uint32_t volatile *pu32Dst = (uint32_t *)&pOptHdr64->DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] - 1;
1599 const uint32_t volatile *pu32Src = (uint32_t *)&pOptHdr32->DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] - 1;
1600 const uint32_t volatile *pu32SrcLast = (uint32_t *)&pOptHdr32->LoaderFlags;
1601 while (pu32Src >= pu32SrcLast)
1602 *pu32Dst-- = *pu32Src--;
1603
1604 /* the previous 4 fields are 32/64 and needs special attention. */
1605 pOptHdr64->SizeOfHeapCommit = pOptHdr32->SizeOfHeapCommit;
1606 pOptHdr64->SizeOfHeapReserve = pOptHdr32->SizeOfHeapReserve;
1607 pOptHdr64->SizeOfStackCommit = pOptHdr32->SizeOfStackCommit;
1608 uint32_t u32SizeOfStackReserve = pOptHdr32->SizeOfStackReserve;
1609 pOptHdr64->SizeOfStackReserve = u32SizeOfStackReserve;
1610
1611 /* The rest matches except for BaseOfData which has been merged into ImageBase in the 64-bit version..
1612 * Thus, ImageBase needs some special treatment. It will probably work fine assigning one to the
1613 * other since this is all declared volatile, but taking now chances, we'll use a temp variable.
1614 */
1615 Assert(RT_OFFSETOF(IMAGE_OPTIONAL_HEADER32, SizeOfStackReserve) == RT_OFFSETOF(IMAGE_OPTIONAL_HEADER64, SizeOfStackReserve));
1616 Assert(RT_OFFSETOF(IMAGE_OPTIONAL_HEADER32, BaseOfData) == RT_OFFSETOF(IMAGE_OPTIONAL_HEADER64, ImageBase));
1617 Assert(RT_OFFSETOF(IMAGE_OPTIONAL_HEADER32, SectionAlignment) == RT_OFFSETOF(IMAGE_OPTIONAL_HEADER64, SectionAlignment));
1618 uint32_t u32ImageBase = pOptHdr32->ImageBase;
1619 pOptHdr64->ImageBase = u32ImageBase;
1620}
1621
1622
1623/**
1624 * Converts the load config directory from 32 bit to 64 bit.
1625 * This is a rather simple task, if you start from the right end.
1626 *
1627 * @param pLoadCfg On input this is a PIMAGE_LOAD_CONFIG_DIRECTORY32.
1628 * On output this will be a PIMAGE_LOAD_CONFIG_DIRECTORY64.
1629 */
1630static void rtldrPEConvert32BitLoadConfigTo64Bit(PIMAGE_LOAD_CONFIG_DIRECTORY64 pLoadCfg)
1631{
1632 /*
1633 * volatile everywhere! Trying to prevent the compiler being a smarta$$ and reorder stuff.
1634 */
1635 IMAGE_LOAD_CONFIG_DIRECTORY32 volatile *pLoadCfg32 = (IMAGE_LOAD_CONFIG_DIRECTORY32 volatile *)pLoadCfg;
1636 IMAGE_LOAD_CONFIG_DIRECTORY64 volatile *pLoadCfg64 = pLoadCfg;
1637
1638 pLoadCfg64->SEHandlerCount = pLoadCfg32->SEHandlerCount;
1639 pLoadCfg64->SEHandlerTable = pLoadCfg32->SEHandlerTable;
1640 pLoadCfg64->SecurityCookie = pLoadCfg32->SecurityCookie;
1641 pLoadCfg64->EditList = pLoadCfg32->EditList;
1642 pLoadCfg64->Reserved1 = pLoadCfg32->Reserved1;
1643 pLoadCfg64->CSDVersion = pLoadCfg32->CSDVersion;
1644 pLoadCfg64->ProcessHeapFlags = pLoadCfg32->ProcessHeapFlags; /* switched place with ProcessAffinityMask, but we're more than 16 byte off by now so it doesn't matter. */
1645 pLoadCfg64->ProcessAffinityMask = pLoadCfg32->ProcessAffinityMask;
1646 pLoadCfg64->VirtualMemoryThreshold = pLoadCfg32->VirtualMemoryThreshold;
1647 pLoadCfg64->MaximumAllocationSize = pLoadCfg32->MaximumAllocationSize;
1648 pLoadCfg64->LockPrefixTable = pLoadCfg32->LockPrefixTable;
1649 pLoadCfg64->DeCommitTotalFreeThreshold = pLoadCfg32->DeCommitTotalFreeThreshold;
1650 uint32_t u32DeCommitFreeBlockThreshold = pLoadCfg32->DeCommitFreeBlockThreshold;
1651 pLoadCfg64->DeCommitFreeBlockThreshold = u32DeCommitFreeBlockThreshold;
1652 /* the rest is equal. */
1653 Assert( RT_OFFSETOF(IMAGE_LOAD_CONFIG_DIRECTORY32, DeCommitFreeBlockThreshold)
1654 == RT_OFFSETOF(IMAGE_LOAD_CONFIG_DIRECTORY64, DeCommitFreeBlockThreshold));
1655}
1656
1657
1658/**
1659 * Validates the file header.
1660 *
1661 * @returns iprt status code.
1662 * @param pFileHdr Pointer to the file header that needs validating.
1663 * @param fFlags Valid RTLDR_O_XXX combination.
1664 * @param pszLogName The log name to prefix the errors with.
1665 * @param penmArch Where to store the CPU architecture.
1666 */
1667static int rtldrPEValidateFileHeader(PIMAGE_FILE_HEADER pFileHdr, uint32_t fFlags, const char *pszLogName, PRTLDRARCH penmArch)
1668{
1669 size_t cbOptionalHeader;
1670 switch (pFileHdr->Machine)
1671 {
1672 case IMAGE_FILE_MACHINE_I386:
1673 cbOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER32);
1674 *penmArch = RTLDRARCH_X86_32;
1675 break;
1676 case IMAGE_FILE_MACHINE_AMD64:
1677 cbOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER64);
1678 *penmArch = RTLDRARCH_AMD64;
1679 break;
1680
1681 default:
1682 Log(("rtldrPEOpen: %s: Unsupported Machine=%#x\n",
1683 pszLogName, pFileHdr->Machine));
1684 *penmArch = RTLDRARCH_INVALID;
1685 return VERR_BAD_EXE_FORMAT;
1686 }
1687 if (pFileHdr->SizeOfOptionalHeader != cbOptionalHeader)
1688 {
1689 Log(("rtldrPEOpen: %s: SizeOfOptionalHeader=%#x expected %#x\n",
1690 pszLogName, pFileHdr->SizeOfOptionalHeader, cbOptionalHeader));
1691 return VERR_BAD_EXE_FORMAT;
1692 }
1693 /* This restriction needs to be implemented elsewhere. */
1694 if ( (pFileHdr->Characteristics & IMAGE_FILE_RELOCS_STRIPPED)
1695 && !(fFlags & RTLDR_O_FOR_DEBUG))
1696 {
1697 Log(("rtldrPEOpen: %s: IMAGE_FILE_RELOCS_STRIPPED\n", pszLogName));
1698 return VERR_BAD_EXE_FORMAT;
1699 }
1700 if (pFileHdr->NumberOfSections > 42)
1701 {
1702 Log(("rtldrPEOpen: %s: NumberOfSections=%d - our limit is 42, please raise it if the binary makes sense.(!!!)\n",
1703 pszLogName, pFileHdr->NumberOfSections));
1704 return VERR_BAD_EXE_FORMAT;
1705 }
1706 if (pFileHdr->NumberOfSections < 1)
1707 {
1708 Log(("rtldrPEOpen: %s: NumberOfSections=%d - we can't have an image without sections (!!!)\n",
1709 pszLogName, pFileHdr->NumberOfSections));
1710 return VERR_BAD_EXE_FORMAT;
1711 }
1712 return VINF_SUCCESS;
1713}
1714
1715
1716/**
1717 * Validates the optional header (64/32-bit)
1718 *
1719 * @returns iprt status code.
1720 * @param pOptHdr Pointer to the optional header which needs validation.
1721 * @param pszLogName The log name to prefix the errors with.
1722 * @param offNtHdrs The offset of the NT headers from the start of the file.
1723 * @param pFileHdr Pointer to the file header (valid).
1724 * @param cbRawImage The raw image size.
1725 * @param fFlags Loader flags, RTLDR_O_XXX.
1726 */
1727static int rtldrPEValidateOptionalHeader(const IMAGE_OPTIONAL_HEADER64 *pOptHdr, const char *pszLogName, RTFOFF offNtHdrs,
1728 const IMAGE_FILE_HEADER *pFileHdr, RTFOFF cbRawImage, uint32_t fFlags)
1729{
1730 const uint16_t CorrectMagic = pFileHdr->SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32)
1731 ? IMAGE_NT_OPTIONAL_HDR32_MAGIC : IMAGE_NT_OPTIONAL_HDR64_MAGIC;
1732 if (pOptHdr->Magic != CorrectMagic)
1733 {
1734 Log(("rtldrPEOpen: %s: Magic=%#x - expected %#x!!!\n", pszLogName, pOptHdr->Magic, CorrectMagic));
1735 return VERR_BAD_EXE_FORMAT;
1736 }
1737 const uint32_t cbImage = pOptHdr->SizeOfImage;
1738 if (cbImage > _1G)
1739 {
1740 Log(("rtldrPEOpen: %s: SizeOfImage=%#x - Our limit is 1GB (%#x)!!!\n", pszLogName, cbImage, _1G));
1741 return VERR_BAD_EXE_FORMAT;
1742 }
1743 const uint32_t cbMinImageSize = pFileHdr->SizeOfOptionalHeader + sizeof(*pFileHdr) + 4 + (uint32_t)offNtHdrs;
1744 if (cbImage < cbMinImageSize)
1745 {
1746 Log(("rtldrPEOpen: %s: SizeOfImage=%#x to small, minimum %#x!!!\n", pszLogName, cbImage, cbMinImageSize));
1747 return VERR_BAD_EXE_FORMAT;
1748 }
1749 if (pOptHdr->AddressOfEntryPoint >= cbImage)
1750 {
1751 Log(("rtldrPEOpen: %s: AddressOfEntryPoint=%#x - beyond image size (%#x)!!!\n",
1752 pszLogName, pOptHdr->AddressOfEntryPoint, cbImage));
1753 return VERR_BAD_EXE_FORMAT;
1754 }
1755 if (pOptHdr->BaseOfCode >= cbImage)
1756 {
1757 Log(("rtldrPEOpen: %s: BaseOfCode=%#x - beyond image size (%#x)!!!\n",
1758 pszLogName, pOptHdr->BaseOfCode, cbImage));
1759 return VERR_BAD_EXE_FORMAT;
1760 }
1761#if 0/* only in 32-bit header */
1762 if (pOptHdr->BaseOfData >= cbImage)
1763 {
1764 Log(("rtldrPEOpen: %s: BaseOfData=%#x - beyond image size (%#x)!!!\n",
1765 pszLogName, pOptHdr->BaseOfData, cbImage));
1766 return VERR_BAD_EXE_FORMAT;
1767 }
1768#endif
1769 if (pOptHdr->SizeOfHeaders >= cbImage)
1770 {
1771 Log(("rtldrPEOpen: %s: SizeOfHeaders=%#x - beyond image size (%#x)!!!\n",
1772 pszLogName, pOptHdr->SizeOfHeaders, cbImage));
1773 return VERR_BAD_EXE_FORMAT;
1774 }
1775 /* don't know how to do the checksum, so ignore it. */
1776 if (pOptHdr->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN)
1777 {
1778 Log(("rtldrPEOpen: %s: Subsystem=%#x (unknown)!!!\n", pszLogName, pOptHdr->Subsystem));
1779 return VERR_BAD_EXE_FORMAT;
1780 }
1781 if (pOptHdr->SizeOfHeaders < cbMinImageSize + pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER))
1782 {
1783 Log(("rtldrPEOpen: %s: SizeOfHeaders=%#x - cbMinImageSize %#x + sections %#x = %#llx!!!\n",
1784 pszLogName, pOptHdr->SizeOfHeaders,
1785 cbImage, cbMinImageSize, pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER),
1786 cbMinImageSize + pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER)));
1787 return VERR_BAD_EXE_FORMAT;
1788 }
1789 if (pOptHdr->SizeOfStackReserve < pOptHdr->SizeOfStackCommit)
1790 {
1791 Log(("rtldrPEOpen: %s: SizeOfStackReserve %#x < SizeOfStackCommit %#x!!!\n",
1792 pszLogName, pOptHdr->SizeOfStackReserve, pOptHdr->SizeOfStackCommit));
1793 return VERR_BAD_EXE_FORMAT;
1794 }
1795 if (pOptHdr->SizeOfHeapReserve < pOptHdr->SizeOfHeapCommit)
1796 {
1797 Log(("rtldrPEOpen: %s: SizeOfStackReserve %#x < SizeOfStackCommit %#x!!!\n",
1798 pszLogName, pOptHdr->SizeOfStackReserve, pOptHdr->SizeOfStackCommit));
1799 return VERR_BAD_EXE_FORMAT;
1800 }
1801
1802 /* DataDirectory */
1803 if (pOptHdr->NumberOfRvaAndSizes != RT_ELEMENTS(pOptHdr->DataDirectory))
1804 {
1805 Log(("rtldrPEOpen: %s: NumberOfRvaAndSizes=%d!!!\n", pszLogName, pOptHdr->NumberOfRvaAndSizes));
1806 return VERR_BAD_EXE_FORMAT;
1807 }
1808 for (unsigned i = 0; i < RT_ELEMENTS(pOptHdr->DataDirectory); i++)
1809 {
1810 IMAGE_DATA_DIRECTORY const *pDir = &pOptHdr->DataDirectory[i];
1811 if (!pDir->Size)
1812 continue;
1813 size_t cb = cbImage;
1814 switch (i)
1815 {
1816 case IMAGE_DIRECTORY_ENTRY_EXPORT: // 0
1817 case IMAGE_DIRECTORY_ENTRY_IMPORT: // 1
1818 case IMAGE_DIRECTORY_ENTRY_RESOURCE: // 2
1819 case IMAGE_DIRECTORY_ENTRY_EXCEPTION: // 3
1820 case IMAGE_DIRECTORY_ENTRY_BASERELOC: // 5
1821 case IMAGE_DIRECTORY_ENTRY_DEBUG: // 6
1822 case IMAGE_DIRECTORY_ENTRY_COPYRIGHT: // 7
1823 case IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT: // 11
1824 case IMAGE_DIRECTORY_ENTRY_IAT: // 12 /* Import Address Table */
1825 break;
1826 case IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG: // 10 - need to check for lock prefixes.
1827 /* Delay inspection after section table is validated. */
1828 break;
1829
1830 case IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT: // 13
1831 Log(("rtldrPEOpen: %s: dir no. %d (DELAY_IMPORT) VirtualAddress=%#x Size=%#x is not supported!!!\n",
1832 pszLogName, i, pDir->VirtualAddress, pDir->Size));
1833 return VERR_LDRPE_DELAY_IMPORT;
1834
1835 case IMAGE_DIRECTORY_ENTRY_SECURITY: // 4
1836 /* The VirtualAddress is a PointerToRawData. */
1837 cb = (size_t)cbRawImage; Assert((RTFOFF)cb == cbRawImage);
1838 Log(("rtldrPEOpen: %s: dir no. %d (SECURITY) VirtualAddress=%#x Size=%#x is not supported!!!\n",
1839 pszLogName, i, pDir->VirtualAddress, pDir->Size));
1840 if (pDir->Size < sizeof(WIN_CERTIFICATE))
1841 {
1842 Log(("rtldrPEOpen: %s: Security directory is too small: %#x bytes\n", pszLogName, i, pDir->Size));
1843 return VERR_LDRPE_CERT_MALFORMED;
1844 }
1845 if (pDir->Size >= _1M)
1846 {
1847 Log(("rtldrPEOpen: %s: Security directory is too large: %#x bytes\n", pszLogName, i, pDir->Size));
1848 return VERR_LDRPE_CERT_MALFORMED;
1849 }
1850 if (pDir->VirtualAddress & 7)
1851 {
1852 Log(("rtldrPEOpen: %s: Security directory is misaligned: %#x\n", pszLogName, i, pDir->VirtualAddress));
1853 return VERR_LDRPE_CERT_MALFORMED;
1854 }
1855 /* When using the in-memory reader with a debugger, we may get
1856 into trouble here since we might not have access to the whole
1857 physical file. So skip the tests below. Makes VBoxGuest.sys
1858 load and check out just fine, for instance. */
1859 if (fFlags & RTLDR_O_FOR_DEBUG)
1860 continue;
1861 break;
1862
1863 case IMAGE_DIRECTORY_ENTRY_GLOBALPTR: // 8 /* (MIPS GP) */
1864 Log(("rtldrPEOpen: %s: dir no. %d (GLOBALPTR) VirtualAddress=%#x Size=%#x is not supported!!!\n",
1865 pszLogName, i, pDir->VirtualAddress, pDir->Size));
1866 return VERR_LDRPE_GLOBALPTR;
1867
1868 case IMAGE_DIRECTORY_ENTRY_TLS: // 9
1869 Log(("rtldrPEOpen: %s: dir no. %d (TLS) VirtualAddress=%#x Size=%#x is not supported!!!\n",
1870 pszLogName, i, pDir->VirtualAddress, pDir->Size));
1871 return VERR_LDRPE_TLS;
1872
1873 case IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR:// 14
1874 Log(("rtldrPEOpen: %s: dir no. %d (COM_DESCRIPTOR) VirtualAddress=%#x Size=%#x is not supported!!!\n",
1875 pszLogName, i, pDir->VirtualAddress, pDir->Size));
1876 return VERR_LDRPE_COM_DESCRIPTOR;
1877
1878 default:
1879 Log(("rtldrPEOpen: %s: dir no. %d VirtualAddress=%#x Size=%#x is not supported!!!\n",
1880 pszLogName, i, pDir->VirtualAddress, pDir->Size));
1881 return VERR_BAD_EXE_FORMAT;
1882 }
1883 if (pDir->VirtualAddress >= cb)
1884 {
1885 Log(("rtldrPEOpen: %s: dir no. %d VirtualAddress=%#x is invalid (limit %#x)!!!\n",
1886 pszLogName, i, pDir->VirtualAddress, cb));
1887 return VERR_BAD_EXE_FORMAT;
1888 }
1889 if (pDir->Size > cb - pDir->VirtualAddress)
1890 {
1891 Log(("rtldrPEOpen: %s: dir no. %d Size=%#x is invalid (rva=%#x, limit=%#x)!!!\n",
1892 pszLogName, i, pDir->Size, pDir->VirtualAddress, cb));
1893 return VERR_BAD_EXE_FORMAT;
1894 }
1895 }
1896 return VINF_SUCCESS;
1897}
1898
1899
1900/**
1901 * Validates the section headers.
1902 *
1903 * @returns iprt status code.
1904 * @param paSections Pointer to the array of sections that is to be validated.
1905 * @param cSections Number of sections in that array.
1906 * @param pszLogName The log name to prefix the errors with.
1907 * @param pOptHdr Pointer to the optional header (valid).
1908 * @param cbRawImage The raw image size.
1909 * @param fFlags Loader flags, RTLDR_O_XXX.
1910 */
1911static int rtldrPEValidateSectionHeaders(const IMAGE_SECTION_HEADER *paSections, unsigned cSections, const char *pszLogName,
1912 const IMAGE_OPTIONAL_HEADER64 *pOptHdr, RTFOFF cbRawImage, uint32_t fFlags)
1913{
1914 const uint32_t cbImage = pOptHdr->SizeOfImage;
1915 const IMAGE_SECTION_HEADER *pSH = &paSections[0];
1916 uint32_t uRvaPrev = pOptHdr->SizeOfHeaders;
1917 Log3(("RTLdrPE: Section Headers:\n"));
1918 for (unsigned cSHdrsLeft = cSections; cSHdrsLeft > 0; cSHdrsLeft--, pSH++)
1919 {
1920 const unsigned iSH = pSH - &paSections[0]; NOREF(iSH);
1921 Log3(("RTLdrPE: #%d '%-8.8s' Characteristics: %08RX32\n"
1922 "RTLdrPE: VirtAddr: %08RX32 VirtSize: %08RX32\n"
1923 "RTLdrPE: FileOff: %08RX32 FileSize: %08RX32\n"
1924 "RTLdrPE: RelocOff: %08RX32 #Relocs: %08RX32\n"
1925 "RTLdrPE: LineOff: %08RX32 #Lines: %08RX32\n",
1926 iSH, pSH->Name, pSH->Characteristics,
1927 pSH->VirtualAddress, pSH->Misc.VirtualSize,
1928 pSH->PointerToRawData, pSH->SizeOfRawData,
1929 pSH->PointerToRelocations, pSH->NumberOfRelocations,
1930 pSH->PointerToLinenumbers, pSH->NumberOfLinenumbers));
1931
1932 AssertCompile(IMAGE_SCN_MEM_16BIT == IMAGE_SCN_MEM_PURGEABLE);
1933 if ( ( pSH->Characteristics & (IMAGE_SCN_MEM_PURGEABLE | IMAGE_SCN_MEM_PRELOAD | IMAGE_SCN_MEM_FARDATA) )
1934 && !(fFlags & RTLDR_O_FOR_DEBUG)) /* purgable/16-bit seen on w2ksp0 hal.dll, ignore the bunch. */
1935 {
1936 Log(("rtldrPEOpen: %s: Unsupported section flag(s) %#x section #%d '%.*s'!!!\n",
1937 pszLogName, pSH->Characteristics, iSH, sizeof(pSH->Name), pSH->Name));
1938 return VERR_BAD_EXE_FORMAT;
1939 }
1940
1941 if ( pSH->Misc.VirtualSize
1942 && !(pSH->Characteristics & IMAGE_SCN_TYPE_NOLOAD)) /* binutils uses this for '.stab' even if it's reserved/obsoleted by MS. */
1943 {
1944 if (pSH->VirtualAddress < uRvaPrev)
1945 {
1946 Log(("rtldrPEOpen: %s: Overlaps previous section or sections aren't in ascending order, VirtualAddress=%#x uRvaPrev=%#x - section #%d '%.*s'!!!\n",
1947 pszLogName, pSH->VirtualAddress, uRvaPrev, iSH, sizeof(pSH->Name), pSH->Name));
1948 return VERR_BAD_EXE_FORMAT;
1949 }
1950 if (pSH->VirtualAddress > cbImage)
1951 {
1952 Log(("rtldrPEOpen: %s: VirtualAddress=%#x - beyond image size (%#x) - section #%d '%.*s'!!!\n",
1953 pszLogName, pSH->VirtualAddress, cbImage, iSH, sizeof(pSH->Name), pSH->Name));
1954 return VERR_BAD_EXE_FORMAT;
1955 }
1956
1957 if (pSH->VirtualAddress & (pOptHdr->SectionAlignment - 1)) //ASSUMES power of 2 alignment.
1958 {
1959 Log(("rtldrPEOpen: %s: VirtualAddress=%#x misaligned (%#x) - section #%d '%.*s'!!!\n",
1960 pszLogName, pSH->VirtualAddress, pOptHdr->SectionAlignment, iSH, sizeof(pSH->Name), pSH->Name));
1961 return VERR_BAD_EXE_FORMAT;
1962 }
1963
1964#ifdef PE_FILE_OFFSET_EQUALS_RVA
1965 /* Our loader code assume rva matches the file offset. */
1966 if ( pSH->SizeOfRawData
1967 && pSH->PointerToRawData != pSH->VirtualAddress)
1968 {
1969 Log(("rtldrPEOpen: %s: ASSUMPTION FAILED: file offset %#x != RVA %#x - section #%d '%.*s'!!!\n",
1970 pszLogName, pSH->PointerToRawData, pSH->VirtualAddress, iSH, sizeof(pSH->Name), pSH->Name));
1971 return VERR_BAD_EXE_FORMAT;
1972 }
1973#endif
1974 }
1975
1976 ///@todo only if SizeOfRawData > 0 ?
1977 if ( pSH->PointerToRawData > cbRawImage /// @todo pSH->PointerToRawData >= cbRawImage ?
1978 || pSH->SizeOfRawData > cbRawImage
1979 || pSH->PointerToRawData + pSH->SizeOfRawData > cbRawImage)
1980 {
1981 Log(("rtldrPEOpen: %s: PointerToRawData=%#x SizeOfRawData=%#x - beyond end of file (%#x) - section #%d '%.*s'!!!\n",
1982 pszLogName, pSH->PointerToRawData, pSH->SizeOfRawData, cbRawImage,
1983 iSH, sizeof(pSH->Name), pSH->Name));
1984 return VERR_BAD_EXE_FORMAT;
1985 }
1986
1987 if (pSH->PointerToRawData & (pOptHdr->FileAlignment - 1)) //ASSUMES power of 2 alignment.
1988 {
1989 Log(("rtldrPEOpen: %s: PointerToRawData=%#x misaligned (%#x) - section #%d '%.*s'!!!\n",
1990 pszLogName, pSH->PointerToRawData, pOptHdr->FileAlignment, iSH, sizeof(pSH->Name), pSH->Name));
1991 return VERR_BAD_EXE_FORMAT;
1992 }
1993
1994 /* ignore the relocations and linenumbers. */
1995
1996 uRvaPrev = pSH->VirtualAddress + pSH->Misc.VirtualSize;
1997 }
1998
1999 /** @todo r=bird: more sanity checks! */
2000 return VINF_SUCCESS;
2001}
2002
2003
2004/**
2005 * Reads image data by RVA using the section headers.
2006 *
2007 * @returns iprt status code.
2008 * @param pModPe The PE module instance.
2009 * @param pvBuf Where to store the bits.
2010 * @param cb Number of bytes to tread.
2011 * @param RVA Where to read from.
2012 */
2013static int rtldrPEReadRVA(PRTLDRMODPE pModPe, void *pvBuf, uint32_t cb, uint32_t RVA)
2014{
2015 const IMAGE_SECTION_HEADER *pSH = pModPe->paSections;
2016 PRTLDRREADER pReader = pModPe->Core.pReader;
2017 uint32_t cbRead;
2018 int rc;
2019
2020 /*
2021 * Is it the headers, i.e. prior to the first section.
2022 */
2023 if (RVA < pModPe->cbHeaders)
2024 {
2025 cbRead = RT_MIN(pModPe->cbHeaders - RVA, cb);
2026 rc = pReader->pfnRead(pReader, pvBuf, cbRead, RVA);
2027 if ( cbRead == cb
2028 || RT_FAILURE(rc))
2029 return rc;
2030 cb -= cbRead;
2031 RVA += cbRead;
2032 pvBuf = (uint8_t *)pvBuf + cbRead;
2033 }
2034
2035 /* In the zero space between headers and the first section? */
2036 if (RVA < pSH->VirtualAddress)
2037 {
2038 cbRead = RT_MIN(pSH->VirtualAddress - RVA, cb);
2039 memset(pvBuf, 0, cbRead);
2040 if (cbRead == cb)
2041 return VINF_SUCCESS;
2042 cb -= cbRead;
2043 RVA += cbRead;
2044 pvBuf = (uint8_t *)pvBuf + cbRead;
2045 }
2046
2047 /*
2048 * Iterate the sections.
2049 */
2050 for (unsigned cLeft = pModPe->cSections;
2051 cLeft > 0;
2052 cLeft--, pSH++)
2053 {
2054 uint32_t off = RVA - pSH->VirtualAddress;
2055 if (off < pSH->Misc.VirtualSize)
2056 {
2057 cbRead = RT_MIN(pSH->Misc.VirtualSize - off, cb);
2058 rc = pReader->pfnRead(pReader, pvBuf, cbRead, pSH->PointerToRawData + off);
2059 if ( cbRead == cb
2060 || RT_FAILURE(rc))
2061 return rc;
2062 cb -= cbRead;
2063 RVA += cbRead;
2064 pvBuf = (uint8_t *)pvBuf + cbRead;
2065 }
2066 uint32_t RVANext = cLeft ? pSH[1].VirtualAddress : pModPe->cbImage;
2067 if (RVA < RVANext)
2068 {
2069 cbRead = RT_MIN(RVANext - RVA, cb);
2070 memset(pvBuf, 0, cbRead);
2071 if (cbRead == cb)
2072 return VINF_SUCCESS;
2073 cb -= cbRead;
2074 RVA += cbRead;
2075 pvBuf = (uint8_t *)pvBuf + cbRead;
2076 }
2077 }
2078
2079 AssertFailed();
2080 return VERR_INTERNAL_ERROR;
2081}
2082
2083
2084/**
2085 * Validates the data of some selected data directories entries.
2086 *
2087 * This requires a valid section table and thus has to wait
2088 * till after we've read and validated it.
2089 *
2090 * @returns iprt status code.
2091 * @param pModPe The PE module instance.
2092 * @param pOptHdr Pointer to the optional header (valid).
2093 * @param fFlags Loader flags, RTLDR_O_XXX.
2094 */
2095static int rtldrPEValidateDirectories(PRTLDRMODPE pModPe, const IMAGE_OPTIONAL_HEADER64 *pOptHdr, uint32_t fFlags)
2096{
2097 const char *pszLogName = pModPe->Core.pReader->pfnLogName(pModPe->Core.pReader); NOREF(pszLogName);
2098 union /* combine stuff we're reading to help reduce stack usage. */
2099 {
2100 IMAGE_LOAD_CONFIG_DIRECTORY64 Cfg64;
2101 } u;
2102
2103 /*
2104 * The load config entry may include lock prefix tables and whatnot which we don't implement.
2105 * It does also include a lot of stuff which we can ignore, so we'll have to inspect the
2106 * actual data before we can make up our mind about it all.
2107 */
2108 IMAGE_DATA_DIRECTORY Dir = pOptHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG];
2109 if (Dir.Size)
2110 {
2111 const size_t cbExpect = pOptHdr->Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC
2112 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32)
2113 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64);
2114 if ( Dir.Size != cbExpect
2115 && ( cbExpect == sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32)
2116 && Dir.Size != (uint32_t)RT_OFFSETOF(IMAGE_LOAD_CONFIG_DIRECTORY32, SEHandlerTable))
2117 )
2118 {
2119 Log(("rtldrPEOpen: %s: load cfg dir: unexpected dir size of %d bytes, expected %d.\n",
2120 pszLogName, Dir.Size, cbExpect));
2121 return VERR_LDRPE_LOAD_CONFIG_SIZE;
2122 }
2123
2124 /*
2125 * Read and convert to 64-bit.
2126 */
2127 memset(&u.Cfg64, 0, sizeof(u.Cfg64));
2128 int rc = rtldrPEReadRVA(pModPe, &u.Cfg64, Dir.Size, Dir.VirtualAddress);
2129 if (RT_FAILURE(rc))
2130 return rc;
2131 rtldrPEConvert32BitLoadConfigTo64Bit(&u.Cfg64);
2132
2133 if (u.Cfg64.Size != cbExpect)
2134 {
2135 Log(("rtldrPEOpen: %s: load cfg dir: unexpected header size of %d bytes, expected %d.\n",
2136 pszLogName, u.Cfg64.Size, cbExpect));
2137 return VERR_LDRPE_LOAD_CONFIG_SIZE;
2138 }
2139 if (u.Cfg64.LockPrefixTable)
2140 {
2141 Log(("rtldrPEOpen: %s: load cfg dir: lock prefix table at %RX64. We don't support lock prefix tables!\n",
2142 pszLogName, u.Cfg64.LockPrefixTable));
2143 return VERR_LDRPE_LOCK_PREFIX_TABLE;
2144 }
2145#if 0/* this seems to be safe to ignore. */
2146 if ( u.Cfg64.SEHandlerTable
2147 || u.Cfg64.SEHandlerCount)
2148 {
2149 Log(("rtldrPEOpen: %s: load cfg dir: SEHandlerTable=%RX64 and SEHandlerCount=%RX64 are unsupported!\n",
2150 pszLogName, u.Cfg64.SEHandlerTable, u.Cfg64.SEHandlerCount));
2151 return VERR_BAD_EXE_FORMAT;
2152 }
2153#endif
2154 if (u.Cfg64.EditList)
2155 {
2156 Log(("rtldrPEOpen: %s: load cfg dir: EditList=%RX64 is unsupported!\n",
2157 pszLogName, u.Cfg64.EditList));
2158 return VERR_BAD_EXE_FORMAT;
2159 }
2160 }
2161
2162 /*
2163 * If the image is signed and we're not doing this for debug purposes,
2164 * take a look at the signature.
2165 */
2166 Dir = pOptHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
2167 if (Dir.Size && !(fFlags & RTLDR_O_FOR_DEBUG))
2168 {
2169 PWIN_CERTIFICATE pFirst = (PWIN_CERTIFICATE)RTMemTmpAlloc(Dir.Size);
2170 if (!pFirst)
2171 return VERR_NO_TMP_MEMORY;
2172 int rc = pModPe->Core.pReader->pfnRead(pModPe->Core.pReader, pFirst, Dir.Size, Dir.VirtualAddress);
2173 if (RT_SUCCESS(rc))
2174 {
2175 uint32_t off = 0;
2176 PWIN_CERTIFICATE pCur = pFirst;
2177 do
2178 {
2179 /* validate the members. */
2180 uint32_t const cbCur = RT_ALIGN_32(pCur->dwLength, 8);
2181 if ( cbCur < sizeof(WIN_CERTIFICATE)
2182 || cbCur + off > RT_ALIGN_32(Dir.Size, 8))
2183 {
2184 Log(("rtldrPEOpen: %s: cert at %#x/%#x: dwLength=%#x\n", pszLogName, off, Dir.Size, pCur->dwLength));
2185 rc = VERR_LDRPE_CERT_MALFORMED;
2186 break;
2187 }
2188 if ( pCur->wRevision != WIN_CERT_REVISION_2_0
2189 && pCur->wRevision != WIN_CERT_REVISION_1_0)
2190 {
2191 Log(("rtldrPEOpen: %s: cert at %#x/%#x: wRevision=%#x\n", pszLogName, off, Dir.Size, pCur->wRevision));
2192 rc = pCur->wRevision >= WIN_CERT_REVISION_1_0 ? VERR_LDRPE_CERT_UNSUPPORTED : VERR_LDRPE_CERT_MALFORMED;
2193 break;
2194 }
2195 if ( pCur->wCertificateType != WIN_CERT_TYPE_PKCS_SIGNED_DATA
2196 && pCur->wCertificateType != WIN_CERT_TYPE_X509
2197 /*&& pCur->wCertificateType != WIN_CERT_TYPE_RESERVED_1*/
2198 /*&& pCur->wCertificateType != WIN_CERT_TYPE_TS_STACK_SIGNED*/
2199 && pCur->wCertificateType != WIN_CERT_TYPE_EFI_PKCS115
2200 && pCur->wCertificateType != WIN_CERT_TYPE_EFI_GUID
2201 )
2202 {
2203 Log(("rtldrPEOpen: %s: cert at %#x/%#x: wRevision=%#x\n", pszLogName, off, Dir.Size, pCur->wRevision));
2204 rc = pCur->wCertificateType ? VERR_LDRPE_CERT_UNSUPPORTED : VERR_LDRPE_CERT_MALFORMED;
2205 break;
2206 }
2207
2208 /** @todo Rainy Day: Implement further verification using openssl. */
2209
2210 /* next */
2211 off += cbCur;
2212 pCur = (PWIN_CERTIFICATE)((uint8_t *)pCur + cbCur);
2213 } while (off < Dir.Size);
2214 }
2215 RTMemTmpFree(pFirst);
2216 if (RT_FAILURE(rc))
2217 return rc;
2218 }
2219
2220
2221 return VINF_SUCCESS;
2222}
2223
2224
2225/**
2226 * Open a PE image.
2227 *
2228 * @returns iprt status code.
2229 * @param pReader The loader reader instance which will provide the raw image bits.
2230 * @param fFlags Loader flags, RTLDR_O_XXX.
2231 * @param enmArch Architecture specifier.
2232 * @param offNtHdrs The offset of the NT headers (where you find "PE\0\0").
2233 * @param phLdrMod Where to store the handle.
2234 */
2235int rtldrPEOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, RTFOFF offNtHdrs, PRTLDRMOD phLdrMod)
2236{
2237 /*
2238 * Read and validate the file header.
2239 */
2240 IMAGE_FILE_HEADER FileHdr;
2241 int rc = pReader->pfnRead(pReader, &FileHdr, sizeof(FileHdr), offNtHdrs + 4);
2242 if (RT_FAILURE(rc))
2243 return rc;
2244 RTLDRARCH enmArchImage;
2245 const char *pszLogName = pReader->pfnLogName(pReader);
2246 rc = rtldrPEValidateFileHeader(&FileHdr, fFlags, pszLogName, &enmArchImage);
2247 if (RT_FAILURE(rc))
2248 return rc;
2249
2250 /*
2251 * Match the CPU architecture.
2252 */
2253 if ( enmArch != RTLDRARCH_WHATEVER
2254 && enmArch != enmArchImage)
2255 return VERR_LDR_ARCH_MISMATCH;
2256
2257 /*
2258 * Read and validate the "optional" header. Convert 32->64 if necessary.
2259 */
2260 IMAGE_OPTIONAL_HEADER64 OptHdr;
2261 rc = pReader->pfnRead(pReader, &OptHdr, FileHdr.SizeOfOptionalHeader, offNtHdrs + 4 + sizeof(IMAGE_FILE_HEADER));
2262 if (RT_FAILURE(rc))
2263 return rc;
2264 if (FileHdr.SizeOfOptionalHeader != sizeof(OptHdr))
2265 rtldrPEConvert32BitOptionalHeaderTo64Bit(&OptHdr);
2266 rc = rtldrPEValidateOptionalHeader(&OptHdr, pszLogName, offNtHdrs, &FileHdr, pReader->pfnSize(pReader), fFlags);
2267 if (RT_FAILURE(rc))
2268 return rc;
2269
2270 /*
2271 * Read and validate section headers.
2272 */
2273 const size_t cbSections = sizeof(IMAGE_SECTION_HEADER) * FileHdr.NumberOfSections;
2274 PIMAGE_SECTION_HEADER paSections = (PIMAGE_SECTION_HEADER)RTMemAlloc(cbSections);
2275 if (!paSections)
2276 return VERR_NO_MEMORY;
2277 rc = pReader->pfnRead(pReader, paSections, cbSections,
2278 offNtHdrs + 4 + sizeof(IMAGE_FILE_HEADER) + FileHdr.SizeOfOptionalHeader);
2279 if (RT_SUCCESS(rc))
2280 {
2281 rc = rtldrPEValidateSectionHeaders(paSections, FileHdr.NumberOfSections, pszLogName,
2282 &OptHdr, pReader->pfnSize(pReader), fFlags);
2283 if (RT_SUCCESS(rc))
2284 {
2285 /*
2286 * Allocate and initialize the PE module structure.
2287 */
2288 PRTLDRMODPE pModPe = (PRTLDRMODPE)RTMemAllocZ(sizeof(*pModPe));
2289 if (pModPe)
2290 {
2291 pModPe->Core.u32Magic = RTLDRMOD_MAGIC;
2292 pModPe->Core.eState = LDR_STATE_OPENED;
2293 if (FileHdr.SizeOfOptionalHeader == sizeof(OptHdr))
2294 pModPe->Core.pOps = &s_rtldrPE64Ops.Core;
2295 else
2296 pModPe->Core.pOps = &s_rtldrPE32Ops.Core;
2297 pModPe->Core.pReader = pReader;
2298 pModPe->Core.enmFormat= RTLDRFMT_PE;
2299 pModPe->Core.enmType = FileHdr.Characteristics & IMAGE_FILE_DLL
2300 ? FileHdr.Characteristics & IMAGE_FILE_RELOCS_STRIPPED
2301 ? RTLDRTYPE_EXECUTABLE_FIXED
2302 : RTLDRTYPE_EXECUTABLE_RELOCATABLE
2303 : FileHdr.Characteristics & IMAGE_FILE_RELOCS_STRIPPED
2304 ? RTLDRTYPE_SHARED_LIBRARY_FIXED
2305 : RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE;
2306 pModPe->Core.enmEndian= RTLDRENDIAN_LITTLE;
2307 pModPe->Core.enmArch = FileHdr.Machine == IMAGE_FILE_MACHINE_I386
2308 ? RTLDRARCH_X86_32
2309 : FileHdr.Machine == IMAGE_FILE_MACHINE_AMD64
2310 ? RTLDRARCH_AMD64
2311 : RTLDRARCH_WHATEVER;
2312 pModPe->pvBits = NULL;
2313 pModPe->offNtHdrs = offNtHdrs;
2314 pModPe->offEndOfHdrs = offNtHdrs + 4 + sizeof(IMAGE_FILE_HEADER) + FileHdr.SizeOfOptionalHeader + cbSections;
2315 pModPe->u16Machine = FileHdr.Machine;
2316 pModPe->fFile = FileHdr.Characteristics;
2317 pModPe->cSections = FileHdr.NumberOfSections;
2318 pModPe->paSections = paSections;
2319 pModPe->uEntryPointRVA= OptHdr.AddressOfEntryPoint;
2320 pModPe->uImageBase = (RTUINTPTR)OptHdr.ImageBase;
2321 pModPe->cbImage = OptHdr.SizeOfImage;
2322 pModPe->cbHeaders = OptHdr.SizeOfHeaders;
2323 pModPe->uTimestamp = FileHdr.TimeDateStamp;
2324 pModPe->ImportDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
2325 pModPe->RelocDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
2326 pModPe->ExportDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
2327 pModPe->DebugDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
2328
2329 /*
2330 * Perform validation of some selected data directories which requires
2331 * inspection of the actual data.
2332 */
2333 rc = rtldrPEValidateDirectories(pModPe, &OptHdr, fFlags);
2334 if (RT_SUCCESS(rc))
2335 {
2336 *phLdrMod = &pModPe->Core;
2337 return VINF_SUCCESS;
2338 }
2339 RTMemFree(pModPe);
2340 }
2341 else
2342 rc = VERR_NO_MEMORY;
2343 }
2344 }
2345 RTMemFree(paSections);
2346 return rc;
2347}
2348
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette