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