Change from rms.
[emacs.git] / nt / addsection.c
blobab69ecc2e270cd609e2b9b07e18e209186a15906
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)
9 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; 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
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <fcntl.h>
28 #include <time.h>
29 #include <windows.h>
31 /* Include relevant definitions from IMAGEHLP.H, which can be found
32 in \\win32sdk\mstools\samples\image\include\imagehlp.h. */
34 PIMAGE_NT_HEADERS
35 (__stdcall * pfnCheckSumMappedFile) (LPVOID BaseAddress,
36 DWORD FileLength,
37 LPDWORD HeaderSum,
38 LPDWORD CheckSum);
40 #undef min
41 #undef max
42 #define min(x, y) (((x) < (y)) ? (x) : (y))
43 #define max(x, y) (((x) > (y)) ? (x) : (y))
46 /* File handling. */
48 typedef struct file_data {
49 char *name;
50 unsigned long size;
51 HANDLE file;
52 HANDLE file_mapping;
53 unsigned char *file_base;
54 } file_data;
56 int
57 open_input_file (file_data *p_file, char *filename)
59 HANDLE file;
60 HANDLE file_mapping;
61 void *file_base;
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)
67 return FALSE;
69 size = GetFileSize (file, &upper_size);
70 file_mapping = CreateFileMapping (file, NULL, PAGE_READONLY,
71 0, size, NULL);
72 if (!file_mapping)
73 return FALSE;
75 file_base = MapViewOfFile (file_mapping, FILE_MAP_READ, 0, 0, size);
76 if (file_base == 0)
77 return FALSE;
79 p_file->name = filename;
80 p_file->size = size;
81 p_file->file = file;
82 p_file->file_mapping = file_mapping;
83 p_file->file_base = file_base;
85 return TRUE;
88 int
89 open_output_file (file_data *p_file, char *filename, unsigned long size)
91 HANDLE file;
92 HANDLE file_mapping;
93 void *file_base;
95 file = CreateFile (filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
96 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
97 if (file == INVALID_HANDLE_VALUE)
98 return FALSE;
100 file_mapping = CreateFileMapping (file, NULL, PAGE_READWRITE,
101 0, size, NULL);
102 if (!file_mapping)
103 return FALSE;
105 file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size);
106 if (file_base == 0)
107 return FALSE;
109 p_file->name = filename;
110 p_file->size = size;
111 p_file->file = file;
112 p_file->file_mapping = file_mapping;
113 p_file->file_base = file_base;
115 return TRUE;
118 /* Close the system structures associated with the given file. */
119 void
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. */
133 unsigned long
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
138 two values.) */
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;
148 int i;
150 section = IMAGE_FIRST_SECTION (nt_header);
152 for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
154 if (strcmp (section->Name, name) == 0)
155 return section;
156 section++;
158 return NULL;
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;
167 int i;
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)
183 return section;
184 section++;
186 return NULL;
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;
195 int i;
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)
203 return section;
204 section++;
206 return NULL;
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). */
212 static DWORD
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);
219 int i = 0;
221 while (offset >= src_section->PointerToRawData)
223 if (offset < src_section->PointerToRawData + src_section->SizeOfRawData)
224 break;
225 i++;
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)
232 dst_section--;
233 while (src_section->PointerToRawData == 0)
234 src_section--;
235 return offset
236 + (dst_section->PointerToRawData + dst_section->SizeOfRawData)
237 - (src_section->PointerToRawData + src_section->SizeOfRawData);
239 src_section++;
240 dst_section++;
242 return offset +
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))
268 static void
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)
274 unsigned char *dst;
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;
280 DWORD offset;
281 int i;
283 #define COPY_CHUNK(message, src, size) \
284 do { \
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); \
292 dst += count; \
293 } while (0)
295 #define DST_TO_OFFSET() PTR_TO_OFFSET (dst, p_outfile)
296 #define ROUND_UP_DST_AND_ZERO(align) \
297 do { \
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); \
302 dst = newdst; \
303 } while (0)
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
330 default anyway). */
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
335 and virtual size. */
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);
339 else
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++)
349 char msg[100];
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. */
359 COPY_CHUNK
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);
371 section++;
372 dst_section++;
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
390 | IMAGE_SCN_MEM_READ
391 | IMAGE_SCN_MEM_WRITE;
394 /* Copy remainder of source image. */
395 section--;
396 offset = ROUND_UP (section->PointerToRawData + section->SizeOfRawData,
397 nt_header->OptionalHeader.FileAlignment);
398 COPY_CHUNK
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) \
411 do { \
412 if ((var) != 0) \
413 (var) = relocate_offset ((var), nt_header, dst_nt_header); \
414 } while (0)
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);
440 if (section)
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];
458 unsigned long size;
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 ());
478 exit (1);
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
484 size exactly. */
485 size = in_file.size
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 ());
492 exit (1);
495 copy_executable_and_add_section (&in_file, &out_file,
496 SECTION_NAME,
497 atoi (SECTION_SIZE) * 1024 * 1024);
499 /* Patch up header fields; profiler is picky about this. */
501 HANDLE hImagehelp = LoadLibrary ("imagehlp.dll");
502 DWORD headersum;
503 DWORD checksum;
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,
518 out_file.size,
519 &headersum,
520 &checksum);
521 nt_header->OptionalHeader.CheckSum = checksum;
523 FreeLibrary (hImagehelp);
526 close_file_data (&in_file);
527 close_file_data (&out_file);
529 return 0;
532 /* eof */