VirtualBox

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

Last change on this file since 48097 was 46273, checked in by vboxsync, 12 years ago

ldr.h,ldrPE.cpp: Report COFF debug type. Correctly report cbImage for CodeView.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 91.3 KB
Line 
1/* $Id: ldrPE.cpp 46273 2013-05-26 22:38:33Z 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.cbImage = pModPe->cbImage;
1189 DbgInfo.u.Cv.uMajorVer = paDbgDir[i].MajorVersion;
1190 DbgInfo.u.Cv.uMinorVer = paDbgDir[i].MinorVersion;
1191 DbgInfo.u.Cv.uTimestamp = paDbgDir[i].TimeDateStamp;
1192 if ( paDbgDir[i].SizeOfData < sizeof(szPath)
1193 && paDbgDir[i].SizeOfData > 16
1194 && ( DbgInfo.LinkAddress != NIL_RTLDRADDR
1195 || DbgInfo.offFile > 0)
1196 )
1197 {
1198 rc = rtldrPEReadPart(pModPe, pvBits, DbgInfo.offFile, DbgInfo.LinkAddress, paDbgDir[i].SizeOfData, &pvPart);
1199 if (RT_SUCCESS(rc))
1200 {
1201 PCCVPDB20INFO pCv20 = (PCCVPDB20INFO)pvPart;
1202 if ( pCv20->u32Magic == CVPDB20INFO_MAGIC
1203 && pCv20->offDbgInfo == 0
1204 && paDbgDir[i].SizeOfData > RT_UOFFSETOF(CVPDB20INFO, szPdbFilename) )
1205 {
1206 DbgInfo.enmType = RTLDRDBGINFOTYPE_CODEVIEW_PDB20;
1207 DbgInfo.u.Pdb20.cbImage = pModPe->cbImage;
1208 DbgInfo.u.Pdb20.uTimestamp = pCv20->uTimestamp;
1209 DbgInfo.u.Pdb20.uAge = pCv20->uAge;
1210 DbgInfo.pszExtFile = (const char *)&pCv20->szPdbFilename[0];
1211 }
1212 else if ( pCv20->u32Magic == CVPDB70INFO_MAGIC
1213 && paDbgDir[i].SizeOfData > RT_UOFFSETOF(CVPDB70INFO, szPdbFilename) )
1214 {
1215 PCCVPDB70INFO pCv70 = (PCCVPDB70INFO)pCv20;
1216 DbgInfo.enmType = RTLDRDBGINFOTYPE_CODEVIEW_PDB70;
1217 DbgInfo.u.Pdb70.cbImage = pModPe->cbImage;
1218 DbgInfo.u.Pdb70.Uuid = pCv70->PdbUuid;
1219 DbgInfo.u.Pdb70.uAge = pCv70->uAge;
1220 DbgInfo.pszExtFile = (const char *)&pCv70->szPdbFilename[0];
1221 }
1222 }
1223 else
1224 rcRet = rc;
1225 }
1226 break;
1227
1228 case IMAGE_DEBUG_TYPE_MISC:
1229 DbgInfo.enmType = RTLDRDBGINFOTYPE_UNKNOWN;
1230 if ( paDbgDir[i].SizeOfData < sizeof(szPath)
1231 && paDbgDir[i].SizeOfData > RT_UOFFSETOF(IMAGE_DEBUG_MISC, Data))
1232 {
1233 DbgInfo.enmType = RTLDRDBGINFOTYPE_CODEVIEW_DBG;
1234 DbgInfo.u.Dbg.cbImage = pModPe->cbImage;
1235 if (DbgInfo.LinkAddress != NIL_RTLDRADDR)
1236 DbgInfo.u.Dbg.uTimestamp = paDbgDir[i].TimeDateStamp;
1237 else
1238 DbgInfo.u.Dbg.uTimestamp = pModPe->uTimestamp; /* NT4 SP1 ntfs.sys hack. Generic? */
1239
1240 rc = rtldrPEReadPart(pModPe, pvBits, DbgInfo.offFile, DbgInfo.LinkAddress, paDbgDir[i].SizeOfData, &pvPart);
1241 if (RT_SUCCESS(rc))
1242 {
1243 PCIMAGE_DEBUG_MISC pMisc = (PCIMAGE_DEBUG_MISC)pvPart;
1244 if ( pMisc->DataType == IMAGE_DEBUG_MISC_EXENAME
1245 && pMisc->Length == paDbgDir[i].SizeOfData)
1246 {
1247 if (!pMisc->Unicode)
1248 DbgInfo.pszExtFile = (const char *)&pMisc->Data[0];
1249 else
1250 {
1251 char *pszPath = szPath;
1252 rc = RTUtf16ToUtf8Ex((PCRTUTF16)&pMisc->Data[0],
1253 (pMisc->Length - RT_OFFSETOF(IMAGE_DEBUG_MISC, Data)) / sizeof(RTUTF16),
1254 &pszPath, sizeof(szPath), NULL);
1255 if (RT_SUCCESS(rc))
1256 DbgInfo.pszExtFile = szPath;
1257 else
1258 rcRet = rc; /* continue without a filename. */
1259 }
1260 }
1261 }
1262 else
1263 rcRet = rc; /* continue without a filename. */
1264 }
1265 break;
1266
1267 case IMAGE_DEBUG_TYPE_COFF:
1268 DbgInfo.enmType = RTLDRDBGINFOTYPE_COFF;
1269 DbgInfo.u.Coff.cbImage = pModPe->cbImage;
1270 DbgInfo.u.Coff.uMajorVer = paDbgDir[i].MajorVersion;
1271 DbgInfo.u.Coff.uMinorVer = paDbgDir[i].MinorVersion;
1272 DbgInfo.u.Coff.uTimestamp = paDbgDir[i].TimeDateStamp;
1273 break;
1274
1275 default:
1276 DbgInfo.enmType = RTLDRDBGINFOTYPE_UNKNOWN;
1277 break;
1278 }
1279
1280 /* Fix (hack) the file name encoding. We don't have Windows-1252 handy,
1281 so we'll be using Latin-1 as a reasonable approximation.
1282 (I don't think we know exactly which encoding this is anyway, as
1283 it's probably the current ANSI/Windows code page for the process
1284 generating the image anyways.) */
1285 if (DbgInfo.pszExtFile && DbgInfo.pszExtFile != szPath)
1286 {
1287 char *pszPath = szPath;
1288 rc = RTLatin1ToUtf8Ex(DbgInfo.pszExtFile,
1289 paDbgDir[i].SizeOfData - ((uintptr_t)DbgInfo.pszExtFile - (uintptr_t)pvBits),
1290 &pszPath, sizeof(szPath), NULL);
1291 if (RT_FAILURE(rc))
1292 {
1293 rcRet = rc;
1294 DbgInfo.pszExtFile = NULL;
1295 }
1296 }
1297 if (DbgInfo.pszExtFile)
1298 RTPathChangeToUnixSlashes(szPath, true /*fForce*/);
1299
1300 rc = pfnCallback(pMod, &DbgInfo, pvUser);
1301 rtldrPEFreePart(pModPe, pvBits, pvPart);
1302 if (rc != VINF_SUCCESS)
1303 {
1304 rcRet = rc;
1305 break;
1306 }
1307 }
1308
1309 rtldrPEFreePart(pModPe, pvBits, paDbgDir);
1310 return rcRet;
1311}
1312
1313
1314/** @copydoc RTLDROPS::pfnEnumSegments. */
1315static DECLCALLBACK(int) rtldrPE_EnumSegments(PRTLDRMODINTERNAL pMod, PFNRTLDRENUMSEGS pfnCallback, void *pvUser)
1316{
1317 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
1318 RTLDRSEG SegInfo;
1319
1320 /*
1321 * The first section is a fake one covering the headers.
1322 */
1323 SegInfo.pszName = "NtHdrs";
1324 SegInfo.cchName = 6;
1325 SegInfo.SelFlat = 0;
1326 SegInfo.Sel16bit = 0;
1327 SegInfo.fFlags = 0;
1328 SegInfo.fProt = RTMEM_PROT_READ;
1329 SegInfo.Alignment = 1;
1330 SegInfo.LinkAddress = pModPe->uImageBase;
1331 SegInfo.RVA = 0;
1332 SegInfo.offFile = 0;
1333 SegInfo.cb = pModPe->cbHeaders;
1334 SegInfo.cbFile = pModPe->cbHeaders;
1335 SegInfo.cbMapped = pModPe->cbHeaders;
1336 if ((pModPe->paSections[0].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
1337 SegInfo.cbMapped = pModPe->paSections[0].VirtualAddress;
1338 int rc = pfnCallback(pMod, &SegInfo, pvUser);
1339
1340 /*
1341 * Then all the normal sections.
1342 */
1343 PCIMAGE_SECTION_HEADER pSh = pModPe->paSections;
1344 for (uint32_t i = 0; i < pModPe->cSections && rc == VINF_SUCCESS; i++, pSh++)
1345 {
1346 char szName[32];
1347 SegInfo.pszName = (const char *)&pSh->Name[0];
1348 SegInfo.cchName = (uint32_t)RTStrNLen(SegInfo.pszName, sizeof(pSh->Name));
1349 if (SegInfo.cchName >= sizeof(pSh->Name))
1350 {
1351 memcpy(szName, &pSh->Name[0], sizeof(pSh->Name));
1352 szName[sizeof(sizeof(pSh->Name))] = '\0';
1353 SegInfo.pszName = szName;
1354 }
1355 else if (SegInfo.cchName == 0)
1356 {
1357 SegInfo.pszName = szName;
1358 SegInfo.cchName = (uint32_t)RTStrPrintf(szName, sizeof(szName), "UnamedSect%02u", i);
1359 }
1360 SegInfo.SelFlat = 0;
1361 SegInfo.Sel16bit = 0;
1362 SegInfo.fFlags = 0;
1363 SegInfo.fProt = RTMEM_PROT_NONE;
1364 if (pSh->Characteristics & IMAGE_SCN_MEM_READ)
1365 SegInfo.fProt |= RTMEM_PROT_READ;
1366 if (pSh->Characteristics & IMAGE_SCN_MEM_WRITE)
1367 SegInfo.fProt |= RTMEM_PROT_WRITE;
1368 if (pSh->Characteristics & IMAGE_SCN_MEM_EXECUTE)
1369 SegInfo.fProt |= RTMEM_PROT_EXEC;
1370 SegInfo.Alignment = (pSh->Characteristics & IMAGE_SCN_ALIGN_MASK) >> IMAGE_SCN_ALIGN_SHIFT;
1371 if (SegInfo.Alignment > 0)
1372 SegInfo.Alignment = RT_BIT_64(SegInfo.Alignment - 1);
1373 if (pSh->Characteristics & IMAGE_SCN_TYPE_NOLOAD)
1374 {
1375 SegInfo.LinkAddress = NIL_RTLDRADDR;
1376 SegInfo.RVA = NIL_RTLDRADDR;
1377 SegInfo.cbMapped = pSh->Misc.VirtualSize;
1378 }
1379 else
1380 {
1381 SegInfo.LinkAddress = pSh->VirtualAddress + pModPe->uImageBase ;
1382 SegInfo.RVA = pSh->VirtualAddress;
1383 SegInfo.cbMapped = RT_ALIGN(SegInfo.cb, SegInfo.Alignment);
1384 if (i + 1 < pModPe->cSections && !(pSh[1].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
1385 SegInfo.cbMapped = pSh[1].VirtualAddress - pSh->VirtualAddress;
1386 }
1387 SegInfo.cb = pSh->Misc.VirtualSize;
1388 if (pSh->PointerToRawData == 0 || pSh->SizeOfRawData == 0)
1389 {
1390 SegInfo.offFile = -1;
1391 SegInfo.cbFile = 0;
1392 }
1393 else
1394 {
1395 SegInfo.offFile = pSh->PointerToRawData;
1396 SegInfo.cbFile = pSh->SizeOfRawData;
1397 }
1398
1399 rc = pfnCallback(pMod, &SegInfo, pvUser);
1400 }
1401
1402 return rc;
1403}
1404
1405
1406/** @copydoc RTLDROPS::pfnLinkAddressToSegOffset. */
1407static DECLCALLBACK(int) rtldrPE_LinkAddressToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress,
1408 uint32_t *piSeg, PRTLDRADDR poffSeg)
1409{
1410 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
1411
1412 LinkAddress -= pModPe->uImageBase;
1413
1414 /* Special header segment. */
1415 if (LinkAddress < pModPe->paSections[0].VirtualAddress)
1416 {
1417 *piSeg = 0;
1418 *poffSeg = LinkAddress;
1419 return VINF_SUCCESS;
1420 }
1421
1422 /*
1423 * Search the normal sections. (Could do this in binary fashion, they're
1424 * sorted, but too much bother right now.)
1425 */
1426 if (LinkAddress > pModPe->cbImage)
1427 return VERR_LDR_INVALID_LINK_ADDRESS;
1428 uint32_t i = pModPe->cSections;
1429 PCIMAGE_SECTION_HEADER paShs = pModPe->paSections;
1430 while (i-- > 0)
1431 if (!(paShs[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
1432 {
1433 uint32_t uAddr = paShs[i].VirtualAddress;
1434 if (LinkAddress >= uAddr)
1435 {
1436 *poffSeg = LinkAddress - uAddr;
1437 *piSeg = i + 1;
1438 return VINF_SUCCESS;
1439 }
1440 }
1441
1442 return VERR_LDR_INVALID_LINK_ADDRESS;
1443}
1444
1445
1446/** @copydoc RTLDROPS::pfnLinkAddressToRva. */
1447static DECLCALLBACK(int) rtldrPE_LinkAddressToRva(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress, PRTLDRADDR pRva)
1448{
1449 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
1450
1451 LinkAddress -= pModPe->uImageBase;
1452 if (LinkAddress > pModPe->cbImage)
1453 return VERR_LDR_INVALID_LINK_ADDRESS;
1454 *pRva = LinkAddress;
1455
1456 return VINF_SUCCESS;
1457}
1458
1459
1460/** @copydoc RTLDROPS::pfnSegOffsetToRva. */
1461static DECLCALLBACK(int) rtldrPE_SegOffsetToRva(PRTLDRMODINTERNAL pMod, uint32_t iSeg, RTLDRADDR offSeg,
1462 PRTLDRADDR pRva)
1463{
1464 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
1465
1466 if (iSeg > pModPe->cSections)
1467 return VERR_LDR_INVALID_SEG_OFFSET;
1468
1469 /** @todo should validate offSeg here... too lazy right now. */
1470 if (iSeg == 0)
1471 *pRva = offSeg;
1472 else if (pModPe->paSections[iSeg].Characteristics & IMAGE_SCN_TYPE_NOLOAD)
1473 return VERR_LDR_INVALID_SEG_OFFSET;
1474 else
1475 *pRva = offSeg + pModPe->paSections[iSeg].VirtualAddress;
1476 return VINF_SUCCESS;
1477}
1478
1479
1480/** @copydoc RTLDROPS::pfnRvaToSegOffset. */
1481static DECLCALLBACK(int) rtldrPE_RvaToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR Rva,
1482 uint32_t *piSeg, PRTLDRADDR poffSeg)
1483{
1484 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
1485 int rc = rtldrPE_LinkAddressToSegOffset(pMod, Rva + pModPe->uImageBase, piSeg, poffSeg);
1486 if (RT_FAILURE(rc))
1487 rc = VERR_LDR_INVALID_RVA;
1488 return rc;
1489}
1490
1491
1492/** @copydoc RTLDROPS::pfnDone */
1493static DECLCALLBACK(int) rtldrPEDone(PRTLDRMODINTERNAL pMod)
1494{
1495 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
1496 if (pModPe->pvBits)
1497 {
1498 RTMemFree(pModPe->pvBits);
1499 pModPe->pvBits = NULL;
1500 }
1501 return VINF_SUCCESS;
1502}
1503
1504/** @copydoc RTLDROPS::pfnClose */
1505static DECLCALLBACK(int) rtldrPEClose(PRTLDRMODINTERNAL pMod)
1506{
1507 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
1508 if (pModPe->paSections)
1509 {
1510 RTMemFree(pModPe->paSections);
1511 pModPe->paSections = NULL;
1512 }
1513 if (pModPe->pvBits)
1514 {
1515 RTMemFree(pModPe->pvBits);
1516 pModPe->pvBits = NULL;
1517 }
1518 if (pModPe->Core.pReader)
1519 {
1520 int rc = pModPe->Core.pReader->pfnDestroy(pModPe->Core.pReader);
1521 AssertRC(rc);
1522 pModPe->Core.pReader = NULL;
1523 }
1524 return VINF_SUCCESS;
1525}
1526
1527
1528/**
1529 * Operations for a 32-bit PE module.
1530 */
1531static const RTLDROPSPE s_rtldrPE32Ops =
1532{
1533 {
1534 "pe32",
1535 rtldrPEClose,
1536 NULL,
1537 rtldrPEDone,
1538 rtldrPEEnumSymbols,
1539 /* ext */
1540 rtldrPEGetImageSize,
1541 rtldrPEGetBits,
1542 rtldrPERelocate,
1543 rtldrPEGetSymbolEx,
1544 rtldrPE_EnumDbgInfo,
1545 rtldrPE_EnumSegments,
1546 rtldrPE_LinkAddressToSegOffset,
1547 rtldrPE_LinkAddressToRva,
1548 rtldrPE_SegOffsetToRva,
1549 rtldrPE_RvaToSegOffset,
1550 NULL,
1551 42
1552 },
1553 rtldrPEResolveImports32,
1554 42
1555};
1556
1557
1558/**
1559 * Operations for a 64-bit PE module.
1560 */
1561static const RTLDROPSPE s_rtldrPE64Ops =
1562{
1563 {
1564 "pe64",
1565 rtldrPEClose,
1566 NULL,
1567 rtldrPEDone,
1568 rtldrPEEnumSymbols,
1569 /* ext */
1570 rtldrPEGetImageSize,
1571 rtldrPEGetBits,
1572 rtldrPERelocate,
1573 rtldrPEGetSymbolEx,
1574 rtldrPE_EnumDbgInfo,
1575 rtldrPE_EnumSegments,
1576 rtldrPE_LinkAddressToSegOffset,
1577 rtldrPE_LinkAddressToRva,
1578 rtldrPE_SegOffsetToRva,
1579 rtldrPE_RvaToSegOffset,
1580 NULL,
1581 42
1582 },
1583 rtldrPEResolveImports64,
1584 42
1585};
1586
1587
1588/**
1589 * Converts the optional header from 32 bit to 64 bit.
1590 * This is a rather simple task, if you start from the right end.
1591 *
1592 * @param pOptHdr On input this is a PIMAGE_OPTIONAL_HEADER32.
1593 * On output this will be a PIMAGE_OPTIONAL_HEADER64.
1594 */
1595static void rtldrPEConvert32BitOptionalHeaderTo64Bit(PIMAGE_OPTIONAL_HEADER64 pOptHdr)
1596{
1597 /*
1598 * volatile everywhere! Trying to prevent the compiler being a smarta$$ and reorder stuff.
1599 */
1600 IMAGE_OPTIONAL_HEADER32 volatile *pOptHdr32 = (IMAGE_OPTIONAL_HEADER32 volatile *)pOptHdr;
1601 IMAGE_OPTIONAL_HEADER64 volatile *pOptHdr64 = pOptHdr;
1602
1603 /* from LoaderFlags and out the difference is 4 * 32-bits. */
1604 Assert(RT_OFFSETOF(IMAGE_OPTIONAL_HEADER32, LoaderFlags) + 16 == RT_OFFSETOF(IMAGE_OPTIONAL_HEADER64, LoaderFlags));
1605 Assert( RT_OFFSETOF(IMAGE_OPTIONAL_HEADER32, DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]) + 16
1606 == RT_OFFSETOF(IMAGE_OPTIONAL_HEADER64, DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]));
1607 uint32_t volatile *pu32Dst = (uint32_t *)&pOptHdr64->DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] - 1;
1608 const uint32_t volatile *pu32Src = (uint32_t *)&pOptHdr32->DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] - 1;
1609 const uint32_t volatile *pu32SrcLast = (uint32_t *)&pOptHdr32->LoaderFlags;
1610 while (pu32Src >= pu32SrcLast)
1611 *pu32Dst-- = *pu32Src--;
1612
1613 /* the previous 4 fields are 32/64 and needs special attention. */
1614 pOptHdr64->SizeOfHeapCommit = pOptHdr32->SizeOfHeapCommit;
1615 pOptHdr64->SizeOfHeapReserve = pOptHdr32->SizeOfHeapReserve;
1616 pOptHdr64->SizeOfStackCommit = pOptHdr32->SizeOfStackCommit;
1617 uint32_t u32SizeOfStackReserve = pOptHdr32->SizeOfStackReserve;
1618 pOptHdr64->SizeOfStackReserve = u32SizeOfStackReserve;
1619
1620 /* The rest matches except for BaseOfData which has been merged into ImageBase in the 64-bit version..
1621 * Thus, ImageBase needs some special treatment. It will probably work fine assigning one to the
1622 * other since this is all declared volatile, but taking now chances, we'll use a temp variable.
1623 */
1624 Assert(RT_OFFSETOF(IMAGE_OPTIONAL_HEADER32, SizeOfStackReserve) == RT_OFFSETOF(IMAGE_OPTIONAL_HEADER64, SizeOfStackReserve));
1625 Assert(RT_OFFSETOF(IMAGE_OPTIONAL_HEADER32, BaseOfData) == RT_OFFSETOF(IMAGE_OPTIONAL_HEADER64, ImageBase));
1626 Assert(RT_OFFSETOF(IMAGE_OPTIONAL_HEADER32, SectionAlignment) == RT_OFFSETOF(IMAGE_OPTIONAL_HEADER64, SectionAlignment));
1627 uint32_t u32ImageBase = pOptHdr32->ImageBase;
1628 pOptHdr64->ImageBase = u32ImageBase;
1629}
1630
1631
1632/**
1633 * Converts the load config directory from 32 bit to 64 bit.
1634 * This is a rather simple task, if you start from the right end.
1635 *
1636 * @param pLoadCfg On input this is a PIMAGE_LOAD_CONFIG_DIRECTORY32.
1637 * On output this will be a PIMAGE_LOAD_CONFIG_DIRECTORY64.
1638 */
1639static void rtldrPEConvert32BitLoadConfigTo64Bit(PIMAGE_LOAD_CONFIG_DIRECTORY64 pLoadCfg)
1640{
1641 /*
1642 * volatile everywhere! Trying to prevent the compiler being a smarta$$ and reorder stuff.
1643 */
1644 IMAGE_LOAD_CONFIG_DIRECTORY32 volatile *pLoadCfg32 = (IMAGE_LOAD_CONFIG_DIRECTORY32 volatile *)pLoadCfg;
1645 IMAGE_LOAD_CONFIG_DIRECTORY64 volatile *pLoadCfg64 = pLoadCfg;
1646
1647 pLoadCfg64->SEHandlerCount = pLoadCfg32->SEHandlerCount;
1648 pLoadCfg64->SEHandlerTable = pLoadCfg32->SEHandlerTable;
1649 pLoadCfg64->SecurityCookie = pLoadCfg32->SecurityCookie;
1650 pLoadCfg64->EditList = pLoadCfg32->EditList;
1651 pLoadCfg64->Reserved1 = pLoadCfg32->Reserved1;
1652 pLoadCfg64->CSDVersion = pLoadCfg32->CSDVersion;
1653 pLoadCfg64->ProcessHeapFlags = pLoadCfg32->ProcessHeapFlags; /* switched place with ProcessAffinityMask, but we're more than 16 byte off by now so it doesn't matter. */
1654 pLoadCfg64->ProcessAffinityMask = pLoadCfg32->ProcessAffinityMask;
1655 pLoadCfg64->VirtualMemoryThreshold = pLoadCfg32->VirtualMemoryThreshold;
1656 pLoadCfg64->MaximumAllocationSize = pLoadCfg32->MaximumAllocationSize;
1657 pLoadCfg64->LockPrefixTable = pLoadCfg32->LockPrefixTable;
1658 pLoadCfg64->DeCommitTotalFreeThreshold = pLoadCfg32->DeCommitTotalFreeThreshold;
1659 uint32_t u32DeCommitFreeBlockThreshold = pLoadCfg32->DeCommitFreeBlockThreshold;
1660 pLoadCfg64->DeCommitFreeBlockThreshold = u32DeCommitFreeBlockThreshold;
1661 /* the rest is equal. */
1662 Assert( RT_OFFSETOF(IMAGE_LOAD_CONFIG_DIRECTORY32, DeCommitFreeBlockThreshold)
1663 == RT_OFFSETOF(IMAGE_LOAD_CONFIG_DIRECTORY64, DeCommitFreeBlockThreshold));
1664}
1665
1666
1667/**
1668 * Validates the file header.
1669 *
1670 * @returns iprt status code.
1671 * @param pFileHdr Pointer to the file header that needs validating.
1672 * @param fFlags Valid RTLDR_O_XXX combination.
1673 * @param pszLogName The log name to prefix the errors with.
1674 * @param penmArch Where to store the CPU architecture.
1675 */
1676static int rtldrPEValidateFileHeader(PIMAGE_FILE_HEADER pFileHdr, uint32_t fFlags, const char *pszLogName, PRTLDRARCH penmArch)
1677{
1678 size_t cbOptionalHeader;
1679 switch (pFileHdr->Machine)
1680 {
1681 case IMAGE_FILE_MACHINE_I386:
1682 cbOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER32);
1683 *penmArch = RTLDRARCH_X86_32;
1684 break;
1685 case IMAGE_FILE_MACHINE_AMD64:
1686 cbOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER64);
1687 *penmArch = RTLDRARCH_AMD64;
1688 break;
1689
1690 default:
1691 Log(("rtldrPEOpen: %s: Unsupported Machine=%#x\n",
1692 pszLogName, pFileHdr->Machine));
1693 *penmArch = RTLDRARCH_INVALID;
1694 return VERR_BAD_EXE_FORMAT;
1695 }
1696 if (pFileHdr->SizeOfOptionalHeader != cbOptionalHeader)
1697 {
1698 Log(("rtldrPEOpen: %s: SizeOfOptionalHeader=%#x expected %#x\n",
1699 pszLogName, pFileHdr->SizeOfOptionalHeader, cbOptionalHeader));
1700 return VERR_BAD_EXE_FORMAT;
1701 }
1702 /* This restriction needs to be implemented elsewhere. */
1703 if ( (pFileHdr->Characteristics & IMAGE_FILE_RELOCS_STRIPPED)
1704 && !(fFlags & RTLDR_O_FOR_DEBUG))
1705 {
1706 Log(("rtldrPEOpen: %s: IMAGE_FILE_RELOCS_STRIPPED\n", pszLogName));
1707 return VERR_BAD_EXE_FORMAT;
1708 }
1709 if (pFileHdr->NumberOfSections > 42)
1710 {
1711 Log(("rtldrPEOpen: %s: NumberOfSections=%d - our limit is 42, please raise it if the binary makes sense.(!!!)\n",
1712 pszLogName, pFileHdr->NumberOfSections));
1713 return VERR_BAD_EXE_FORMAT;
1714 }
1715 if (pFileHdr->NumberOfSections < 1)
1716 {
1717 Log(("rtldrPEOpen: %s: NumberOfSections=%d - we can't have an image without sections (!!!)\n",
1718 pszLogName, pFileHdr->NumberOfSections));
1719 return VERR_BAD_EXE_FORMAT;
1720 }
1721 return VINF_SUCCESS;
1722}
1723
1724
1725/**
1726 * Validates the optional header (64/32-bit)
1727 *
1728 * @returns iprt status code.
1729 * @param pOptHdr Pointer to the optional header which needs validation.
1730 * @param pszLogName The log name to prefix the errors with.
1731 * @param offNtHdrs The offset of the NT headers from the start of the file.
1732 * @param pFileHdr Pointer to the file header (valid).
1733 * @param cbRawImage The raw image size.
1734 * @param fFlags Loader flags, RTLDR_O_XXX.
1735 */
1736static int rtldrPEValidateOptionalHeader(const IMAGE_OPTIONAL_HEADER64 *pOptHdr, const char *pszLogName, RTFOFF offNtHdrs,
1737 const IMAGE_FILE_HEADER *pFileHdr, RTFOFF cbRawImage, uint32_t fFlags)
1738{
1739 const uint16_t CorrectMagic = pFileHdr->SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32)
1740 ? IMAGE_NT_OPTIONAL_HDR32_MAGIC : IMAGE_NT_OPTIONAL_HDR64_MAGIC;
1741 if (pOptHdr->Magic != CorrectMagic)
1742 {
1743 Log(("rtldrPEOpen: %s: Magic=%#x - expected %#x!!!\n", pszLogName, pOptHdr->Magic, CorrectMagic));
1744 return VERR_BAD_EXE_FORMAT;
1745 }
1746 const uint32_t cbImage = pOptHdr->SizeOfImage;
1747 if (cbImage > _1G)
1748 {
1749 Log(("rtldrPEOpen: %s: SizeOfImage=%#x - Our limit is 1GB (%#x)!!!\n", pszLogName, cbImage, _1G));
1750 return VERR_BAD_EXE_FORMAT;
1751 }
1752 const uint32_t cbMinImageSize = pFileHdr->SizeOfOptionalHeader + sizeof(*pFileHdr) + 4 + (uint32_t)offNtHdrs;
1753 if (cbImage < cbMinImageSize)
1754 {
1755 Log(("rtldrPEOpen: %s: SizeOfImage=%#x to small, minimum %#x!!!\n", pszLogName, cbImage, cbMinImageSize));
1756 return VERR_BAD_EXE_FORMAT;
1757 }
1758 if (pOptHdr->AddressOfEntryPoint >= cbImage)
1759 {
1760 Log(("rtldrPEOpen: %s: AddressOfEntryPoint=%#x - beyond image size (%#x)!!!\n",
1761 pszLogName, pOptHdr->AddressOfEntryPoint, cbImage));
1762 return VERR_BAD_EXE_FORMAT;
1763 }
1764 if (pOptHdr->BaseOfCode >= cbImage)
1765 {
1766 Log(("rtldrPEOpen: %s: BaseOfCode=%#x - beyond image size (%#x)!!!\n",
1767 pszLogName, pOptHdr->BaseOfCode, cbImage));
1768 return VERR_BAD_EXE_FORMAT;
1769 }
1770#if 0/* only in 32-bit header */
1771 if (pOptHdr->BaseOfData >= cbImage)
1772 {
1773 Log(("rtldrPEOpen: %s: BaseOfData=%#x - beyond image size (%#x)!!!\n",
1774 pszLogName, pOptHdr->BaseOfData, cbImage));
1775 return VERR_BAD_EXE_FORMAT;
1776 }
1777#endif
1778 if (pOptHdr->SizeOfHeaders >= cbImage)
1779 {
1780 Log(("rtldrPEOpen: %s: SizeOfHeaders=%#x - beyond image size (%#x)!!!\n",
1781 pszLogName, pOptHdr->SizeOfHeaders, cbImage));
1782 return VERR_BAD_EXE_FORMAT;
1783 }
1784 /* don't know how to do the checksum, so ignore it. */
1785 if (pOptHdr->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN)
1786 {
1787 Log(("rtldrPEOpen: %s: Subsystem=%#x (unknown)!!!\n", pszLogName, pOptHdr->Subsystem));
1788 return VERR_BAD_EXE_FORMAT;
1789 }
1790 if (pOptHdr->SizeOfHeaders < cbMinImageSize + pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER))
1791 {
1792 Log(("rtldrPEOpen: %s: SizeOfHeaders=%#x - cbMinImageSize %#x + sections %#x = %#llx!!!\n",
1793 pszLogName, pOptHdr->SizeOfHeaders,
1794 cbImage, cbMinImageSize, pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER),
1795 cbMinImageSize + pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER)));
1796 return VERR_BAD_EXE_FORMAT;
1797 }
1798 if (pOptHdr->SizeOfStackReserve < pOptHdr->SizeOfStackCommit)
1799 {
1800 Log(("rtldrPEOpen: %s: SizeOfStackReserve %#x < SizeOfStackCommit %#x!!!\n",
1801 pszLogName, pOptHdr->SizeOfStackReserve, pOptHdr->SizeOfStackCommit));
1802 return VERR_BAD_EXE_FORMAT;
1803 }
1804 if (pOptHdr->SizeOfHeapReserve < pOptHdr->SizeOfHeapCommit)
1805 {
1806 Log(("rtldrPEOpen: %s: SizeOfStackReserve %#x < SizeOfStackCommit %#x!!!\n",
1807 pszLogName, pOptHdr->SizeOfStackReserve, pOptHdr->SizeOfStackCommit));
1808 return VERR_BAD_EXE_FORMAT;
1809 }
1810
1811 /* DataDirectory */
1812 if (pOptHdr->NumberOfRvaAndSizes != RT_ELEMENTS(pOptHdr->DataDirectory))
1813 {
1814 Log(("rtldrPEOpen: %s: NumberOfRvaAndSizes=%d!!!\n", pszLogName, pOptHdr->NumberOfRvaAndSizes));
1815 return VERR_BAD_EXE_FORMAT;
1816 }
1817 for (unsigned i = 0; i < RT_ELEMENTS(pOptHdr->DataDirectory); i++)
1818 {
1819 IMAGE_DATA_DIRECTORY const *pDir = &pOptHdr->DataDirectory[i];
1820 if (!pDir->Size)
1821 continue;
1822 size_t cb = cbImage;
1823 switch (i)
1824 {
1825 case IMAGE_DIRECTORY_ENTRY_EXPORT: // 0
1826 case IMAGE_DIRECTORY_ENTRY_IMPORT: // 1
1827 case IMAGE_DIRECTORY_ENTRY_RESOURCE: // 2
1828 case IMAGE_DIRECTORY_ENTRY_EXCEPTION: // 3
1829 case IMAGE_DIRECTORY_ENTRY_BASERELOC: // 5
1830 case IMAGE_DIRECTORY_ENTRY_DEBUG: // 6
1831 case IMAGE_DIRECTORY_ENTRY_COPYRIGHT: // 7
1832 case IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT: // 11
1833 case IMAGE_DIRECTORY_ENTRY_IAT: // 12 /* Import Address Table */
1834 break;
1835 case IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG: // 10 - need to check for lock prefixes.
1836 /* Delay inspection after section table is validated. */
1837 break;
1838
1839 case IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT: // 13
1840 Log(("rtldrPEOpen: %s: dir no. %d (DELAY_IMPORT) VirtualAddress=%#x Size=%#x is not supported!!!\n",
1841 pszLogName, i, pDir->VirtualAddress, pDir->Size));
1842 return VERR_LDRPE_DELAY_IMPORT;
1843
1844 case IMAGE_DIRECTORY_ENTRY_SECURITY: // 4
1845 /* The VirtualAddress is a PointerToRawData. */
1846 cb = (size_t)cbRawImage; Assert((RTFOFF)cb == cbRawImage);
1847 Log(("rtldrPEOpen: %s: dir no. %d (SECURITY) VirtualAddress=%#x Size=%#x is not supported!!!\n",
1848 pszLogName, i, pDir->VirtualAddress, pDir->Size));
1849 if (pDir->Size < sizeof(WIN_CERTIFICATE))
1850 {
1851 Log(("rtldrPEOpen: %s: Security directory is too small: %#x bytes\n", pszLogName, i, pDir->Size));
1852 return VERR_LDRPE_CERT_MALFORMED;
1853 }
1854 if (pDir->Size >= _1M)
1855 {
1856 Log(("rtldrPEOpen: %s: Security directory is too large: %#x bytes\n", pszLogName, i, pDir->Size));
1857 return VERR_LDRPE_CERT_MALFORMED;
1858 }
1859 if (pDir->VirtualAddress & 7)
1860 {
1861 Log(("rtldrPEOpen: %s: Security directory is misaligned: %#x\n", pszLogName, i, pDir->VirtualAddress));
1862 return VERR_LDRPE_CERT_MALFORMED;
1863 }
1864 /* When using the in-memory reader with a debugger, we may get
1865 into trouble here since we might not have access to the whole
1866 physical file. So skip the tests below. Makes VBoxGuest.sys
1867 load and check out just fine, for instance. */
1868 if (fFlags & RTLDR_O_FOR_DEBUG)
1869 continue;
1870 break;
1871
1872 case IMAGE_DIRECTORY_ENTRY_GLOBALPTR: // 8 /* (MIPS GP) */
1873 Log(("rtldrPEOpen: %s: dir no. %d (GLOBALPTR) VirtualAddress=%#x Size=%#x is not supported!!!\n",
1874 pszLogName, i, pDir->VirtualAddress, pDir->Size));
1875 return VERR_LDRPE_GLOBALPTR;
1876
1877 case IMAGE_DIRECTORY_ENTRY_TLS: // 9
1878 Log(("rtldrPEOpen: %s: dir no. %d (TLS) VirtualAddress=%#x Size=%#x is not supported!!!\n",
1879 pszLogName, i, pDir->VirtualAddress, pDir->Size));
1880 return VERR_LDRPE_TLS;
1881
1882 case IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR:// 14
1883 Log(("rtldrPEOpen: %s: dir no. %d (COM_DESCRIPTOR) VirtualAddress=%#x Size=%#x is not supported!!!\n",
1884 pszLogName, i, pDir->VirtualAddress, pDir->Size));
1885 return VERR_LDRPE_COM_DESCRIPTOR;
1886
1887 default:
1888 Log(("rtldrPEOpen: %s: dir no. %d VirtualAddress=%#x Size=%#x is not supported!!!\n",
1889 pszLogName, i, pDir->VirtualAddress, pDir->Size));
1890 return VERR_BAD_EXE_FORMAT;
1891 }
1892 if (pDir->VirtualAddress >= cb)
1893 {
1894 Log(("rtldrPEOpen: %s: dir no. %d VirtualAddress=%#x is invalid (limit %#x)!!!\n",
1895 pszLogName, i, pDir->VirtualAddress, cb));
1896 return VERR_BAD_EXE_FORMAT;
1897 }
1898 if (pDir->Size > cb - pDir->VirtualAddress)
1899 {
1900 Log(("rtldrPEOpen: %s: dir no. %d Size=%#x is invalid (rva=%#x, limit=%#x)!!!\n",
1901 pszLogName, i, pDir->Size, pDir->VirtualAddress, cb));
1902 return VERR_BAD_EXE_FORMAT;
1903 }
1904 }
1905 return VINF_SUCCESS;
1906}
1907
1908
1909/**
1910 * Validates the section headers.
1911 *
1912 * @returns iprt status code.
1913 * @param paSections Pointer to the array of sections that is to be validated.
1914 * @param cSections Number of sections in that array.
1915 * @param pszLogName The log name to prefix the errors with.
1916 * @param pOptHdr Pointer to the optional header (valid).
1917 * @param cbRawImage The raw image size.
1918 * @param fFlags Loader flags, RTLDR_O_XXX.
1919 */
1920static int rtldrPEValidateSectionHeaders(const IMAGE_SECTION_HEADER *paSections, unsigned cSections, const char *pszLogName,
1921 const IMAGE_OPTIONAL_HEADER64 *pOptHdr, RTFOFF cbRawImage, uint32_t fFlags)
1922{
1923 const uint32_t cbImage = pOptHdr->SizeOfImage;
1924 const IMAGE_SECTION_HEADER *pSH = &paSections[0];
1925 uint32_t uRvaPrev = pOptHdr->SizeOfHeaders;
1926 Log3(("RTLdrPE: Section Headers:\n"));
1927 for (unsigned cSHdrsLeft = cSections; cSHdrsLeft > 0; cSHdrsLeft--, pSH++)
1928 {
1929 const unsigned iSH = pSH - &paSections[0]; NOREF(iSH);
1930 Log3(("RTLdrPE: #%d '%-8.8s' Characteristics: %08RX32\n"
1931 "RTLdrPE: VirtAddr: %08RX32 VirtSize: %08RX32\n"
1932 "RTLdrPE: FileOff: %08RX32 FileSize: %08RX32\n"
1933 "RTLdrPE: RelocOff: %08RX32 #Relocs: %08RX32\n"
1934 "RTLdrPE: LineOff: %08RX32 #Lines: %08RX32\n",
1935 iSH, pSH->Name, pSH->Characteristics,
1936 pSH->VirtualAddress, pSH->Misc.VirtualSize,
1937 pSH->PointerToRawData, pSH->SizeOfRawData,
1938 pSH->PointerToRelocations, pSH->NumberOfRelocations,
1939 pSH->PointerToLinenumbers, pSH->NumberOfLinenumbers));
1940
1941 AssertCompile(IMAGE_SCN_MEM_16BIT == IMAGE_SCN_MEM_PURGEABLE);
1942 if ( ( pSH->Characteristics & (IMAGE_SCN_MEM_PURGEABLE | IMAGE_SCN_MEM_PRELOAD | IMAGE_SCN_MEM_FARDATA) )
1943 && !(fFlags & RTLDR_O_FOR_DEBUG)) /* purgable/16-bit seen on w2ksp0 hal.dll, ignore the bunch. */
1944 {
1945 Log(("rtldrPEOpen: %s: Unsupported section flag(s) %#x section #%d '%.*s'!!!\n",
1946 pszLogName, pSH->Characteristics, iSH, sizeof(pSH->Name), pSH->Name));
1947 return VERR_BAD_EXE_FORMAT;
1948 }
1949
1950 if ( pSH->Misc.VirtualSize
1951 && !(pSH->Characteristics & IMAGE_SCN_TYPE_NOLOAD)) /* binutils uses this for '.stab' even if it's reserved/obsoleted by MS. */
1952 {
1953 if (pSH->VirtualAddress < uRvaPrev)
1954 {
1955 Log(("rtldrPEOpen: %s: Overlaps previous section or sections aren't in ascending order, VirtualAddress=%#x uRvaPrev=%#x - section #%d '%.*s'!!!\n",
1956 pszLogName, pSH->VirtualAddress, uRvaPrev, iSH, sizeof(pSH->Name), pSH->Name));
1957 return VERR_BAD_EXE_FORMAT;
1958 }
1959 if (pSH->VirtualAddress > cbImage)
1960 {
1961 Log(("rtldrPEOpen: %s: VirtualAddress=%#x - beyond image size (%#x) - section #%d '%.*s'!!!\n",
1962 pszLogName, pSH->VirtualAddress, cbImage, iSH, sizeof(pSH->Name), pSH->Name));
1963 return VERR_BAD_EXE_FORMAT;
1964 }
1965
1966 if (pSH->VirtualAddress & (pOptHdr->SectionAlignment - 1)) //ASSUMES power of 2 alignment.
1967 {
1968 Log(("rtldrPEOpen: %s: VirtualAddress=%#x misaligned (%#x) - section #%d '%.*s'!!!\n",
1969 pszLogName, pSH->VirtualAddress, pOptHdr->SectionAlignment, iSH, sizeof(pSH->Name), pSH->Name));
1970 return VERR_BAD_EXE_FORMAT;
1971 }
1972
1973#ifdef PE_FILE_OFFSET_EQUALS_RVA
1974 /* Our loader code assume rva matches the file offset. */
1975 if ( pSH->SizeOfRawData
1976 && pSH->PointerToRawData != pSH->VirtualAddress)
1977 {
1978 Log(("rtldrPEOpen: %s: ASSUMPTION FAILED: file offset %#x != RVA %#x - section #%d '%.*s'!!!\n",
1979 pszLogName, pSH->PointerToRawData, pSH->VirtualAddress, iSH, sizeof(pSH->Name), pSH->Name));
1980 return VERR_BAD_EXE_FORMAT;
1981 }
1982#endif
1983 }
1984
1985 ///@todo only if SizeOfRawData > 0 ?
1986 if ( pSH->PointerToRawData > cbRawImage /// @todo pSH->PointerToRawData >= cbRawImage ?
1987 || pSH->SizeOfRawData > cbRawImage
1988 || pSH->PointerToRawData + pSH->SizeOfRawData > cbRawImage)
1989 {
1990 Log(("rtldrPEOpen: %s: PointerToRawData=%#x SizeOfRawData=%#x - beyond end of file (%#x) - section #%d '%.*s'!!!\n",
1991 pszLogName, pSH->PointerToRawData, pSH->SizeOfRawData, cbRawImage,
1992 iSH, sizeof(pSH->Name), pSH->Name));
1993 return VERR_BAD_EXE_FORMAT;
1994 }
1995
1996 if (pSH->PointerToRawData & (pOptHdr->FileAlignment - 1)) //ASSUMES power of 2 alignment.
1997 {
1998 Log(("rtldrPEOpen: %s: PointerToRawData=%#x misaligned (%#x) - section #%d '%.*s'!!!\n",
1999 pszLogName, pSH->PointerToRawData, pOptHdr->FileAlignment, iSH, sizeof(pSH->Name), pSH->Name));
2000 return VERR_BAD_EXE_FORMAT;
2001 }
2002
2003 /* ignore the relocations and linenumbers. */
2004
2005 uRvaPrev = pSH->VirtualAddress + pSH->Misc.VirtualSize;
2006 }
2007
2008 /** @todo r=bird: more sanity checks! */
2009 return VINF_SUCCESS;
2010}
2011
2012
2013/**
2014 * Reads image data by RVA using the section headers.
2015 *
2016 * @returns iprt status code.
2017 * @param pModPe The PE module instance.
2018 * @param pvBuf Where to store the bits.
2019 * @param cb Number of bytes to tread.
2020 * @param RVA Where to read from.
2021 */
2022static int rtldrPEReadRVA(PRTLDRMODPE pModPe, void *pvBuf, uint32_t cb, uint32_t RVA)
2023{
2024 const IMAGE_SECTION_HEADER *pSH = pModPe->paSections;
2025 PRTLDRREADER pReader = pModPe->Core.pReader;
2026 uint32_t cbRead;
2027 int rc;
2028
2029 /*
2030 * Is it the headers, i.e. prior to the first section.
2031 */
2032 if (RVA < pModPe->cbHeaders)
2033 {
2034 cbRead = RT_MIN(pModPe->cbHeaders - RVA, cb);
2035 rc = pReader->pfnRead(pReader, pvBuf, cbRead, RVA);
2036 if ( cbRead == cb
2037 || RT_FAILURE(rc))
2038 return rc;
2039 cb -= cbRead;
2040 RVA += cbRead;
2041 pvBuf = (uint8_t *)pvBuf + cbRead;
2042 }
2043
2044 /* In the zero space between headers and the first section? */
2045 if (RVA < pSH->VirtualAddress)
2046 {
2047 cbRead = RT_MIN(pSH->VirtualAddress - RVA, cb);
2048 memset(pvBuf, 0, cbRead);
2049 if (cbRead == cb)
2050 return VINF_SUCCESS;
2051 cb -= cbRead;
2052 RVA += cbRead;
2053 pvBuf = (uint8_t *)pvBuf + cbRead;
2054 }
2055
2056 /*
2057 * Iterate the sections.
2058 */
2059 for (unsigned cLeft = pModPe->cSections;
2060 cLeft > 0;
2061 cLeft--, pSH++)
2062 {
2063 uint32_t off = RVA - pSH->VirtualAddress;
2064 if (off < pSH->Misc.VirtualSize)
2065 {
2066 cbRead = RT_MIN(pSH->Misc.VirtualSize - off, cb);
2067 rc = pReader->pfnRead(pReader, pvBuf, cbRead, pSH->PointerToRawData + off);
2068 if ( cbRead == cb
2069 || RT_FAILURE(rc))
2070 return rc;
2071 cb -= cbRead;
2072 RVA += cbRead;
2073 pvBuf = (uint8_t *)pvBuf + cbRead;
2074 }
2075 uint32_t RVANext = cLeft ? pSH[1].VirtualAddress : pModPe->cbImage;
2076 if (RVA < RVANext)
2077 {
2078 cbRead = RT_MIN(RVANext - RVA, cb);
2079 memset(pvBuf, 0, cbRead);
2080 if (cbRead == cb)
2081 return VINF_SUCCESS;
2082 cb -= cbRead;
2083 RVA += cbRead;
2084 pvBuf = (uint8_t *)pvBuf + cbRead;
2085 }
2086 }
2087
2088 AssertFailed();
2089 return VERR_INTERNAL_ERROR;
2090}
2091
2092
2093/**
2094 * Validates the data of some selected data directories entries.
2095 *
2096 * This requires a valid section table and thus has to wait
2097 * till after we've read and validated it.
2098 *
2099 * @returns iprt status code.
2100 * @param pModPe The PE module instance.
2101 * @param pOptHdr Pointer to the optional header (valid).
2102 * @param fFlags Loader flags, RTLDR_O_XXX.
2103 */
2104static int rtldrPEValidateDirectories(PRTLDRMODPE pModPe, const IMAGE_OPTIONAL_HEADER64 *pOptHdr, uint32_t fFlags)
2105{
2106 const char *pszLogName = pModPe->Core.pReader->pfnLogName(pModPe->Core.pReader); NOREF(pszLogName);
2107 union /* combine stuff we're reading to help reduce stack usage. */
2108 {
2109 IMAGE_LOAD_CONFIG_DIRECTORY64 Cfg64;
2110 } u;
2111
2112 /*
2113 * The load config entry may include lock prefix tables and whatnot which we don't implement.
2114 * It does also include a lot of stuff which we can ignore, so we'll have to inspect the
2115 * actual data before we can make up our mind about it all.
2116 */
2117 IMAGE_DATA_DIRECTORY Dir = pOptHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG];
2118 if (Dir.Size)
2119 {
2120 const size_t cbExpect = pOptHdr->Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC
2121 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32)
2122 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64);
2123 if ( Dir.Size != cbExpect
2124 && ( cbExpect == sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32)
2125 && Dir.Size != (uint32_t)RT_OFFSETOF(IMAGE_LOAD_CONFIG_DIRECTORY32, SEHandlerTable))
2126 )
2127 {
2128 Log(("rtldrPEOpen: %s: load cfg dir: unexpected dir size of %d bytes, expected %d.\n",
2129 pszLogName, Dir.Size, cbExpect));
2130 return VERR_LDRPE_LOAD_CONFIG_SIZE;
2131 }
2132
2133 /*
2134 * Read and convert to 64-bit.
2135 */
2136 memset(&u.Cfg64, 0, sizeof(u.Cfg64));
2137 int rc = rtldrPEReadRVA(pModPe, &u.Cfg64, Dir.Size, Dir.VirtualAddress);
2138 if (RT_FAILURE(rc))
2139 return rc;
2140 rtldrPEConvert32BitLoadConfigTo64Bit(&u.Cfg64);
2141
2142 if (u.Cfg64.Size != cbExpect)
2143 {
2144 Log(("rtldrPEOpen: %s: load cfg dir: unexpected header size of %d bytes, expected %d.\n",
2145 pszLogName, u.Cfg64.Size, cbExpect));
2146 return VERR_LDRPE_LOAD_CONFIG_SIZE;
2147 }
2148 if (u.Cfg64.LockPrefixTable)
2149 {
2150 Log(("rtldrPEOpen: %s: load cfg dir: lock prefix table at %RX64. We don't support lock prefix tables!\n",
2151 pszLogName, u.Cfg64.LockPrefixTable));
2152 return VERR_LDRPE_LOCK_PREFIX_TABLE;
2153 }
2154#if 0/* this seems to be safe to ignore. */
2155 if ( u.Cfg64.SEHandlerTable
2156 || u.Cfg64.SEHandlerCount)
2157 {
2158 Log(("rtldrPEOpen: %s: load cfg dir: SEHandlerTable=%RX64 and SEHandlerCount=%RX64 are unsupported!\n",
2159 pszLogName, u.Cfg64.SEHandlerTable, u.Cfg64.SEHandlerCount));
2160 return VERR_BAD_EXE_FORMAT;
2161 }
2162#endif
2163 if (u.Cfg64.EditList)
2164 {
2165 Log(("rtldrPEOpen: %s: load cfg dir: EditList=%RX64 is unsupported!\n",
2166 pszLogName, u.Cfg64.EditList));
2167 return VERR_BAD_EXE_FORMAT;
2168 }
2169 }
2170
2171 /*
2172 * If the image is signed and we're not doing this for debug purposes,
2173 * take a look at the signature.
2174 */
2175 Dir = pOptHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
2176 if (Dir.Size && !(fFlags & RTLDR_O_FOR_DEBUG))
2177 {
2178 PWIN_CERTIFICATE pFirst = (PWIN_CERTIFICATE)RTMemTmpAlloc(Dir.Size);
2179 if (!pFirst)
2180 return VERR_NO_TMP_MEMORY;
2181 int rc = pModPe->Core.pReader->pfnRead(pModPe->Core.pReader, pFirst, Dir.Size, Dir.VirtualAddress);
2182 if (RT_SUCCESS(rc))
2183 {
2184 uint32_t off = 0;
2185 PWIN_CERTIFICATE pCur = pFirst;
2186 do
2187 {
2188 /* validate the members. */
2189 uint32_t const cbCur = RT_ALIGN_32(pCur->dwLength, 8);
2190 if ( cbCur < sizeof(WIN_CERTIFICATE)
2191 || cbCur + off > RT_ALIGN_32(Dir.Size, 8))
2192 {
2193 Log(("rtldrPEOpen: %s: cert at %#x/%#x: dwLength=%#x\n", pszLogName, off, Dir.Size, pCur->dwLength));
2194 rc = VERR_LDRPE_CERT_MALFORMED;
2195 break;
2196 }
2197 if ( pCur->wRevision != WIN_CERT_REVISION_2_0
2198 && pCur->wRevision != WIN_CERT_REVISION_1_0)
2199 {
2200 Log(("rtldrPEOpen: %s: cert at %#x/%#x: wRevision=%#x\n", pszLogName, off, Dir.Size, pCur->wRevision));
2201 rc = pCur->wRevision >= WIN_CERT_REVISION_1_0 ? VERR_LDRPE_CERT_UNSUPPORTED : VERR_LDRPE_CERT_MALFORMED;
2202 break;
2203 }
2204 if ( pCur->wCertificateType != WIN_CERT_TYPE_PKCS_SIGNED_DATA
2205 && pCur->wCertificateType != WIN_CERT_TYPE_X509
2206 /*&& pCur->wCertificateType != WIN_CERT_TYPE_RESERVED_1*/
2207 /*&& pCur->wCertificateType != WIN_CERT_TYPE_TS_STACK_SIGNED*/
2208 && pCur->wCertificateType != WIN_CERT_TYPE_EFI_PKCS115
2209 && pCur->wCertificateType != WIN_CERT_TYPE_EFI_GUID
2210 )
2211 {
2212 Log(("rtldrPEOpen: %s: cert at %#x/%#x: wRevision=%#x\n", pszLogName, off, Dir.Size, pCur->wRevision));
2213 rc = pCur->wCertificateType ? VERR_LDRPE_CERT_UNSUPPORTED : VERR_LDRPE_CERT_MALFORMED;
2214 break;
2215 }
2216
2217 /** @todo Rainy Day: Implement further verification using openssl. */
2218
2219 /* next */
2220 off += cbCur;
2221 pCur = (PWIN_CERTIFICATE)((uint8_t *)pCur + cbCur);
2222 } while (off < Dir.Size);
2223 }
2224 RTMemTmpFree(pFirst);
2225 if (RT_FAILURE(rc))
2226 return rc;
2227 }
2228
2229
2230 return VINF_SUCCESS;
2231}
2232
2233
2234/**
2235 * Open a PE image.
2236 *
2237 * @returns iprt status code.
2238 * @param pReader The loader reader instance which will provide the raw image bits.
2239 * @param fFlags Loader flags, RTLDR_O_XXX.
2240 * @param enmArch Architecture specifier.
2241 * @param offNtHdrs The offset of the NT headers (where you find "PE\0\0").
2242 * @param phLdrMod Where to store the handle.
2243 */
2244int rtldrPEOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, RTFOFF offNtHdrs, PRTLDRMOD phLdrMod)
2245{
2246 /*
2247 * Read and validate the file header.
2248 */
2249 IMAGE_FILE_HEADER FileHdr;
2250 int rc = pReader->pfnRead(pReader, &FileHdr, sizeof(FileHdr), offNtHdrs + 4);
2251 if (RT_FAILURE(rc))
2252 return rc;
2253 RTLDRARCH enmArchImage;
2254 const char *pszLogName = pReader->pfnLogName(pReader);
2255 rc = rtldrPEValidateFileHeader(&FileHdr, fFlags, pszLogName, &enmArchImage);
2256 if (RT_FAILURE(rc))
2257 return rc;
2258
2259 /*
2260 * Match the CPU architecture.
2261 */
2262 if ( enmArch != RTLDRARCH_WHATEVER
2263 && enmArch != enmArchImage)
2264 return VERR_LDR_ARCH_MISMATCH;
2265
2266 /*
2267 * Read and validate the "optional" header. Convert 32->64 if necessary.
2268 */
2269 IMAGE_OPTIONAL_HEADER64 OptHdr;
2270 rc = pReader->pfnRead(pReader, &OptHdr, FileHdr.SizeOfOptionalHeader, offNtHdrs + 4 + sizeof(IMAGE_FILE_HEADER));
2271 if (RT_FAILURE(rc))
2272 return rc;
2273 if (FileHdr.SizeOfOptionalHeader != sizeof(OptHdr))
2274 rtldrPEConvert32BitOptionalHeaderTo64Bit(&OptHdr);
2275 rc = rtldrPEValidateOptionalHeader(&OptHdr, pszLogName, offNtHdrs, &FileHdr, pReader->pfnSize(pReader), fFlags);
2276 if (RT_FAILURE(rc))
2277 return rc;
2278
2279 /*
2280 * Read and validate section headers.
2281 */
2282 const size_t cbSections = sizeof(IMAGE_SECTION_HEADER) * FileHdr.NumberOfSections;
2283 PIMAGE_SECTION_HEADER paSections = (PIMAGE_SECTION_HEADER)RTMemAlloc(cbSections);
2284 if (!paSections)
2285 return VERR_NO_MEMORY;
2286 rc = pReader->pfnRead(pReader, paSections, cbSections,
2287 offNtHdrs + 4 + sizeof(IMAGE_FILE_HEADER) + FileHdr.SizeOfOptionalHeader);
2288 if (RT_SUCCESS(rc))
2289 {
2290 rc = rtldrPEValidateSectionHeaders(paSections, FileHdr.NumberOfSections, pszLogName,
2291 &OptHdr, pReader->pfnSize(pReader), fFlags);
2292 if (RT_SUCCESS(rc))
2293 {
2294 /*
2295 * Allocate and initialize the PE module structure.
2296 */
2297 PRTLDRMODPE pModPe = (PRTLDRMODPE)RTMemAllocZ(sizeof(*pModPe));
2298 if (pModPe)
2299 {
2300 pModPe->Core.u32Magic = RTLDRMOD_MAGIC;
2301 pModPe->Core.eState = LDR_STATE_OPENED;
2302 if (FileHdr.SizeOfOptionalHeader == sizeof(OptHdr))
2303 pModPe->Core.pOps = &s_rtldrPE64Ops.Core;
2304 else
2305 pModPe->Core.pOps = &s_rtldrPE32Ops.Core;
2306 pModPe->Core.pReader = pReader;
2307 pModPe->Core.enmFormat= RTLDRFMT_PE;
2308 pModPe->Core.enmType = FileHdr.Characteristics & IMAGE_FILE_DLL
2309 ? FileHdr.Characteristics & IMAGE_FILE_RELOCS_STRIPPED
2310 ? RTLDRTYPE_EXECUTABLE_FIXED
2311 : RTLDRTYPE_EXECUTABLE_RELOCATABLE
2312 : FileHdr.Characteristics & IMAGE_FILE_RELOCS_STRIPPED
2313 ? RTLDRTYPE_SHARED_LIBRARY_FIXED
2314 : RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE;
2315 pModPe->Core.enmEndian= RTLDRENDIAN_LITTLE;
2316 pModPe->Core.enmArch = FileHdr.Machine == IMAGE_FILE_MACHINE_I386
2317 ? RTLDRARCH_X86_32
2318 : FileHdr.Machine == IMAGE_FILE_MACHINE_AMD64
2319 ? RTLDRARCH_AMD64
2320 : RTLDRARCH_WHATEVER;
2321 pModPe->pvBits = NULL;
2322 pModPe->offNtHdrs = offNtHdrs;
2323 pModPe->offEndOfHdrs = offNtHdrs + 4 + sizeof(IMAGE_FILE_HEADER) + FileHdr.SizeOfOptionalHeader + cbSections;
2324 pModPe->u16Machine = FileHdr.Machine;
2325 pModPe->fFile = FileHdr.Characteristics;
2326 pModPe->cSections = FileHdr.NumberOfSections;
2327 pModPe->paSections = paSections;
2328 pModPe->uEntryPointRVA= OptHdr.AddressOfEntryPoint;
2329 pModPe->uImageBase = (RTUINTPTR)OptHdr.ImageBase;
2330 pModPe->cbImage = OptHdr.SizeOfImage;
2331 pModPe->cbHeaders = OptHdr.SizeOfHeaders;
2332 pModPe->uTimestamp = FileHdr.TimeDateStamp;
2333 pModPe->ImportDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
2334 pModPe->RelocDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
2335 pModPe->ExportDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
2336 pModPe->DebugDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
2337
2338 /*
2339 * Perform validation of some selected data directories which requires
2340 * inspection of the actual data.
2341 */
2342 rc = rtldrPEValidateDirectories(pModPe, &OptHdr, fFlags);
2343 if (RT_SUCCESS(rc))
2344 {
2345 *phLdrMod = &pModPe->Core;
2346 return VINF_SUCCESS;
2347 }
2348 RTMemFree(pModPe);
2349 }
2350 else
2351 rc = VERR_NO_MEMORY;
2352 }
2353 }
2354 RTMemFree(paSections);
2355 return rc;
2356}
2357
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