1/* Add an uninitialized data section to an executable.
2   Copyright (C) 1999, 2001, 2002, 2003, 2004, 2005,
3      2006, 2007  Free Software Foundation, Inc.
4
5This file is part of GNU Emacs.
6
7GNU Emacs is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2, or (at your option)
10any later version.
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Emacs; see the file COPYING.  If not, write to
19the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20Boston, MA 02110-1301, USA.
21
22   Andrew Innes <andrewi@harlequin.co.uk>       04-Jan-1999
23     based on code from unexw32.c
24*/
25
26#include <stdlib.h>
27#include <stdio.h>
28#include <fcntl.h>
29#include <time.h>
30#ifdef __GNUC__
31#define _ANONYMOUS_UNION
32#define _ANONYMOUS_STRUCT
33#endif
34#include <windows.h>
35
36/* Include relevant definitions from IMAGEHLP.H, which can be found
37   in \\win32sdk\mstools\samples\image\include\imagehlp.h. */
38
39PIMAGE_NT_HEADERS
40(__stdcall * pfnCheckSumMappedFile) (LPVOID BaseAddress,
41				    DWORD FileLength,
42				    LPDWORD HeaderSum,
43				    LPDWORD CheckSum);
44
45#undef min
46#undef max
47#define min(x, y) (((x) < (y)) ? (x) : (y))
48#define max(x, y) (((x) > (y)) ? (x) : (y))
49
50
51/* File handling.  */
52
53typedef struct file_data {
54    char          *name;
55    unsigned long  size;
56    HANDLE         file;
57    HANDLE         file_mapping;
58    unsigned char *file_base;
59} file_data;
60
61int
62open_input_file (file_data *p_file, char *filename)
63{
64  HANDLE file;
65  HANDLE file_mapping;
66  void  *file_base;
67  unsigned long size, upper_size;
68
69  file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
70		     OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
71  if (file == INVALID_HANDLE_VALUE)
72    return FALSE;
73
74  size = GetFileSize (file, &upper_size);
75  file_mapping = CreateFileMapping (file, NULL, PAGE_READONLY,
76				    0, size, NULL);
77  if (!file_mapping)
78    return FALSE;
79
80  file_base = MapViewOfFile (file_mapping, FILE_MAP_READ, 0, 0, size);
81  if (file_base == 0)
82    return FALSE;
83
84  p_file->name = filename;
85  p_file->size = size;
86  p_file->file = file;
87  p_file->file_mapping = file_mapping;
88  p_file->file_base = file_base;
89
90  return TRUE;
91}
92
93int
94open_output_file (file_data *p_file, char *filename, unsigned long size)
95{
96  HANDLE file;
97  HANDLE file_mapping;
98  void  *file_base;
99
100  file = CreateFile (filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
101		     CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
102  if (file == INVALID_HANDLE_VALUE)
103    return FALSE;
104
105  file_mapping = CreateFileMapping (file, NULL, PAGE_READWRITE,
106				    0, size, NULL);
107  if (!file_mapping)
108    return FALSE;
109
110  file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size);
111  if (file_base == 0)
112    return FALSE;
113
114  p_file->name = filename;
115  p_file->size = size;
116  p_file->file = file;
117  p_file->file_mapping = file_mapping;
118  p_file->file_base = file_base;
119
120  return TRUE;
121}
122
123/* Close the system structures associated with the given file.  */
124void
125close_file_data (file_data *p_file)
126{
127  UnmapViewOfFile (p_file->file_base);
128  CloseHandle (p_file->file_mapping);
129  /* For the case of output files, set final size.  */
130  SetFilePointer (p_file->file, p_file->size, NULL, FILE_BEGIN);
131  SetEndOfFile (p_file->file);
132  CloseHandle (p_file->file);
133}
134
135
136/* Routines to manipulate NT executable file sections.  */
137
138unsigned long
139get_unrounded_section_size (PIMAGE_SECTION_HEADER p_section)
140{
141  /* The true section size, before rounding, for an initialized data or
142     code section.  (Supposedly some linkers swap the meaning of these
143     two values.)  */
144  return min (p_section->SizeOfRawData,
145	      p_section->Misc.VirtualSize);
146}
147
148/* Return pointer to section header for named section. */
149IMAGE_SECTION_HEADER *
150find_section (char * name, IMAGE_NT_HEADERS * nt_header)
151{
152  PIMAGE_SECTION_HEADER section;
153  int i;
154
155  section = IMAGE_FIRST_SECTION (nt_header);
156
157  for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
158    {
159      if (strcmp (section->Name, name) == 0)
160	return section;
161      section++;
162    }
163  return NULL;
164}
165
166/* Return pointer to section header for section containing the given
167   relative virtual address. */
168IMAGE_SECTION_HEADER *
169rva_to_section (DWORD rva, IMAGE_NT_HEADERS * nt_header)
170{
171  PIMAGE_SECTION_HEADER section;
172  int i;
173
174  section = IMAGE_FIRST_SECTION (nt_header);
175
176  for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
177    {
178      /* Some linkers (eg. the NT SDK linker I believe) swapped the
179	 meaning of these two values - or rather, they ignored
180	 VirtualSize entirely and always set it to zero.  This affects
181	 some very old exes (eg. gzip dated Dec 1993).  Since
182	 w32_executable_type relies on this function to work reliably,
183	 we need to cope with this.  */
184      DWORD real_size = max (section->SizeOfRawData,
185			     section->Misc.VirtualSize);
186      if (rva >= section->VirtualAddress
187	  && rva < section->VirtualAddress + real_size)
188	return section;
189      section++;
190    }
191  return NULL;
192}
193
194/* Return pointer to section header for section containing the given
195   offset in its raw data area. */
196IMAGE_SECTION_HEADER *
197offset_to_section (DWORD offset, IMAGE_NT_HEADERS * nt_header)
198{
199  PIMAGE_SECTION_HEADER section;
200  int i;
201
202  section = IMAGE_FIRST_SECTION (nt_header);
203
204  for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
205    {
206      if (offset >= section->PointerToRawData
207	  && offset < section->PointerToRawData + section->SizeOfRawData)
208	return section;
209      section++;
210    }
211  return NULL;
212}
213
214/* Return offset to an object in dst, given offset in src.  We assume
215   there is at least one section in both src and dst images, and that
216   the some sections may have been added to dst (after sections in src).  */
217static DWORD
218relocate_offset (DWORD offset,
219		 IMAGE_NT_HEADERS * src_nt_header,
220		 IMAGE_NT_HEADERS * dst_nt_header)
221{
222  PIMAGE_SECTION_HEADER src_section = IMAGE_FIRST_SECTION (src_nt_header);
223  PIMAGE_SECTION_HEADER dst_section = IMAGE_FIRST_SECTION (dst_nt_header);
224  int i = 0;
225
226  while (offset >= src_section->PointerToRawData)
227    {
228      if (offset < src_section->PointerToRawData + src_section->SizeOfRawData)
229	break;
230      i++;
231      if (i == src_nt_header->FileHeader.NumberOfSections)
232	{
233	  /* Handle offsets after the last section.  */
234	  dst_section = IMAGE_FIRST_SECTION (dst_nt_header);
235	  dst_section += dst_nt_header->FileHeader.NumberOfSections - 1;
236	  while (dst_section->PointerToRawData == 0)
237	    dst_section--;
238	  while (src_section->PointerToRawData == 0)
239	    src_section--;
240	  return offset
241	    + (dst_section->PointerToRawData + dst_section->SizeOfRawData)
242	    - (src_section->PointerToRawData + src_section->SizeOfRawData);
243	}
244      src_section++;
245      dst_section++;
246    }
247  return offset +
248    (dst_section->PointerToRawData - src_section->PointerToRawData);
249}
250
251#define OFFSET_TO_RVA(offset, section) \
252	  (section->VirtualAddress + ((DWORD)(offset) - section->PointerToRawData))
253
254#define RVA_TO_OFFSET(rva, section) \
255	  (section->PointerToRawData + ((DWORD)(rva) - section->VirtualAddress))
256
257#define RVA_TO_SECTION_OFFSET(rva, section) \
258	  ((DWORD)(rva) - section->VirtualAddress)
259
260/* Convert address in executing image to RVA.  */
261#define PTR_TO_RVA(ptr) ((DWORD)(ptr) - (DWORD) GetModuleHandle (NULL))
262
263#define PTR_TO_OFFSET(ptr, pfile_data) \
264          ((unsigned char *)(ptr) - (pfile_data)->file_base)
265
266#define OFFSET_TO_PTR(offset, pfile_data) \
267          ((pfile_data)->file_base + (DWORD)(offset))
268
269#define ROUND_UP(p, align)   (((DWORD)(p) + (align)-1) & ~((align)-1))
270#define ROUND_DOWN(p, align) ((DWORD)(p) & ~((align)-1))
271
272
273static void
274copy_executable_and_add_section (file_data *p_infile,
275				 file_data *p_outfile,
276				 char *new_section_name,
277				 DWORD new_section_size)
278{
279  unsigned char *dst;
280  PIMAGE_DOS_HEADER dos_header;
281  PIMAGE_NT_HEADERS nt_header;
282  PIMAGE_NT_HEADERS dst_nt_header;
283  PIMAGE_SECTION_HEADER section;
284  PIMAGE_SECTION_HEADER dst_section;
285  DWORD offset;
286  int i;
287  int be_verbose = GetEnvironmentVariable ("DEBUG_DUMP", NULL, 0) > 0;
288
289#define COPY_CHUNK(message, src, size, verbose)					\
290  do {										\
291    unsigned char *s = (void *)(src);						\
292    unsigned long count = (size);						\
293    if (verbose)								\
294      {										\
295	printf ("%s\n", (message));						\
296	printf ("\t0x%08x Offset in input file.\n", s - p_infile->file_base); 	\
297	printf ("\t0x%08x Offset in output file.\n", dst - p_outfile->file_base); \
298	printf ("\t0x%08x Size in bytes.\n", count);				\
299      }										\
300    memcpy (dst, s, count);							\
301    dst += count;								\
302  } while (0)
303
304#define DST_TO_OFFSET()  PTR_TO_OFFSET (dst, p_outfile)
305#define ROUND_UP_DST_AND_ZERO(align)						\
306  do {										\
307    unsigned char *newdst = p_outfile->file_base				\
308      + ROUND_UP (DST_TO_OFFSET (), (align));					\
309    /* Zero the alignment slop; it may actually initialize real data.  */	\
310    memset (dst, 0, newdst - dst);						\
311    dst = newdst;								\
312  } while (0)
313
314  /* Copy the source image sequentially, ie. section by section after
315     copying the headers and section table, to simplify the process of
316     adding an extra section table entry (which might force the raw
317     section data to be relocated).
318
319     Note that dst is updated implicitly by each COPY_CHUNK.  */
320
321  dos_header = (PIMAGE_DOS_HEADER) p_infile->file_base;
322  nt_header = (PIMAGE_NT_HEADERS) (((unsigned long) dos_header) +
323				   dos_header->e_lfanew);
324  section = IMAGE_FIRST_SECTION (nt_header);
325
326  dst = (unsigned char *) p_outfile->file_base;
327
328  COPY_CHUNK ("Copying DOS header...", dos_header,
329	      (DWORD) nt_header - (DWORD) dos_header, be_verbose);
330  dst_nt_header = (PIMAGE_NT_HEADERS) dst;
331  COPY_CHUNK ("Copying NT header...", nt_header,
332	      (DWORD) section - (DWORD) nt_header, be_verbose);
333  dst_section = (PIMAGE_SECTION_HEADER) dst;
334  COPY_CHUNK ("Copying section table...", section,
335	      nt_header->FileHeader.NumberOfSections * sizeof (*section),
336	      be_verbose);
337
338  /* To improve the efficiency of demand loading, make the file
339     alignment match the section alignment (VC++ 6.0 does this by
340     default anyway).  */
341  dst_nt_header->OptionalHeader.FileAlignment =
342    dst_nt_header->OptionalHeader.SectionAlignment;
343
344  /* Add an uninitialized data section at the end, of the specified name
345     and virtual size.  */
346  if (find_section (new_section_name, nt_header) == NULL)
347    /* Leave room for extra section table entry; filled in below.  */
348    dst += sizeof (*section);
349  else
350    new_section_name = NULL;
351
352  /* Align the first section's raw data area, and set the header size
353     field accordingly.  */
354  ROUND_UP_DST_AND_ZERO (dst_nt_header->OptionalHeader.FileAlignment);
355  dst_nt_header->OptionalHeader.SizeOfHeaders = DST_TO_OFFSET ();
356
357  for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
358    {
359      char msg[100];
360      /* Windows section names are fixed 8-char strings, only
361	 zero-terminated if the name is shorter than 8 characters.  */
362      sprintf (msg, "Copying raw data for %.8s...", section->Name);
363
364      /* Update the file-relative offset for this section's raw data (if
365         it has any) in case things have been relocated; we will update
366         the other offsets below once we know where everything is.  */
367      if (dst_section->PointerToRawData)
368	dst_section->PointerToRawData = DST_TO_OFFSET ();
369
370      /* Can always copy the original raw data.  */
371      COPY_CHUNK
372	(msg, OFFSET_TO_PTR (section->PointerToRawData, p_infile),
373	 section->SizeOfRawData, be_verbose);
374
375      /* Round up the raw data size to the new alignment.  */
376      dst_section->SizeOfRawData =
377	ROUND_UP (dst_section->SizeOfRawData,
378		  dst_nt_header->OptionalHeader.FileAlignment);
379
380      /* Align the next section's raw data area.  */
381      ROUND_UP_DST_AND_ZERO (dst_nt_header->OptionalHeader.FileAlignment);
382
383      section++;
384      dst_section++;
385    }
386
387  /* Add the extra section entry (which adds no raw data).  */
388  if (new_section_name != NULL)
389    {
390      dst_nt_header->FileHeader.NumberOfSections++;
391      dst_nt_header->OptionalHeader.SizeOfImage += new_section_size;
392      strncpy (dst_section->Name, new_section_name, sizeof (dst_section->Name));
393      dst_section->VirtualAddress =
394	section[-1].VirtualAddress
395	+ ROUND_UP (section[-1].Misc.VirtualSize,
396		    dst_nt_header->OptionalHeader.SectionAlignment);
397      dst_section->Misc.VirtualSize = new_section_size;
398      dst_section->PointerToRawData = 0;
399      dst_section->SizeOfRawData = 0;
400      dst_section->Characteristics =
401	IMAGE_SCN_CNT_UNINITIALIZED_DATA
402	| IMAGE_SCN_MEM_READ
403	| IMAGE_SCN_MEM_WRITE;
404    }
405
406  /* Copy remainder of source image.  */
407  section--;
408  offset = ROUND_UP (section->PointerToRawData + section->SizeOfRawData,
409		     nt_header->OptionalHeader.FileAlignment);
410  COPY_CHUNK
411    ("Copying remainder of executable...",
412     OFFSET_TO_PTR (offset, p_infile),
413     p_infile->size - offset, be_verbose);
414
415  /* Final size for new image.  */
416  p_outfile->size = DST_TO_OFFSET ();
417
418  /* Now patch up remaining file-relative offsets.  */
419  section = IMAGE_FIRST_SECTION (nt_header);
420  dst_section = IMAGE_FIRST_SECTION (dst_nt_header);
421
422#define ADJUST_OFFSET(var)						\
423  do {									\
424    if ((var) != 0)							\
425      (var) = relocate_offset ((var), nt_header, dst_nt_header);	\
426  } while (0)
427
428  dst_nt_header->OptionalHeader.SizeOfInitializedData = 0;
429  dst_nt_header->OptionalHeader.SizeOfUninitializedData = 0;
430  for (i = 0; i < dst_nt_header->FileHeader.NumberOfSections; i++)
431    {
432      /* Recompute data sizes for completeness.  */
433      if (dst_section[i].Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
434	dst_nt_header->OptionalHeader.SizeOfInitializedData +=
435	  ROUND_UP (dst_section[i].Misc.VirtualSize, dst_nt_header->OptionalHeader.FileAlignment);
436      else if (dst_section[i].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
437	dst_nt_header->OptionalHeader.SizeOfUninitializedData +=
438	  ROUND_UP (dst_section[i].Misc.VirtualSize, dst_nt_header->OptionalHeader.FileAlignment);
439
440      ADJUST_OFFSET (dst_section[i].PointerToLinenumbers);
441    }
442
443  ADJUST_OFFSET (dst_nt_header->FileHeader.PointerToSymbolTable);
444
445  /* Update offsets in debug directory entries. */
446  {
447    IMAGE_DATA_DIRECTORY debug_dir =
448      dst_nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
449    PIMAGE_DEBUG_DIRECTORY debug_entry;
450
451    section = rva_to_section (debug_dir.VirtualAddress, dst_nt_header);
452    if (section)
453      {
454	debug_entry = (PIMAGE_DEBUG_DIRECTORY)
455	  (RVA_TO_OFFSET (debug_dir.VirtualAddress, section) + p_outfile->file_base);
456	debug_dir.Size /= sizeof (IMAGE_DEBUG_DIRECTORY);
457
458	for (i = 0; i < debug_dir.Size; i++, debug_entry++)
459	  ADJUST_OFFSET (debug_entry->PointerToRawData);
460      }
461  }
462}
463
464
465int
466main (int argc, char **argv)
467{
468  file_data in_file, out_file;
469  char out_filename[MAX_PATH], in_filename[MAX_PATH];
470  unsigned long size;
471  PIMAGE_DOS_HEADER dos_header;
472  PIMAGE_NT_HEADERS nt_header;
473
474#define OLD_NAME        argv[1]
475#define NEW_NAME        argv[2]
476#define SECTION_NAME    argv[3]
477#define SECTION_SIZE    argv[4]
478
479  strcpy (in_filename, OLD_NAME);
480  strcpy (out_filename, NEW_NAME);
481
482  printf ("Dumping from %s\n", in_filename);
483  printf ("          to %s\n", out_filename);
484
485  /* Open the undumped executable file.  */
486  if (!open_input_file (&in_file, in_filename))
487    {
488      printf ("Failed to open %s (%d)...bailing.\n",
489	      in_filename, GetLastError ());
490      exit (1);
491    }
492  dos_header = (PIMAGE_DOS_HEADER) in_file.file_base;
493  nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew);
494  /* Allow for expansion due to increasing file align to section align.
495     We can overestimate here, since close_file_data will update the
496     size exactly.  */
497  size = in_file.size
498    + nt_header->OptionalHeader.SectionAlignment
499    * nt_header->FileHeader.NumberOfSections;
500  if (!open_output_file (&out_file, out_filename, size))
501    {
502      printf ("Failed to open %s (%d)...bailing.\n",
503	      out_filename, GetLastError ());
504      exit (1);
505    }
506
507  copy_executable_and_add_section (&in_file, &out_file,
508				   SECTION_NAME,
509				   atoi (SECTION_SIZE) * 1024 * 1024);
510
511  /* Patch up header fields; profiler is picky about this. */
512  {
513    HANDLE hImagehelp = LoadLibrary ("imagehlp.dll");
514    DWORD  headersum;
515    DWORD  checksum;
516
517    dos_header = (PIMAGE_DOS_HEADER) out_file.file_base;
518    nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew);
519
520    nt_header->OptionalHeader.CheckSum = 0;
521//    nt_header->FileHeader.TimeDateStamp = time (NULL);
522//    dos_header->e_cp = size / 512;
523//    nt_header->OptionalHeader.SizeOfImage = size;
524
525    pfnCheckSumMappedFile = (void *) GetProcAddress (hImagehelp, "CheckSumMappedFile");
526    if (pfnCheckSumMappedFile)
527      {
528//	nt_header->FileHeader.TimeDateStamp = time (NULL);
529	pfnCheckSumMappedFile (out_file.file_base,
530			       out_file.size,
531			       &headersum,
532			       &checksum);
533	nt_header->OptionalHeader.CheckSum = checksum;
534      }
535    FreeLibrary (hImagehelp);
536  }
537
538  close_file_data (&in_file);
539  close_file_data (&out_file);
540
541  return 0;
542}
543
544/* eof */
545
546/* arch-tag: 17e2b0aa-8c17-4bd1-b24b-1cda689245fa
547   (do not change this comment) */
548