1 /* Add an uninitialized data section to an executable.
2 Copyright (C) 1999 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 2, or (at your option)
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; see the file COPYING. If not, write to
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA.
21 Andrew Innes <andrewi@harlequin.co.uk> 04-Jan-1999
22 based on code from unexw32.c
31 /* Include relevant definitions from IMAGEHLP.H, which can be found
32 in \\win32sdk\mstools\samples\image\include\imagehlp.h. */
35 (__stdcall
* pfnCheckSumMappedFile
) (LPVOID BaseAddress
,
42 #define min(x, y) (((x) < (y)) ? (x) : (y))
43 #define max(x, y) (((x) > (y)) ? (x) : (y))
48 typedef struct file_data
{
53 unsigned char *file_base
;
57 open_input_file (file_data
*p_file
, char *filename
)
62 unsigned long size
, upper_size
;
64 file
= CreateFile (filename
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
65 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
66 if (file
== INVALID_HANDLE_VALUE
)
69 size
= GetFileSize (file
, &upper_size
);
70 file_mapping
= CreateFileMapping (file
, NULL
, PAGE_READONLY
,
75 file_base
= MapViewOfFile (file_mapping
, FILE_MAP_READ
, 0, 0, size
);
79 p_file
->name
= filename
;
82 p_file
->file_mapping
= file_mapping
;
83 p_file
->file_base
= file_base
;
89 open_output_file (file_data
*p_file
, char *filename
, unsigned long size
)
95 file
= CreateFile (filename
, GENERIC_READ
| GENERIC_WRITE
, 0, NULL
,
96 CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
97 if (file
== INVALID_HANDLE_VALUE
)
100 file_mapping
= CreateFileMapping (file
, NULL
, PAGE_READWRITE
,
105 file_base
= MapViewOfFile (file_mapping
, FILE_MAP_WRITE
, 0, 0, size
);
109 p_file
->name
= filename
;
112 p_file
->file_mapping
= file_mapping
;
113 p_file
->file_base
= file_base
;
118 /* Close the system structures associated with the given file. */
120 close_file_data (file_data
*p_file
)
122 UnmapViewOfFile (p_file
->file_base
);
123 CloseHandle (p_file
->file_mapping
);
124 /* For the case of output files, set final size. */
125 SetFilePointer (p_file
->file
, p_file
->size
, NULL
, FILE_BEGIN
);
126 SetEndOfFile (p_file
->file
);
127 CloseHandle (p_file
->file
);
131 /* Routines to manipulate NT executable file sections. */
134 get_unrounded_section_size (PIMAGE_SECTION_HEADER p_section
)
136 /* The true section size, before rounding, for an initialized data or
137 code section. (Supposedly some linkers swap the meaning of these
139 return min (p_section
->SizeOfRawData
,
140 p_section
->Misc
.VirtualSize
);
143 /* Return pointer to section header for named section. */
144 IMAGE_SECTION_HEADER
*
145 find_section (char * name
, IMAGE_NT_HEADERS
* nt_header
)
147 PIMAGE_SECTION_HEADER section
;
150 section
= IMAGE_FIRST_SECTION (nt_header
);
152 for (i
= 0; i
< nt_header
->FileHeader
.NumberOfSections
; i
++)
154 if (strcmp (section
->Name
, name
) == 0)
161 /* Return pointer to section header for section containing the given
162 relative virtual address. */
163 IMAGE_SECTION_HEADER
*
164 rva_to_section (DWORD rva
, IMAGE_NT_HEADERS
* nt_header
)
166 PIMAGE_SECTION_HEADER section
;
169 section
= IMAGE_FIRST_SECTION (nt_header
);
171 for (i
= 0; i
< nt_header
->FileHeader
.NumberOfSections
; i
++)
173 /* Some linkers (eg. the NT SDK linker I believe) swapped the
174 meaning of these two values - or rather, they ignored
175 VirtualSize entirely and always set it to zero. This affects
176 some very old exes (eg. gzip dated Dec 1993). Since
177 w32_executable_type relies on this function to work reliably,
178 we need to cope with this. */
179 DWORD real_size
= max (section
->SizeOfRawData
,
180 section
->Misc
.VirtualSize
);
181 if (rva
>= section
->VirtualAddress
182 && rva
< section
->VirtualAddress
+ real_size
)
189 /* Return pointer to section header for section containing the given
190 offset in its raw data area. */
191 IMAGE_SECTION_HEADER
*
192 offset_to_section (DWORD offset
, IMAGE_NT_HEADERS
* nt_header
)
194 PIMAGE_SECTION_HEADER section
;
197 section
= IMAGE_FIRST_SECTION (nt_header
);
199 for (i
= 0; i
< nt_header
->FileHeader
.NumberOfSections
; i
++)
201 if (offset
>= section
->PointerToRawData
202 && offset
< section
->PointerToRawData
+ section
->SizeOfRawData
)
209 /* Return offset to an object in dst, given offset in src. We assume
210 there is at least one section in both src and dst images, and that
211 the some sections may have been added to dst (after sections in src). */
213 relocate_offset (DWORD offset
,
214 IMAGE_NT_HEADERS
* src_nt_header
,
215 IMAGE_NT_HEADERS
* dst_nt_header
)
217 PIMAGE_SECTION_HEADER src_section
= IMAGE_FIRST_SECTION (src_nt_header
);
218 PIMAGE_SECTION_HEADER dst_section
= IMAGE_FIRST_SECTION (dst_nt_header
);
221 while (offset
>= src_section
->PointerToRawData
)
223 if (offset
< src_section
->PointerToRawData
+ src_section
->SizeOfRawData
)
226 if (i
== src_nt_header
->FileHeader
.NumberOfSections
)
228 /* Handle offsets after the last section. */
229 dst_section
= IMAGE_FIRST_SECTION (dst_nt_header
);
230 dst_section
+= dst_nt_header
->FileHeader
.NumberOfSections
- 1;
231 while (dst_section
->PointerToRawData
== 0)
233 while (src_section
->PointerToRawData
== 0)
236 + (dst_section
->PointerToRawData
+ dst_section
->SizeOfRawData
)
237 - (src_section
->PointerToRawData
+ src_section
->SizeOfRawData
);
243 (dst_section
->PointerToRawData
- src_section
->PointerToRawData
);
246 #define OFFSET_TO_RVA(offset, section) \
247 (section->VirtualAddress + ((DWORD)(offset) - section->PointerToRawData))
249 #define RVA_TO_OFFSET(rva, section) \
250 (section->PointerToRawData + ((DWORD)(rva) - section->VirtualAddress))
252 #define RVA_TO_SECTION_OFFSET(rva, section) \
253 ((DWORD)(rva) - section->VirtualAddress)
255 /* Convert address in executing image to RVA. */
256 #define PTR_TO_RVA(ptr) ((DWORD)(ptr) - (DWORD) GetModuleHandle (NULL))
258 #define PTR_TO_OFFSET(ptr, pfile_data) \
259 ((char *)(ptr) - (pfile_data)->file_base)
261 #define OFFSET_TO_PTR(offset, pfile_data) \
262 ((pfile_data)->file_base + (DWORD)(offset))
264 #define ROUND_UP(p, align) (((DWORD)(p) + (align)-1) & ~((align)-1))
265 #define ROUND_DOWN(p, align) ((DWORD)(p) & ~((align)-1))
269 copy_executable_and_add_section (file_data
*p_infile
,
270 file_data
*p_outfile
,
271 char *new_section_name
,
272 DWORD new_section_size
)
275 PIMAGE_DOS_HEADER dos_header
;
276 PIMAGE_NT_HEADERS nt_header
;
277 PIMAGE_NT_HEADERS dst_nt_header
;
278 PIMAGE_SECTION_HEADER section
;
279 PIMAGE_SECTION_HEADER dst_section
;
283 #define COPY_CHUNK(message, src, size) \
285 unsigned char *s = (void *)(src); \
286 unsigned long count = (size); \
287 printf ("%s\n", (message)); \
288 printf ("\t0x%08x Offset in input file.\n", s - p_infile->file_base); \
289 printf ("\t0x%08x Offset in output file.\n", dst - p_outfile->file_base); \
290 printf ("\t0x%08x Size in bytes.\n", count); \
291 memcpy (dst, s, count); \
295 #define DST_TO_OFFSET() PTR_TO_OFFSET (dst, p_outfile)
296 #define ROUND_UP_DST_AND_ZERO(align) \
298 unsigned char *newdst = p_outfile->file_base \
299 + ROUND_UP (DST_TO_OFFSET (), (align)); \
300 /* Zero the alignment slop; it may actually initialize real data. */ \
301 memset (dst, 0, newdst - dst); \
305 /* Copy the source image sequentially, ie. section by section after
306 copying the headers and section table, to simplify the process of
307 adding an extra section table entry (which might force the raw
308 section data to be relocated).
310 Note that dst is updated implicitly by each COPY_CHUNK. */
312 dos_header
= (PIMAGE_DOS_HEADER
) p_infile
->file_base
;
313 nt_header
= (PIMAGE_NT_HEADERS
) (((unsigned long) dos_header
) +
314 dos_header
->e_lfanew
);
315 section
= IMAGE_FIRST_SECTION (nt_header
);
317 dst
= (unsigned char *) p_outfile
->file_base
;
319 COPY_CHUNK ("Copying DOS header...", dos_header
,
320 (DWORD
) nt_header
- (DWORD
) dos_header
);
321 dst_nt_header
= (PIMAGE_NT_HEADERS
) dst
;
322 COPY_CHUNK ("Copying NT header...", nt_header
,
323 (DWORD
) section
- (DWORD
) nt_header
);
324 dst_section
= (PIMAGE_SECTION_HEADER
) dst
;
325 COPY_CHUNK ("Copying section table...", section
,
326 nt_header
->FileHeader
.NumberOfSections
* sizeof (*section
));
328 /* To improve the efficiency of demand loading, make the file
329 alignment match the section alignment (VC++ 6.0 does this by
331 dst_nt_header
->OptionalHeader
.FileAlignment
=
332 dst_nt_header
->OptionalHeader
.SectionAlignment
;
334 /* Add an uninitialized data section at the end, of the specified name
336 if (find_section (new_section_name
, nt_header
) == NULL
)
337 /* Leave room for extra section table entry; filled in below. */
338 dst
+= sizeof (*section
);
340 new_section_name
= NULL
;
342 /* Align the first section's raw data area, and set the header size
343 field accordingly. */
344 ROUND_UP_DST_AND_ZERO (dst_nt_header
->OptionalHeader
.FileAlignment
);
345 dst_nt_header
->OptionalHeader
.SizeOfHeaders
= DST_TO_OFFSET ();
347 for (i
= 0; i
< nt_header
->FileHeader
.NumberOfSections
; i
++)
350 sprintf (msg
, "Copying raw data for %s...", section
->Name
);
352 /* Update the file-relative offset for this section's raw data (if
353 it has any) in case things have been relocated; we will update
354 the other offsets below once we know where everything is. */
355 if (dst_section
->PointerToRawData
)
356 dst_section
->PointerToRawData
= DST_TO_OFFSET ();
358 /* Can always copy the original raw data. */
360 (msg
, OFFSET_TO_PTR (section
->PointerToRawData
, p_infile
),
361 section
->SizeOfRawData
);
363 /* Round up the raw data size to the new alignment. */
364 dst_section
->SizeOfRawData
=
365 ROUND_UP (dst_section
->SizeOfRawData
,
366 dst_nt_header
->OptionalHeader
.FileAlignment
);
368 /* Align the next section's raw data area. */
369 ROUND_UP_DST_AND_ZERO (dst_nt_header
->OptionalHeader
.FileAlignment
);
375 /* Add the extra section entry (which adds no raw data). */
376 if (new_section_name
!= NULL
)
378 dst_nt_header
->FileHeader
.NumberOfSections
++;
379 dst_nt_header
->OptionalHeader
.SizeOfImage
+= new_section_size
;
380 strncpy (dst_section
->Name
, new_section_name
, sizeof (dst_section
->Name
));
381 dst_section
->VirtualAddress
=
382 section
[-1].VirtualAddress
383 + ROUND_UP (section
[-1].Misc
.VirtualSize
,
384 dst_nt_header
->OptionalHeader
.SectionAlignment
);
385 dst_section
->Misc
.VirtualSize
= new_section_size
;
386 dst_section
->PointerToRawData
= 0;
387 dst_section
->SizeOfRawData
= 0;
388 dst_section
->Characteristics
=
389 IMAGE_SCN_CNT_UNINITIALIZED_DATA
391 | IMAGE_SCN_MEM_WRITE
;
394 /* Copy remainder of source image. */
396 offset
= ROUND_UP (section
->PointerToRawData
+ section
->SizeOfRawData
,
397 nt_header
->OptionalHeader
.FileAlignment
);
399 ("Copying remainder of executable...",
400 OFFSET_TO_PTR (offset
, p_infile
),
401 p_infile
->size
- offset
);
403 /* Final size for new image. */
404 p_outfile
->size
= DST_TO_OFFSET ();
406 /* Now patch up remaining file-relative offsets. */
407 section
= IMAGE_FIRST_SECTION (nt_header
);
408 dst_section
= IMAGE_FIRST_SECTION (dst_nt_header
);
410 #define ADJUST_OFFSET(var) \
413 (var) = relocate_offset ((var), nt_header, dst_nt_header); \
416 dst_nt_header
->OptionalHeader
.SizeOfInitializedData
= 0;
417 dst_nt_header
->OptionalHeader
.SizeOfUninitializedData
= 0;
418 for (i
= 0; i
< dst_nt_header
->FileHeader
.NumberOfSections
; i
++)
420 /* Recompute data sizes for completeness. */
421 if (dst_section
[i
].Characteristics
& IMAGE_SCN_CNT_INITIALIZED_DATA
)
422 dst_nt_header
->OptionalHeader
.SizeOfInitializedData
+=
423 ROUND_UP (dst_section
[i
].Misc
.VirtualSize
, dst_nt_header
->OptionalHeader
.FileAlignment
);
424 else if (dst_section
[i
].Characteristics
& IMAGE_SCN_CNT_UNINITIALIZED_DATA
)
425 dst_nt_header
->OptionalHeader
.SizeOfUninitializedData
+=
426 ROUND_UP (dst_section
[i
].Misc
.VirtualSize
, dst_nt_header
->OptionalHeader
.FileAlignment
);
428 ADJUST_OFFSET (dst_section
[i
].PointerToLinenumbers
);
431 ADJUST_OFFSET (dst_nt_header
->FileHeader
.PointerToSymbolTable
);
433 /* Update offsets in debug directory entries. */
435 IMAGE_DATA_DIRECTORY debug_dir
=
436 dst_nt_header
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_DEBUG
];
437 PIMAGE_DEBUG_DIRECTORY debug_entry
;
439 section
= rva_to_section (debug_dir
.VirtualAddress
, dst_nt_header
);
442 debug_entry
= (PIMAGE_DEBUG_DIRECTORY
)
443 (RVA_TO_OFFSET (debug_dir
.VirtualAddress
, section
) + p_outfile
->file_base
);
444 debug_dir
.Size
/= sizeof (IMAGE_DEBUG_DIRECTORY
);
446 for (i
= 0; i
< debug_dir
.Size
; i
++, debug_entry
++)
447 ADJUST_OFFSET (debug_entry
->PointerToRawData
);
454 main (int argc
, char **argv
)
456 file_data in_file
, out_file
;
457 char out_filename
[MAX_PATH
], in_filename
[MAX_PATH
];
459 PIMAGE_DOS_HEADER dos_header
;
460 PIMAGE_NT_HEADERS nt_header
;
462 #define OLD_NAME argv[1]
463 #define NEW_NAME argv[2]
464 #define SECTION_NAME argv[3]
465 #define SECTION_SIZE argv[4]
467 strcpy (in_filename
, OLD_NAME
);
468 strcpy (out_filename
, NEW_NAME
);
470 printf ("Dumping from %s\n", in_filename
);
471 printf (" to %s\n", out_filename
);
473 /* Open the undumped executable file. */
474 if (!open_input_file (&in_file
, in_filename
))
476 printf ("Failed to open %s (%d)...bailing.\n",
477 in_filename
, GetLastError ());
480 dos_header
= (PIMAGE_DOS_HEADER
) in_file
.file_base
;
481 nt_header
= (PIMAGE_NT_HEADERS
) ((char *) dos_header
+ dos_header
->e_lfanew
);
482 /* Allow for expansion due to increasing file align to section align.
483 We can overestimate here, since close_file_data will update the
486 + nt_header
->OptionalHeader
.SectionAlignment
487 * nt_header
->FileHeader
.NumberOfSections
;
488 if (!open_output_file (&out_file
, out_filename
, size
))
490 printf ("Failed to open %s (%d)...bailing.\n",
491 out_filename
, GetLastError ());
495 copy_executable_and_add_section (&in_file
, &out_file
,
497 atoi (SECTION_SIZE
) * 1024 * 1024);
499 /* Patch up header fields; profiler is picky about this. */
501 HANDLE hImagehelp
= LoadLibrary ("imagehlp.dll");
505 dos_header
= (PIMAGE_DOS_HEADER
) out_file
.file_base
;
506 nt_header
= (PIMAGE_NT_HEADERS
) ((char *) dos_header
+ dos_header
->e_lfanew
);
508 nt_header
->OptionalHeader
.CheckSum
= 0;
509 // nt_header->FileHeader.TimeDateStamp = time (NULL);
510 // dos_header->e_cp = size / 512;
511 // nt_header->OptionalHeader.SizeOfImage = size;
513 pfnCheckSumMappedFile
= (void *) GetProcAddress (hImagehelp
, "CheckSumMappedFile");
514 if (pfnCheckSumMappedFile
)
516 // nt_header->FileHeader.TimeDateStamp = time (NULL);
517 pfnCheckSumMappedFile (out_file
.file_base
,
521 nt_header
->OptionalHeader
.CheckSum
= checksum
;
523 FreeLibrary (hImagehelp
);
526 close_file_data (&in_file
);
527 close_file_data (&out_file
);