Trailing whitespace deleted.
[bpt/emacs.git] / nt / addsection.c
1 /* Add an uninitialized data section to an executable.
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> 04-Jan-1999
22 based on code from unexw32.c
23 */
24
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <fcntl.h>
28 #include <time.h>
29 #ifdef __GNUC__
30 #define _ANONYMOUS_UNION
31 #define _ANONYMOUS_STRUCT
32 #endif
33 #include <windows.h>
34
35 /* Include relevant definitions from IMAGEHLP.H, which can be found
36 in \\win32sdk\mstools\samples\image\include\imagehlp.h. */
37
38 PIMAGE_NT_HEADERS
39 (__stdcall * pfnCheckSumMappedFile) (LPVOID BaseAddress,
40 DWORD FileLength,
41 LPDWORD HeaderSum,
42 LPDWORD CheckSum);
43
44 #undef min
45 #undef max
46 #define min(x, y) (((x) < (y)) ? (x) : (y))
47 #define max(x, y) (((x) > (y)) ? (x) : (y))
48
49
50 /* File handling. */
51
52 typedef struct file_data {
53 char *name;
54 unsigned long size;
55 HANDLE file;
56 HANDLE file_mapping;
57 unsigned char *file_base;
58 } file_data;
59
60 int
61 open_input_file (file_data *p_file, char *filename)
62 {
63 HANDLE file;
64 HANDLE file_mapping;
65 void *file_base;
66 unsigned long size, upper_size;
67
68 file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
69 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
70 if (file == INVALID_HANDLE_VALUE)
71 return FALSE;
72
73 size = GetFileSize (file, &upper_size);
74 file_mapping = CreateFileMapping (file, NULL, PAGE_READONLY,
75 0, size, NULL);
76 if (!file_mapping)
77 return FALSE;
78
79 file_base = MapViewOfFile (file_mapping, FILE_MAP_READ, 0, 0, size);
80 if (file_base == 0)
81 return FALSE;
82
83 p_file->name = filename;
84 p_file->size = size;
85 p_file->file = file;
86 p_file->file_mapping = file_mapping;
87 p_file->file_base = file_base;
88
89 return TRUE;
90 }
91
92 int
93 open_output_file (file_data *p_file, char *filename, unsigned long size)
94 {
95 HANDLE file;
96 HANDLE file_mapping;
97 void *file_base;
98
99 file = CreateFile (filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
100 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
101 if (file == INVALID_HANDLE_VALUE)
102 return FALSE;
103
104 file_mapping = CreateFileMapping (file, NULL, PAGE_READWRITE,
105 0, size, NULL);
106 if (!file_mapping)
107 return FALSE;
108
109 file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size);
110 if (file_base == 0)
111 return FALSE;
112
113 p_file->name = filename;
114 p_file->size = size;
115 p_file->file = file;
116 p_file->file_mapping = file_mapping;
117 p_file->file_base = file_base;
118
119 return TRUE;
120 }
121
122 /* Close the system structures associated with the given file. */
123 void
124 close_file_data (file_data *p_file)
125 {
126 UnmapViewOfFile (p_file->file_base);
127 CloseHandle (p_file->file_mapping);
128 /* For the case of output files, set final size. */
129 SetFilePointer (p_file->file, p_file->size, NULL, FILE_BEGIN);
130 SetEndOfFile (p_file->file);
131 CloseHandle (p_file->file);
132 }
133
134
135 /* Routines to manipulate NT executable file sections. */
136
137 unsigned long
138 get_unrounded_section_size (PIMAGE_SECTION_HEADER p_section)
139 {
140 /* The true section size, before rounding, for an initialized data or
141 code section. (Supposedly some linkers swap the meaning of these
142 two values.) */
143 return min (p_section->SizeOfRawData,
144 p_section->Misc.VirtualSize);
145 }
146
147 /* Return pointer to section header for named section. */
148 IMAGE_SECTION_HEADER *
149 find_section (char * name, IMAGE_NT_HEADERS * nt_header)
150 {
151 PIMAGE_SECTION_HEADER section;
152 int i;
153
154 section = IMAGE_FIRST_SECTION (nt_header);
155
156 for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
157 {
158 if (strcmp (section->Name, name) == 0)
159 return section;
160 section++;
161 }
162 return NULL;
163 }
164
165 /* Return pointer to section header for section containing the given
166 relative virtual address. */
167 IMAGE_SECTION_HEADER *
168 rva_to_section (DWORD rva, IMAGE_NT_HEADERS * nt_header)
169 {
170 PIMAGE_SECTION_HEADER section;
171 int i;
172
173 section = IMAGE_FIRST_SECTION (nt_header);
174
175 for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
176 {
177 /* Some linkers (eg. the NT SDK linker I believe) swapped the
178 meaning of these two values - or rather, they ignored
179 VirtualSize entirely and always set it to zero. This affects
180 some very old exes (eg. gzip dated Dec 1993). Since
181 w32_executable_type relies on this function to work reliably,
182 we need to cope with this. */
183 DWORD real_size = max (section->SizeOfRawData,
184 section->Misc.VirtualSize);
185 if (rva >= section->VirtualAddress
186 && rva < section->VirtualAddress + real_size)
187 return section;
188 section++;
189 }
190 return NULL;
191 }
192
193 /* Return pointer to section header for section containing the given
194 offset in its raw data area. */
195 IMAGE_SECTION_HEADER *
196 offset_to_section (DWORD offset, 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 if (offset >= section->PointerToRawData
206 && offset < section->PointerToRawData + section->SizeOfRawData)
207 return section;
208 section++;
209 }
210 return NULL;
211 }
212
213 /* Return offset to an object in dst, given offset in src. We assume
214 there is at least one section in both src and dst images, and that
215 the some sections may have been added to dst (after sections in src). */
216 static DWORD
217 relocate_offset (DWORD offset,
218 IMAGE_NT_HEADERS * src_nt_header,
219 IMAGE_NT_HEADERS * dst_nt_header)
220 {
221 PIMAGE_SECTION_HEADER src_section = IMAGE_FIRST_SECTION (src_nt_header);
222 PIMAGE_SECTION_HEADER dst_section = IMAGE_FIRST_SECTION (dst_nt_header);
223 int i = 0;
224
225 while (offset >= src_section->PointerToRawData)
226 {
227 if (offset < src_section->PointerToRawData + src_section->SizeOfRawData)
228 break;
229 i++;
230 if (i == src_nt_header->FileHeader.NumberOfSections)
231 {
232 /* Handle offsets after the last section. */
233 dst_section = IMAGE_FIRST_SECTION (dst_nt_header);
234 dst_section += dst_nt_header->FileHeader.NumberOfSections - 1;
235 while (dst_section->PointerToRawData == 0)
236 dst_section--;
237 while (src_section->PointerToRawData == 0)
238 src_section--;
239 return offset
240 + (dst_section->PointerToRawData + dst_section->SizeOfRawData)
241 - (src_section->PointerToRawData + src_section->SizeOfRawData);
242 }
243 src_section++;
244 dst_section++;
245 }
246 return offset +
247 (dst_section->PointerToRawData - src_section->PointerToRawData);
248 }
249
250 #define OFFSET_TO_RVA(offset, section) \
251 (section->VirtualAddress + ((DWORD)(offset) - section->PointerToRawData))
252
253 #define RVA_TO_OFFSET(rva, section) \
254 (section->PointerToRawData + ((DWORD)(rva) - section->VirtualAddress))
255
256 #define RVA_TO_SECTION_OFFSET(rva, section) \
257 ((DWORD)(rva) - section->VirtualAddress)
258
259 /* Convert address in executing image to RVA. */
260 #define PTR_TO_RVA(ptr) ((DWORD)(ptr) - (DWORD) GetModuleHandle (NULL))
261
262 #define PTR_TO_OFFSET(ptr, pfile_data) \
263 ((unsigned char *)(ptr) - (pfile_data)->file_base)
264
265 #define OFFSET_TO_PTR(offset, pfile_data) \
266 ((pfile_data)->file_base + (DWORD)(offset))
267
268 #define ROUND_UP(p, align) (((DWORD)(p) + (align)-1) & ~((align)-1))
269 #define ROUND_DOWN(p, align) ((DWORD)(p) & ~((align)-1))
270
271
272 static void
273 copy_executable_and_add_section (file_data *p_infile,
274 file_data *p_outfile,
275 char *new_section_name,
276 DWORD new_section_size)
277 {
278 unsigned char *dst;
279 PIMAGE_DOS_HEADER dos_header;
280 PIMAGE_NT_HEADERS nt_header;
281 PIMAGE_NT_HEADERS dst_nt_header;
282 PIMAGE_SECTION_HEADER section;
283 PIMAGE_SECTION_HEADER dst_section;
284 DWORD offset;
285 int i;
286
287 #define COPY_CHUNK(message, src, size) \
288 do { \
289 unsigned char *s = (void *)(src); \
290 unsigned long count = (size); \
291 printf ("%s\n", (message)); \
292 printf ("\t0x%08x Offset in input file.\n", s - p_infile->file_base); \
293 printf ("\t0x%08x Offset in output file.\n", dst - p_outfile->file_base); \
294 printf ("\t0x%08x Size in bytes.\n", count); \
295 memcpy (dst, s, count); \
296 dst += count; \
297 } while (0)
298
299 #define DST_TO_OFFSET() PTR_TO_OFFSET (dst, p_outfile)
300 #define ROUND_UP_DST_AND_ZERO(align) \
301 do { \
302 unsigned char *newdst = p_outfile->file_base \
303 + ROUND_UP (DST_TO_OFFSET (), (align)); \
304 /* Zero the alignment slop; it may actually initialize real data. */ \
305 memset (dst, 0, newdst - dst); \
306 dst = newdst; \
307 } while (0)
308
309 /* Copy the source image sequentially, ie. section by section after
310 copying the headers and section table, to simplify the process of
311 adding an extra section table entry (which might force the raw
312 section data to be relocated).
313
314 Note that dst is updated implicitly by each COPY_CHUNK. */
315
316 dos_header = (PIMAGE_DOS_HEADER) p_infile->file_base;
317 nt_header = (PIMAGE_NT_HEADERS) (((unsigned long) dos_header) +
318 dos_header->e_lfanew);
319 section = IMAGE_FIRST_SECTION (nt_header);
320
321 dst = (unsigned char *) p_outfile->file_base;
322
323 COPY_CHUNK ("Copying DOS header...", dos_header,
324 (DWORD) nt_header - (DWORD) dos_header);
325 dst_nt_header = (PIMAGE_NT_HEADERS) dst;
326 COPY_CHUNK ("Copying NT header...", nt_header,
327 (DWORD) section - (DWORD) nt_header);
328 dst_section = (PIMAGE_SECTION_HEADER) dst;
329 COPY_CHUNK ("Copying section table...", section,
330 nt_header->FileHeader.NumberOfSections * sizeof (*section));
331
332 /* To improve the efficiency of demand loading, make the file
333 alignment match the section alignment (VC++ 6.0 does this by
334 default anyway). */
335 dst_nt_header->OptionalHeader.FileAlignment =
336 dst_nt_header->OptionalHeader.SectionAlignment;
337
338 /* Add an uninitialized data section at the end, of the specified name
339 and virtual size. */
340 if (find_section (new_section_name, nt_header) == NULL)
341 /* Leave room for extra section table entry; filled in below. */
342 dst += sizeof (*section);
343 else
344 new_section_name = NULL;
345
346 /* Align the first section's raw data area, and set the header size
347 field accordingly. */
348 ROUND_UP_DST_AND_ZERO (dst_nt_header->OptionalHeader.FileAlignment);
349 dst_nt_header->OptionalHeader.SizeOfHeaders = DST_TO_OFFSET ();
350
351 for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
352 {
353 char msg[100];
354 sprintf (msg, "Copying raw data for %s...", section->Name);
355
356 /* Update the file-relative offset for this section's raw data (if
357 it has any) in case things have been relocated; we will update
358 the other offsets below once we know where everything is. */
359 if (dst_section->PointerToRawData)
360 dst_section->PointerToRawData = DST_TO_OFFSET ();
361
362 /* Can always copy the original raw data. */
363 COPY_CHUNK
364 (msg, OFFSET_TO_PTR (section->PointerToRawData, p_infile),
365 section->SizeOfRawData);
366
367 /* Round up the raw data size to the new alignment. */
368 dst_section->SizeOfRawData =
369 ROUND_UP (dst_section->SizeOfRawData,
370 dst_nt_header->OptionalHeader.FileAlignment);
371
372 /* Align the next section's raw data area. */
373 ROUND_UP_DST_AND_ZERO (dst_nt_header->OptionalHeader.FileAlignment);
374
375 section++;
376 dst_section++;
377 }
378
379 /* Add the extra section entry (which adds no raw data). */
380 if (new_section_name != NULL)
381 {
382 dst_nt_header->FileHeader.NumberOfSections++;
383 dst_nt_header->OptionalHeader.SizeOfImage += new_section_size;
384 strncpy (dst_section->Name, new_section_name, sizeof (dst_section->Name));
385 dst_section->VirtualAddress =
386 section[-1].VirtualAddress
387 + ROUND_UP (section[-1].Misc.VirtualSize,
388 dst_nt_header->OptionalHeader.SectionAlignment);
389 dst_section->Misc.VirtualSize = new_section_size;
390 dst_section->PointerToRawData = 0;
391 dst_section->SizeOfRawData = 0;
392 dst_section->Characteristics =
393 IMAGE_SCN_CNT_UNINITIALIZED_DATA
394 | IMAGE_SCN_MEM_READ
395 | IMAGE_SCN_MEM_WRITE;
396 }
397
398 /* Copy remainder of source image. */
399 section--;
400 offset = ROUND_UP (section->PointerToRawData + section->SizeOfRawData,
401 nt_header->OptionalHeader.FileAlignment);
402 COPY_CHUNK
403 ("Copying remainder of executable...",
404 OFFSET_TO_PTR (offset, p_infile),
405 p_infile->size - offset);
406
407 /* Final size for new image. */
408 p_outfile->size = DST_TO_OFFSET ();
409
410 /* Now patch up remaining file-relative offsets. */
411 section = IMAGE_FIRST_SECTION (nt_header);
412 dst_section = IMAGE_FIRST_SECTION (dst_nt_header);
413
414 #define ADJUST_OFFSET(var) \
415 do { \
416 if ((var) != 0) \
417 (var) = relocate_offset ((var), nt_header, dst_nt_header); \
418 } while (0)
419
420 dst_nt_header->OptionalHeader.SizeOfInitializedData = 0;
421 dst_nt_header->OptionalHeader.SizeOfUninitializedData = 0;
422 for (i = 0; i < dst_nt_header->FileHeader.NumberOfSections; i++)
423 {
424 /* Recompute data sizes for completeness. */
425 if (dst_section[i].Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
426 dst_nt_header->OptionalHeader.SizeOfInitializedData +=
427 ROUND_UP (dst_section[i].Misc.VirtualSize, dst_nt_header->OptionalHeader.FileAlignment);
428 else if (dst_section[i].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
429 dst_nt_header->OptionalHeader.SizeOfUninitializedData +=
430 ROUND_UP (dst_section[i].Misc.VirtualSize, dst_nt_header->OptionalHeader.FileAlignment);
431
432 ADJUST_OFFSET (dst_section[i].PointerToLinenumbers);
433 }
434
435 ADJUST_OFFSET (dst_nt_header->FileHeader.PointerToSymbolTable);
436
437 /* Update offsets in debug directory entries. */
438 {
439 IMAGE_DATA_DIRECTORY debug_dir =
440 dst_nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
441 PIMAGE_DEBUG_DIRECTORY debug_entry;
442
443 section = rva_to_section (debug_dir.VirtualAddress, dst_nt_header);
444 if (section)
445 {
446 debug_entry = (PIMAGE_DEBUG_DIRECTORY)
447 (RVA_TO_OFFSET (debug_dir.VirtualAddress, section) + p_outfile->file_base);
448 debug_dir.Size /= sizeof (IMAGE_DEBUG_DIRECTORY);
449
450 for (i = 0; i < debug_dir.Size; i++, debug_entry++)
451 ADJUST_OFFSET (debug_entry->PointerToRawData);
452 }
453 }
454 }
455
456
457 int
458 main (int argc, char **argv)
459 {
460 file_data in_file, out_file;
461 char out_filename[MAX_PATH], in_filename[MAX_PATH];
462 unsigned long size;
463 PIMAGE_DOS_HEADER dos_header;
464 PIMAGE_NT_HEADERS nt_header;
465
466 #define OLD_NAME argv[1]
467 #define NEW_NAME argv[2]
468 #define SECTION_NAME argv[3]
469 #define SECTION_SIZE argv[4]
470
471 strcpy (in_filename, OLD_NAME);
472 strcpy (out_filename, NEW_NAME);
473
474 printf ("Dumping from %s\n", in_filename);
475 printf (" to %s\n", out_filename);
476
477 /* Open the undumped executable file. */
478 if (!open_input_file (&in_file, in_filename))
479 {
480 printf ("Failed to open %s (%d)...bailing.\n",
481 in_filename, GetLastError ());
482 exit (1);
483 }
484 dos_header = (PIMAGE_DOS_HEADER) in_file.file_base;
485 nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew);
486 /* Allow for expansion due to increasing file align to section align.
487 We can overestimate here, since close_file_data will update the
488 size exactly. */
489 size = in_file.size
490 + nt_header->OptionalHeader.SectionAlignment
491 * nt_header->FileHeader.NumberOfSections;
492 if (!open_output_file (&out_file, out_filename, size))
493 {
494 printf ("Failed to open %s (%d)...bailing.\n",
495 out_filename, GetLastError ());
496 exit (1);
497 }
498
499 copy_executable_and_add_section (&in_file, &out_file,
500 SECTION_NAME,
501 atoi (SECTION_SIZE) * 1024 * 1024);
502
503 /* Patch up header fields; profiler is picky about this. */
504 {
505 HANDLE hImagehelp = LoadLibrary ("imagehlp.dll");
506 DWORD headersum;
507 DWORD checksum;
508
509 dos_header = (PIMAGE_DOS_HEADER) out_file.file_base;
510 nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew);
511
512 nt_header->OptionalHeader.CheckSum = 0;
513 // nt_header->FileHeader.TimeDateStamp = time (NULL);
514 // dos_header->e_cp = size / 512;
515 // nt_header->OptionalHeader.SizeOfImage = size;
516
517 pfnCheckSumMappedFile = (void *) GetProcAddress (hImagehelp, "CheckSumMappedFile");
518 if (pfnCheckSumMappedFile)
519 {
520 // nt_header->FileHeader.TimeDateStamp = time (NULL);
521 pfnCheckSumMappedFile (out_file.file_base,
522 out_file.size,
523 &headersum,
524 &checksum);
525 nt_header->OptionalHeader.CheckSum = checksum;
526 }
527 FreeLibrary (hImagehelp);
528 }
529
530 close_file_data (&in_file);
531 close_file_data (&out_file);
532
533 return 0;
534 }
535
536 /* eof */