unexw32.c 26.9 KB
Newer Older
1
/* unexec for GNU Emacs on Windows NT.
2
   Copyright (C) 1994, 2001-2011  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"
Geoff Voelker's avatar
Geoff Voelker committed
25

Karl Heuer's avatar
Karl Heuer committed
26 27
#include <stdio.h>
#include <fcntl.h>
Geoff Voelker's avatar
Geoff Voelker committed
28
#include <time.h>
Karl Heuer's avatar
Karl Heuer committed
29 30
#include <windows.h>

Geoff Voelker's avatar
Geoff Voelker committed
31 32
/* 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
33

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

Geoff Voelker's avatar
Geoff Voelker committed
40 41 42 43 44 45
extern BOOL ctrl_c_handler (unsigned long type);

extern char my_begdata[];
extern char my_edata[];
extern char my_begbss[];
extern char my_endbss[];
46 47
extern char *my_begbss_static;
extern char *my_endbss_static;
Karl Heuer's avatar
Karl Heuer committed
48

Geoff Voelker's avatar
Geoff Voelker committed
49
#include "w32heap.h"
50

Karl Heuer's avatar
Karl Heuer committed
51 52 53 54 55
#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
56
/* Basically, our "initialized" flag.  */
57
BOOL using_dynamic_heap = FALSE;
Karl Heuer's avatar
Karl Heuer committed
58

Geoff Voelker's avatar
Geoff Voelker committed
59 60
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
61 62 63
void close_file_data (file_data *p_file);

void get_section_info (file_data *p_file);
64
void copy_executable_and_dump_data (file_data *, file_data *);
Karl Heuer's avatar
Karl Heuer committed
65 66 67
void dump_bss_and_heap (file_data *p_infile, file_data *p_outfile);

/* Cached info about the .data section in the executable.  */
68
PIMAGE_SECTION_HEADER data_section;
Andrew Innes's avatar
Andrew Innes committed
69
PCHAR  data_start = 0;
Karl Heuer's avatar
Karl Heuer committed
70 71 72
DWORD  data_size = 0;

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

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

85 86 87 88 89 90 91
#ifdef HAVE_NTGUI
HINSTANCE hinst = NULL;
HINSTANCE hprevinst = NULL;
LPSTR lpCmdLine = "";
int nCmdShow = 0;
#endif /* HAVE_NTGUI */

Karl Heuer's avatar
Karl Heuer committed
92 93 94 95 96 97 98 99 100
/* 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);

101
#if 1
Geoff Voelker's avatar
Geoff Voelker committed
102 103 104 105 106 107
  /* 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
108 109 110
  /* Cache system info, e.g., the NT page size.  */
  cache_system_info ();

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

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

118 119 120 121
  /* 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
122 123
  /* Invoke the NT CRT startup routine now that our housecleaning
     is finished.  */
124
#ifdef HAVE_NTGUI
Geoff Voelker's avatar
Geoff Voelker committed
125 126 127 128 129
  /* determine WinMain args like crt0.c does */
  hinst = GetModuleHandle(NULL);
  lpCmdLine = GetCommandLine();
  nCmdShow = SW_SHOWDEFAULT;
#endif
Karl Heuer's avatar
Karl Heuer committed
130 131 132 133 134 135
  mainCRTStartup ();
}


/* File handling.  */

Geoff Voelker's avatar
Geoff Voelker committed
136
int
Karl Heuer's avatar
Karl Heuer committed
137 138 139 140 141 142 143 144 145
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);
146
  if (file == INVALID_HANDLE_VALUE)
Geoff Voelker's avatar
Geoff Voelker committed
147
    return FALSE;
Karl Heuer's avatar
Karl Heuer committed
148 149

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

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

  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
164 165

  return TRUE;
Karl Heuer's avatar
Karl Heuer committed
166 167
}

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

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

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

Karl Heuer's avatar
Karl Heuer committed
185
  file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size);
186
  if (file_base == 0)
Geoff Voelker's avatar
Geoff Voelker committed
187
    return FALSE;
188

Karl Heuer's avatar
Karl Heuer committed
189 190 191 192 193
  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
194 195

  return TRUE;
Karl Heuer's avatar
Karl Heuer committed
196 197 198
}

/* Close the system structures associated with the given file.  */
Geoff Voelker's avatar
Geoff Voelker committed
199
void
Karl Heuer's avatar
Karl Heuer committed
200 201
close_file_data (file_data *p_file)
{
202 203 204 205 206 207
  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
208 209 210 211 212
}


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

Geoff Voelker's avatar
Geoff Voelker committed
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
/* 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 *
rva_to_section (DWORD rva, 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++)
Karl Heuer's avatar
Karl Heuer committed
242
    {
243 244 245 246 247 248 249 250
      /* 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.  */
      DWORD real_size = max (section->SizeOfRawData,
			     section->Misc.VirtualSize);
Geoff Voelker's avatar
Geoff Voelker committed
251
      if (rva >= section->VirtualAddress
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
	  && 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 *
offset_to_section (DWORD offset, 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 (offset >= section->PointerToRawData
	  && offset < section->PointerToRawData + section->SizeOfRawData)
Geoff Voelker's avatar
Geoff Voelker committed
273 274
	return section;
      section++;
Karl Heuer's avatar
Karl Heuer committed
275
    }
Geoff Voelker's avatar
Geoff Voelker committed
276
  return NULL;
Karl Heuer's avatar
Karl Heuer committed
277 278
}

279 280 281
/* 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).  */
Andrew Innes's avatar
Andrew Innes committed
282
DWORD
283 284 285 286 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 320 321 322 323 324 325 326 327
relocate_offset (DWORD offset,
		 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) \
	  (section->VirtualAddress + ((DWORD)(offset) - section->PointerToRawData))

#define RVA_TO_OFFSET(rva, section) \
	  (section->PointerToRawData + ((DWORD)(rva) - section->VirtualAddress))

#define RVA_TO_SECTION_OFFSET(rva, section) \
	  ((DWORD)(rva) - section->VirtualAddress)

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

328 329 330
#define RVA_TO_PTR(var,section,filedata) \
	  ((void *)(RVA_TO_OFFSET(var,section) + (filedata).file_base))

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

#define OFFSET_TO_PTR(offset, pfile_data) \
          ((pfile_data)->file_base + (DWORD)(offset))

Geoff Voelker's avatar
Geoff Voelker committed
337

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

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

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

370 371 372 373 374 375 376 377 378 379 380
  /* 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.  */

381 382 383 384 385 386 387 388
  /* 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
389
    {
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
      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);
	}
406 407 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
    }

  /* 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.  */
448 449 450
#ifdef _ALPHA_
  overlap = 1;			/* force all bss data to be dumped */
#else
451
  overlap = 0;
452
#endif
453 454 455 456 457 458 459 460 461 462 463 464 465
  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
466
	{
467 468
	  printf ("BSS data not in a single section...bailing\n");
	  exit (1);
Karl Heuer's avatar
Karl Heuer committed
469
	}
470 471 472 473 474 475 476 477 478 479 480 481
      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
482
void
483
copy_executable_and_dump_data (file_data *p_infile,
484 485 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;
  DWORD offset;
  int i;
494
  int be_verbose = GetEnvironmentVariable ("DEBUG_DUMP", NULL, 0) > 0;
495

496
#define COPY_CHUNK(message, src, size, verbose)					\
497 498 499
  do {										\
    unsigned char *s = (void *)(src);						\
    unsigned long count = (size);						\
500 501 502 503 504 505 506
    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);				\
      }										\
507 508 509 510
    memcpy (dst, s, count);							\
    dst += count;								\
  } while (0)

511
#define COPY_PROC_CHUNK(message, src, size, verbose)				\
512 513 514
  do {										\
    unsigned char *s = (void *)(src);						\
    unsigned long count = (size);						\
515 516 517 518 519 520 521
    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);				\
      }										\
522 523 524 525 526 527 528
    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)))
529 530 531 532 533 534 535 536
#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)
537 538 539 540 541 542 543 544

  /* 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;
545
  nt_header = (PIMAGE_NT_HEADERS) (((unsigned long) dos_header) +
546 547
				   dos_header->e_lfanew);
  section = IMAGE_FIRST_SECTION (nt_header);
548

549 550 551
  dst = (unsigned char *) p_outfile->file_base;

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

561 562 563 564 565
  /* 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 ();

566 567 568
  for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
    {
      char msg[100];
569 570 571
      /* 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);
572 573 574 575 576 577 578 579 580 581 582 583

      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),
584
	 section->SizeOfRawData, be_verbose);
585 586
      /* Ensure alignment slop is zeroed.  */
      ROUND_UP_DST_AND_ZERO (dst_nt_header->OptionalHeader.FileAlignment);
587 588 589

      /* Note that various sections below may be aliases.  */
      if (section == data_section)
Karl Heuer's avatar
Karl Heuer committed
590
	{
591 592
	  dst = dst_save
	    + RVA_TO_SECTION_OFFSET (PTR_TO_RVA (data_start), dst_section);
593 594
	  COPY_PROC_CHUNK ("Dumping initialized data...",
			   data_start, data_size, be_verbose);
595
	  dst = dst_save + dst_section->SizeOfRawData;
Karl Heuer's avatar
Karl Heuer committed
596
	}
597
      if (section == bss_section)
Geoff Voelker's avatar
Geoff Voelker committed
598
	{
599 600 601 602
	  /* 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);
603 604
	  COPY_PROC_CHUNK ("Dumping bss data...", bss_start,
			   bss_size, be_verbose);
605 606 607 608 609 610 611
	  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
612
	}
613 614 615 616 617 618
      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);
619 620
	  COPY_PROC_CHUNK ("Dumping static bss data...", bss_start_static,
			   bss_size_static, be_verbose);
621 622 623 624 625 626 627 628 629 630
	  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)
	{
Andrew Innes's avatar
Andrew Innes committed
631
	  DWORD heap_start = (DWORD) get_heap_start ();
632 633 634 635 636 637
	  DWORD heap_size = get_committed_heap_size ();

	  /* 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);
638 639
	  COPY_PROC_CHUNK ("Dumping heap...", heap_start, heap_size,
			   be_verbose);
640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
	  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;
	}

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

Karl Heuer's avatar
Karl Heuer committed
661
      section++;
662
      dst_section++;
Karl Heuer's avatar
Karl Heuer committed
663
    }
664

665 666 667 668 669 670 671 672 673
  /* 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),
674
     p_infile->size - offset, be_verbose);
675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691

  /* 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++)
692
    {
693 694 695 696 697 698 699 700 701
      /* 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);
702
    }
Karl Heuer's avatar
Karl Heuer committed
703

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

706 707 708 709 710
  /* 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
711

712 713 714 715 716 717
    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
718

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


726
/* Dump out .data and .bss sections into a new executable.  */
727
void
728
unexec (const char *new_name, const char *old_name)
729
{
730 731 732
  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
733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749
  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);
750

Andrew Innes's avatar
Andrew Innes committed
751 752 753 754
  /* 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"))
755
    strcat (out_filename, ".exe");
Karl Heuer's avatar
Karl Heuer committed
756

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

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

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

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

774 775 776 777 778 779 780
  /* 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))
781
    {
782
      printf ("Failed to open %s (%d)...bailing.\n",
783 784
	      out_filename, GetLastError ());
      exit (1);
785 786
    }

787 788
  /* Set the flag (before dumping).  */
  using_dynamic_heap = TRUE;
789

790
  copy_executable_and_dump_data (&in_file, &out_file);
791

792 793 794 795 796 797 798
  /* 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;
799

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

803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819
    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);
  }
820

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

/* eof */