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