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