1 /* Add an uninitialized data section to an executable.
2 Copyright (C) 1999, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
3 2008, 2009 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 of the License, or
10 (at your option) any later version.
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. If not, see <http://www.gnu.org/licenses/>.
21 Andrew Innes <andrewi@harlequin.co.uk> 04-Jan-1999
22 based on code from unexw32.c
30 #define _ANONYMOUS_UNION
31 #define _ANONYMOUS_STRUCT
35 /* Include relevant definitions from IMAGEHLP.H, which can be found
36 in \\win32sdk\mstools\samples\image\include\imagehlp.h. */
39 (__stdcall
* pfnCheckSumMappedFile
) (LPVOID BaseAddress
,
46 #define min(x, y) (((x) < (y)) ? (x) : (y))
47 #define max(x, y) (((x) > (y)) ? (x) : (y))
52 typedef struct file_data
{
57 unsigned char *file_base
;
61 open_input_file (file_data
*p_file
, char *filename
)
66 unsigned long size
, upper_size
;
68 file
= CreateFile (filename
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
69 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
70 if (file
== INVALID_HANDLE_VALUE
)
73 size
= GetFileSize (file
, &upper_size
);
74 file_mapping
= CreateFileMapping (file
, NULL
, PAGE_READONLY
,
79 file_base
= MapViewOfFile (file_mapping
, FILE_MAP_READ
, 0, 0, size
);
83 p_file
->name
= filename
;
86 p_file
->file_mapping
= file_mapping
;
87 p_file
->file_base
= file_base
;
93 open_output_file (file_data
*p_file
, char *filename
, unsigned long size
)
99 file
= CreateFile (filename
, GENERIC_READ
| GENERIC_WRITE
, 0, NULL
,
100 CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
101 if (file
== INVALID_HANDLE_VALUE
)
104 file_mapping
= CreateFileMapping (file
, NULL
, PAGE_READWRITE
,
109 file_base
= MapViewOfFile (file_mapping
, FILE_MAP_WRITE
, 0, 0, size
);
113 p_file
->name
= filename
;
116 p_file
->file_mapping
= file_mapping
;
117 p_file
->file_base
= file_base
;
122 /* Close the system structures associated with the given file. */
124 close_file_data (file_data
*p_file
)
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
);
135 /* Routines to manipulate NT executable file sections. */
138 get_unrounded_section_size (PIMAGE_SECTION_HEADER p_section
)
140 /* The true section size, before rounding, for an initialized data or
141 code section. (Supposedly some linkers swap the meaning of these
143 return min (p_section
->SizeOfRawData
,
144 p_section
->Misc
.VirtualSize
);
147 /* Return pointer to section header for named section. */
148 IMAGE_SECTION_HEADER
*
149 find_section (char * name
, IMAGE_NT_HEADERS
* nt_header
)
151 PIMAGE_SECTION_HEADER section
;
154 section
= IMAGE_FIRST_SECTION (nt_header
);
156 for (i
= 0; i
< nt_header
->FileHeader
.NumberOfSections
; i
++)
158 if (strcmp (section
->Name
, name
) == 0)
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
)
170 PIMAGE_SECTION_HEADER section
;
173 section
= IMAGE_FIRST_SECTION (nt_header
);
175 for (i
= 0; i
< nt_header
->FileHeader
.NumberOfSections
; i
++)
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
)
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
)
198 PIMAGE_SECTION_HEADER section
;
201 section
= IMAGE_FIRST_SECTION (nt_header
);
203 for (i
= 0; i
< nt_header
->FileHeader
.NumberOfSections
; i
++)
205 if (offset
>= section
->PointerToRawData
206 && offset
< section
->PointerToRawData
+ section
->SizeOfRawData
)
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). */
217 relocate_offset (DWORD offset
,
218 IMAGE_NT_HEADERS
* src_nt_header
,
219 IMAGE_NT_HEADERS
* dst_nt_header
)
221 PIMAGE_SECTION_HEADER src_section
= IMAGE_FIRST_SECTION (src_nt_header
);
222 PIMAGE_SECTION_HEADER dst_section
= IMAGE_FIRST_SECTION (dst_nt_header
);
225 while (offset
>= src_section
->PointerToRawData
)
227 if (offset
< src_section
->PointerToRawData
+ src_section
->SizeOfRawData
)
230 if (i
== src_nt_header
->FileHeader
.NumberOfSections
)
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)
237 while (src_section
->PointerToRawData
== 0)
240 + (dst_section
->PointerToRawData
+ dst_section
->SizeOfRawData
)
241 - (src_section
->PointerToRawData
+ src_section
->SizeOfRawData
);
247 (dst_section
->PointerToRawData
- src_section
->PointerToRawData
);
250 #define OFFSET_TO_RVA(offset, section) \
251 (section->VirtualAddress + ((DWORD)(offset) - section->PointerToRawData))
253 #define RVA_TO_OFFSET(rva, section) \
254 (section->PointerToRawData + ((DWORD)(rva) - section->VirtualAddress))
256 #define RVA_TO_SECTION_OFFSET(rva, section) \
257 ((DWORD)(rva) - section->VirtualAddress)
259 /* Convert address in executing image to RVA. */
260 #define PTR_TO_RVA(ptr) ((DWORD)(ptr) - (DWORD) GetModuleHandle (NULL))
262 #define PTR_TO_OFFSET(ptr, pfile_data) \
263 ((unsigned char *)(ptr) - (pfile_data)->file_base)
265 #define OFFSET_TO_PTR(offset, pfile_data) \
266 ((pfile_data)->file_base + (DWORD)(offset))
268 #define ROUND_UP(p, align) (((DWORD)(p) + (align)-1) & ~((align)-1))
269 #define ROUND_DOWN(p, align) ((DWORD)(p) & ~((align)-1))
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
)
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
;
286 int be_verbose
= GetEnvironmentVariable ("DEBUG_DUMP", NULL
, 0) > 0;
288 #define COPY_CHUNK(message, src, size, verbose) \
290 unsigned char *s = (void *)(src); \
291 unsigned long count = (size); \
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); \
299 memcpy (dst, s, count); \
303 #define DST_TO_OFFSET() PTR_TO_OFFSET (dst, p_outfile)
304 #define ROUND_UP_DST_AND_ZERO(align) \
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); \
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).
318 Note that dst is updated implicitly by each COPY_CHUNK. */
320 dos_header
= (PIMAGE_DOS_HEADER
) p_infile
->file_base
;
321 nt_header
= (PIMAGE_NT_HEADERS
) (((unsigned long) dos_header
) +
322 dos_header
->e_lfanew
);
323 section
= IMAGE_FIRST_SECTION (nt_header
);
325 dst
= (unsigned char *) p_outfile
->file_base
;
327 COPY_CHUNK ("Copying DOS header...", dos_header
,
328 (DWORD
) nt_header
- (DWORD
) dos_header
, be_verbose
);
329 dst_nt_header
= (PIMAGE_NT_HEADERS
) dst
;
330 COPY_CHUNK ("Copying NT header...", nt_header
,
331 (DWORD
) section
- (DWORD
) 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
),
337 /* To improve the efficiency of demand loading, make the file
338 alignment match the section alignment (VC++ 6.0 does this by
340 dst_nt_header
->OptionalHeader
.FileAlignment
=
341 dst_nt_header
->OptionalHeader
.SectionAlignment
;
343 /* Add an uninitialized data section at the end, of the specified name
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
);
349 new_section_name
= NULL
;
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 ();
356 for (i
= 0; i
< nt_header
->FileHeader
.NumberOfSections
; i
++)
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
);
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 ();
369 /* Can always copy the original raw data. */
371 (msg
, OFFSET_TO_PTR (section
->PointerToRawData
, p_infile
),
372 section
->SizeOfRawData
, be_verbose
);
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
);
379 /* Align the next section's raw data area. */
380 ROUND_UP_DST_AND_ZERO (dst_nt_header
->OptionalHeader
.FileAlignment
);
386 /* Add the extra section entry (which adds no raw data). */
387 if (new_section_name
!= NULL
)
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
402 | IMAGE_SCN_MEM_WRITE
;
405 /* Copy remainder of source image. */
407 offset
= ROUND_UP (section
->PointerToRawData
+ section
->SizeOfRawData
,
408 nt_header
->OptionalHeader
.FileAlignment
);
410 ("Copying remainder of executable...",
411 OFFSET_TO_PTR (offset
, p_infile
),
412 p_infile
->size
- offset
, be_verbose
);
414 /* Final size for new image. */
415 p_outfile
->size
= DST_TO_OFFSET ();
417 /* Now patch up remaining file-relative offsets. */
418 section
= IMAGE_FIRST_SECTION (nt_header
);
419 dst_section
= IMAGE_FIRST_SECTION (dst_nt_header
);
421 #define ADJUST_OFFSET(var) \
424 (var) = relocate_offset ((var), nt_header, dst_nt_header); \
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
++)
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
);
439 ADJUST_OFFSET (dst_section
[i
].PointerToLinenumbers
);
442 ADJUST_OFFSET (dst_nt_header
->FileHeader
.PointerToSymbolTable
);
444 /* Update offsets in debug directory entries. */
446 IMAGE_DATA_DIRECTORY debug_dir
=
447 dst_nt_header
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_DEBUG
];
448 PIMAGE_DEBUG_DIRECTORY debug_entry
;
450 section
= rva_to_section (debug_dir
.VirtualAddress
, dst_nt_header
);
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
);
457 for (i
= 0; i
< debug_dir
.Size
; i
++, debug_entry
++)
458 ADJUST_OFFSET (debug_entry
->PointerToRawData
);
465 main (int argc
, char **argv
)
467 file_data in_file
, out_file
;
468 char out_filename
[MAX_PATH
], in_filename
[MAX_PATH
];
470 PIMAGE_DOS_HEADER dos_header
;
471 PIMAGE_NT_HEADERS nt_header
;
473 #define OLD_NAME argv[1]
474 #define NEW_NAME argv[2]
475 #define SECTION_NAME argv[3]
476 #define SECTION_SIZE argv[4]
478 strcpy (in_filename
, OLD_NAME
);
479 strcpy (out_filename
, NEW_NAME
);
481 printf ("Dumping from %s\n", in_filename
);
482 printf (" to %s\n", out_filename
);
484 /* Open the undumped executable file. */
485 if (!open_input_file (&in_file
, in_filename
))
487 printf ("Failed to open %s (%d)...bailing.\n",
488 in_filename
, GetLastError ());
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
497 + nt_header
->OptionalHeader
.SectionAlignment
498 * nt_header
->FileHeader
.NumberOfSections
;
499 if (!open_output_file (&out_file
, out_filename
, size
))
501 printf ("Failed to open %s (%d)...bailing.\n",
502 out_filename
, GetLastError ());
506 copy_executable_and_add_section (&in_file
, &out_file
,
508 atoi (SECTION_SIZE
) * 1024 * 1024);
510 /* Patch up header fields; profiler is picky about this. */
512 HANDLE hImagehelp
= LoadLibrary ("imagehlp.dll");
516 dos_header
= (PIMAGE_DOS_HEADER
) out_file
.file_base
;
517 nt_header
= (PIMAGE_NT_HEADERS
) ((char *) dos_header
+ dos_header
->e_lfanew
);
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;
524 pfnCheckSumMappedFile
= (void *) GetProcAddress (hImagehelp
, "CheckSumMappedFile");
525 if (pfnCheckSumMappedFile
)
527 // nt_header->FileHeader.TimeDateStamp = time (NULL);
528 pfnCheckSumMappedFile (out_file
.file_base
,
532 nt_header
->OptionalHeader
.CheckSum
= checksum
;
534 FreeLibrary (hImagehelp
);
537 close_file_data (&in_file
);
538 close_file_data (&out_file
);
545 /* arch-tag: 17e2b0aa-8c17-4bd1-b24b-1cda689245fa
546 (do not change this comment) */