1 /* Add an uninitialized data section to an executable.
2 Copyright (C) 1999, 2001-2012 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
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.
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.
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/>.
20 Andrew Innes <andrewi@harlequin.co.uk> 04-Jan-1999
21 based on code from unexw32.c
29 #define _ANONYMOUS_UNION
30 #define _ANONYMOUS_STRUCT
34 /* Include relevant definitions from IMAGEHLP.H, which can be found
35 in \\win32sdk\mstools\samples\image\include\imagehlp.h. */
38 (__stdcall
* pfnCheckSumMappedFile
) (LPVOID BaseAddress
,
45 #define min(x, y) (((x) < (y)) ? (x) : (y))
46 #define max(x, y) (((x) > (y)) ? (x) : (y))
51 typedef struct file_data
{
56 unsigned char *file_base
;
60 open_input_file (file_data
*p_file
, const char *filename
)
65 unsigned long size
, upper_size
;
67 file
= CreateFile (filename
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
68 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
69 if (file
== INVALID_HANDLE_VALUE
)
72 size
= GetFileSize (file
, &upper_size
);
73 file_mapping
= CreateFileMapping (file
, NULL
, PAGE_READONLY
,
78 file_base
= MapViewOfFile (file_mapping
, FILE_MAP_READ
, 0, 0, size
);
82 p_file
->name
= filename
;
85 p_file
->file_mapping
= file_mapping
;
86 p_file
->file_base
= file_base
;
92 open_output_file (file_data
*p_file
, const char *filename
, unsigned long size
)
98 file
= CreateFile (filename
, GENERIC_READ
| GENERIC_WRITE
, 0, NULL
,
99 CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
100 if (file
== INVALID_HANDLE_VALUE
)
103 file_mapping
= CreateFileMapping (file
, NULL
, PAGE_READWRITE
,
108 file_base
= MapViewOfFile (file_mapping
, FILE_MAP_WRITE
, 0, 0, size
);
112 p_file
->name
= filename
;
115 p_file
->file_mapping
= file_mapping
;
116 p_file
->file_base
= file_base
;
121 /* Close the system structures associated with the given file. */
123 close_file_data (file_data
*p_file
)
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
);
134 /* Routines to manipulate NT executable file sections. */
137 get_unrounded_section_size (PIMAGE_SECTION_HEADER p_section
)
139 /* The true section size, before rounding, for an initialized data or
140 code section. (Supposedly some linkers swap the meaning of these
142 return min (p_section
->SizeOfRawData
,
143 p_section
->Misc
.VirtualSize
);
146 /* Return pointer to section header for named section. */
147 IMAGE_SECTION_HEADER
*
148 find_section (const char *name
, IMAGE_NT_HEADERS
*nt_header
)
150 PIMAGE_SECTION_HEADER section
;
153 section
= IMAGE_FIRST_SECTION (nt_header
);
155 for (i
= 0; i
< nt_header
->FileHeader
.NumberOfSections
; i
++)
157 if (strcmp (section
->Name
, name
) == 0)
164 /* Return pointer to section header for section containing the given
165 relative virtual address. */
166 IMAGE_SECTION_HEADER
*
167 rva_to_section (DWORD rva
, IMAGE_NT_HEADERS
* nt_header
)
169 PIMAGE_SECTION_HEADER section
;
172 section
= IMAGE_FIRST_SECTION (nt_header
);
174 for (i
= 0; i
< nt_header
->FileHeader
.NumberOfSections
; i
++)
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
)
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 offset
, IMAGE_NT_HEADERS
* nt_header
)
197 PIMAGE_SECTION_HEADER section
;
200 section
= IMAGE_FIRST_SECTION (nt_header
);
202 for (i
= 0; i
< nt_header
->FileHeader
.NumberOfSections
; i
++)
204 if (offset
>= section
->PointerToRawData
205 && offset
< section
->PointerToRawData
+ section
->SizeOfRawData
)
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). */
216 relocate_offset (DWORD offset
,
217 IMAGE_NT_HEADERS
* src_nt_header
,
218 IMAGE_NT_HEADERS
* dst_nt_header
)
220 PIMAGE_SECTION_HEADER src_section
= IMAGE_FIRST_SECTION (src_nt_header
);
221 PIMAGE_SECTION_HEADER dst_section
= IMAGE_FIRST_SECTION (dst_nt_header
);
224 while (offset
>= src_section
->PointerToRawData
)
226 if (offset
< src_section
->PointerToRawData
+ src_section
->SizeOfRawData
)
229 if (i
== src_nt_header
->FileHeader
.NumberOfSections
)
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)
236 while (src_section
->PointerToRawData
== 0)
239 + (dst_section
->PointerToRawData
+ dst_section
->SizeOfRawData
)
240 - (src_section
->PointerToRawData
+ src_section
->SizeOfRawData
);
246 (dst_section
->PointerToRawData
- src_section
->PointerToRawData
);
249 #define OFFSET_TO_RVA(offset, section) \
250 (section->VirtualAddress + ((DWORD)(offset) - section->PointerToRawData))
252 #define RVA_TO_OFFSET(rva, section) \
253 (section->PointerToRawData + ((DWORD)(rva) - section->VirtualAddress))
255 #define RVA_TO_SECTION_OFFSET(rva, section) \
256 ((DWORD)(rva) - section->VirtualAddress)
258 /* Convert address in executing image to RVA. */
259 #define PTR_TO_RVA(ptr) ((DWORD)(ptr) - (DWORD) GetModuleHandle (NULL))
261 #define PTR_TO_OFFSET(ptr, pfile_data) \
262 ((unsigned const char *)(ptr) - (pfile_data)->file_base)
264 #define OFFSET_TO_PTR(offset, pfile_data) \
265 ((pfile_data)->file_base + (DWORD)(offset))
267 #define ROUND_UP(p, align) (((DWORD)(p) + (align)-1) & ~((align)-1))
268 #define ROUND_DOWN(p, align) ((DWORD)(p) & ~((align)-1))
272 copy_executable_and_add_section (file_data
*p_infile
,
273 file_data
*p_outfile
,
274 const char *new_section_name
,
275 DWORD new_section_size
)
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
;
285 int be_verbose
= GetEnvironmentVariable ("DEBUG_DUMP", NULL
, 0) > 0;
287 #define COPY_CHUNK(message, src, size, verbose) \
289 unsigned const char *s = (void *)(src); \
290 unsigned long count = (size); \
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); \
298 memcpy (dst, s, count); \
302 #define DST_TO_OFFSET() PTR_TO_OFFSET (dst, p_outfile)
303 #define ROUND_UP_DST_AND_ZERO(align) \
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); \
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).
317 Note that dst is updated implicitly by each COPY_CHUNK. */
319 dos_header
= (PIMAGE_DOS_HEADER
) p_infile
->file_base
;
320 nt_header
= (PIMAGE_NT_HEADERS
) (((unsigned long) dos_header
) +
321 dos_header
->e_lfanew
);
322 section
= IMAGE_FIRST_SECTION (nt_header
);
324 dst
= (unsigned char *) p_outfile
->file_base
;
326 COPY_CHUNK ("Copying DOS header...", dos_header
,
327 (DWORD
) nt_header
- (DWORD
) dos_header
, be_verbose
);
328 dst_nt_header
= (PIMAGE_NT_HEADERS
) dst
;
329 COPY_CHUNK ("Copying NT header...", nt_header
,
330 (DWORD
) section
- (DWORD
) nt_header
, be_verbose
);
331 dst_section
= (PIMAGE_SECTION_HEADER
) dst
;
332 COPY_CHUNK ("Copying section table...", section
,
333 nt_header
->FileHeader
.NumberOfSections
* sizeof (*section
),
336 /* To improve the efficiency of demand loading, make the file
337 alignment match the section alignment (VC++ 6.0 does this by
339 dst_nt_header
->OptionalHeader
.FileAlignment
=
340 dst_nt_header
->OptionalHeader
.SectionAlignment
;
342 /* Add an uninitialized data section at the end, of the specified name
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
);
348 new_section_name
= NULL
;
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 ();
355 for (i
= 0; i
< nt_header
->FileHeader
.NumberOfSections
; i
++)
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
);
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 ();
368 /* Can always copy the original raw data. */
370 (msg
, OFFSET_TO_PTR (section
->PointerToRawData
, p_infile
),
371 section
->SizeOfRawData
, be_verbose
);
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
);
378 /* Align the next section's raw data area. */
379 ROUND_UP_DST_AND_ZERO (dst_nt_header
->OptionalHeader
.FileAlignment
);
385 /* Add the extra section entry (which adds no raw data). */
386 if (new_section_name
!= NULL
)
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
401 | IMAGE_SCN_MEM_WRITE
;
404 /* Copy remainder of source image. */
406 offset
= ROUND_UP (section
->PointerToRawData
+ section
->SizeOfRawData
,
407 nt_header
->OptionalHeader
.FileAlignment
);
409 ("Copying remainder of executable...",
410 OFFSET_TO_PTR (offset
, p_infile
),
411 p_infile
->size
- offset
, be_verbose
);
413 /* Final size for new image. */
414 p_outfile
->size
= DST_TO_OFFSET ();
416 /* Now patch up remaining file-relative offsets. */
417 section
= IMAGE_FIRST_SECTION (nt_header
);
418 dst_section
= IMAGE_FIRST_SECTION (dst_nt_header
);
420 #define ADJUST_OFFSET(var) \
423 (var) = relocate_offset ((var), nt_header, dst_nt_header); \
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
++)
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
);
438 ADJUST_OFFSET (dst_section
[i
].PointerToLinenumbers
);
441 ADJUST_OFFSET (dst_nt_header
->FileHeader
.PointerToSymbolTable
);
443 /* Update offsets in debug directory entries. */
445 IMAGE_DATA_DIRECTORY debug_dir
=
446 dst_nt_header
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_DEBUG
];
447 PIMAGE_DEBUG_DIRECTORY debug_entry
;
449 section
= rva_to_section (debug_dir
.VirtualAddress
, dst_nt_header
);
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
);
456 for (i
= 0; i
< debug_dir
.Size
; i
++, debug_entry
++)
457 ADJUST_OFFSET (debug_entry
->PointerToRawData
);
464 main (int argc
, char **argv
)
466 file_data in_file
, out_file
;
467 char out_filename
[MAX_PATH
], in_filename
[MAX_PATH
];
469 PIMAGE_DOS_HEADER dos_header
;
470 PIMAGE_NT_HEADERS nt_header
;
472 #define OLD_NAME argv[1]
473 #define NEW_NAME argv[2]
474 #define SECTION_NAME argv[3]
475 #define SECTION_SIZE argv[4]
477 strcpy (in_filename
, OLD_NAME
);
478 strcpy (out_filename
, NEW_NAME
);
480 printf ("Dumping from %s\n", in_filename
);
481 printf (" to %s\n", out_filename
);
483 /* Open the undumped executable file. */
484 if (!open_input_file (&in_file
, in_filename
))
486 printf ("Failed to open %s (%d)...bailing.\n",
487 in_filename
, GetLastError ());
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
496 + nt_header
->OptionalHeader
.SectionAlignment
497 * nt_header
->FileHeader
.NumberOfSections
;
498 if (!open_output_file (&out_file
, out_filename
, size
))
500 printf ("Failed to open %s (%d)...bailing.\n",
501 out_filename
, GetLastError ());
505 copy_executable_and_add_section (&in_file
, &out_file
,
507 atoi (SECTION_SIZE
) * 1024 * 1024);
509 /* Patch up header fields; profiler is picky about this. */
511 HANDLE hImagehelp
= LoadLibrary ("imagehlp.dll");
515 dos_header
= (PIMAGE_DOS_HEADER
) out_file
.file_base
;
516 nt_header
= (PIMAGE_NT_HEADERS
) ((char *) dos_header
+ dos_header
->e_lfanew
);
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;
523 pfnCheckSumMappedFile
= (void *) GetProcAddress (hImagehelp
, "CheckSumMappedFile");
524 if (pfnCheckSumMappedFile
)
526 // nt_header->FileHeader.TimeDateStamp = time (NULL);
527 pfnCheckSumMappedFile (out_file
.file_base
,
531 nt_header
->OptionalHeader
.CheckSum
= checksum
;
533 FreeLibrary (hImagehelp
);
536 close_file_data (&in_file
);
537 close_file_data (&out_file
);