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