Refine the last change
[emacs.git] / nt / preprep.c
blob42855ce7f20d12153192fab4e027160ac18539df
1 /* Pre-process emacs.exe for profiling by MSVC.
2 Copyright (C) 1999, 2001-2013 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
20 Andrew Innes <andrewi@harlequin.co.uk> 16-Jan-1999
21 based on code from addsection.c
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <fcntl.h>
27 #include <time.h>
28 #if defined(__GNUC__) && !defined(_W64)
29 #define _ANONYMOUS_UNION
30 #define _ANONYMOUS_STRUCT
31 #endif
32 #include <windows.h>
34 /* Include relevant definitions from IMAGEHLP.H, which can be found
35 in \\win32sdk\mstools\samples\image\include\imagehlp.h. */
37 PIMAGE_NT_HEADERS
38 (__stdcall * pfnCheckSumMappedFile) (LPVOID BaseAddress,
39 DWORD_PTR FileLength,
40 PDWORD_PTR HeaderSum,
41 PDWORD_PTR CheckSum);
43 #undef min
44 #undef max
45 #define min(x, y) (((x) < (y)) ? (x) : (y))
46 #define max(x, y) (((x) > (y)) ? (x) : (y))
49 /* File handling. */
51 typedef struct file_data {
52 const char *name;
53 unsigned long size;
54 HANDLE file;
55 HANDLE file_mapping;
56 unsigned char *file_base;
57 } file_data;
59 int
60 open_input_file (file_data *p_file, const char *filename)
62 HANDLE file;
63 HANDLE file_mapping;
64 void *file_base;
65 unsigned long size, upper_size;
67 file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
68 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
69 if (file == INVALID_HANDLE_VALUE)
70 return FALSE;
72 size = GetFileSize (file, &upper_size);
73 file_mapping = CreateFileMapping (file, NULL, PAGE_READONLY,
74 0, size, NULL);
75 if (!file_mapping)
76 return FALSE;
78 file_base = MapViewOfFile (file_mapping, FILE_MAP_READ, 0, 0, size);
79 if (file_base == 0)
80 return FALSE;
82 p_file->name = filename;
83 p_file->size = size;
84 p_file->file = file;
85 p_file->file_mapping = file_mapping;
86 p_file->file_base = file_base;
88 return TRUE;
91 int
92 open_output_file (file_data *p_file, const char *filename, unsigned long size)
94 HANDLE file;
95 HANDLE file_mapping;
96 void *file_base;
98 file = CreateFile (filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
99 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
100 if (file == INVALID_HANDLE_VALUE)
101 return FALSE;
103 file_mapping = CreateFileMapping (file, NULL, PAGE_READWRITE,
104 0, size, NULL);
105 if (!file_mapping)
106 return FALSE;
108 file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size);
109 if (file_base == 0)
110 return FALSE;
112 p_file->name = filename;
113 p_file->size = size;
114 p_file->file = file;
115 p_file->file_mapping = file_mapping;
116 p_file->file_base = file_base;
118 return TRUE;
122 open_inout_file (file_data *p_file, const char *filename)
124 HANDLE file;
125 HANDLE file_mapping;
126 void *file_base;
127 unsigned long size, upper_size;
129 file = CreateFile (filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
130 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
131 if (file == INVALID_HANDLE_VALUE)
132 return FALSE;
134 size = GetFileSize (file, &upper_size);
135 file_mapping = CreateFileMapping (file, NULL, PAGE_READWRITE,
136 0, size, NULL);
137 if (!file_mapping)
138 return FALSE;
140 file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size);
141 if (file_base == 0)
142 return FALSE;
144 p_file->name = filename;
145 p_file->size = size;
146 p_file->file = file;
147 p_file->file_mapping = file_mapping;
148 p_file->file_base = file_base;
150 return TRUE;
153 /* Close the system structures associated with the given file. */
154 void
155 close_file_data (file_data *p_file)
157 UnmapViewOfFile (p_file->file_base);
158 CloseHandle (p_file->file_mapping);
159 /* For the case of output files, set final size. */
160 SetFilePointer (p_file->file, p_file->size, NULL, FILE_BEGIN);
161 SetEndOfFile (p_file->file);
162 CloseHandle (p_file->file);
166 /* Routines to manipulate NT executable file sections. */
168 unsigned long
169 get_unrounded_section_size (PIMAGE_SECTION_HEADER p_section)
171 /* The true section size, before rounding, for an initialized data or
172 code section. (Supposedly some linkers swap the meaning of these
173 two values.) */
174 return min (p_section->SizeOfRawData,
175 p_section->Misc.VirtualSize);
178 /* Return pointer to section header for named section. */
179 IMAGE_SECTION_HEADER *
180 find_section (const char *name, IMAGE_NT_HEADERS *nt_header)
182 PIMAGE_SECTION_HEADER section;
183 int i;
185 section = IMAGE_FIRST_SECTION (nt_header);
187 for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
189 if (strcmp (section->Name, name) == 0)
190 return section;
191 section++;
193 return NULL;
196 /* Return pointer to section header for section containing the given
197 relative virtual address. */
198 IMAGE_SECTION_HEADER *
199 rva_to_section (DWORD_PTR rva, IMAGE_NT_HEADERS * nt_header)
201 PIMAGE_SECTION_HEADER section;
202 int i;
204 section = IMAGE_FIRST_SECTION (nt_header);
206 for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
208 /* Some linkers (eg. the NT SDK linker I believe) swapped the
209 meaning of these two values - or rather, they ignored
210 VirtualSize entirely and always set it to zero. This affects
211 some very old exes (eg. gzip dated Dec 1993). Since
212 w32_executable_type relies on this function to work reliably,
213 we need to cope with this. */
214 DWORD_PTR real_size = max (section->SizeOfRawData,
215 section->Misc.VirtualSize);
216 if (rva >= section->VirtualAddress
217 && rva < section->VirtualAddress + real_size)
218 return section;
219 section++;
221 return NULL;
224 /* Return pointer to section header for section containing the given
225 offset in its raw data area. */
226 IMAGE_SECTION_HEADER *
227 offset_to_section (DWORD_PTR offset, IMAGE_NT_HEADERS * nt_header)
229 PIMAGE_SECTION_HEADER section;
230 int i;
232 section = IMAGE_FIRST_SECTION (nt_header);
234 for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
236 if (offset >= section->PointerToRawData
237 && offset < section->PointerToRawData + section->SizeOfRawData)
238 return section;
239 section++;
241 return NULL;
244 /* Return offset to an object in dst, given offset in src. We assume
245 there is at least one section in both src and dst images, and that
246 the some sections may have been added to dst (after sections in src). */
247 static DWORD_PTR
248 relocate_offset (DWORD_PTR offset,
249 IMAGE_NT_HEADERS * src_nt_header,
250 IMAGE_NT_HEADERS * dst_nt_header)
252 PIMAGE_SECTION_HEADER src_section = IMAGE_FIRST_SECTION (src_nt_header);
253 PIMAGE_SECTION_HEADER dst_section = IMAGE_FIRST_SECTION (dst_nt_header);
254 int i = 0;
256 while (offset >= src_section->PointerToRawData)
258 if (offset < src_section->PointerToRawData + src_section->SizeOfRawData)
259 break;
260 i++;
261 if (i == src_nt_header->FileHeader.NumberOfSections)
263 /* Handle offsets after the last section. */
264 dst_section = IMAGE_FIRST_SECTION (dst_nt_header);
265 dst_section += dst_nt_header->FileHeader.NumberOfSections - 1;
266 while (dst_section->PointerToRawData == 0)
267 dst_section--;
268 while (src_section->PointerToRawData == 0)
269 src_section--;
270 return offset
271 + (dst_section->PointerToRawData + dst_section->SizeOfRawData)
272 - (src_section->PointerToRawData + src_section->SizeOfRawData);
274 src_section++;
275 dst_section++;
277 return offset +
278 (dst_section->PointerToRawData - src_section->PointerToRawData);
281 #define OFFSET_TO_RVA(offset, section) \
282 ((section)->VirtualAddress + ((DWORD_PTR)(offset) - (section)->PointerToRawData))
284 #define RVA_TO_OFFSET(rva, section) \
285 ((section)->PointerToRawData + ((DWORD_PTR)(rva) - (section)->VirtualAddress))
287 #define RVA_TO_SECTION_OFFSET(rva, section) \
288 ((DWORD_PTR)(rva) - (section)->VirtualAddress)
290 #define RVA_TO_PTR(var,section,filedata) \
291 ((void *)((unsigned char *)(RVA_TO_OFFSET(var,section) + (filedata)->file_base)))
293 /* Convert address in executing image to RVA. */
294 #define PTR_TO_RVA(ptr) ((DWORD_PTR)(ptr) - (DWORD_PTR) GetModuleHandle (NULL))
296 #define PTR_TO_OFFSET(ptr, pfile_data) \
297 ((unsigned const char *)(ptr) - (pfile_data)->file_base)
299 #define OFFSET_TO_PTR(offset, pfile_data) \
300 ((pfile_data)->file_base + (DWORD_PTR)(offset))
302 #define ROUND_UP(p, align) \
303 (((DWORD_PTR)(p) + (align)-1) & ~((DWORD_PTR)(align)-1))
304 #define ROUND_DOWN(p, align) ((DWORD_PTR)(p) & ~((DWORD_PTR)(align)-1))
307 /* The MSVC prep program generates a ._xe file from .exe, where relevant
308 function calls etc have been patched to go through thunks (generated
309 by prep) that record timing/call information. Because the thunks
310 need to make references to functions imported from profile.dll, the
311 import table must be expanded; the end result is that all the
312 sections following .rdata are relocated to higher RVAs (add a final
313 code section is added holding all the thunks). The .reloc section is
314 also expanded, so that the thunks themselves are relocatable.
316 It is this relocation which kills emacs._xe, because the dumped heap
317 pointers aren't relocated, because there is no relocation data for
318 either the relevant global/static variables or the heap section
319 itself, both of which contain pointers into the heap. [Note that
320 static variables which aren't initialized during linking may become
321 initialized with heap pointers, or even pointers to other static
322 variables, because of dumping.]
324 We could potentially generate the relocation data ourselves by making
325 two versions of temacs, one with an extra dummy section before
326 EMHEAP to offset it, and then compare the dumped executables from
327 both. That is a lot of work though, and it doesn't solve the problem
328 of dumped pointers to static variables, which also can be relocated.
330 A better solution is to pre-process emacs.exe so that the .rdata and
331 .reloc sections are moved to the end of the section table, and thus
332 prep won't relocate anything else. (Of course, we leave "dead"
333 copies of these two sections in place, so that the virtual address of
334 everything else is unaffected.) Relocating the .reloc data is
335 trivial - we just update the IMAGE_BASE_RELOCATION address in the
336 header (the data itself doesn't change). Relocating the import table
337 is more complicated though, because the calls to imported functions
338 must be patched up. That requires us to selectively apply the base
339 relocations when we encounter references to imported functions (or
340 variables) in other sections, but at least the base relocations are
341 easy to parse. */
343 static void
344 copy_executable_and_move_sections (file_data *p_infile,
345 file_data *p_outfile)
347 unsigned char *dst;
348 PIMAGE_DOS_HEADER dos_header;
349 PIMAGE_NT_HEADERS nt_header;
350 PIMAGE_NT_HEADERS dst_nt_header;
351 PIMAGE_SECTION_HEADER section;
352 PIMAGE_SECTION_HEADER dst_section;
353 PIMAGE_SECTION_HEADER import_section;
354 PIMAGE_SECTION_HEADER reloc_section;
355 PIMAGE_DATA_DIRECTORY import_dir;
356 PIMAGE_DATA_DIRECTORY reloc_dir;
357 DWORD_PTR import_delta_rva;
358 DWORD_PTR reloc_delta_rva;
359 DWORD_PTR offset;
360 int i;
362 #define COPY_CHUNK(message, src, size) \
363 do { \
364 unsigned const char *s = (void *)(src); \
365 unsigned long count = (size); \
366 printf ("%s\n", (message)); \
367 printf ("\t0x%08x Offset in input file.\n", s - p_infile->file_base); \
368 printf ("\t0x%08x Offset in output file.\n", dst - p_outfile->file_base); \
369 printf ("\t0x%08x Size in bytes.\n", count); \
370 memcpy (dst, s, count); \
371 dst += count; \
372 } while (0)
374 #define DST_TO_OFFSET() PTR_TO_OFFSET (dst, p_outfile)
375 #define ROUND_UP_DST_AND_ZERO(align) \
376 do { \
377 unsigned char *newdst = p_outfile->file_base \
378 + ROUND_UP (DST_TO_OFFSET (), (align)); \
379 /* Zero the alignment slop; it may actually initialize real data. */ \
380 memset (dst, 0, newdst - dst); \
381 dst = newdst; \
382 } while (0)
384 /* Copy the source image sequentially, ie. section by section after
385 copying the headers and section table, to simplify the process of
386 relocating the .rdata and .reloc section table entries (which might
387 force the raw section data to be relocated).
389 Note that dst is updated implicitly by each COPY_CHUNK. */
391 dos_header = (PIMAGE_DOS_HEADER) p_infile->file_base;
392 nt_header = (PIMAGE_NT_HEADERS) (((unsigned char *) dos_header) +
393 dos_header->e_lfanew);
394 section = IMAGE_FIRST_SECTION (nt_header);
396 import_dir = &nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
397 import_section = rva_to_section (import_dir->VirtualAddress, nt_header);
399 reloc_dir = &nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
400 reloc_section = rva_to_section (reloc_dir->VirtualAddress, nt_header);
401 if (!reloc_section)
403 printf ("No relocation data, cannot prepare for profile prepping.\n");
404 exit (1);
407 dst = (unsigned char *) p_outfile->file_base;
409 COPY_CHUNK ("Copying DOS header...", dos_header,
410 (DWORD_PTR) nt_header - (DWORD_PTR) dos_header);
411 dst_nt_header = (PIMAGE_NT_HEADERS) dst;
412 COPY_CHUNK ("Copying NT header...", nt_header,
413 (DWORD_PTR) section - (DWORD_PTR) nt_header);
414 dst_section = (PIMAGE_SECTION_HEADER) dst;
415 COPY_CHUNK ("Copying section table...", section,
416 nt_header->FileHeader.NumberOfSections * sizeof (*section));
418 /* Leave room for extra section table entries; filled in below. */
419 dst += 2 * sizeof (*section);
421 /* Align the first section's raw data area, and set the header size
422 field accordingly. */
423 ROUND_UP_DST_AND_ZERO (dst_nt_header->OptionalHeader.FileAlignment);
424 dst_nt_header->OptionalHeader.SizeOfHeaders = DST_TO_OFFSET ();
426 for (i = 0; i < nt_header->FileHeader.NumberOfSections;
427 i++, section++, dst_section++)
429 char msg[100];
430 sprintf (msg, "Copying raw data for %s...", section->Name);
432 /* "Blank out" the two sections being relocated. */
433 if (section == import_section || section == reloc_section)
435 dst_section->Name[0] = 'X';
436 dst_section->Misc.VirtualSize =
437 ROUND_UP (dst_section->Misc.VirtualSize,
438 dst_nt_header->OptionalHeader.SectionAlignment);
439 dst_section->PointerToRawData = 0;
440 dst_section->SizeOfRawData = 0;
441 dst_section->Characteristics &= ~IMAGE_SCN_CNT_INITIALIZED_DATA;
442 dst_section->Characteristics |= IMAGE_SCN_CNT_UNINITIALIZED_DATA;
443 dst_section->Characteristics &= ~IMAGE_SCN_MEM_WRITE;
444 continue;
447 /* Update the file-relative offset for this section's raw data (if
448 it has any) in case things have been relocated; we will update
449 the other offsets below once we know where everything is. */
450 if (dst_section->PointerToRawData)
451 dst_section->PointerToRawData = DST_TO_OFFSET ();
453 /* Copy the original raw data. */
454 COPY_CHUNK
455 (msg, OFFSET_TO_PTR (section->PointerToRawData, p_infile),
456 section->SizeOfRawData);
458 /* Round up the raw data size to the new alignment. */
459 dst_section->SizeOfRawData =
460 ROUND_UP (dst_section->SizeOfRawData,
461 dst_nt_header->OptionalHeader.FileAlignment);
463 /* Align the next section's raw data area. */
464 ROUND_UP_DST_AND_ZERO (dst_nt_header->OptionalHeader.FileAlignment);
467 /* Add the extra section entries, copying the raw data we skipped
468 earlier. We'll patch up the data itself below. */
469 if (import_section != NULL)
471 dst_nt_header->FileHeader.NumberOfSections++;
472 dst_nt_header->OptionalHeader.SizeOfImage +=
473 ROUND_UP (import_section->Misc.VirtualSize,
474 dst_nt_header->OptionalHeader.SectionAlignment);
475 *dst_section = *import_section;
476 dst_section->VirtualAddress =
477 dst_section[-1].VirtualAddress
478 + ROUND_UP (dst_section[-1].Misc.VirtualSize,
479 dst_nt_header->OptionalHeader.SectionAlignment);
480 dst_section->PointerToRawData = DST_TO_OFFSET ();
481 /* Remember delta applied to import section. */
482 import_delta_rva = dst_section->VirtualAddress - import_section->VirtualAddress;
483 COPY_CHUNK
484 ("Relocating import directory",
485 OFFSET_TO_PTR (import_section->PointerToRawData, p_infile),
486 import_section->SizeOfRawData);
487 ROUND_UP_DST_AND_ZERO (dst_nt_header->OptionalHeader.FileAlignment);
488 dst_section++;
490 if (reloc_section != NULL)
492 dst_nt_header->FileHeader.NumberOfSections++;
493 dst_nt_header->OptionalHeader.SizeOfImage +=
494 ROUND_UP (reloc_section->Misc.VirtualSize,
495 dst_nt_header->OptionalHeader.SectionAlignment);
496 *dst_section = *reloc_section;
497 dst_section->VirtualAddress =
498 dst_section[-1].VirtualAddress
499 + ROUND_UP (dst_section[-1].Misc.VirtualSize,
500 dst_nt_header->OptionalHeader.SectionAlignment);
501 dst_section->PointerToRawData = DST_TO_OFFSET ();
502 /* Remember delta applied to reloc section. */
503 reloc_delta_rva = dst_section->VirtualAddress - reloc_section->VirtualAddress;
504 COPY_CHUNK
505 ("Relocating base relocations directory",
506 OFFSET_TO_PTR (reloc_section->PointerToRawData, p_infile),
507 reloc_section->SizeOfRawData);
508 ROUND_UP_DST_AND_ZERO (dst_nt_header->OptionalHeader.FileAlignment);
509 reloc_dir = &dst_nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
510 reloc_dir->VirtualAddress += reloc_delta_rva;
511 dst_section++;
514 /* Copy remainder of source image. */
515 section--;
516 offset = ROUND_UP (section->PointerToRawData + section->SizeOfRawData,
517 nt_header->OptionalHeader.FileAlignment);
518 COPY_CHUNK
519 ("Copying remainder of executable...",
520 OFFSET_TO_PTR (offset, p_infile),
521 p_infile->size - offset);
523 /* Final size for new image. */
524 p_outfile->size = DST_TO_OFFSET ();
526 /* Now patch up remaining file-relative offsets. */
527 printf ("Patching up raw data offsets...\n");
529 section = IMAGE_FIRST_SECTION (nt_header);
530 dst_section = IMAGE_FIRST_SECTION (dst_nt_header);
532 #define ADJUST_OFFSET(var) \
533 do { \
534 if ((var) != 0) \
535 (var) = relocate_offset ((var), nt_header, dst_nt_header); \
536 } while (0)
538 #define ADJUST_IMPORT_RVA(var) \
539 do { \
540 if ((var) != 0) \
541 *((DWORD_PTR *)&(var)) += import_delta_rva; \
542 } while (0)
544 dst_nt_header->OptionalHeader.SizeOfInitializedData = 0;
545 dst_nt_header->OptionalHeader.SizeOfUninitializedData = 0;
546 for (i = 0; i < dst_nt_header->FileHeader.NumberOfSections; i++)
548 /* Recompute data sizes for completeness. */
549 if (dst_section[i].Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
550 dst_nt_header->OptionalHeader.SizeOfInitializedData +=
551 ROUND_UP (dst_section[i].Misc.VirtualSize, dst_nt_header->OptionalHeader.FileAlignment);
552 else if (dst_section[i].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
553 dst_nt_header->OptionalHeader.SizeOfUninitializedData +=
554 ROUND_UP (dst_section[i].Misc.VirtualSize, dst_nt_header->OptionalHeader.FileAlignment);
556 ADJUST_OFFSET (dst_section[i].PointerToLinenumbers);
559 ADJUST_OFFSET (dst_nt_header->FileHeader.PointerToSymbolTable);
561 /* Update offsets in debug directory entries. Note that the debug
562 directory may be in the same section as the import table, so its
563 RVA may need to be adjusted too. */
565 PIMAGE_DATA_DIRECTORY debug_dir =
566 &dst_nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
567 PIMAGE_DEBUG_DIRECTORY debug_entry;
569 /* Update debug_dir if part of import_section. */
570 if (rva_to_section (debug_dir->VirtualAddress, nt_header) == import_section)
571 debug_dir->VirtualAddress += import_delta_rva;
573 section = rva_to_section (debug_dir->VirtualAddress, dst_nt_header);
574 if (section)
576 int size;
578 debug_entry = RVA_TO_PTR (debug_dir->VirtualAddress, section, p_outfile);
579 size = debug_dir->Size / sizeof (IMAGE_DEBUG_DIRECTORY);
581 for (i = 0; i < size; i++, debug_entry++)
583 /* The debug data itself is normally not part of any
584 section, but stored after all the raw section data. So
585 let relocate_offset do the work. */
586 ADJUST_OFFSET (debug_entry->PointerToRawData);
587 ADJUST_IMPORT_RVA (debug_entry->AddressOfRawData);
592 /* Update RVAs in import directory entries. */
594 PIMAGE_IMPORT_DESCRIPTOR imports;
595 PIMAGE_THUNK_DATA import_thunks;
597 import_dir = &dst_nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
598 import_dir->VirtualAddress += import_delta_rva;
600 section = rva_to_section (import_dir->VirtualAddress, dst_nt_header);
601 imports = RVA_TO_PTR (import_dir->VirtualAddress, section, p_outfile);
603 for ( ; imports->Name != 0; imports++)
605 ADJUST_IMPORT_RVA (imports->OriginalFirstThunk);
606 ADJUST_IMPORT_RVA (imports->FirstThunk);
607 ADJUST_IMPORT_RVA (imports->Name);
609 for (import_thunks = RVA_TO_PTR (imports->OriginalFirstThunk, section, p_outfile);
610 import_thunks->u1.Function != 0;
611 import_thunks++)
612 if ((import_thunks->u1.Ordinal >> 31) == 0)
613 ADJUST_IMPORT_RVA (import_thunks->u1.Ordinal);
615 for (import_thunks = RVA_TO_PTR (imports->FirstThunk, section, p_outfile);
616 import_thunks->u1.Function != 0;
617 import_thunks++)
618 if ((import_thunks->u1.Ordinal >> 31) == 0)
619 ADJUST_IMPORT_RVA (import_thunks->u1.Ordinal);
622 import_dir = &dst_nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT];
623 import_dir->VirtualAddress += import_delta_rva;
626 /* Fix up references to the import section. */
627 printf ("Applying fixups to import references...\n");
630 IMAGE_BASE_RELOCATION *relocs, *block, *start_block, *end_block;
631 DWORD_PTR import_start = import_section->VirtualAddress + dst_nt_header->OptionalHeader.ImageBase;
632 DWORD_PTR import_end = import_start + import_section->Misc.VirtualSize;
633 DWORD_PTR len_import_relocs;
634 DWORD_PTR len_remaining_relocs;
635 int seen_high = 0;
636 WORD * high_word;
637 void * holder;
639 reloc_dir = &dst_nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
640 reloc_section = rva_to_section (reloc_dir->VirtualAddress, dst_nt_header);
641 relocs = RVA_TO_PTR (reloc_dir->VirtualAddress, reloc_section, p_outfile);
643 /* Move the base relocations for the import section, if there are
644 any; the profiler needs to be able to patch RVAs in the import
645 section itself. */
646 for (block = relocs, start_block = 0;
647 (DWORD_PTR) block - (DWORD_PTR) relocs < reloc_dir->Size;
648 block = (void *)((DWORD_PTR) block + block->SizeOfBlock))
650 if (block->VirtualAddress >= import_section->VirtualAddress + import_section->Misc.VirtualSize)
652 end_block = block;
653 break;
655 if (block->VirtualAddress >= import_section->VirtualAddress)
657 if (start_block == 0)
658 start_block = block;
659 block->VirtualAddress += import_delta_rva;
662 if (start_block)
664 len_import_relocs = (DWORD_PTR) end_block - (DWORD_PTR) start_block;
665 len_remaining_relocs = (DWORD_PTR) relocs + reloc_dir->Size - (DWORD_PTR) end_block;
666 holder = malloc (len_import_relocs);
667 if (holder == 0)
668 abort ();
669 memcpy (holder, start_block, len_import_relocs);
670 memcpy (start_block, end_block, len_remaining_relocs);
671 memcpy ((char *) start_block + len_remaining_relocs, holder, len_import_relocs);
672 free (holder);
675 /* Walk up the list of base relocations, checking for references
676 to the old import section location, and patching them to
677 reference the new location. */
678 for (block = relocs;
679 (DWORD_PTR) block - (DWORD_PTR) relocs < reloc_dir->Size;
680 block = (void *)((DWORD_PTR) block + block->SizeOfBlock))
682 DWORD_PTR page_rva = block->VirtualAddress;
683 DWORD_PTR page_offset;
684 union {
685 WORD word;
686 DWORD_PTR dword;
687 } * ploc;
688 WORD *fixup;
690 section = rva_to_section (page_rva, dst_nt_header);
691 /* Don't apply fixups to the blanked sections. */
692 if (section->Name[0] == 'X')
693 continue;
695 for (fixup = (WORD *) &block[1];
696 (DWORD_PTR) fixup - (DWORD_PTR) block < block->SizeOfBlock;
697 fixup++)
699 page_offset = (*fixup) & 0xfff;
700 ploc = RVA_TO_PTR (page_rva + page_offset, section, p_outfile);
702 /* Unless our assumption is wrong, all low word fixups
703 should immediately follow a high fixup. */
704 if (seen_high && ((*fixup) >> 12) != IMAGE_REL_BASED_LOW)
705 abort ();
707 switch ((*fixup) >> 12)
709 case IMAGE_REL_BASED_ABSOLUTE:
710 break;
711 case IMAGE_REL_BASED_HIGH:
712 /* We must assume that high and low fixups occur in
713 pairs, specifically a low fixup immediately follows a
714 high fixup (normally separated by two bytes). We
715 have to process the two fixups together, to find out
716 the full pointer value and decide whether to apply
717 the fixup. */
718 seen_high = 1;
719 high_word = &ploc->word;
720 break;
721 case IMAGE_REL_BASED_LOW:
722 offset = (*high_word << 16) + ploc->word;
723 if (offset >= import_start && offset < import_end)
725 (*high_word) += import_delta_rva >> 16;
726 ploc->dword += import_delta_rva & 0xffff;
728 seen_high = 0;
729 break;
730 case IMAGE_REL_BASED_HIGHLOW:
731 /* Docs imply two words in big-endian order, so perhaps
732 this is only used on big-endian platforms, in which
733 case the obvious code will work. */
734 if (ploc->dword >= import_start && ploc->dword < import_end)
735 ploc->dword += import_delta_rva;
736 break;
737 case IMAGE_REL_BASED_HIGHADJ:
738 /* Docs don't say, but I guess this is the equivalent
739 for little-endian platforms. */
740 if (ploc->dword >= import_start && ploc->dword < import_end)
741 ploc->dword += import_delta_rva;
742 break;
743 case IMAGE_REL_BASED_MIPS_JMPADDR:
744 /* Don't know how to handle this; MIPS support has been
745 dropped from NT4 anyway. */
746 abort ();
747 break;
748 #ifdef IMAGE_REL_BASED_SECTION
749 case IMAGE_REL_BASED_SECTION:
750 case IMAGE_REL_BASED_REL32:
751 /* Docs don't say what these values mean. */
752 #endif
753 default:
754 abort ();
763 main (int argc, char **argv)
765 PIMAGE_DOS_HEADER dos_header;
766 PIMAGE_NT_HEADERS nt_header;
767 file_data in_file, out_file;
768 char out_filename[MAX_PATH], in_filename[MAX_PATH];
770 strcpy (in_filename, argv[1]);
771 strcpy (out_filename, argv[2]);
773 printf ("Preparing %s for profile prepping\n", out_filename);
775 /* Open the original (dumped) executable file for reference. */
776 if (!open_input_file (&in_file, in_filename))
778 printf ("Failed to open %s (%d)...bailing.\n",
779 in_filename, GetLastError ());
780 exit (1);
783 /* Create a new image that can be prepped; we don't expect the size to
784 change because we are only adding two new section table entries,
785 which should fit in the alignment slop. */
786 if (!open_output_file (&out_file, out_filename, in_file.size))
788 printf ("Failed to open %s (%d)...bailing.\n",
789 out_filename, GetLastError ());
790 exit (1);
793 copy_executable_and_move_sections (&in_file, &out_file);
795 /* Patch up header fields; profiler is picky about this. */
797 HANDLE hImagehelp = LoadLibrary ("imagehlp.dll");
798 DWORD_PTR headersum;
799 DWORD_PTR checksum;
801 dos_header = (PIMAGE_DOS_HEADER) out_file.file_base;
802 nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew);
804 nt_header->OptionalHeader.CheckSum = 0;
805 // nt_header->FileHeader.TimeDateStamp = time (NULL);
806 // dos_header->e_cp = size / 512;
807 // nt_header->OptionalHeader.SizeOfImage = size;
809 pfnCheckSumMappedFile = (void *) GetProcAddress (hImagehelp, "CheckSumMappedFile");
810 if (pfnCheckSumMappedFile)
812 // nt_header->FileHeader.TimeDateStamp = time (NULL);
813 pfnCheckSumMappedFile (out_file.file_base,
814 out_file.size,
815 &headersum,
816 &checksum);
817 nt_header->OptionalHeader.CheckSum = checksum;
819 FreeLibrary (hImagehelp);
822 close_file_data (&out_file);
823 close_file_data (&in_file);
825 return 0;
828 /* eof */