VirtualBox

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

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

Made it possible to find symbols for windows nt using a image-in-guest-memory loader fallback.

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