1 /* Add an uninitialized data section to an executable.
2 Copyright (C) 1999, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007 Free Software Foundation, Inc.
5 This file is part of GNU Emacs.
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 3, or (at your option)
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.
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.
22 Andrew Innes <andrewi@harlequin.co.uk> 04-Jan-1999
23 based on code from unexw32.c
31 #define _ANONYMOUS_UNION
32 #define _ANONYMOUS_STRUCT
36 /* Include relevant definitions from IMAGEHLP.H, which can be found
37 in \\win32sdk\mstools\samples\image\include\imagehlp.h. */
40 (__stdcall
* pfnCheckSumMappedFile
) (LPVOID BaseAddress
,
47 #define min(x, y) (((x) < (y)) ? (x) : (y))
48 #define max(x, y) (((x) > (y)) ? (x) : (y))
53 typedef struct file_data
{
58 unsigned char *file_base
;
62 open_input_file (file_data
*p_file
, char *filename
)
67 unsigned long size
, upper_size
;
69 file
= CreateFile (filename
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
70 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
71 if (file
== INVALID_HANDLE_VALUE
)
74 size
= GetFileSize (file
, &upper_size
);
75 file_mapping
= CreateFileMapping (file
, NULL
, PAGE_READONLY
,
80 file_base
= MapViewOfFile (file_mapping
, FILE_MAP_READ
, 0, 0, size
);
84 p_file
->name
= filename
;
87 p_file
->file_mapping
= file_mapping
;
88 p_file
->file_base
= file_base
;
94 open_output_file (file_data
*p_file
, char *filename
, unsigned long size
)
100 file
= CreateFile (filename
, GENERIC_READ
| GENERIC_WRITE
, 0, NULL
,
101 CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
102 if (file
== INVALID_HANDLE_VALUE
)
105 file_mapping
= CreateFileMapping (file
, NULL
, PAGE_READWRITE
,
110 file_base
= MapViewOfFile (file_mapping
, FILE_MAP_WRITE
, 0, 0, size
);
114 p_file
->name
= filename
;
117 p_file
->file_mapping
= file_mapping
;
118 p_file
->file_base
= file_base
;
123 /* Close the system structures associated with the given file. */
125 close_file_data (file_data
*p_file
)
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
);
136 /* Routines to manipulate NT executable file sections. */
139 get_unrounded_section_size (PIMAGE_SECTION_HEADER p_section
)
141 /* The true section size, before rounding, for an initialized data or
142 code section. (Supposedly some linkers swap the meaning of these
144 return min (p_section
->SizeOfRawData
,
145 p_section
->Misc
.VirtualSize
);
148 /* Return pointer to section header for named section. */
149 IMAGE_SECTION_HEADER
*
150 find_section (char * name
, IMAGE_NT_HEADERS
* nt_header
)
152 PIMAGE_SECTION_HEADER section
;
155 section
= IMAGE_FIRST_SECTION (nt_header
);
157 for (i
= 0; i
< nt_header
->FileHeader
.NumberOfSections
; i
++)
159 if (strcmp (section
->Name
, name
) == 0)
166 /* Return pointer to section header for section containing the given
167 relative virtual address. */
168 IMAGE_SECTION_HEADER
*
169 rva_to_section (DWORD rva
, IMAGE_NT_HEADERS
* nt_header
)
171 PIMAGE_SECTION_HEADER section
;
174 section
= IMAGE_FIRST_SECTION (nt_header
);
176 for (i
= 0; i
< nt_header
->FileHeader
.NumberOfSections
; i
++)
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
)
194 /* Return pointer to section header for section containing the given
195 offset in its raw data area. */
196 IMAGE_SECTION_HEADER
*
197 offset_to_section (DWORD offset
, IMAGE_NT_HEADERS
* nt_header
)
199 PIMAGE_SECTION_HEADER section
;
202 section
= IMAGE_FIRST_SECTION (nt_header
);
204 for (i
= 0; i
< nt_header
->FileHeader
.NumberOfSections
; i
++)
206 if (offset
>= section
->PointerToRawData
207 && offset
< section
->PointerToRawData
+ section
->SizeOfRawData
)
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). */
218 relocate_offset (DWORD offset
,
219 IMAGE_NT_HEADERS
* src_nt_header
,
220 IMAGE_NT_HEADERS
* dst_nt_header
)
222 PIMAGE_SECTION_HEADER src_section
= IMAGE_FIRST_SECTION (src_nt_header
);
223 PIMAGE_SECTION_HEADER dst_section
= IMAGE_FIRST_SECTION (dst_nt_header
);
226 while (offset
>= src_section
->PointerToRawData
)
228 if (offset
< src_section
->PointerToRawData
+ src_section
->SizeOfRawData
)
231 if (i
== src_nt_header
->FileHeader
.NumberOfSections
)
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)
238 while (src_section
->PointerToRawData
== 0)
241 + (dst_section
->PointerToRawData
+ dst_section
->SizeOfRawData
)
242 - (src_section
->PointerToRawData
+ src_section
->SizeOfRawData
);
248 (dst_section
->PointerToRawData
- src_section
->PointerToRawData
);
251 #define OFFSET_TO_RVA(offset, section) \
252 (section->VirtualAddress + ((DWORD)(offset) - section->PointerToRawData))
254 #define RVA_TO_OFFSET(rva, section) \
255 (section->PointerToRawData + ((DWORD)(rva) - section->VirtualAddress))
257 #define RVA_TO_SECTION_OFFSET(rva, section) \
258 ((DWORD)(rva) - section->VirtualAddress)
260 /* Convert address in executing image to RVA. */
261 #define PTR_TO_RVA(ptr) ((DWORD)(ptr) - (DWORD) GetModuleHandle (NULL))
263 #define PTR_TO_OFFSET(ptr, pfile_data) \
264 ((unsigned char *)(ptr) - (pfile_data)->file_base)
266 #define OFFSET_TO_PTR(offset, pfile_data) \
267 ((pfile_data)->file_base + (DWORD)(offset))
269 #define ROUND_UP(p, align) (((DWORD)(p) + (align)-1) & ~((align)-1))
270 #define ROUND_DOWN(p, align) ((DWORD)(p) & ~((align)-1))
274 copy_executable_and_add_section (file_data
*p_infile
,
275 file_data
*p_outfile
,
276 char *new_section_name
,
277 DWORD new_section_size
)
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
;
287 int be_verbose
= GetEnvironmentVariable ("DEBUG_DUMP", NULL
, 0) > 0;
289 #define COPY_CHUNK(message, src, size, verbose) \
291 unsigned char *s = (void *)(src); \
292 unsigned long count = (size); \
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); \
300 memcpy (dst, s, count); \
304 #define DST_TO_OFFSET() PTR_TO_OFFSET (dst, p_outfile)
305 #define ROUND_UP_DST_AND_ZERO(align) \
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); \
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).
319 Note that dst is updated implicitly by each COPY_CHUNK. */
321 dos_header
= (PIMAGE_DOS_HEADER
) p_infile
->file_base
;
322 nt_header
= (PIMAGE_NT_HEADERS
) (((unsigned long) dos_header
) +
323 dos_header
->e_lfanew
);
324 section
= IMAGE_FIRST_SECTION (nt_header
);
326 dst
= (unsigned char *) p_outfile
->file_base
;
328 COPY_CHUNK ("Copying DOS header...", dos_header
,
329 (DWORD
) nt_header
- (DWORD
) dos_header
, be_verbose
);
330 dst_nt_header
= (PIMAGE_NT_HEADERS
) dst
;
331 COPY_CHUNK ("Copying NT header...", nt_header
,
332 (DWORD
) section
- (DWORD
) nt_header
, be_verbose
);
333 dst_section
= (PIMAGE_SECTION_HEADER
) dst
;
334 COPY_CHUNK ("Copying section table...", section
,
335 nt_header
->FileHeader
.NumberOfSections
* sizeof (*section
),
338 /* To improve the efficiency of demand loading, make the file
339 alignment match the section alignment (VC++ 6.0 does this by
341 dst_nt_header
->OptionalHeader
.FileAlignment
=
342 dst_nt_header
->OptionalHeader
.SectionAlignment
;
344 /* Add an uninitialized data section at the end, of the specified name
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
);
350 new_section_name
= NULL
;
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 ();
357 for (i
= 0; i
< nt_header
->FileHeader
.NumberOfSections
; i
++)
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
);
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 ();
370 /* Can always copy the original raw data. */
372 (msg
, OFFSET_TO_PTR (section
->PointerToRawData
, p_infile
),
373 section
->SizeOfRawData
, be_verbose
);
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
);
380 /* Align the next section's raw data area. */
381 ROUND_UP_DST_AND_ZERO (dst_nt_header
->OptionalHeader
.FileAlignment
);
387 /* Add the extra section entry (which adds no raw data). */
388 if (new_section_name
!= NULL
)
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
403 | IMAGE_SCN_MEM_WRITE
;
406 /* Copy remainder of source image. */
408 offset
= ROUND_UP (section
->PointerToRawData
+ section
->SizeOfRawData
,
409 nt_header
->OptionalHeader
.FileAlignment
);
411 ("Copying remainder of executable...",
412 OFFSET_TO_PTR (offset
, p_infile
),
413 p_infile
->size
- offset
, be_verbose
);
415 /* Final size for new image. */
416 p_outfile
->size
= DST_TO_OFFSET ();
418 /* Now patch up remaining file-relative offsets. */
419 section
= IMAGE_FIRST_SECTION (nt_header
);
420 dst_section
= IMAGE_FIRST_SECTION (dst_nt_header
);
422 #define ADJUST_OFFSET(var) \
425 (var) = relocate_offset ((var), nt_header, dst_nt_header); \
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
++)
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
);
440 ADJUST_OFFSET (dst_section
[i
].PointerToLinenumbers
);
443 ADJUST_OFFSET (dst_nt_header
->FileHeader
.PointerToSymbolTable
);
445 /* Update offsets in debug directory entries. */
447 IMAGE_DATA_DIRECTORY debug_dir
=
448 dst_nt_header
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_DEBUG
];
449 PIMAGE_DEBUG_DIRECTORY debug_entry
;
451 section
= rva_to_section (debug_dir
.VirtualAddress
, dst_nt_header
);
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
);
458 for (i
= 0; i
< debug_dir
.Size
; i
++, debug_entry
++)
459 ADJUST_OFFSET (debug_entry
->PointerToRawData
);
466 main (int argc
, char **argv
)
468 file_data in_file
, out_file
;
469 char out_filename
[MAX_PATH
], in_filename
[MAX_PATH
];
471 PIMAGE_DOS_HEADER dos_header
;
472 PIMAGE_NT_HEADERS nt_header
;
474 #define OLD_NAME argv[1]
475 #define NEW_NAME argv[2]
476 #define SECTION_NAME argv[3]
477 #define SECTION_SIZE argv[4]
479 strcpy (in_filename
, OLD_NAME
);
480 strcpy (out_filename
, NEW_NAME
);
482 printf ("Dumping from %s\n", in_filename
);
483 printf (" to %s\n", out_filename
);
485 /* Open the undumped executable file. */
486 if (!open_input_file (&in_file
, in_filename
))
488 printf ("Failed to open %s (%d)...bailing.\n",
489 in_filename
, GetLastError ());
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
498 + nt_header
->OptionalHeader
.SectionAlignment
499 * nt_header
->FileHeader
.NumberOfSections
;
500 if (!open_output_file (&out_file
, out_filename
, size
))
502 printf ("Failed to open %s (%d)...bailing.\n",
503 out_filename
, GetLastError ());
507 copy_executable_and_add_section (&in_file
, &out_file
,
509 atoi (SECTION_SIZE
) * 1024 * 1024);
511 /* Patch up header fields; profiler is picky about this. */
513 HANDLE hImagehelp
= LoadLibrary ("imagehlp.dll");
517 dos_header
= (PIMAGE_DOS_HEADER
) out_file
.file_base
;
518 nt_header
= (PIMAGE_NT_HEADERS
) ((char *) dos_header
+ dos_header
->e_lfanew
);
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;
525 pfnCheckSumMappedFile
= (void *) GetProcAddress (hImagehelp
, "CheckSumMappedFile");
526 if (pfnCheckSumMappedFile
)
528 // nt_header->FileHeader.TimeDateStamp = time (NULL);
529 pfnCheckSumMappedFile (out_file
.file_base
,
533 nt_header
->OptionalHeader
.CheckSum
= checksum
;
535 FreeLibrary (hImagehelp
);
538 close_file_data (&in_file
);
539 close_file_data (&out_file
);
546 /* arch-tag: 17e2b0aa-8c17-4bd1-b24b-1cda689245fa
547 (do not change this comment) */