file-attribute-collect: New defun
[emacs.git] / nt / preprep.c
blob491a93ba3b039220ff40fdd3496bcd07350afdc3
1 /* Pre-process emacs.exe for profiling by MSVC.
2 Copyright (C) 1999, 2001-2016 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 (at
9 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(MINGW_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 (__stdcall * pfnCheckSumMappedFile) (LPVOID BaseAddress,
38 DWORD_PTR FileLength,
39 PDWORD_PTR HeaderSum,
40 PDWORD_PTR CheckSum);
42 #undef min
43 #undef max
44 #define min(x, y) (((x) < (y)) ? (x) : (y))
45 #define max(x, y) (((x) > (y)) ? (x) : (y))
48 /* File handling. */
50 typedef struct file_data {
51 const char *name;
52 unsigned long size;
53 HANDLE file;
54 HANDLE file_mapping;
55 unsigned char *file_base;
56 } file_data;
58 int
59 open_input_file (file_data *p_file, const char *filename)
61 HANDLE file;
62 HANDLE file_mapping;
63 void *file_base;
64 unsigned long size, upper_size;
66 file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
67 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
68 if (file == INVALID_HANDLE_VALUE)
69 return FALSE;
71 size = GetFileSize (file, &upper_size);
72 file_mapping = CreateFileMapping (file, NULL, PAGE_READONLY,
73 0, size, NULL);
74 if (!file_mapping)
75 return FALSE;
77 file_base = MapViewOfFile (file_mapping, FILE_MAP_READ, 0, 0, size);
78 if (file_base == 0)
79 return FALSE;
81 p_file->name = filename;
82 p_file->size = size;
83 p_file->file = file;
84 p_file->file_mapping = file_mapping;
85 p_file->file_base = file_base;
87 return TRUE;
90 int
91 open_output_file (file_data *p_file, const char *filename, unsigned long size)
93 HANDLE file;
94 HANDLE file_mapping;
95 void *file_base;
97 file = CreateFile (filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
98 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
99 if (file == INVALID_HANDLE_VALUE)
100 return FALSE;
102 file_mapping = CreateFileMapping (file, NULL, PAGE_READWRITE,
103 0, size, NULL);
104 if (!file_mapping)
105 return FALSE;
107 file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size);
108 if (file_base == 0)
109 return FALSE;
111 p_file->name = filename;
112 p_file->size = size;
113 p_file->file = file;
114 p_file->file_mapping = file_mapping;
115 p_file->file_base = file_base;
117 return TRUE;
121 open_inout_file (file_data *p_file, const char *filename)
123 HANDLE file;
124 HANDLE file_mapping;
125 void *file_base;
126 unsigned long size, upper_size;
128 file = CreateFile (filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
129 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
130 if (file == INVALID_HANDLE_VALUE)
131 return FALSE;
133 size = GetFileSize (file, &upper_size);
134 file_mapping = CreateFileMapping (file, NULL, PAGE_READWRITE,
135 0, size, NULL);
136 if (!file_mapping)
137 return FALSE;
139 file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size);
140 if (file_base == 0)
141 return FALSE;
143 p_file->name = filename;
144 p_file->size = size;
145 p_file->file = file;
146 p_file->file_mapping = file_mapping;
147 p_file->file_base = file_base;
149 return TRUE;
152 /* Close the system structures associated with the given file. */
153 void
154 close_file_data (file_data *p_file)
156 UnmapViewOfFile (p_file->file_base);
157 CloseHandle (p_file->file_mapping);
158 /* For the case of output files, set final size. */
159 SetFilePointer (p_file->file, p_file->size, NULL, FILE_BEGIN);
160 SetEndOfFile (p_file->file);
161 CloseHandle (p_file->file);
165 /* Routines to manipulate NT executable file sections. */
167 unsigned long
168 get_unrounded_section_size (PIMAGE_SECTION_HEADER p_section)
170 /* The true section size, before rounding, for an initialized data or
171 code section. (Supposedly some linkers swap the meaning of these
172 two values.) */
173 return min (p_section->SizeOfRawData,
174 p_section->Misc.VirtualSize);
177 /* Return pointer to section header for named section. */
178 IMAGE_SECTION_HEADER *
179 find_section (const char *name, IMAGE_NT_HEADERS *nt_header)
181 PIMAGE_SECTION_HEADER section;
182 int i;
184 section = IMAGE_FIRST_SECTION (nt_header);
186 for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
188 if (strcmp (section->Name, name) == 0)
189 return section;
190 section++;
192 return NULL;
195 /* Return pointer to section header for section containing the given
196 relative virtual address. */
197 IMAGE_SECTION_HEADER *
198 rva_to_section (DWORD_PTR rva, IMAGE_NT_HEADERS * nt_header)
200 PIMAGE_SECTION_HEADER section;
201 int i;
203 section = IMAGE_FIRST_SECTION (nt_header);
205 for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
207 /* Some linkers (eg. the NT SDK linker I believe) swapped the
208 meaning of these two values - or rather, they ignored
209 VirtualSize entirely and always set it to zero. This affects
210 some very old exes (eg. gzip dated Dec 1993). Since
211 w32_executable_type relies on this function to work reliably,
212 we need to cope with this. */
213 DWORD_PTR real_size = max (section->SizeOfRawData,
214 section->Misc.VirtualSize);
215 if (rva >= section->VirtualAddress
216 && rva < section->VirtualAddress + real_size)
217 return section;
218 section++;
220 return NULL;
223 /* Return pointer to section header for section containing the given
224 offset in its raw data area. */
225 IMAGE_SECTION_HEADER *
226 offset_to_section (DWORD_PTR offset, IMAGE_NT_HEADERS * nt_header)
228 PIMAGE_SECTION_HEADER section;
229 int i;
231 section = IMAGE_FIRST_SECTION (nt_header);
233 for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
235 if (offset >= section->PointerToRawData
236 && offset < section->PointerToRawData + section->SizeOfRawData)
237 return section;
238 section++;
240 return NULL;
243 /* Return offset to an object in dst, given offset in src. We assume
244 there is at least one section in both src and dst images, and that
245 the some sections may have been added to dst (after sections in src). */
246 static DWORD_PTR
247 relocate_offset (DWORD_PTR offset,
248 IMAGE_NT_HEADERS * src_nt_header,
249 IMAGE_NT_HEADERS * dst_nt_header)
251 PIMAGE_SECTION_HEADER src_section = IMAGE_FIRST_SECTION (src_nt_header);
252 PIMAGE_SECTION_HEADER dst_section = IMAGE_FIRST_SECTION (dst_nt_header);
253 int i = 0;
255 while (offset >= src_section->PointerToRawData)
257 if (offset < src_section->PointerToRawData + src_section->SizeOfRawData)
258 break;
259 i++;
260 if (i == src_nt_header->FileHeader.NumberOfSections)
262 /* Handle offsets after the last section. */
263 dst_section = IMAGE_FIRST_SECTION (dst_nt_header);
264 dst_section += dst_nt_header->FileHeader.NumberOfSections - 1;
265 while (dst_section->PointerToRawData == 0)
266 dst_section--;
267 while (src_section->PointerToRawData == 0)
268 src_section--;
269 return offset
270 + (dst_section->PointerToRawData + dst_section->SizeOfRawData)
271 - (src_section->PointerToRawData + src_section->SizeOfRawData);
273 src_section++;
274 dst_section++;
276 return offset +
277 (dst_section->PointerToRawData - src_section->PointerToRawData);
280 #define OFFSET_TO_RVA(offset, section) \
281 ((section)->VirtualAddress + ((DWORD_PTR)(offset) - (section)->PointerToRawData))
283 #define RVA_TO_OFFSET(rva, section) \
284 ((section)->PointerToRawData + ((DWORD_PTR)(rva) - (section)->VirtualAddress))
286 #define RVA_TO_SECTION_OFFSET(rva, section) \
287 ((DWORD_PTR)(rva) - (section)->VirtualAddress)
289 #define RVA_TO_PTR(var,section,filedata) \
290 ((void *)((unsigned char *)(RVA_TO_OFFSET(var,section) + (filedata)->file_base)))
292 /* Convert address in executing image to RVA. */
293 #define PTR_TO_RVA(ptr) ((DWORD_PTR)(ptr) - (DWORD_PTR) GetModuleHandle (NULL))
295 #define PTR_TO_OFFSET(ptr, pfile_data) \
296 ((unsigned const char *)(ptr) - (pfile_data)->file_base)
298 #define OFFSET_TO_PTR(offset, pfile_data) \
299 ((pfile_data)->file_base + (DWORD_PTR)(offset))
301 #define ROUND_UP(p, align) \
302 (((DWORD_PTR)(p) + (align)-1) & ~((DWORD_PTR)(align)-1))
303 #define ROUND_DOWN(p, align) ((DWORD_PTR)(p) & ~((DWORD_PTR)(align)-1))
306 /* The MSVC prep program generates a ._xe file from .exe, where relevant
307 function calls etc have been patched to go through thunks (generated
308 by prep) that record timing/call information. Because the thunks
309 need to make references to functions imported from profile.dll, the
310 import table must be expanded; the end result is that all the
311 sections following .rdata are relocated to higher RVAs (add a final
312 code section is added holding all the thunks). The .reloc section is
313 also expanded, so that the thunks themselves are relocatable.
315 It is this relocation which kills emacs._xe, because the dumped heap
316 pointers aren't relocated, because there is no relocation data for
317 either the relevant global/static variables or the heap section
318 itself, both of which contain pointers into the heap. [Note that
319 static variables which aren't initialized during linking may become
320 initialized with heap pointers, or even pointers to other static
321 variables, because of dumping.]
323 We could potentially generate the relocation data ourselves by making
324 two versions of temacs, one with an extra dummy section before
325 EMHEAP to offset it, and then compare the dumped executables from
326 both. That is a lot of work though, and it doesn't solve the problem
327 of dumped pointers to static variables, which also can be relocated.
329 A better solution is to pre-process emacs.exe so that the .rdata and
330 .reloc sections are moved to the end of the section table, and thus
331 prep won't relocate anything else. (Of course, we leave "dead"
332 copies of these two sections in place, so that the virtual address of
333 everything else is unaffected.) Relocating the .reloc data is
334 trivial - we just update the IMAGE_BASE_RELOCATION address in the
335 header (the data itself doesn't change). Relocating the import table
336 is more complicated though, because the calls to imported functions
337 must be patched up. That requires us to selectively apply the base
338 relocations when we encounter references to imported functions (or
339 variables) in other sections, but at least the base relocations are
340 easy to parse. */
342 static void
343 copy_executable_and_move_sections (file_data *p_infile,
344 file_data *p_outfile)
346 unsigned char *dst;
347 PIMAGE_DOS_HEADER dos_header;
348 PIMAGE_NT_HEADERS nt_header;
349 PIMAGE_NT_HEADERS dst_nt_header;
350 PIMAGE_SECTION_HEADER section;
351 PIMAGE_SECTION_HEADER dst_section;
352 PIMAGE_SECTION_HEADER import_section;
353 PIMAGE_SECTION_HEADER reloc_section;
354 PIMAGE_DATA_DIRECTORY import_dir;
355 PIMAGE_DATA_DIRECTORY reloc_dir;
356 DWORD_PTR import_delta_rva;
357 DWORD_PTR reloc_delta_rva;
358 DWORD_PTR offset;
359 int i;
361 #define COPY_CHUNK(message, src, size) \
362 do { \
363 unsigned const char *s = (void *)(src); \
364 unsigned long count = (size); \
365 printf ("%s\n", (message)); \
366 printf ("\t0x%08x Offset in input file.\n", s - p_infile->file_base); \
367 printf ("\t0x%08x Offset in output file.\n", dst - p_outfile->file_base); \
368 printf ("\t0x%08x Size in bytes.\n", count); \
369 memcpy (dst, s, count); \
370 dst += count; \
371 } while (0)
373 #define DST_TO_OFFSET() PTR_TO_OFFSET (dst, p_outfile)
374 #define ROUND_UP_DST_AND_ZERO(align) \
375 do { \
376 unsigned char *newdst = p_outfile->file_base \
377 + ROUND_UP (DST_TO_OFFSET (), (align)); \
378 /* Zero the alignment slop; it may actually initialize real data. */ \
379 memset (dst, 0, newdst - dst); \
380 dst = newdst; \
381 } while (0)
383 /* Copy the source image sequentially, ie. section by section after
384 copying the headers and section table, to simplify the process of
385 relocating the .rdata and .reloc section table entries (which might
386 force the raw section data to be relocated).
388 Note that dst is updated implicitly by each COPY_CHUNK. */
390 dos_header = (PIMAGE_DOS_HEADER) p_infile->file_base;
391 nt_header = (PIMAGE_NT_HEADERS) (((unsigned char *) dos_header) +
392 dos_header->e_lfanew);
393 section = IMAGE_FIRST_SECTION (nt_header);
395 import_dir = &nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
396 import_section = rva_to_section (import_dir->VirtualAddress, nt_header);
398 reloc_dir = &nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
399 reloc_section = rva_to_section (reloc_dir->VirtualAddress, nt_header);
400 if (!reloc_section)
402 printf ("No relocation data, cannot prepare for profile prepping.\n");
403 exit (1);
406 dst = (unsigned char *) p_outfile->file_base;
408 COPY_CHUNK ("Copying DOS header...", dos_header,
409 (DWORD_PTR) nt_header - (DWORD_PTR) dos_header);
410 dst_nt_header = (PIMAGE_NT_HEADERS) dst;
411 COPY_CHUNK ("Copying NT header...", nt_header,
412 (DWORD_PTR) section - (DWORD_PTR) nt_header);
413 dst_section = (PIMAGE_SECTION_HEADER) dst;
414 COPY_CHUNK ("Copying section table...", section,
415 nt_header->FileHeader.NumberOfSections * sizeof (*section));
417 /* Leave room for extra section table entries; filled in below. */
418 dst += 2 * sizeof (*section);
420 /* Align the first section's raw data area, and set the header size
421 field accordingly. */
422 ROUND_UP_DST_AND_ZERO (dst_nt_header->OptionalHeader.FileAlignment);
423 dst_nt_header->OptionalHeader.SizeOfHeaders = DST_TO_OFFSET ();
425 for (i = 0; i < nt_header->FileHeader.NumberOfSections;
426 i++, section++, dst_section++)
428 char msg[100];
429 sprintf (msg, "Copying raw data for %s...", section->Name);
431 /* "Blank out" the two sections being relocated. */
432 if (section == import_section || section == reloc_section)
434 dst_section->Name[0] = 'X';
435 dst_section->Misc.VirtualSize =
436 ROUND_UP (dst_section->Misc.VirtualSize,
437 dst_nt_header->OptionalHeader.SectionAlignment);
438 dst_section->PointerToRawData = 0;
439 dst_section->SizeOfRawData = 0;
440 dst_section->Characteristics &= ~IMAGE_SCN_CNT_INITIALIZED_DATA;
441 dst_section->Characteristics |= IMAGE_SCN_CNT_UNINITIALIZED_DATA;
442 dst_section->Characteristics &= ~IMAGE_SCN_MEM_WRITE;
443 continue;
446 /* Update the file-relative offset for this section's raw data (if
447 it has any) in case things have been relocated; we will update
448 the other offsets below once we know where everything is. */
449 if (dst_section->PointerToRawData)
450 dst_section->PointerToRawData = DST_TO_OFFSET ();
452 /* Copy the original raw data. */
453 COPY_CHUNK
454 (msg, OFFSET_TO_PTR (section->PointerToRawData, p_infile),
455 section->SizeOfRawData);
457 /* Round up the raw data size to the new alignment. */
458 dst_section->SizeOfRawData =
459 ROUND_UP (dst_section->SizeOfRawData,
460 dst_nt_header->OptionalHeader.FileAlignment);
462 /* Align the next section's raw data area. */
463 ROUND_UP_DST_AND_ZERO (dst_nt_header->OptionalHeader.FileAlignment);
466 /* Add the extra section entries, copying the raw data we skipped
467 earlier. We'll patch up the data itself below. */
468 if (import_section != NULL)
470 dst_nt_header->FileHeader.NumberOfSections++;
471 dst_nt_header->OptionalHeader.SizeOfImage +=
472 ROUND_UP (import_section->Misc.VirtualSize,
473 dst_nt_header->OptionalHeader.SectionAlignment);
474 *dst_section = *import_section;
475 dst_section->VirtualAddress =
476 dst_section[-1].VirtualAddress
477 + ROUND_UP (dst_section[-1].Misc.VirtualSize,
478 dst_nt_header->OptionalHeader.SectionAlignment);
479 dst_section->PointerToRawData = DST_TO_OFFSET ();
480 /* Remember delta applied to import section. */
481 import_delta_rva = dst_section->VirtualAddress - import_section->VirtualAddress;
482 COPY_CHUNK
483 ("Relocating import directory",
484 OFFSET_TO_PTR (import_section->PointerToRawData, p_infile),
485 import_section->SizeOfRawData);
486 ROUND_UP_DST_AND_ZERO (dst_nt_header->OptionalHeader.FileAlignment);
487 dst_section++;
489 if (reloc_section != NULL)
491 dst_nt_header->FileHeader.NumberOfSections++;
492 dst_nt_header->OptionalHeader.SizeOfImage +=
493 ROUND_UP (reloc_section->Misc.VirtualSize,
494 dst_nt_header->OptionalHeader.SectionAlignment);
495 *dst_section = *reloc_section;
496 dst_section->VirtualAddress =
497 dst_section[-1].VirtualAddress
498 + ROUND_UP (dst_section[-1].Misc.VirtualSize,
499 dst_nt_header->OptionalHeader.SectionAlignment);
500 dst_section->PointerToRawData = DST_TO_OFFSET ();
501 /* Remember delta applied to reloc section. */
502 reloc_delta_rva = dst_section->VirtualAddress - reloc_section->VirtualAddress;
503 COPY_CHUNK
504 ("Relocating base relocations directory",
505 OFFSET_TO_PTR (reloc_section->PointerToRawData, p_infile),
506 reloc_section->SizeOfRawData);
507 ROUND_UP_DST_AND_ZERO (dst_nt_header->OptionalHeader.FileAlignment);
508 reloc_dir = &dst_nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
509 reloc_dir->VirtualAddress += reloc_delta_rva;
510 dst_section++;
513 /* Copy remainder of source image. */
514 section--;
515 offset = ROUND_UP (section->PointerToRawData + section->SizeOfRawData,
516 nt_header->OptionalHeader.FileAlignment);
517 COPY_CHUNK
518 ("Copying remainder of executable...",
519 OFFSET_TO_PTR (offset, p_infile),
520 p_infile->size - offset);
522 /* Final size for new image. */
523 p_outfile->size = DST_TO_OFFSET ();
525 /* Now patch up remaining file-relative offsets. */
526 printf ("Patching up raw data offsets...\n");
528 section = IMAGE_FIRST_SECTION (nt_header);
529 dst_section = IMAGE_FIRST_SECTION (dst_nt_header);
531 #define ADJUST_OFFSET(var) \
532 do { \
533 if ((var) != 0) \
534 (var) = relocate_offset ((var), nt_header, dst_nt_header); \
535 } while (0)
537 #define ADJUST_IMPORT_RVA(var) \
538 do { \
539 if ((var) != 0) \
540 *((DWORD_PTR *)&(var)) += import_delta_rva; \
541 } while (0)
543 dst_nt_header->OptionalHeader.SizeOfInitializedData = 0;
544 dst_nt_header->OptionalHeader.SizeOfUninitializedData = 0;
545 for (i = 0; i < dst_nt_header->FileHeader.NumberOfSections; i++)
547 /* Recompute data sizes for completeness. */
548 if (dst_section[i].Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
549 dst_nt_header->OptionalHeader.SizeOfInitializedData +=
550 ROUND_UP (dst_section[i].Misc.VirtualSize, dst_nt_header->OptionalHeader.FileAlignment);
551 else if (dst_section[i].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
552 dst_nt_header->OptionalHeader.SizeOfUninitializedData +=
553 ROUND_UP (dst_section[i].Misc.VirtualSize, dst_nt_header->OptionalHeader.FileAlignment);
555 ADJUST_OFFSET (dst_section[i].PointerToLinenumbers);
558 ADJUST_OFFSET (dst_nt_header->FileHeader.PointerToSymbolTable);
560 /* Update offsets in debug directory entries. Note that the debug
561 directory may be in the same section as the import table, so its
562 RVA may need to be adjusted too. */
564 PIMAGE_DATA_DIRECTORY debug_dir =
565 &dst_nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
566 PIMAGE_DEBUG_DIRECTORY debug_entry;
568 /* Update debug_dir if part of import_section. */
569 if (rva_to_section (debug_dir->VirtualAddress, nt_header) == import_section)
570 debug_dir->VirtualAddress += import_delta_rva;
572 section = rva_to_section (debug_dir->VirtualAddress, dst_nt_header);
573 if (section)
575 int size;
577 debug_entry = RVA_TO_PTR (debug_dir->VirtualAddress, section, p_outfile);
578 size = debug_dir->Size / sizeof (IMAGE_DEBUG_DIRECTORY);
580 for (i = 0; i < size; i++, debug_entry++)
582 /* The debug data itself is normally not part of any
583 section, but stored after all the raw section data. So
584 let relocate_offset do the work. */
585 ADJUST_OFFSET (debug_entry->PointerToRawData);
586 ADJUST_IMPORT_RVA (debug_entry->AddressOfRawData);
591 /* Update RVAs in import directory entries. */
593 PIMAGE_IMPORT_DESCRIPTOR imports;
594 PIMAGE_THUNK_DATA import_thunks;
596 import_dir = &dst_nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
597 import_dir->VirtualAddress += import_delta_rva;
599 section = rva_to_section (import_dir->VirtualAddress, dst_nt_header);
600 imports = RVA_TO_PTR (import_dir->VirtualAddress, section, p_outfile);
602 for ( ; imports->Name != 0; imports++)
604 ADJUST_IMPORT_RVA (imports->OriginalFirstThunk);
605 ADJUST_IMPORT_RVA (imports->FirstThunk);
606 ADJUST_IMPORT_RVA (imports->Name);
608 for (import_thunks = RVA_TO_PTR (imports->OriginalFirstThunk, section, p_outfile);
609 import_thunks->u1.Function != 0;
610 import_thunks++)
611 if ((import_thunks->u1.Ordinal >> 31) == 0)
612 ADJUST_IMPORT_RVA (import_thunks->u1.Ordinal);
614 for (import_thunks = RVA_TO_PTR (imports->FirstThunk, section, p_outfile);
615 import_thunks->u1.Function != 0;
616 import_thunks++)
617 if ((import_thunks->u1.Ordinal >> 31) == 0)
618 ADJUST_IMPORT_RVA (import_thunks->u1.Ordinal);
621 import_dir = &dst_nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT];
622 import_dir->VirtualAddress += import_delta_rva;
625 /* Fix up references to the import section. */
626 printf ("Applying fixups to import references...\n");
629 IMAGE_BASE_RELOCATION *relocs, *block, *start_block, *end_block;
630 DWORD_PTR import_start = import_section->VirtualAddress + dst_nt_header->OptionalHeader.ImageBase;
631 DWORD_PTR import_end = import_start + import_section->Misc.VirtualSize;
632 DWORD_PTR len_import_relocs;
633 DWORD_PTR len_remaining_relocs;
634 int seen_high = 0;
635 WORD * high_word;
636 void * holder;
638 reloc_dir = &dst_nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
639 reloc_section = rva_to_section (reloc_dir->VirtualAddress, dst_nt_header);
640 relocs = RVA_TO_PTR (reloc_dir->VirtualAddress, reloc_section, p_outfile);
642 /* Move the base relocations for the import section, if there are
643 any; the profiler needs to be able to patch RVAs in the import
644 section itself. */
645 for (block = relocs, start_block = 0;
646 (DWORD_PTR) block - (DWORD_PTR) relocs < reloc_dir->Size;
647 block = (void *)((DWORD_PTR) block + block->SizeOfBlock))
649 if (block->VirtualAddress >= import_section->VirtualAddress + import_section->Misc.VirtualSize)
651 end_block = block;
652 break;
654 if (block->VirtualAddress >= import_section->VirtualAddress)
656 if (start_block == 0)
657 start_block = block;
658 block->VirtualAddress += import_delta_rva;
661 if (start_block)
663 len_import_relocs = (DWORD_PTR) end_block - (DWORD_PTR) start_block;
664 len_remaining_relocs = (DWORD_PTR) relocs + reloc_dir->Size - (DWORD_PTR) end_block;
665 holder = malloc (len_import_relocs);
666 if (holder == 0)
667 abort ();
668 memcpy (holder, start_block, len_import_relocs);
669 memcpy (start_block, end_block, len_remaining_relocs);
670 memcpy ((char *) start_block + len_remaining_relocs, holder, len_import_relocs);
671 free (holder);
674 /* Walk up the list of base relocations, checking for references
675 to the old import section location, and patching them to
676 reference the new location. */
677 for (block = relocs;
678 (DWORD_PTR) block - (DWORD_PTR) relocs < reloc_dir->Size;
679 block = (void *)((DWORD_PTR) block + block->SizeOfBlock))
681 DWORD_PTR page_rva = block->VirtualAddress;
682 DWORD_PTR page_offset;
683 union {
684 WORD word;
685 DWORD_PTR dword;
686 } * ploc;
687 WORD *fixup;
689 section = rva_to_section (page_rva, dst_nt_header);
690 /* Don't apply fixups to the blanked sections. */
691 if (section->Name[0] == 'X')
692 continue;
694 for (fixup = (WORD *) &block[1];
695 (DWORD_PTR) fixup - (DWORD_PTR) block < block->SizeOfBlock;
696 fixup++)
698 page_offset = (*fixup) & 0xfff;
699 ploc = RVA_TO_PTR (page_rva + page_offset, section, p_outfile);
701 /* Unless our assumption is wrong, all low word fixups
702 should immediately follow a high fixup. */
703 if (seen_high && ((*fixup) >> 12) != IMAGE_REL_BASED_LOW)
704 abort ();
706 switch ((*fixup) >> 12)
708 case IMAGE_REL_BASED_ABSOLUTE:
709 break;
710 case IMAGE_REL_BASED_HIGH:
711 /* We must assume that high and low fixups occur in
712 pairs, specifically a low fixup immediately follows a
713 high fixup (normally separated by two bytes). We
714 have to process the two fixups together, to find out
715 the full pointer value and decide whether to apply
716 the fixup. */
717 seen_high = 1;
718 high_word = &ploc->word;
719 break;
720 case IMAGE_REL_BASED_LOW:
721 offset = (*high_word << 16) + ploc->word;
722 if (offset >= import_start && offset < import_end)
724 (*high_word) += import_delta_rva >> 16;
725 ploc->dword += import_delta_rva & 0xffff;
727 seen_high = 0;
728 break;
729 case IMAGE_REL_BASED_HIGHLOW:
730 /* Docs imply two words in big-endian order, so perhaps
731 this is only used on big-endian platforms, in which
732 case the obvious code will work. */
733 if (ploc->dword >= import_start && ploc->dword < import_end)
734 ploc->dword += import_delta_rva;
735 break;
736 case IMAGE_REL_BASED_HIGHADJ:
737 /* Docs don't say, but I guess this is the equivalent
738 for little-endian platforms. */
739 if (ploc->dword >= import_start && ploc->dword < import_end)
740 ploc->dword += import_delta_rva;
741 break;
742 case IMAGE_REL_BASED_MIPS_JMPADDR:
743 /* Don't know how to handle this; MIPS support has been
744 dropped from NT4 anyway. */
745 abort ();
746 break;
747 #ifdef IMAGE_REL_BASED_SECTION
748 case IMAGE_REL_BASED_SECTION:
749 case IMAGE_REL_BASED_REL32:
750 /* Docs don't say what these values mean. */
751 #endif
752 default:
753 abort ();
762 main (int argc, char **argv)
764 PIMAGE_DOS_HEADER dos_header;
765 PIMAGE_NT_HEADERS nt_header;
766 file_data in_file, out_file;
767 char out_filename[MAX_PATH], in_filename[MAX_PATH];
769 strcpy (in_filename, argv[1]);
770 strcpy (out_filename, argv[2]);
772 printf ("Preparing %s for profile prepping\n", out_filename);
774 /* Open the original (dumped) executable file for reference. */
775 if (!open_input_file (&in_file, in_filename))
777 printf ("Failed to open %s (%d)...bailing.\n",
778 in_filename, GetLastError ());
779 exit (1);
782 /* Create a new image that can be prepped; we don't expect the size to
783 change because we are only adding two new section table entries,
784 which should fit in the alignment slop. */
785 if (!open_output_file (&out_file, out_filename, in_file.size))
787 printf ("Failed to open %s (%d)...bailing.\n",
788 out_filename, GetLastError ());
789 exit (1);
792 copy_executable_and_move_sections (&in_file, &out_file);
794 /* Patch up header fields; profiler is picky about this. */
796 HANDLE hImagehelp = LoadLibrary ("imagehlp.dll");
797 DWORD_PTR headersum;
798 DWORD_PTR checksum;
800 dos_header = (PIMAGE_DOS_HEADER) out_file.file_base;
801 nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew);
803 nt_header->OptionalHeader.CheckSum = 0;
804 // nt_header->FileHeader.TimeDateStamp = time (NULL);
805 // dos_header->e_cp = size / 512;
806 // nt_header->OptionalHeader.SizeOfImage = size;
808 pfnCheckSumMappedFile = (void *) GetProcAddress (hImagehelp, "CheckSumMappedFile");
809 if (pfnCheckSumMappedFile)
811 // nt_header->FileHeader.TimeDateStamp = time (NULL);
812 pfnCheckSumMappedFile (out_file.file_base,
813 out_file.size,
814 &headersum,
815 &checksum);
816 nt_header->OptionalHeader.CheckSum = checksum;
818 FreeLibrary (hImagehelp);
821 close_file_data (&out_file);
822 close_file_data (&in_file);
824 return 0;
827 /* eof */