Add 2012 to FSF copyright years for Emacs files
[bpt/emacs.git] / nt / addsection.c
CommitLineData
6012853d 1/* Add an uninitialized data section to an executable.
acaf905b 2 Copyright (C) 1999, 2001-2012 Free Software Foundation, Inc.
6012853d
AI
3
4This file is part of GNU Emacs.
5
eef0be9e 6GNU Emacs is free software: you can redistribute it and/or modify
6012853d 7it under the terms of the GNU General Public License as published by
eef0be9e
GM
8the Free Software Foundation, either version 3 of the License, or
9(at your option) any later version.
6012853d
AI
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
eef0be9e
GM
17along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
18
6012853d
AI
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>
a1375c9f
AI
28#ifdef __GNUC__
29#define _ANONYMOUS_UNION
30#define _ANONYMOUS_STRUCT
31#endif
6012853d
AI
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
37PIMAGE_NT_HEADERS
38(__stdcall * pfnCheckSumMappedFile) (LPVOID BaseAddress,
0597ab06
JB
39 DWORD FileLength,
40 LPDWORD HeaderSum,
41 LPDWORD CheckSum);
6012853d
AI
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
51typedef struct file_data {
0597ab06
JB
52 const char *name;
53 unsigned long size;
54 HANDLE file;
55 HANDLE file_mapping;
56 unsigned char *file_base;
6012853d
AI
57} file_data;
58
59int
0597ab06 60open_input_file (file_data *p_file, const char *filename)
6012853d
AI
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);
177c0ea7 69 if (file == INVALID_HANDLE_VALUE)
6012853d
AI
70 return FALSE;
71
72 size = GetFileSize (file, &upper_size);
177c0ea7 73 file_mapping = CreateFileMapping (file, NULL, PAGE_READONLY,
6012853d 74 0, size, NULL);
177c0ea7 75 if (!file_mapping)
6012853d
AI
76 return FALSE;
77
78 file_base = MapViewOfFile (file_mapping, FILE_MAP_READ, 0, 0, size);
177c0ea7 79 if (file_base == 0)
6012853d
AI
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
91int
0597ab06 92open_output_file (file_data *p_file, const char *filename, unsigned long size)
6012853d
AI
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);
177c0ea7 100 if (file == INVALID_HANDLE_VALUE)
6012853d
AI
101 return FALSE;
102
177c0ea7 103 file_mapping = CreateFileMapping (file, NULL, PAGE_READWRITE,
6012853d 104 0, size, NULL);
177c0ea7 105 if (!file_mapping)
6012853d 106 return FALSE;
177c0ea7 107
6012853d 108 file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size);
177c0ea7 109 if (file_base == 0)
6012853d 110 return FALSE;
177c0ea7 111
6012853d
AI
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. */
122void
123close_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
136unsigned long
137get_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. */
147IMAGE_SECTION_HEADER *
0597ab06 148find_section (const char *name, IMAGE_NT_HEADERS *nt_header)
6012853d
AI
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. */
166IMAGE_SECTION_HEADER *
167rva_to_section (DWORD 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 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. */
194IMAGE_SECTION_HEADER *
195offset_to_section (DWORD 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). */
215static DWORD
216relocate_offset (DWORD 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)(offset) - section->PointerToRawData))
251
252#define RVA_TO_OFFSET(rva, section) \
253 (section->PointerToRawData + ((DWORD)(rva) - section->VirtualAddress))
254
255#define RVA_TO_SECTION_OFFSET(rva, section) \
256 ((DWORD)(rva) - section->VirtualAddress)
257
258/* Convert address in executing image to RVA. */
259#define PTR_TO_RVA(ptr) ((DWORD)(ptr) - (DWORD) GetModuleHandle (NULL))
260
261#define PTR_TO_OFFSET(ptr, pfile_data) \
0597ab06 262 ((unsigned const char *)(ptr) - (pfile_data)->file_base)
6012853d
AI
263
264#define OFFSET_TO_PTR(offset, pfile_data) \
265 ((pfile_data)->file_base + (DWORD)(offset))
266
267#define ROUND_UP(p, align) (((DWORD)(p) + (align)-1) & ~((align)-1))
268#define ROUND_DOWN(p, align) ((DWORD)(p) & ~((align)-1))
269
270
271static void
177c0ea7 272copy_executable_and_add_section (file_data *p_infile,
6012853d 273 file_data *p_outfile,
0597ab06 274 const char *new_section_name,
6012853d
AI
275 DWORD new_section_size)
276{
277 unsigned char *dst;
278 PIMAGE_DOS_HEADER dos_header;
279 PIMAGE_NT_HEADERS nt_header;
280 PIMAGE_NT_HEADERS dst_nt_header;
281 PIMAGE_SECTION_HEADER section;
282 PIMAGE_SECTION_HEADER dst_section;
283 DWORD offset;
284 int i;
d2fcf769 285 int be_verbose = GetEnvironmentVariable ("DEBUG_DUMP", NULL, 0) > 0;
6012853d 286
d2fcf769 287#define COPY_CHUNK(message, src, size, verbose) \
6012853d 288 do { \
0597ab06 289 unsigned const char *s = (void *)(src); \
6012853d 290 unsigned long count = (size); \
d2fcf769
EZ
291 if (verbose) \
292 { \
293 printf ("%s\n", (message)); \
294 printf ("\t0x%08x Offset in input file.\n", s - p_infile->file_base); \
295 printf ("\t0x%08x Offset in output file.\n", dst - p_outfile->file_base); \
296 printf ("\t0x%08x Size in bytes.\n", count); \
297 } \
6012853d
AI
298 memcpy (dst, s, count); \
299 dst += count; \
300 } while (0)
301
302#define DST_TO_OFFSET() PTR_TO_OFFSET (dst, p_outfile)
7466aec4
AI
303#define ROUND_UP_DST_AND_ZERO(align) \
304 do { \
305 unsigned char *newdst = p_outfile->file_base \
306 + ROUND_UP (DST_TO_OFFSET (), (align)); \
307 /* Zero the alignment slop; it may actually initialize real data. */ \
308 memset (dst, 0, newdst - dst); \
309 dst = newdst; \
310 } while (0)
6012853d
AI
311
312 /* Copy the source image sequentially, ie. section by section after
313 copying the headers and section table, to simplify the process of
314 adding an extra section table entry (which might force the raw
315 section data to be relocated).
316
317 Note that dst is updated implicitly by each COPY_CHUNK. */
318
319 dos_header = (PIMAGE_DOS_HEADER) p_infile->file_base;
177c0ea7 320 nt_header = (PIMAGE_NT_HEADERS) (((unsigned long) dos_header) +
6012853d
AI
321 dos_header->e_lfanew);
322 section = IMAGE_FIRST_SECTION (nt_header);
177c0ea7 323
6012853d
AI
324 dst = (unsigned char *) p_outfile->file_base;
325
326 COPY_CHUNK ("Copying DOS header...", dos_header,
d2fcf769 327 (DWORD) nt_header - (DWORD) dos_header, be_verbose);
6012853d
AI
328 dst_nt_header = (PIMAGE_NT_HEADERS) dst;
329 COPY_CHUNK ("Copying NT header...", nt_header,
d2fcf769 330 (DWORD) section - (DWORD) nt_header, be_verbose);
6012853d
AI
331 dst_section = (PIMAGE_SECTION_HEADER) dst;
332 COPY_CHUNK ("Copying section table...", section,
d2fcf769
EZ
333 nt_header->FileHeader.NumberOfSections * sizeof (*section),
334 be_verbose);
6012853d
AI
335
336 /* To improve the efficiency of demand loading, make the file
337 alignment match the section alignment (VC++ 6.0 does this by
338 default anyway). */
339 dst_nt_header->OptionalHeader.FileAlignment =
340 dst_nt_header->OptionalHeader.SectionAlignment;
341
342 /* Add an uninitialized data section at the end, of the specified name
343 and virtual size. */
344 if (find_section (new_section_name, nt_header) == NULL)
345 /* Leave room for extra section table entry; filled in below. */
346 dst += sizeof (*section);
347 else
348 new_section_name = NULL;
349
7466aec4
AI
350 /* Align the first section's raw data area, and set the header size
351 field accordingly. */
352 ROUND_UP_DST_AND_ZERO (dst_nt_header->OptionalHeader.FileAlignment);
353 dst_nt_header->OptionalHeader.SizeOfHeaders = DST_TO_OFFSET ();
354
6012853d
AI
355 for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
356 {
357 char msg[100];
d2fcf769
EZ
358 /* Windows section names are fixed 8-char strings, only
359 zero-terminated if the name is shorter than 8 characters. */
360 sprintf (msg, "Copying raw data for %.8s...", section->Name);
6012853d 361
6012853d
AI
362 /* Update the file-relative offset for this section's raw data (if
363 it has any) in case things have been relocated; we will update
364 the other offsets below once we know where everything is. */
365 if (dst_section->PointerToRawData)
366 dst_section->PointerToRawData = DST_TO_OFFSET ();
367
368 /* Can always copy the original raw data. */
369 COPY_CHUNK
370 (msg, OFFSET_TO_PTR (section->PointerToRawData, p_infile),
d2fcf769 371 section->SizeOfRawData, be_verbose);
6012853d
AI
372
373 /* Round up the raw data size to the new alignment. */
374 dst_section->SizeOfRawData =
375 ROUND_UP (dst_section->SizeOfRawData,
376 dst_nt_header->OptionalHeader.FileAlignment);
377
7466aec4
AI
378 /* Align the next section's raw data area. */
379 ROUND_UP_DST_AND_ZERO (dst_nt_header->OptionalHeader.FileAlignment);
380
6012853d
AI
381 section++;
382 dst_section++;
383 }
384
6012853d
AI
385 /* Add the extra section entry (which adds no raw data). */
386 if (new_section_name != NULL)
387 {
388 dst_nt_header->FileHeader.NumberOfSections++;
389 dst_nt_header->OptionalHeader.SizeOfImage += new_section_size;
390 strncpy (dst_section->Name, new_section_name, sizeof (dst_section->Name));
391 dst_section->VirtualAddress =
392 section[-1].VirtualAddress
393 + ROUND_UP (section[-1].Misc.VirtualSize,
394 dst_nt_header->OptionalHeader.SectionAlignment);
395 dst_section->Misc.VirtualSize = new_section_size;
396 dst_section->PointerToRawData = 0;
397 dst_section->SizeOfRawData = 0;
398 dst_section->Characteristics =
399 IMAGE_SCN_CNT_UNINITIALIZED_DATA
400 | IMAGE_SCN_MEM_READ
401 | IMAGE_SCN_MEM_WRITE;
402 }
403
404 /* Copy remainder of source image. */
405 section--;
406 offset = ROUND_UP (section->PointerToRawData + section->SizeOfRawData,
407 nt_header->OptionalHeader.FileAlignment);
408 COPY_CHUNK
409 ("Copying remainder of executable...",
410 OFFSET_TO_PTR (offset, p_infile),
d2fcf769 411 p_infile->size - offset, be_verbose);
6012853d
AI
412
413 /* Final size for new image. */
414 p_outfile->size = DST_TO_OFFSET ();
415
416 /* Now patch up remaining file-relative offsets. */
417 section = IMAGE_FIRST_SECTION (nt_header);
418 dst_section = IMAGE_FIRST_SECTION (dst_nt_header);
419
420#define ADJUST_OFFSET(var) \
421 do { \
422 if ((var) != 0) \
423 (var) = relocate_offset ((var), nt_header, dst_nt_header); \
424 } while (0)
425
426 dst_nt_header->OptionalHeader.SizeOfInitializedData = 0;
427 dst_nt_header->OptionalHeader.SizeOfUninitializedData = 0;
428 for (i = 0; i < dst_nt_header->FileHeader.NumberOfSections; i++)
429 {
430 /* Recompute data sizes for completeness. */
431 if (dst_section[i].Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
432 dst_nt_header->OptionalHeader.SizeOfInitializedData +=
433 ROUND_UP (dst_section[i].Misc.VirtualSize, dst_nt_header->OptionalHeader.FileAlignment);
434 else if (dst_section[i].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
435 dst_nt_header->OptionalHeader.SizeOfUninitializedData +=
436 ROUND_UP (dst_section[i].Misc.VirtualSize, dst_nt_header->OptionalHeader.FileAlignment);
437
438 ADJUST_OFFSET (dst_section[i].PointerToLinenumbers);
439 }
440
441 ADJUST_OFFSET (dst_nt_header->FileHeader.PointerToSymbolTable);
442
443 /* Update offsets in debug directory entries. */
444 {
445 IMAGE_DATA_DIRECTORY debug_dir =
446 dst_nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
447 PIMAGE_DEBUG_DIRECTORY debug_entry;
448
449 section = rva_to_section (debug_dir.VirtualAddress, dst_nt_header);
450 if (section)
451 {
452 debug_entry = (PIMAGE_DEBUG_DIRECTORY)
453 (RVA_TO_OFFSET (debug_dir.VirtualAddress, section) + p_outfile->file_base);
454 debug_dir.Size /= sizeof (IMAGE_DEBUG_DIRECTORY);
455
456 for (i = 0; i < debug_dir.Size; i++, debug_entry++)
457 ADJUST_OFFSET (debug_entry->PointerToRawData);
458 }
459 }
460}
461
462
463int
464main (int argc, char **argv)
465{
466 file_data in_file, out_file;
467 char out_filename[MAX_PATH], in_filename[MAX_PATH];
468 unsigned long size;
469 PIMAGE_DOS_HEADER dos_header;
470 PIMAGE_NT_HEADERS nt_header;
471
472#define OLD_NAME argv[1]
473#define NEW_NAME argv[2]
474#define SECTION_NAME argv[3]
475#define SECTION_SIZE argv[4]
476
477 strcpy (in_filename, OLD_NAME);
478 strcpy (out_filename, NEW_NAME);
479
480 printf ("Dumping from %s\n", in_filename);
481 printf (" to %s\n", out_filename);
482
483 /* Open the undumped executable file. */
484 if (!open_input_file (&in_file, in_filename))
485 {
177c0ea7 486 printf ("Failed to open %s (%d)...bailing.\n",
6012853d
AI
487 in_filename, GetLastError ());
488 exit (1);
489 }
490 dos_header = (PIMAGE_DOS_HEADER) in_file.file_base;
491 nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew);
492 /* Allow for expansion due to increasing file align to section align.
493 We can overestimate here, since close_file_data will update the
494 size exactly. */
495 size = in_file.size
496 + nt_header->OptionalHeader.SectionAlignment
497 * nt_header->FileHeader.NumberOfSections;
498 if (!open_output_file (&out_file, out_filename, size))
499 {
177c0ea7 500 printf ("Failed to open %s (%d)...bailing.\n",
6012853d
AI
501 out_filename, GetLastError ());
502 exit (1);
503 }
504
505 copy_executable_and_add_section (&in_file, &out_file,
506 SECTION_NAME,
507 atoi (SECTION_SIZE) * 1024 * 1024);
508
509 /* Patch up header fields; profiler is picky about this. */
510 {
511 HANDLE hImagehelp = LoadLibrary ("imagehlp.dll");
512 DWORD headersum;
513 DWORD checksum;
514
515 dos_header = (PIMAGE_DOS_HEADER) out_file.file_base;
516 nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew);
517
518 nt_header->OptionalHeader.CheckSum = 0;
519// nt_header->FileHeader.TimeDateStamp = time (NULL);
520// dos_header->e_cp = size / 512;
521// nt_header->OptionalHeader.SizeOfImage = size;
522
523 pfnCheckSumMappedFile = (void *) GetProcAddress (hImagehelp, "CheckSumMappedFile");
524 if (pfnCheckSumMappedFile)
525 {
526// nt_header->FileHeader.TimeDateStamp = time (NULL);
527 pfnCheckSumMappedFile (out_file.file_base,
528 out_file.size,
529 &headersum,
530 &checksum);
531 nt_header->OptionalHeader.CheckSum = checksum;
532 }
533 FreeLibrary (hImagehelp);
534 }
535
536 close_file_data (&in_file);
537 close_file_data (&out_file);
538
539 return 0;
540}
541
542/* eof */
ab5796a9 543