unexw32.c 27.1 KB
Newer Older
1
/* unexec for GNU Emacs on Windows NT.
2
   Copyright (C) 1994, 2001-2012  Free Software Foundation, Inc.
Karl Heuer's avatar
Karl Heuer committed
3

4
This file is part of GNU Emacs.
Karl Heuer's avatar
Karl Heuer committed
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 9
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Karl Heuer's avatar
Karl Heuer committed
10

11 12 13 14
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
Karl Heuer's avatar
Karl Heuer committed
15

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/>.  */
Karl Heuer's avatar
Karl Heuer committed
18

19
/*
Karl Heuer's avatar
Karl Heuer committed
20 21 22
   Geoff Voelker (voelker@cs.washington.edu)                         8-12-94
*/

Geoff Voelker's avatar
Geoff Voelker committed
23
#include <config.h>
24
#include "unexec.h"
25
#include "lisp.h"
Daniel Colascione's avatar
Daniel Colascione committed
26
#include "w32common.h"
27
#include "w32.h"
Geoff Voelker's avatar
Geoff Voelker committed
28

Karl Heuer's avatar
Karl Heuer committed
29 30
#include <stdio.h>
#include <fcntl.h>
Geoff Voelker's avatar
Geoff Voelker committed
31
#include <time.h>
Karl Heuer's avatar
Karl Heuer committed
32 33
#include <windows.h>

Geoff Voelker's avatar
Geoff Voelker committed
34 35
/* Include relevant definitions from IMAGEHLP.H, which can be found
   in \\win32sdk\mstools\samples\image\include\imagehlp.h. */
Karl Heuer's avatar
Karl Heuer committed
36

Geoff Voelker's avatar
Geoff Voelker committed
37 38 39 40 41
PIMAGE_NT_HEADERS
(__stdcall * pfnCheckSumMappedFile) (LPVOID BaseAddress,
				    DWORD FileLength,
				    LPDWORD HeaderSum,
				    LPDWORD CheckSum);
Karl Heuer's avatar
Karl Heuer committed
42

Geoff Voelker's avatar
Geoff Voelker committed
43 44 45 46 47 48
extern BOOL ctrl_c_handler (unsigned long type);

extern char my_begdata[];
extern char my_edata[];
extern char my_begbss[];
extern char my_endbss[];
49 50
extern char *my_begbss_static;
extern char *my_endbss_static;
Karl Heuer's avatar
Karl Heuer committed
51

Geoff Voelker's avatar
Geoff Voelker committed
52
#include "w32heap.h"
53

Karl Heuer's avatar
Karl Heuer committed
54 55 56 57 58
#undef min
#undef max
#define min(x, y) (((x) < (y)) ? (x) : (y))
#define max(x, y) (((x) > (y)) ? (x) : (y))

Karl Heuer's avatar
Karl Heuer committed
59
/* Basically, our "initialized" flag.  */
60
BOOL using_dynamic_heap = FALSE;
Karl Heuer's avatar
Karl Heuer committed
61

Geoff Voelker's avatar
Geoff Voelker committed
62 63
int open_input_file (file_data *p_file, char *name);
int open_output_file (file_data *p_file, char *name, unsigned long size);
Karl Heuer's avatar
Karl Heuer committed
64 65 66
void close_file_data (file_data *p_file);

void get_section_info (file_data *p_file);
67
void copy_executable_and_dump_data (file_data *, file_data *);
Karl Heuer's avatar
Karl Heuer committed
68 69 70
void dump_bss_and_heap (file_data *p_infile, file_data *p_outfile);

/* Cached info about the .data section in the executable.  */
71
PIMAGE_SECTION_HEADER data_section;
Andrew Innes's avatar
Andrew Innes committed
72
PCHAR  data_start = 0;
73
DWORD_PTR  data_size = 0;
Karl Heuer's avatar
Karl Heuer committed
74 75

/* Cached info about the .bss section in the executable.  */
76
PIMAGE_SECTION_HEADER bss_section;
Andrew Innes's avatar
Andrew Innes committed
77
PCHAR  bss_start = 0;
78 79
DWORD_PTR  bss_size = 0;
DWORD_PTR  extra_bss_size = 0;
80 81
/* bss data that is static might be discontiguous from non-static.  */
PIMAGE_SECTION_HEADER bss_section_static;
Andrew Innes's avatar
Andrew Innes committed
82
PCHAR  bss_start_static = 0;
83 84
DWORD_PTR  bss_size_static = 0;
DWORD_PTR  extra_bss_size_static = 0;
85 86

PIMAGE_SECTION_HEADER heap_section;
Karl Heuer's avatar
Karl Heuer committed
87

88
#ifdef HAVE_NTGUI
Daniel Colascione's avatar
Daniel Colascione committed
89
extern HINSTANCE hinst;
90 91 92 93 94
HINSTANCE hprevinst = NULL;
LPSTR lpCmdLine = "";
int nCmdShow = 0;
#endif /* HAVE_NTGUI */

Karl Heuer's avatar
Karl Heuer committed
95 96 97 98 99 100 101 102 103
/* Startup code for running on NT.  When we are running as the dumped
   version, we need to bootstrap our heap and .bss section into our
   address space before we can actually hand off control to the startup
   code supplied by NT (primarily because that code relies upon malloc ()).  */
void
_start (void)
{
  extern void mainCRTStartup (void);

104
#if 1
Geoff Voelker's avatar
Geoff Voelker committed
105 106 107 108 109 110
  /* Give us a way to debug problems with crashes on startup when
     running under the MSVC profiler. */
  if (GetEnvironmentVariable ("EMACS_DEBUG", NULL, 0) > 0)
    DebugBreak ();
#endif

Karl Heuer's avatar
Karl Heuer committed
111 112 113
  /* Cache system info, e.g., the NT page size.  */
  cache_system_info ();

114 115
  /* Grab our malloc arena space now, before CRT starts up. */
  init_heap ();
Karl Heuer's avatar
Karl Heuer committed
116 117 118 119 120

  /* This prevents ctrl-c's in shells running while we're suspended from
     having us exit.  */
  SetConsoleCtrlHandler ((PHANDLER_ROUTINE) ctrl_c_handler, TRUE);

121 122 123 124
  /* Prevent Emacs from being locked up (eg. in batch mode) when
     accessing devices that aren't mounted (eg. removable media drives).  */
  SetErrorMode (SEM_FAILCRITICALERRORS);

Karl Heuer's avatar
Karl Heuer committed
125 126
  /* Invoke the NT CRT startup routine now that our housecleaning
     is finished.  */
127
#ifdef HAVE_NTGUI
Geoff Voelker's avatar
Geoff Voelker committed
128
  /* determine WinMain args like crt0.c does */
Juanma Barranquero's avatar
Juanma Barranquero committed
129 130
  hinst = GetModuleHandle (NULL);
  lpCmdLine = GetCommandLine ();
Geoff Voelker's avatar
Geoff Voelker committed
131 132
  nCmdShow = SW_SHOWDEFAULT;
#endif
Karl Heuer's avatar
Karl Heuer committed
133 134 135 136 137 138
  mainCRTStartup ();
}


/* File handling.  */

Geoff Voelker's avatar
Geoff Voelker committed
139
int
Karl Heuer's avatar
Karl Heuer committed
140 141 142 143 144 145 146 147 148
open_input_file (file_data *p_file, char *filename)
{
  HANDLE file;
  HANDLE file_mapping;
  void  *file_base;
  unsigned long size, upper_size;

  file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
		     OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
149
  if (file == INVALID_HANDLE_VALUE)
Geoff Voelker's avatar
Geoff Voelker committed
150
    return FALSE;
Karl Heuer's avatar
Karl Heuer committed
151 152

  size = GetFileSize (file, &upper_size);
153
  file_mapping = CreateFileMapping (file, NULL, PAGE_READONLY,
Karl Heuer's avatar
Karl Heuer committed
154
				    0, size, NULL);
155
  if (!file_mapping)
Geoff Voelker's avatar
Geoff Voelker committed
156
    return FALSE;
Karl Heuer's avatar
Karl Heuer committed
157 158

  file_base = MapViewOfFile (file_mapping, FILE_MAP_READ, 0, 0, size);
159
  if (file_base == 0)
Geoff Voelker's avatar
Geoff Voelker committed
160
    return FALSE;
Karl Heuer's avatar
Karl Heuer committed
161 162 163 164 165 166

  p_file->name = filename;
  p_file->size = size;
  p_file->file = file;
  p_file->file_mapping = file_mapping;
  p_file->file_base = file_base;
Geoff Voelker's avatar
Geoff Voelker committed
167 168

  return TRUE;
Karl Heuer's avatar
Karl Heuer committed
169 170
}

Geoff Voelker's avatar
Geoff Voelker committed
171
int
Karl Heuer's avatar
Karl Heuer committed
172 173 174 175 176
open_output_file (file_data *p_file, char *filename, unsigned long size)
{
  HANDLE file;
  HANDLE file_mapping;
  void  *file_base;
177

Karl Heuer's avatar
Karl Heuer committed
178 179
  file = CreateFile (filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
		     CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
180
  if (file == INVALID_HANDLE_VALUE)
Geoff Voelker's avatar
Geoff Voelker committed
181 182
    return FALSE;

183
  file_mapping = CreateFileMapping (file, NULL, PAGE_READWRITE,
Karl Heuer's avatar
Karl Heuer committed
184
				    0, size, NULL);
185
  if (!file_mapping)
Geoff Voelker's avatar
Geoff Voelker committed
186
    return FALSE;
187

Karl Heuer's avatar
Karl Heuer committed
188
  file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size);
189
  if (file_base == 0)
Geoff Voelker's avatar
Geoff Voelker committed
190
    return FALSE;
191

Karl Heuer's avatar
Karl Heuer committed
192 193 194 195 196
  p_file->name = filename;
  p_file->size = size;
  p_file->file = file;
  p_file->file_mapping = file_mapping;
  p_file->file_base = file_base;
Geoff Voelker's avatar
Geoff Voelker committed
197 198

  return TRUE;
Karl Heuer's avatar
Karl Heuer committed
199 200 201
}

/* Close the system structures associated with the given file.  */
Geoff Voelker's avatar
Geoff Voelker committed
202
void
Karl Heuer's avatar
Karl Heuer committed
203 204
close_file_data (file_data *p_file)
{
205 206 207 208 209 210
  UnmapViewOfFile (p_file->file_base);
  CloseHandle (p_file->file_mapping);
  /* For the case of output files, set final size.  */
  SetFilePointer (p_file->file, p_file->size, NULL, FILE_BEGIN);
  SetEndOfFile (p_file->file);
  CloseHandle (p_file->file);
Karl Heuer's avatar
Karl Heuer committed
211 212 213 214 215
}


/* Routines to manipulate NT executable file sections.  */

Geoff Voelker's avatar
Geoff Voelker committed
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
/* Return pointer to section header for named section. */
IMAGE_SECTION_HEADER *
find_section (char * name, IMAGE_NT_HEADERS * nt_header)
{
  PIMAGE_SECTION_HEADER section;
  int i;

  section = IMAGE_FIRST_SECTION (nt_header);

  for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
    {
      if (strcmp (section->Name, name) == 0)
	return section;
      section++;
    }
  return NULL;
}

/* Return pointer to section header for section containing the given
   relative virtual address. */
IMAGE_SECTION_HEADER *
237
rva_to_section (DWORD_PTR rva, IMAGE_NT_HEADERS * nt_header)
Geoff Voelker's avatar
Geoff Voelker committed
238 239 240 241 242 243 244
{
  PIMAGE_SECTION_HEADER section;
  int i;

  section = IMAGE_FIRST_SECTION (nt_header);

  for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
Karl Heuer's avatar
Karl Heuer committed
245
    {
246 247 248 249 250 251
      /* Some linkers (eg. the NT SDK linker I believe) swapped the
	 meaning of these two values - or rather, they ignored
	 VirtualSize entirely and always set it to zero.  This affects
	 some very old exes (eg. gzip dated Dec 1993).  Since
	 w32_executable_type relies on this function to work reliably,
	 we need to cope with this.  */
252
      DWORD_PTR real_size = max (section->SizeOfRawData,
253
			     section->Misc.VirtualSize);
Geoff Voelker's avatar
Geoff Voelker committed
254
      if (rva >= section->VirtualAddress
255 256 257 258 259 260 261 262 263 264
	  && rva < section->VirtualAddress + real_size)
	return section;
      section++;
    }
  return NULL;
}

/* Return pointer to section header for section containing the given
   offset in its raw data area. */
IMAGE_SECTION_HEADER *
265
offset_to_section (DWORD_PTR offset, IMAGE_NT_HEADERS * nt_header)
266 267 268 269 270 271 272 273 274 275
{
  PIMAGE_SECTION_HEADER section;
  int i;

  section = IMAGE_FIRST_SECTION (nt_header);

  for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
    {
      if (offset >= section->PointerToRawData
	  && offset < section->PointerToRawData + section->SizeOfRawData)
Geoff Voelker's avatar
Geoff Voelker committed
276 277
	return section;
      section++;
Karl Heuer's avatar
Karl Heuer committed
278
    }
Geoff Voelker's avatar
Geoff Voelker committed
279
  return NULL;
Karl Heuer's avatar
Karl Heuer committed
280 281
}

282 283 284
/* Return offset to an object in dst, given offset in src.  We assume
   there is at least one section in both src and dst images, and that
   the some sections may have been added to dst (after sections in src).  */
285 286
DWORD_PTR
relocate_offset (DWORD_PTR offset,
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
		 IMAGE_NT_HEADERS * src_nt_header,
		 IMAGE_NT_HEADERS * dst_nt_header)
{
  PIMAGE_SECTION_HEADER src_section = IMAGE_FIRST_SECTION (src_nt_header);
  PIMAGE_SECTION_HEADER dst_section = IMAGE_FIRST_SECTION (dst_nt_header);
  int i = 0;

  while (offset >= src_section->PointerToRawData)
    {
      if (offset < src_section->PointerToRawData + src_section->SizeOfRawData)
	break;
      i++;
      if (i == src_nt_header->FileHeader.NumberOfSections)
	{
	  /* Handle offsets after the last section.  */
	  dst_section = IMAGE_FIRST_SECTION (dst_nt_header);
	  dst_section += dst_nt_header->FileHeader.NumberOfSections - 1;
	  while (dst_section->PointerToRawData == 0)
	    dst_section--;
	  while (src_section->PointerToRawData == 0)
	    src_section--;
	  return offset
	    + (dst_section->PointerToRawData + dst_section->SizeOfRawData)
	    - (src_section->PointerToRawData + src_section->SizeOfRawData);
	}
      src_section++;
      dst_section++;
    }
  return offset +
    (dst_section->PointerToRawData - src_section->PointerToRawData);
}

#define OFFSET_TO_RVA(offset, section) \
320
  ((section)->VirtualAddress + ((DWORD_PTR)(offset) - (section)->PointerToRawData))
321 322

#define RVA_TO_OFFSET(rva, section) \
323
  ((section)->PointerToRawData + ((DWORD_PTR)(rva) - (section)->VirtualAddress))
324 325

#define RVA_TO_SECTION_OFFSET(rva, section) \
326
  ((DWORD_PTR)(rva) - (section)->VirtualAddress)
327 328

/* Convert address in executing image to RVA.  */
329
#define PTR_TO_RVA(ptr) ((DWORD_PTR)(ptr) - (DWORD_PTR) GetModuleHandle (NULL))
330

331
#define RVA_TO_PTR(var,section,filedata) \
332
	  ((unsigned char *)(RVA_TO_OFFSET (var,section) + (filedata).file_base))
333

334
#define PTR_TO_OFFSET(ptr, pfile_data) \
Andrew Innes's avatar
Andrew Innes committed
335
          ((unsigned char *)(ptr) - (pfile_data)->file_base)
336 337

#define OFFSET_TO_PTR(offset, pfile_data) \
338
          ((pfile_data)->file_base + (DWORD_PTR)(offset))
339

Geoff Voelker's avatar
Geoff Voelker committed
340

Karl Heuer's avatar
Karl Heuer committed
341
/* Flip through the executable and cache the info necessary for dumping.  */
Andrew Innes's avatar
Andrew Innes committed
342
void
Karl Heuer's avatar
Karl Heuer committed
343 344 345 346
get_section_info (file_data *p_infile)
{
  PIMAGE_DOS_HEADER dos_header;
  PIMAGE_NT_HEADERS nt_header;
347
  int overlap;
348

Karl Heuer's avatar
Karl Heuer committed
349
  dos_header = (PIMAGE_DOS_HEADER) p_infile->file_base;
350
  if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
Karl Heuer's avatar
Karl Heuer committed
351 352 353 354
    {
      printf ("Unknown EXE header in %s...bailing.\n", p_infile->name);
      exit (1);
    }
355
  nt_header = (PIMAGE_NT_HEADERS) (((DWORD_PTR) dos_header) +
Karl Heuer's avatar
Karl Heuer committed
356
				   dos_header->e_lfanew);
357
  if (nt_header == NULL)
Karl Heuer's avatar
Karl Heuer committed
358
    {
359
      printf ("Failed to find IMAGE_NT_HEADER in %s...bailing.\n",
Karl Heuer's avatar
Karl Heuer committed
360 361 362 363 364
	     p_infile->name);
      exit (1);
    }

  /* Check the NT header signature ...  */
365
  if (nt_header->Signature != IMAGE_NT_SIGNATURE)
Karl Heuer's avatar
Karl Heuer committed
366 367 368
    {
      printf ("Invalid IMAGE_NT_SIGNATURE 0x%x in %s...bailing.\n",
	      nt_header->Signature, p_infile->name);
369
      exit (1);
Karl Heuer's avatar
Karl Heuer committed
370 371
    }

372 373 374 375 376 377 378 379 380 381 382
  /* Locate the ".data" and ".bss" sections for Emacs.  (Note that the
     actual section names are probably different from these, and might
     actually be the same section.)

     We do this as follows: first we determine the virtual address
     ranges in this process for the data and bss variables that we wish
     to preserve.  Then we map these VAs to the section entries in the
     source image.  Finally, we determine the new size of the raw data
     area for the bss section, so we can make the new image the correct
     size.  */

383 384 385 386 387 388 389 390
  /* We arrange for the Emacs initialized data to be in a separate
     section if possible, because we cannot rely on my_begdata and
     my_edata marking out the full extent of the initialized data, at
     least on the Alpha where the linker freely reorders variables
     across libraries.  If we can arrange for this, all we need to do is
     find the start and size of the EMDATA section.  */
  data_section = find_section ("EMDATA", nt_header);
  if (data_section)
Karl Heuer's avatar
Karl Heuer committed
391
    {
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
      data_start = (char *) nt_header->OptionalHeader.ImageBase +
	data_section->VirtualAddress;
      data_size = data_section->Misc.VirtualSize;
    }
  else
    {
      /* Fallback on the old method if compiler doesn't support the
         data_set #pragma (or its equivalent).  */
      data_start = my_begdata;
      data_size = my_edata - my_begdata;
      data_section = rva_to_section (PTR_TO_RVA (my_begdata), nt_header);
      if (data_section != rva_to_section (PTR_TO_RVA (my_edata), nt_header))
	{
	  printf ("Initialized data is not in a single section...bailing\n");
	  exit (1);
	}
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
    }

  /* As noted in lastfile.c, the Alpha (but not the Intel) MSVC linker
     globally segregates all static and public bss data (ie. across all
     linked modules, not just per module), so we must take both static
     and public bss areas into account to determine the true extent of
     the bss area used by Emacs.

     To be strictly correct, we dump the static and public bss areas
     used by Emacs separately if non-overlapping (since otherwise we are
     dumping bss data belonging to system libraries, eg. the static bss
     system data on the Alpha).  */

  bss_start = my_begbss;
  bss_size = my_endbss - my_begbss;
  bss_section = rva_to_section (PTR_TO_RVA (my_begbss), nt_header);
  if (bss_section != rva_to_section (PTR_TO_RVA (my_endbss), nt_header))
    {
      printf ("Uninitialized data is not in a single section...bailing\n");
      exit (1);
    }
  /* Compute how much the .bss section's raw data will grow.  */
  extra_bss_size =
    ROUND_UP (RVA_TO_SECTION_OFFSET (PTR_TO_RVA (my_endbss), bss_section),
	      nt_header->OptionalHeader.FileAlignment)
    - bss_section->SizeOfRawData;

  bss_start_static = my_begbss_static;
  bss_size_static = my_endbss_static - my_begbss_static;
  bss_section_static = rva_to_section (PTR_TO_RVA (my_begbss_static), nt_header);
  if (bss_section_static != rva_to_section (PTR_TO_RVA (my_endbss_static), nt_header))
    {
      printf ("Uninitialized static data is not in a single section...bailing\n");
      exit (1);
    }
  /* Compute how much the static .bss section's raw data will grow.  */
  extra_bss_size_static =
    ROUND_UP (RVA_TO_SECTION_OFFSET (PTR_TO_RVA (my_endbss_static), bss_section_static),
	      nt_header->OptionalHeader.FileAlignment)
    - bss_section_static->SizeOfRawData;

  /* Combine the bss sections into one if they overlap.  */
450 451 452
#ifdef _ALPHA_
  overlap = 1;			/* force all bss data to be dumped */
#else
453
  overlap = 0;
454
#endif
455 456 457 458 459 460 461 462 463 464 465 466 467
  if (bss_start < bss_start_static)
    {
      if (bss_start_static < bss_start + bss_size)
	overlap = 1;
    }
  else
    {
      if (bss_start < bss_start_static + bss_size_static)
	overlap = 1;
    }
  if (overlap)
    {
      if (bss_section != bss_section_static)
Karl Heuer's avatar
Karl Heuer committed
468
	{
469 470
	  printf ("BSS data not in a single section...bailing\n");
	  exit (1);
Karl Heuer's avatar
Karl Heuer committed
471
	}
472 473 474 475 476 477 478 479 480 481 482 483
      bss_start = min (bss_start, bss_start_static);
      bss_size = max (my_endbss, my_endbss_static) - bss_start;
      bss_section_static = 0;
      extra_bss_size_static = 0;
    }

  heap_section = rva_to_section (PTR_TO_RVA (get_heap_start ()), nt_header);
}


/* The dump routines.  */

Andrew Innes's avatar
Andrew Innes committed
484
void
485
copy_executable_and_dump_data (file_data *p_infile,
486 487 488 489 490 491 492 493
			       file_data *p_outfile)
{
  unsigned char *dst, *dst_save;
  PIMAGE_DOS_HEADER dos_header;
  PIMAGE_NT_HEADERS nt_header;
  PIMAGE_NT_HEADERS dst_nt_header;
  PIMAGE_SECTION_HEADER section;
  PIMAGE_SECTION_HEADER dst_section;
494
  DWORD_PTR offset;
495
  int i;
496
  int be_verbose = GetEnvironmentVariable ("DEBUG_DUMP", NULL, 0) > 0;
497

498
#define COPY_CHUNK(message, src, size, verbose)					\
499 500 501
  do {										\
    unsigned char *s = (void *)(src);						\
    unsigned long count = (size);						\
502 503 504 505 506 507 508
    if (verbose)								\
      {										\
	printf ("%s\n", (message));						\
	printf ("\t0x%08x Offset in input file.\n", s - p_infile->file_base); 	\
	printf ("\t0x%08x Offset in output file.\n", dst - p_outfile->file_base); \
	printf ("\t0x%08x Size in bytes.\n", count);				\
      }										\
509 510 511 512
    memcpy (dst, s, count);							\
    dst += count;								\
  } while (0)

513
#define COPY_PROC_CHUNK(message, src, size, verbose)				\
514 515 516
  do {										\
    unsigned char *s = (void *)(src);						\
    unsigned long count = (size);						\
517 518 519 520 521 522 523
    if (verbose)								\
      {										\
	printf ("%s\n", (message));						\
	printf ("\t0x%08x Address in process.\n", s);				\
	printf ("\t0x%08x Offset in output file.\n", dst - p_outfile->file_base); \
	printf ("\t0x%08x Size in bytes.\n", count);				\
      }										\
524 525 526 527 528 529 530
    memcpy (dst, s, count);							\
    dst += count;								\
  } while (0)

#define DST_TO_OFFSET()  PTR_TO_OFFSET (dst, p_outfile)
#define ROUND_UP_DST(align) \
  (dst = p_outfile->file_base + ROUND_UP (DST_TO_OFFSET (), (align)))
531 532 533 534 535 536 537 538
#define ROUND_UP_DST_AND_ZERO(align)						\
  do {										\
    unsigned char *newdst = p_outfile->file_base				\
      + ROUND_UP (DST_TO_OFFSET (), (align));					\
    /* Zero the alignment slop; it may actually initialize real data.  */	\
    memset (dst, 0, newdst - dst);						\
    dst = newdst;								\
  } while (0)
539 540 541 542 543 544 545 546

  /* Copy the source image sequentially, ie. section by section after
     copying the headers and section table, to simplify the process of
     dumping the raw data for the bss and heap sections.

     Note that dst is updated implicitly by each COPY_CHUNK.  */

  dos_header = (PIMAGE_DOS_HEADER) p_infile->file_base;
547
  nt_header = (PIMAGE_NT_HEADERS) (((DWORD_PTR) dos_header) +
548 549
				   dos_header->e_lfanew);
  section = IMAGE_FIRST_SECTION (nt_header);
550

551 552 553
  dst = (unsigned char *) p_outfile->file_base;

  COPY_CHUNK ("Copying DOS header...", dos_header,
554
	      (DWORD_PTR) nt_header - (DWORD_PTR) dos_header, be_verbose);
555 556
  dst_nt_header = (PIMAGE_NT_HEADERS) dst;
  COPY_CHUNK ("Copying NT header...", nt_header,
557
	      (DWORD_PTR) section - (DWORD_PTR) nt_header, be_verbose);
558 559
  dst_section = (PIMAGE_SECTION_HEADER) dst;
  COPY_CHUNK ("Copying section table...", section,
560 561
	      nt_header->FileHeader.NumberOfSections * sizeof (*section),
	      be_verbose);
562

563 564 565 566 567
  /* Align the first section's raw data area, and set the header size
     field accordingly.  */
  ROUND_UP_DST_AND_ZERO (dst_nt_header->OptionalHeader.FileAlignment);
  dst_nt_header->OptionalHeader.SizeOfHeaders = DST_TO_OFFSET ();

568 569 570
  for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
    {
      char msg[100];
571 572 573
      /* Windows section names are fixed 8-char strings, only
	 zero-terminated if the name is shorter than 8 characters.  */
      sprintf (msg, "Copying raw data for %.8s...", section->Name);
574 575 576 577 578 579 580 581 582 583 584 585

      dst_save = dst;

      /* Update the file-relative offset for this section's raw data (if
         it has any) in case things have been relocated; we will update
         the other offsets below once we know where everything is.  */
      if (dst_section->PointerToRawData)
	dst_section->PointerToRawData = DST_TO_OFFSET ();

      /* Can always copy the original raw data.  */
      COPY_CHUNK
	(msg, OFFSET_TO_PTR (section->PointerToRawData, p_infile),
586
	 section->SizeOfRawData, be_verbose);
587 588
      /* Ensure alignment slop is zeroed.  */
      ROUND_UP_DST_AND_ZERO (dst_nt_header->OptionalHeader.FileAlignment);
589 590 591

      /* Note that various sections below may be aliases.  */
      if (section == data_section)
Karl Heuer's avatar
Karl Heuer committed
592
	{
593 594
	  dst = dst_save
	    + RVA_TO_SECTION_OFFSET (PTR_TO_RVA (data_start), dst_section);
595 596
	  COPY_PROC_CHUNK ("Dumping initialized data...",
			   data_start, data_size, be_verbose);
597
	  dst = dst_save + dst_section->SizeOfRawData;
Karl Heuer's avatar
Karl Heuer committed
598
	}
599
      if (section == bss_section)
Geoff Voelker's avatar
Geoff Voelker committed
600
	{
601 602 603 604
	  /* Dump contents of bss variables, adjusting the section's raw
             data size as necessary.  */
	  dst = dst_save
	    + RVA_TO_SECTION_OFFSET (PTR_TO_RVA (bss_start), dst_section);
605 606
	  COPY_PROC_CHUNK ("Dumping bss data...", bss_start,
			   bss_size, be_verbose);
607 608 609 610 611 612 613
	  ROUND_UP_DST (dst_nt_header->OptionalHeader.FileAlignment);
	  dst_section->PointerToRawData = PTR_TO_OFFSET (dst_save, p_outfile);
	  /* Determine new size of raw data area.  */
	  dst = max (dst, dst_save + dst_section->SizeOfRawData);
	  dst_section->SizeOfRawData = dst - dst_save;
	  dst_section->Characteristics &= ~IMAGE_SCN_CNT_UNINITIALIZED_DATA;
	  dst_section->Characteristics |= IMAGE_SCN_CNT_INITIALIZED_DATA;
Geoff Voelker's avatar
Geoff Voelker committed
614
	}
615 616 617 618 619 620
      if (section == bss_section_static)
	{
	  /* Dump contents of static bss variables, adjusting the
             section's raw data size as necessary.  */
	  dst = dst_save
	    + RVA_TO_SECTION_OFFSET (PTR_TO_RVA (bss_start_static), dst_section);
621 622
	  COPY_PROC_CHUNK ("Dumping static bss data...", bss_start_static,
			   bss_size_static, be_verbose);
623 624 625 626 627 628 629 630 631 632
	  ROUND_UP_DST (dst_nt_header->OptionalHeader.FileAlignment);
	  dst_section->PointerToRawData = PTR_TO_OFFSET (dst_save, p_outfile);
	  /* Determine new size of raw data area.  */
	  dst = max (dst, dst_save + dst_section->SizeOfRawData);
	  dst_section->SizeOfRawData = dst - dst_save;
	  dst_section->Characteristics &= ~IMAGE_SCN_CNT_UNINITIALIZED_DATA;
	  dst_section->Characteristics |= IMAGE_SCN_CNT_INITIALIZED_DATA;
	}
      if (section == heap_section)
	{
633 634
	  DWORD_PTR heap_start = (DWORD_PTR) get_heap_start ();
	  DWORD_PTR heap_size = get_committed_heap_size ();
635 636 637 638 639

	  /* Dump the used portion of the predump heap, adjusting the
             section's size to the appropriate size.  */
	  dst = dst_save
	    + RVA_TO_SECTION_OFFSET (PTR_TO_RVA (heap_start), dst_section);
640 641
	  COPY_PROC_CHUNK ("Dumping heap...", heap_start, heap_size,
			   be_verbose);
642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
	  ROUND_UP_DST (dst_nt_header->OptionalHeader.FileAlignment);
	  dst_section->PointerToRawData = PTR_TO_OFFSET (dst_save, p_outfile);
	  /* Determine new size of raw data area.  */
	  dst = max (dst, dst_save + dst_section->SizeOfRawData);
	  dst_section->SizeOfRawData = dst - dst_save;
	  /* Reduce the size of the heap section to fit (must be last
             section).  */
	  dst_nt_header->OptionalHeader.SizeOfImage -=
	    dst_section->Misc.VirtualSize
	    - ROUND_UP (dst_section->SizeOfRawData,
			dst_nt_header->OptionalHeader.SectionAlignment);
	  dst_section->Misc.VirtualSize =
	    ROUND_UP (dst_section->SizeOfRawData,
		      dst_nt_header->OptionalHeader.SectionAlignment);
	  dst_section->Characteristics &= ~IMAGE_SCN_CNT_UNINITIALIZED_DATA;
	  dst_section->Characteristics |= IMAGE_SCN_CNT_INITIALIZED_DATA;
	}

660 661 662
      /* Align the section's raw data area.  */
      ROUND_UP_DST (dst_nt_header->OptionalHeader.FileAlignment);

Karl Heuer's avatar
Karl Heuer committed
663
      section++;
664
      dst_section++;
Karl Heuer's avatar
Karl Heuer committed
665
    }
666

667 668 669 670 671 672 673 674 675
  /* Copy remainder of source image.  */
  do
    section--;
  while (section->PointerToRawData == 0);
  offset = ROUND_UP (section->PointerToRawData + section->SizeOfRawData,
		     nt_header->OptionalHeader.FileAlignment);
  COPY_CHUNK
    ("Copying remainder of executable...",
     OFFSET_TO_PTR (offset, p_infile),
676
     p_infile->size - offset, be_verbose);
677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693

  /* Final size for new image.  */
  p_outfile->size = DST_TO_OFFSET ();

  /* Now patch up remaining file-relative offsets.  */
  section = IMAGE_FIRST_SECTION (nt_header);
  dst_section = IMAGE_FIRST_SECTION (dst_nt_header);

#define ADJUST_OFFSET(var)						\
  do {									\
    if ((var) != 0)							\
      (var) = relocate_offset ((var), nt_header, dst_nt_header);	\
  } while (0)

  dst_nt_header->OptionalHeader.SizeOfInitializedData = 0;
  dst_nt_header->OptionalHeader.SizeOfUninitializedData = 0;
  for (i = 0; i < dst_nt_header->FileHeader.NumberOfSections; i++)
694
    {
695 696 697 698 699 700 701 702 703
      /* Recompute data sizes for completeness.  */
      if (dst_section[i].Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
	dst_nt_header->OptionalHeader.SizeOfInitializedData +=
	  ROUND_UP (dst_section[i].Misc.VirtualSize, dst_nt_header->OptionalHeader.FileAlignment);
      else if (dst_section[i].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
	dst_nt_header->OptionalHeader.SizeOfUninitializedData +=
	  ROUND_UP (dst_section[i].Misc.VirtualSize, dst_nt_header->OptionalHeader.FileAlignment);

      ADJUST_OFFSET (dst_section[i].PointerToLinenumbers);
704
    }
Karl Heuer's avatar
Karl Heuer committed
705

706
  ADJUST_OFFSET (dst_nt_header->FileHeader.PointerToSymbolTable);
Karl Heuer's avatar
Karl Heuer committed
707

708 709 710 711 712
  /* Update offsets in debug directory entries. */
  {
    IMAGE_DATA_DIRECTORY debug_dir =
      dst_nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
    PIMAGE_DEBUG_DIRECTORY debug_entry;
Karl Heuer's avatar
Karl Heuer committed
713

714 715 716 717 718 719
    section = rva_to_section (debug_dir.VirtualAddress, dst_nt_header);
    if (section)
      {
	debug_entry = (PIMAGE_DEBUG_DIRECTORY)
	  (RVA_TO_OFFSET (debug_dir.VirtualAddress, section) + p_outfile->file_base);
	debug_dir.Size /= sizeof (IMAGE_DEBUG_DIRECTORY);
Karl Heuer's avatar
Karl Heuer committed
720

721 722 723 724
	for (i = 0; i < debug_dir.Size; i++, debug_entry++)
	  ADJUST_OFFSET (debug_entry->PointerToRawData);
      }
  }
Karl Heuer's avatar
Karl Heuer committed
725 726 727
}


728
/* Dump out .data and .bss sections into a new executable.  */
729
void
730
unexec (const char *new_name, const char *old_name)
731
{
732 733 734
  file_data in_file, out_file;
  char out_filename[MAX_PATH], in_filename[MAX_PATH];
  unsigned long size;
Andrew Innes's avatar
Andrew Innes committed
735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751
  char *p;
  char *q;

  /* Ignore old_name, and get our actual location from the OS.  */
  if (!GetModuleFileName (NULL, in_filename, MAX_PATH))
    abort ();
  dostounix_filename (in_filename);
  strcpy (out_filename, in_filename);

  /* Change the base of the output filename to match the requested name.  */
  if ((p = strrchr (out_filename, '/')) == NULL)
    abort ();
  /* The filenames have already been expanded, and will be in Unix
     format, so it is safe to expect an absolute name.  */
  if ((q = strrchr (new_name, '/')) == NULL)
    abort ();
  strcpy (p, q);
752

Andrew Innes's avatar
Andrew Innes committed
753 754 755 756
  /* Make sure that the output filename has the ".exe" extension...patch
     it up if not.  */
  p = out_filename + strlen (out_filename) - 4;
  if (strcmp (p, ".exe"))
757
    strcat (out_filename, ".exe");
Karl Heuer's avatar
Karl Heuer committed
758

759 760
  printf ("Dumping from %s\n", in_filename);
  printf ("          to %s\n", out_filename);
Karl Heuer's avatar
Karl Heuer committed
761

762 763
  /* We need to round off our heap to NT's page size.  */
  round_heap (get_page_size ());
764

765 766 767
  /* Open the undumped executable file.  */
  if (!open_input_file (&in_file, in_filename))
    {
768
      printf ("Failed to open %s (%d)...bailing.\n",
769 770 771
	      in_filename, GetLastError ());
      exit (1);
    }
Karl Heuer's avatar
Karl Heuer committed
772

773 774
  /* Get the interesting section info, like start and size of .bss...  */
  get_section_info (&in_file);
Karl Heuer's avatar
Karl Heuer committed
775

776 777 778 779 780 781 782
  /* The size of the dumped executable is the size of the original
     executable plus the size of the heap and the size of the .bss section.  */
  size = in_file.size +
    get_committed_heap_size () +
    extra_bss_size +
    extra_bss_size_static;
  if (!open_output_file (&out_file, out_filename, size))
783
    {
784
      printf ("Failed to open %s (%d)...bailing.\n",
785 786
	      out_filename, GetLastError ());
      exit (1);
787 788
    }

789 790
  /* Set the flag (before dumping).  */
  using_dynamic_heap = TRUE;
791

792
  copy_executable_and_dump_data (&in_file, &out_file);
793

794 795 796 797 798 799 800
  /* Patch up header fields; profiler is picky about this. */
  {
    PIMAGE_DOS_HEADER dos_header;
    PIMAGE_NT_HEADERS nt_header;
    HANDLE hImagehelp = LoadLibrary ("imagehlp.dll");
    DWORD  headersum;
    DWORD  checksum;
801

802 803
    dos_header = (PIMAGE_DOS_HEADER) out_file.file_base;
    nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew);
804

805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821
    nt_header->OptionalHeader.CheckSum = 0;
//    nt_header->FileHeader.TimeDateStamp = time (NULL);
//    dos_header->e_cp = size / 512;
//    nt_header->OptionalHeader.SizeOfImage = size;

    pfnCheckSumMappedFile = (void *) GetProcAddress (hImagehelp, "CheckSumMappedFile");
    if (pfnCheckSumMappedFile)
      {
//	nt_header->FileHeader.TimeDateStamp = time (NULL);
	pfnCheckSumMappedFile (out_file.file_base,
			       out_file.size,
			       &headersum,
			       &checksum);
	nt_header->OptionalHeader.CheckSum = checksum;
      }
    FreeLibrary (hImagehelp);
  }
822

823 824
  close_file_data (&in_file);
  close_file_data (&out_file);
Karl Heuer's avatar
Karl Heuer committed
825
}
826 827

/* eof */