]> code.delx.au - gnu-emacs/blob - nt/addsection.c
Initial revision
[gnu-emacs] / nt / addsection.c
1 /* Add an uninitialized data section to an executable.
2 Copyright (C) 1999 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
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.
10
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.
15
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.
20
21 Andrew Innes <andrewi@harlequin.co.uk> 04-Jan-1999
22 based on code from unexw32.c
23 */
24
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <fcntl.h>
28 #include <time.h>
29 #include <windows.h>
30
31 /* Include relevant definitions from IMAGEHLP.H, which can be found
32 in \\win32sdk\mstools\samples\image\include\imagehlp.h. */
33
34 PIMAGE_NT_HEADERS
35 (__stdcall * pfnCheckSumMappedFile) (LPVOID BaseAddress,
36 DWORD FileLength,
37 LPDWORD HeaderSum,
38 LPDWORD CheckSum);
39
40 #undef min
41 #undef max
42 #define min(x, y) (((x) < (y)) ? (x) : (y))
43 #define max(x, y) (((x) > (y)) ? (x) : (y))
44
45
46 /* File handling. */
47
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;
55
56 int
57 open_input_file (file_data *p_file, char *filename)
58 {
59 HANDLE file;
60 HANDLE file_mapping;
61 void *file_base;
62 unsigned long size, upper_size;
63
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;
68
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;
74
75 file_base = MapViewOfFile (file_mapping, FILE_MAP_READ, 0, 0, size);
76 if (file_base == 0)
77 return FALSE;
78
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;
84
85 return TRUE;
86 }
87
88 int
89 open_output_file (file_data *p_file, char *filename, unsigned long size)
90 {
91 HANDLE file;
92 HANDLE file_mapping;
93 void *file_base;
94
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;
99
100 file_mapping = CreateFileMapping (file, NULL, PAGE_READWRITE,
101 0, size, NULL);
102 if (!file_mapping)
103 return FALSE;
104
105 file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size);
106 if (file_base == 0)
107 return FALSE;
108
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;
114
115 return TRUE;
116 }
117
118 /* Close the system structures associated with the given file. */
119 void
120 close_file_data (file_data *p_file)
121 {
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);
128 }
129
130
131 /* Routines to manipulate NT executable file sections. */
132
133 unsigned long
134 get_unrounded_section_size (PIMAGE_SECTION_HEADER p_section)
135 {
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);
141 }
142
143 /* Return pointer to section header for named section. */
144 IMAGE_SECTION_HEADER *
145 find_section (char * name, IMAGE_NT_HEADERS * nt_header)
146 {
147 PIMAGE_SECTION_HEADER section;
148 int i;
149
150 section = IMAGE_FIRST_SECTION (nt_header);
151
152 for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
153 {
154 if (strcmp (section->Name, name) == 0)
155 return section;
156 section++;
157 }
158 return NULL;
159 }
160
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)
165 {
166 PIMAGE_SECTION_HEADER section;
167 int i;
168
169 section = IMAGE_FIRST_SECTION (nt_header);
170
171 for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
172 {
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++;
185 }
186 return NULL;
187 }
188
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)
193 {
194 PIMAGE_SECTION_HEADER section;
195 int i;
196
197 section = IMAGE_FIRST_SECTION (nt_header);
198
199 for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
200 {
201 if (offset >= section->PointerToRawData
202 && offset < section->PointerToRawData + section->SizeOfRawData)
203 return section;
204 section++;
205 }
206 return NULL;
207 }
208
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)
216 {
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;
220
221 while (offset >= src_section->PointerToRawData)
222 {
223 if (offset < src_section->PointerToRawData + src_section->SizeOfRawData)
224 break;
225 i++;
226 if (i == src_nt_header->FileHeader.NumberOfSections)
227 {
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);
238 }
239 src_section++;
240 dst_section++;
241 }
242 return offset +
243 (dst_section->PointerToRawData - src_section->PointerToRawData);
244 }
245
246 #define OFFSET_TO_RVA(offset, section) \
247 (section->VirtualAddress + ((DWORD)(offset) - section->PointerToRawData))
248
249 #define RVA_TO_OFFSET(rva, section) \
250 (section->PointerToRawData + ((DWORD)(rva) - section->VirtualAddress))
251
252 #define RVA_TO_SECTION_OFFSET(rva, section) \
253 ((DWORD)(rva) - section->VirtualAddress)
254
255 /* Convert address in executing image to RVA. */
256 #define PTR_TO_RVA(ptr) ((DWORD)(ptr) - (DWORD) GetModuleHandle (NULL))
257
258 #define PTR_TO_OFFSET(ptr, pfile_data) \
259 ((char *)(ptr) - (pfile_data)->file_base)
260
261 #define OFFSET_TO_PTR(offset, pfile_data) \
262 ((pfile_data)->file_base + (DWORD)(offset))
263
264 #define ROUND_UP(p, align) (((DWORD)(p) + (align)-1) & ~((align)-1))
265 #define ROUND_DOWN(p, align) ((DWORD)(p) & ~((align)-1))
266
267
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)
273 {
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;
282
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)
294
295 #define DST_TO_OFFSET() PTR_TO_OFFSET (dst, p_outfile)
296 #define ROUND_UP_DST(align) \
297 (dst = p_outfile->file_base + ROUND_UP (DST_TO_OFFSET (), (align)))
298
299 /* Copy the source image sequentially, ie. section by section after
300 copying the headers and section table, to simplify the process of
301 adding an extra section table entry (which might force the raw
302 section data to be relocated).
303
304 Note that dst is updated implicitly by each COPY_CHUNK. */
305
306 dos_header = (PIMAGE_DOS_HEADER) p_infile->file_base;
307 nt_header = (PIMAGE_NT_HEADERS) (((unsigned long) dos_header) +
308 dos_header->e_lfanew);
309 section = IMAGE_FIRST_SECTION (nt_header);
310
311 dst = (unsigned char *) p_outfile->file_base;
312
313 COPY_CHUNK ("Copying DOS header...", dos_header,
314 (DWORD) nt_header - (DWORD) dos_header);
315 dst_nt_header = (PIMAGE_NT_HEADERS) dst;
316 COPY_CHUNK ("Copying NT header...", nt_header,
317 (DWORD) section - (DWORD) nt_header);
318 dst_section = (PIMAGE_SECTION_HEADER) dst;
319 COPY_CHUNK ("Copying section table...", section,
320 nt_header->FileHeader.NumberOfSections * sizeof (*section));
321
322 /* To improve the efficiency of demand loading, make the file
323 alignment match the section alignment (VC++ 6.0 does this by
324 default anyway). */
325 dst_nt_header->OptionalHeader.FileAlignment =
326 dst_nt_header->OptionalHeader.SectionAlignment;
327
328 /* Add an uninitialized data section at the end, of the specified name
329 and virtual size. */
330 if (find_section (new_section_name, nt_header) == NULL)
331 /* Leave room for extra section table entry; filled in below. */
332 dst += sizeof (*section);
333 else
334 new_section_name = NULL;
335
336 for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
337 {
338 char msg[100];
339 sprintf (msg, "Copying raw data for %s...", section->Name);
340
341 /* Align the section's raw data area. */
342 ROUND_UP_DST (dst_nt_header->OptionalHeader.FileAlignment);
343
344 /* Update the file-relative offset for this section's raw data (if
345 it has any) in case things have been relocated; we will update
346 the other offsets below once we know where everything is. */
347 if (dst_section->PointerToRawData)
348 dst_section->PointerToRawData = DST_TO_OFFSET ();
349
350 /* Can always copy the original raw data. */
351 COPY_CHUNK
352 (msg, OFFSET_TO_PTR (section->PointerToRawData, p_infile),
353 section->SizeOfRawData);
354
355 /* Round up the raw data size to the new alignment. */
356 dst_section->SizeOfRawData =
357 ROUND_UP (dst_section->SizeOfRawData,
358 dst_nt_header->OptionalHeader.FileAlignment);
359
360 section++;
361 dst_section++;
362 }
363
364 /* Pad out the final section raw data area. */
365 ROUND_UP_DST (dst_nt_header->OptionalHeader.FileAlignment);
366
367 /* Add the extra section entry (which adds no raw data). */
368 if (new_section_name != NULL)
369 {
370 dst_nt_header->FileHeader.NumberOfSections++;
371 dst_nt_header->OptionalHeader.SizeOfImage += new_section_size;
372 strncpy (dst_section->Name, new_section_name, sizeof (dst_section->Name));
373 dst_section->VirtualAddress =
374 section[-1].VirtualAddress
375 + ROUND_UP (section[-1].Misc.VirtualSize,
376 dst_nt_header->OptionalHeader.SectionAlignment);
377 dst_section->Misc.VirtualSize = new_section_size;
378 dst_section->PointerToRawData = 0;
379 dst_section->SizeOfRawData = 0;
380 dst_section->Characteristics =
381 IMAGE_SCN_CNT_UNINITIALIZED_DATA
382 | IMAGE_SCN_MEM_READ
383 | IMAGE_SCN_MEM_WRITE;
384 }
385
386 /* Copy remainder of source image. */
387 section--;
388 offset = ROUND_UP (section->PointerToRawData + section->SizeOfRawData,
389 nt_header->OptionalHeader.FileAlignment);
390 COPY_CHUNK
391 ("Copying remainder of executable...",
392 OFFSET_TO_PTR (offset, p_infile),
393 p_infile->size - offset);
394
395 /* Final size for new image. */
396 p_outfile->size = DST_TO_OFFSET ();
397
398 /* Now patch up remaining file-relative offsets. */
399 section = IMAGE_FIRST_SECTION (nt_header);
400 dst_section = IMAGE_FIRST_SECTION (dst_nt_header);
401
402 #define ADJUST_OFFSET(var) \
403 do { \
404 if ((var) != 0) \
405 (var) = relocate_offset ((var), nt_header, dst_nt_header); \
406 } while (0)
407
408 dst_nt_header->OptionalHeader.SizeOfInitializedData = 0;
409 dst_nt_header->OptionalHeader.SizeOfUninitializedData = 0;
410 for (i = 0; i < dst_nt_header->FileHeader.NumberOfSections; i++)
411 {
412 /* Recompute data sizes for completeness. */
413 if (dst_section[i].Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
414 dst_nt_header->OptionalHeader.SizeOfInitializedData +=
415 ROUND_UP (dst_section[i].Misc.VirtualSize, dst_nt_header->OptionalHeader.FileAlignment);
416 else if (dst_section[i].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
417 dst_nt_header->OptionalHeader.SizeOfUninitializedData +=
418 ROUND_UP (dst_section[i].Misc.VirtualSize, dst_nt_header->OptionalHeader.FileAlignment);
419
420 ADJUST_OFFSET (dst_section[i].PointerToLinenumbers);
421 }
422
423 ADJUST_OFFSET (dst_nt_header->FileHeader.PointerToSymbolTable);
424
425 /* Update offsets in debug directory entries. */
426 {
427 IMAGE_DATA_DIRECTORY debug_dir =
428 dst_nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
429 PIMAGE_DEBUG_DIRECTORY debug_entry;
430
431 section = rva_to_section (debug_dir.VirtualAddress, dst_nt_header);
432 if (section)
433 {
434 debug_entry = (PIMAGE_DEBUG_DIRECTORY)
435 (RVA_TO_OFFSET (debug_dir.VirtualAddress, section) + p_outfile->file_base);
436 debug_dir.Size /= sizeof (IMAGE_DEBUG_DIRECTORY);
437
438 for (i = 0; i < debug_dir.Size; i++, debug_entry++)
439 ADJUST_OFFSET (debug_entry->PointerToRawData);
440 }
441 }
442 }
443
444
445 int
446 main (int argc, char **argv)
447 {
448 file_data in_file, out_file;
449 char out_filename[MAX_PATH], in_filename[MAX_PATH];
450 unsigned long size;
451 PIMAGE_DOS_HEADER dos_header;
452 PIMAGE_NT_HEADERS nt_header;
453
454 #define OLD_NAME argv[1]
455 #define NEW_NAME argv[2]
456 #define SECTION_NAME argv[3]
457 #define SECTION_SIZE argv[4]
458
459 strcpy (in_filename, OLD_NAME);
460 strcpy (out_filename, NEW_NAME);
461
462 printf ("Dumping from %s\n", in_filename);
463 printf (" to %s\n", out_filename);
464
465 /* Open the undumped executable file. */
466 if (!open_input_file (&in_file, in_filename))
467 {
468 printf ("Failed to open %s (%d)...bailing.\n",
469 in_filename, GetLastError ());
470 exit (1);
471 }
472 dos_header = (PIMAGE_DOS_HEADER) in_file.file_base;
473 nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew);
474 /* Allow for expansion due to increasing file align to section align.
475 We can overestimate here, since close_file_data will update the
476 size exactly. */
477 size = in_file.size
478 + nt_header->OptionalHeader.SectionAlignment
479 * nt_header->FileHeader.NumberOfSections;
480 if (!open_output_file (&out_file, out_filename, size))
481 {
482 printf ("Failed to open %s (%d)...bailing.\n",
483 out_filename, GetLastError ());
484 exit (1);
485 }
486
487 copy_executable_and_add_section (&in_file, &out_file,
488 SECTION_NAME,
489 atoi (SECTION_SIZE) * 1024 * 1024);
490
491 /* Patch up header fields; profiler is picky about this. */
492 {
493 HANDLE hImagehelp = LoadLibrary ("imagehlp.dll");
494 DWORD headersum;
495 DWORD checksum;
496
497 dos_header = (PIMAGE_DOS_HEADER) out_file.file_base;
498 nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew);
499
500 nt_header->OptionalHeader.CheckSum = 0;
501 // nt_header->FileHeader.TimeDateStamp = time (NULL);
502 // dos_header->e_cp = size / 512;
503 // nt_header->OptionalHeader.SizeOfImage = size;
504
505 pfnCheckSumMappedFile = (void *) GetProcAddress (hImagehelp, "CheckSumMappedFile");
506 if (pfnCheckSumMappedFile)
507 {
508 // nt_header->FileHeader.TimeDateStamp = time (NULL);
509 pfnCheckSumMappedFile (out_file.file_base,
510 out_file.size,
511 &headersum,
512 &checksum);
513 nt_header->OptionalHeader.CheckSum = checksum;
514 }
515 FreeLibrary (hImagehelp);
516 }
517
518 close_file_data (&in_file);
519 close_file_data (&out_file);
520
521 return 0;
522 }
523
524 /* eof */