VirtualBox

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

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

Added a RTDbgMod reader that employs DbgHelp.dll.

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

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