/* unexec for GNU Emacs on Windows NT.
- Copyright (C) 1994 Free Software Foundation, Inc.
+ Copyright (C) 1994, 2001, 2002, 2003, 2004, 2005,
+ 2006, 2007 Free Software Foundation, Inc.
This file is part of GNU Emacs.
You should have received a copy of the GNU General Public License
along with GNU Emacs; see the file COPYING. If not, write to
-the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.
Geoff Voelker (voelker@cs.washington.edu) 8-12-94
*/
-#include <stdlib.h> /* _fmode */
+#include <config.h>
+
#include <stdio.h>
#include <fcntl.h>
+#include <time.h>
#include <windows.h>
+/* Include relevant definitions from IMAGEHLP.H, which can be found
+ in \\win32sdk\mstools\samples\image\include\imagehlp.h. */
+
+PIMAGE_NT_HEADERS
+(__stdcall * pfnCheckSumMappedFile) (LPVOID BaseAddress,
+ DWORD FileLength,
+ LPDWORD HeaderSum,
+ LPDWORD CheckSum);
+
extern BOOL ctrl_c_handler (unsigned long type);
-#include "w32heap.h"
+extern char my_begdata[];
+extern char my_edata[];
+extern char my_begbss[];
+extern char my_endbss[];
+extern char *my_begbss_static;
+extern char *my_endbss_static;
-/* A convenient type for keeping all the info about a mapped file together. */
-typedef struct file_data {
- char *name;
- unsigned long size;
- HANDLE file;
- HANDLE file_mapping;
- unsigned char *file_base;
-} file_data;
+#include "w32heap.h"
-/* Force zero initialized variables to be placed in the .data segment;
- MSVC 5.0 otherwise places them in .bss, which breaks the dumping code. */
-#pragma data_seg(".data")
+#undef min
+#undef max
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+#define max(x, y) (((x) > (y)) ? (x) : (y))
/* Basically, our "initialized" flag. */
-BOOL need_to_recreate_heap = FALSE;
-
-/* So we can find our heap in the file to recreate it. */
-unsigned long heap_index_in_executable = 0;
+BOOL using_dynamic_heap = FALSE;
-void open_input_file (file_data *p_file, char *name);
-void open_output_file (file_data *p_file, char *name, unsigned long size);
+int open_input_file (file_data *p_file, char *name);
+int open_output_file (file_data *p_file, char *name, unsigned long size);
void close_file_data (file_data *p_file);
void get_section_info (file_data *p_file);
-void copy_executable_and_dump_data_section (file_data *, file_data *);
+void copy_executable_and_dump_data (file_data *, file_data *);
void dump_bss_and_heap (file_data *p_infile, file_data *p_outfile);
/* Cached info about the .data section in the executable. */
-PUCHAR data_start_va = 0;
-DWORD data_start_file = 0;
+PIMAGE_SECTION_HEADER data_section;
+PCHAR data_start = 0;
DWORD data_size = 0;
/* Cached info about the .bss section in the executable. */
-PUCHAR bss_start = 0;
+PIMAGE_SECTION_HEADER bss_section;
+PCHAR bss_start = 0;
DWORD bss_size = 0;
+DWORD extra_bss_size = 0;
+/* bss data that is static might be discontiguous from non-static. */
+PIMAGE_SECTION_HEADER bss_section_static;
+PCHAR bss_start_static = 0;
+DWORD bss_size_static = 0;
+DWORD extra_bss_size_static = 0;
+
+PIMAGE_SECTION_HEADER heap_section;
#ifdef HAVE_NTGUI
HINSTANCE hinst = NULL;
{
extern void mainCRTStartup (void);
+#if 1
+ /* 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
+
/* Cache system info, e.g., the NT page size. */
cache_system_info ();
- /* If we're a dumped version of emacs then we need to recreate
- our heap and play tricks with our .bss section. Do this before
- start up. (WARNING: Do not put any code before this section
- that relies upon malloc () and runs in the dumped version. It
- won't work.) */
- if (need_to_recreate_heap)
- {
- char executable_path[MAX_PATH];
-
- if (GetModuleFileName (NULL, executable_path, MAX_PATH) == 0)
- {
- printf ("Failed to find path for executable.\n");
- exit (1);
- }
- recreate_heap (executable_path);
- need_to_recreate_heap = FALSE;
- }
-
- /* The default behavior is to treat files as binary and patch up
- text files appropriately, in accordance with the MSDOS code. */
- _fmode = O_BINARY;
+ /* Grab our malloc arena space now, before CRT starts up. */
+ init_heap ();
/* This prevents ctrl-c's in shells running while we're suspended from
having us exit. */
SetConsoleCtrlHandler ((PHANDLER_ROUTINE) ctrl_c_handler, TRUE);
+ /* Prevent Emacs from being locked up (eg. in batch mode) when
+ accessing devices that aren't mounted (eg. removable media drives). */
+ SetErrorMode (SEM_FAILCRITICALERRORS);
+
/* Invoke the NT CRT startup routine now that our housecleaning
is finished. */
#ifdef HAVE_NTGUI
mainCRTStartup ();
}
-/* Dump out .data and .bss sections into a new executable. */
-void
-unexec (char *new_name, char *old_name, void *start_data, void *start_bss,
- void *entry_address)
-{
- file_data in_file, out_file;
- char out_filename[MAX_PATH], in_filename[MAX_PATH];
- unsigned long size;
- char *ptr;
-
- /* Make sure that the input and output filenames have the
- ".exe" extension...patch them up if they don't. */
- strcpy (in_filename, old_name);
- ptr = in_filename + strlen (in_filename) - 4;
- if (strcmp (ptr, ".exe"))
- strcat (in_filename, ".exe");
-
- strcpy (out_filename, new_name);
- ptr = out_filename + strlen (out_filename) - 4;
- if (strcmp (ptr, ".exe"))
- strcat (out_filename, ".exe");
-
- printf ("Dumping from %s\n", in_filename);
- printf (" to %s\n", out_filename);
-
- /* We need to round off our heap to NT's allocation unit (64KB). */
- round_heap (get_allocation_unit ());
-
- /* Open the undumped executable file. */
- open_input_file (&in_file, in_filename);
-
- /* Get the interesting section info, like start and size of .bss... */
- get_section_info (&in_file);
-
- /* 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. */
- heap_index_in_executable = (unsigned long)
- round_to_next ((unsigned char *) in_file.size, get_allocation_unit ());
- size = heap_index_in_executable + get_committed_heap_size () + bss_size;
- open_output_file (&out_file, out_filename, size);
-
- /* Set the flag (before dumping). */
- need_to_recreate_heap = TRUE;
-
- copy_executable_and_dump_data_section (&in_file, &out_file);
- dump_bss_and_heap (&in_file, &out_file);
-
- close_file_data (&in_file);
- close_file_data (&out_file);
-}
-
/* File handling. */
-
-void
+int
open_input_file (file_data *p_file, char *filename)
{
HANDLE file;
file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
- if (file == INVALID_HANDLE_VALUE)
- {
- printf ("Failed to open %s (%d)...bailing.\n",
- filename, GetLastError ());
- exit (1);
- }
+ if (file == INVALID_HANDLE_VALUE)
+ return FALSE;
size = GetFileSize (file, &upper_size);
- file_mapping = CreateFileMapping (file, NULL, PAGE_READONLY,
+ file_mapping = CreateFileMapping (file, NULL, PAGE_READONLY,
0, size, NULL);
- if (!file_mapping)
- {
- printf ("Failed to create file mapping of %s (%d)...bailing.\n",
- filename, GetLastError ());
- exit (1);
- }
+ if (!file_mapping)
+ return FALSE;
file_base = MapViewOfFile (file_mapping, FILE_MAP_READ, 0, 0, size);
- if (file_base == 0)
- {
- printf ("Failed to map view of file of %s (%d)...bailing.\n",
- filename, GetLastError ());
- exit (1);
- }
+ if (file_base == 0)
+ return FALSE;
p_file->name = filename;
p_file->size = size;
p_file->file = file;
p_file->file_mapping = file_mapping;
p_file->file_base = file_base;
+
+ return TRUE;
}
-void
+int
open_output_file (file_data *p_file, char *filename, unsigned long size)
{
HANDLE file;
HANDLE file_mapping;
void *file_base;
- int i;
file = CreateFile (filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
- if (file == INVALID_HANDLE_VALUE)
- {
- i = GetLastError ();
- printf ("open_output_file: Failed to open %s (%d).\n",
- filename, i);
- exit (1);
- }
-
- file_mapping = CreateFileMapping (file, NULL, PAGE_READWRITE,
+ if (file == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ file_mapping = CreateFileMapping (file, NULL, PAGE_READWRITE,
0, size, NULL);
- if (!file_mapping)
- {
- i = GetLastError ();
- printf ("open_output_file: Failed to create file mapping of %s (%d).\n",
- filename, i);
- exit (1);
- }
-
+ if (!file_mapping)
+ return FALSE;
+
file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size);
- if (file_base == 0)
- {
- i = GetLastError ();
- printf ("open_output_file: Failed to map view of file of %s (%d).\n",
- filename, i);
- exit (1);
- }
-
+ if (file_base == 0)
+ return FALSE;
+
p_file->name = filename;
p_file->size = size;
p_file->file = file;
p_file->file_mapping = file_mapping;
p_file->file_base = file_base;
+
+ return TRUE;
}
/* Close the system structures associated with the given file. */
-static void
+void
close_file_data (file_data *p_file)
{
- UnmapViewOfFile (p_file->file_base);
- CloseHandle (p_file->file_mapping);
- CloseHandle (p_file->file);
+ 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);
}
/* Routines to manipulate NT executable file sections. */
-static void
-get_bss_info_from_map_file (file_data *p_infile, PUCHAR *p_bss_start,
- DWORD *p_bss_size)
+/* 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)
{
- int n, start, len;
- char map_filename[MAX_PATH];
- char buffer[256];
- FILE *map;
-
- /* Overwrite the .exe extension on the executable file name with
- the .map extension. */
- strcpy (map_filename, p_infile->name);
- n = strlen (map_filename) - 3;
- strcpy (&map_filename[n], "map");
-
- map = fopen (map_filename, "r");
- if (!map)
+ PIMAGE_SECTION_HEADER section;
+ int i;
+
+ section = IMAGE_FIRST_SECTION (nt_header);
+
+ for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
{
- printf ("Failed to open map file %s, error %d...bailing out.\n",
- map_filename, GetLastError ());
- exit (-1);
+ /* 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);
+ if (rva >= section->VirtualAddress
+ && rva < section->VirtualAddress + real_size)
+ return section;
+ section++;
}
+ return NULL;
+}
- while (fgets (buffer, sizeof (buffer), map))
+/* 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 (!(strstr (buffer, ".bss") && strstr (buffer, "DATA")))
- continue;
- n = sscanf (buffer, " %*d:%x %x", &start, &len);
- if (n != 2)
- {
- printf ("Failed to scan the .bss section line:\n%s", buffer);
- exit (-1);
- }
- break;
+ if (offset >= section->PointerToRawData
+ && offset < section->PointerToRawData + section->SizeOfRawData)
+ return section;
+ section++;
}
- *p_bss_start = (PUCHAR) start;
- *p_bss_size = (DWORD) len;
+ return NULL;
}
-static unsigned long
-get_section_size (PIMAGE_SECTION_HEADER p_section)
+/* 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). */
+DWORD
+relocate_offset (DWORD offset,
+ IMAGE_NT_HEADERS * src_nt_header,
+ IMAGE_NT_HEADERS * dst_nt_header)
{
- /* The section size is in different locations in the different versions. */
- switch (get_w32_minor_version ())
+ 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)
{
- case 10:
- return p_section->SizeOfRawData;
- default:
- return p_section->Misc.VirtualSize;
+ 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))
+
+#define RVA_TO_PTR(var,section,filedata) \
+ ((void *)(RVA_TO_OFFSET(var,section) + (filedata).file_base))
+
+#define PTR_TO_OFFSET(ptr, pfile_data) \
+ ((unsigned char *)(ptr) - (pfile_data)->file_base)
+
+#define OFFSET_TO_PTR(offset, pfile_data) \
+ ((pfile_data)->file_base + (DWORD)(offset))
+
+
/* Flip through the executable and cache the info necessary for dumping. */
-static void
+void
get_section_info (file_data *p_infile)
{
PIMAGE_DOS_HEADER dos_header;
PIMAGE_NT_HEADERS nt_header;
- PIMAGE_SECTION_HEADER section, data_section;
- unsigned char *ptr;
- int i;
-
+ PIMAGE_SECTION_HEADER section;
+ int overlap;
+
dos_header = (PIMAGE_DOS_HEADER) p_infile->file_base;
- if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
+ if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
{
printf ("Unknown EXE header in %s...bailing.\n", p_infile->name);
exit (1);
}
- nt_header = (PIMAGE_NT_HEADERS) (((unsigned long) dos_header) +
+ nt_header = (PIMAGE_NT_HEADERS) (((unsigned long) dos_header) +
dos_header->e_lfanew);
- if (nt_header == NULL)
+ if (nt_header == NULL)
{
- printf ("Failed to find IMAGE_NT_HEADER in %s...bailing.\n",
+ printf ("Failed to find IMAGE_NT_HEADER in %s...bailing.\n",
p_infile->name);
exit (1);
}
/* Check the NT header signature ... */
- if (nt_header->Signature != IMAGE_NT_SIGNATURE)
+ if (nt_header->Signature != IMAGE_NT_SIGNATURE)
{
printf ("Invalid IMAGE_NT_SIGNATURE 0x%x in %s...bailing.\n",
nt_header->Signature, p_infile->name);
+ exit (1);
}
- /* Flip through the sections for .data and .bss ... */
- section = (PIMAGE_SECTION_HEADER) IMAGE_FIRST_SECTION (nt_header);
- for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
+ /* 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. */
+
+ /* 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)
{
- if (!strcmp (section->Name, ".bss"))
- {
- /* The .bss section. */
- ptr = (char *) nt_header->OptionalHeader.ImageBase +
- section->VirtualAddress;
- bss_start = ptr;
- bss_size = get_section_size (section);
- }
- if (!strcmp (section->Name, ".data"))
+ 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))
{
- /* From lastfile.c */
- extern char my_edata[];
-
- /* The .data section. */
- data_section = section;
- ptr = (char *) nt_header->OptionalHeader.ImageBase +
- section->VirtualAddress;
- data_start_va = ptr;
- data_start_file = section->PointerToRawData;
-
- /* We want to only write Emacs data back to the executable,
- not any of the library data (if library data is included,
- then a dumped Emacs won't run on system versions other
- than the one Emacs was dumped on). */
- data_size = my_edata - data_start_va;
+ printf ("Initialized data is not in a single section...bailing\n");
+ exit (1);
}
- section++;
}
- if (!bss_start && !bss_size)
+ /* 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))
{
- /* Starting with MSVC 4.0, the .bss section has been eliminated
- and appended virtually to the end of the .data section. Our
- only hint about where the .bss section starts in the address
- comes from the SizeOfRawData field in the .data section
- header. Unfortunately, this field is only approximate, as it
- is a rounded number and is typically rounded just beyond the
- start of the .bss section. To find the start and size of the
- .bss section exactly, we have to peek into the map file. */
- get_bss_info_from_map_file (p_infile, &ptr, &bss_size);
- bss_start = ptr + nt_header->OptionalHeader.ImageBase
- + data_section->VirtualAddress;
+ 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. */
+#ifdef _ALPHA_
+ overlap = 1; /* force all bss data to be dumped */
+#else
+ overlap = 0;
+#endif
+ 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)
+ {
+ printf ("BSS data not in a single section...bailing\n");
+ exit (1);
+ }
+ 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;
}
-}
-
-
-/* The dump routines. */
-
-static void
-copy_executable_and_dump_data_section (file_data *p_infile,
- file_data *p_outfile)
-{
- unsigned char *data_file, *data_va;
- unsigned long size, index;
-
- /* Get a pointer to where the raw data should go in the executable file. */
- data_file = (char *) p_outfile->file_base + data_start_file;
-
- /* Get a pointer to the raw data in our address space. */
- data_va = data_start_va;
-
- size = (DWORD) data_file - (DWORD) p_outfile->file_base;
- printf ("Copying executable up to data section...\n");
- printf ("\t0x%08x Offset in input file.\n", 0);
- printf ("\t0x%08x Offset in output file.\n", 0);
- printf ("\t0x%08x Size in bytes.\n", size);
- memcpy (p_outfile->file_base, p_infile->file_base, size);
-
- size = data_size;
- printf ("Dumping .data section...\n");
- printf ("\t0x%08x Address in process.\n", data_va);
- printf ("\t0x%08x Offset in output file.\n",
- data_file - p_outfile->file_base);
- printf ("\t0x%08x Size in bytes.\n", size);
- memcpy (data_file, data_va, size);
-
- index = (DWORD) data_file + size - (DWORD) p_outfile->file_base;
- size = p_infile->size - index;
- printf ("Copying rest of executable...\n");
- printf ("\t0x%08x Offset in input file.\n", index);
- printf ("\t0x%08x Offset in output file.\n", index);
- printf ("\t0x%08x Size in bytes.\n", size);
- memcpy ((char *) p_outfile->file_base + index,
- (char *) p_infile->file_base + index, size);
-}
-static void
-dump_bss_and_heap (file_data *p_infile, file_data *p_outfile)
-{
- unsigned char *heap_data, *bss_data;
- unsigned long size, index;
-
- printf ("Dumping heap into executable...\n");
-
- index = heap_index_in_executable;
- size = get_committed_heap_size ();
- heap_data = get_heap_start ();
-
- printf ("\t0x%08x Heap start in process.\n", heap_data);
- printf ("\t0x%08x Heap offset in executable.\n", index);
- printf ("\t0x%08x Heap size in bytes.\n", size);
-
- memcpy ((PUCHAR) p_outfile->file_base + index, heap_data, size);
-
- printf ("Dumping .bss into executable...\n");
-
- index += size;
- size = bss_size;
- bss_data = bss_start;
-
- printf ("\t0x%08x BSS start in process.\n", bss_data);
- printf ("\t0x%08x BSS offset in executable.\n", index);
- printf ("\t0x%08x BSS size in bytes.\n", size);
- memcpy ((char *) p_outfile->file_base + index, bss_data, size);
+ heap_section = rva_to_section (PTR_TO_RVA (get_heap_start ()), nt_header);
}
-/* Reload and remap routines. */
-
+/* The dump routines. */
-/* Load the dumped .bss section into the .bss area of our address space. */
void
-read_in_bss (char *filename)
+copy_executable_and_dump_data (file_data *p_infile,
+ file_data *p_outfile)
{
- HANDLE file;
- unsigned long size, index, n_read, total_read;
- char buffer[512], *bss;
- int i;
+ 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;
+ int be_verbose = GetEnvironmentVariable ("DEBUG_DUMP", NULL, 0) > 0;
+
+#define COPY_CHUNK(message, src, size, verbose) \
+ do { \
+ unsigned char *s = (void *)(src); \
+ unsigned long count = (size); \
+ 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); \
+ } \
+ memcpy (dst, s, count); \
+ dst += count; \
+ } while (0)
+
+#define COPY_PROC_CHUNK(message, src, size, verbose) \
+ do { \
+ unsigned char *s = (void *)(src); \
+ unsigned long count = (size); \
+ 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); \
+ } \
+ 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)))
+#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)
+
+ /* 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. */
- file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
- OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
- if (file == INVALID_HANDLE_VALUE)
+ dos_header = (PIMAGE_DOS_HEADER) p_infile->file_base;
+ nt_header = (PIMAGE_NT_HEADERS) (((unsigned long) dos_header) +
+ dos_header->e_lfanew);
+ section = IMAGE_FIRST_SECTION (nt_header);
+
+ dst = (unsigned char *) p_outfile->file_base;
+
+ COPY_CHUNK ("Copying DOS header...", dos_header,
+ (DWORD) nt_header - (DWORD) dos_header, be_verbose);
+ dst_nt_header = (PIMAGE_NT_HEADERS) dst;
+ COPY_CHUNK ("Copying NT header...", nt_header,
+ (DWORD) section - (DWORD) nt_header, be_verbose);
+ dst_section = (PIMAGE_SECTION_HEADER) dst;
+ COPY_CHUNK ("Copying section table...", section,
+ nt_header->FileHeader.NumberOfSections * sizeof (*section),
+ be_verbose);
+
+ /* 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 ();
+
+ for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
{
- i = GetLastError ();
- exit (1);
- }
+ char msg[100];
+ /* 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);
+
+ 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),
+ section->SizeOfRawData, be_verbose);
+ /* Ensure alignment slop is zeroed. */
+ ROUND_UP_DST_AND_ZERO (dst_nt_header->OptionalHeader.FileAlignment);
+
+ /* Note that various sections below may be aliases. */
+ if (section == data_section)
+ {
+ dst = dst_save
+ + RVA_TO_SECTION_OFFSET (PTR_TO_RVA (data_start), dst_section);
+ COPY_PROC_CHUNK ("Dumping initialized data...",
+ data_start, data_size, be_verbose);
+ dst = dst_save + dst_section->SizeOfRawData;
+ }
+ if (section == bss_section)
+ {
+ /* 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);
+ COPY_PROC_CHUNK ("Dumping bss data...", bss_start,
+ bss_size, be_verbose);
+ 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 == 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);
+ COPY_PROC_CHUNK ("Dumping static bss data...", bss_start_static,
+ bss_size_static, be_verbose);
+ 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)
+ {
+ DWORD heap_start = (DWORD) get_heap_start ();
+ 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);
+ COPY_PROC_CHUNK ("Dumping heap...", heap_start, heap_size,
+ be_verbose);
+ 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;
+ }
- /* Seek to where the .bss section is tucked away after the heap... */
- index = heap_index_in_executable + get_committed_heap_size ();
- if (SetFilePointer (file, index, NULL, FILE_BEGIN) == 0xFFFFFFFF)
- {
- i = GetLastError ();
- exit (1);
+ /* Align the section's raw data area. */
+ ROUND_UP_DST (dst_nt_header->OptionalHeader.FileAlignment);
+
+ section++;
+ dst_section++;
}
-
- /* Ok, read in the saved .bss section and initialize all
- uninitialized variables. */
- if (!ReadFile (file, bss_start, bss_size, &n_read, NULL))
+ /* 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),
+ p_infile->size - offset, be_verbose);
+
+ /* 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++)
{
- i = GetLastError ();
- exit (1);
+ /* 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);
}
- CloseHandle (file);
+ ADJUST_OFFSET (dst_nt_header->FileHeader.PointerToSymbolTable);
+
+ /* 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;
+
+ 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);
+
+ for (i = 0; i < debug_dir.Size; i++, debug_entry++)
+ ADJUST_OFFSET (debug_entry->PointerToRawData);
+ }
+ }
}
-/* Map the heap dumped into the executable file into our address space. */
-void
-map_in_heap (char *filename)
+
+/* Dump out .data and .bss sections into a new executable. */
+void
+unexec (char *new_name, char *old_name, void *start_data, void *start_bss,
+ void *entry_address)
{
- HANDLE file;
- HANDLE file_mapping;
- void *file_base;
- unsigned long size, upper_size, n_read;
- int i;
+ file_data in_file, out_file;
+ char out_filename[MAX_PATH], in_filename[MAX_PATH];
+ unsigned long size;
+ 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);
+
+ /* 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"))
+ strcat (out_filename, ".exe");
- file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
- OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
- if (file == INVALID_HANDLE_VALUE)
+ printf ("Dumping from %s\n", in_filename);
+ printf (" to %s\n", out_filename);
+
+ /* We need to round off our heap to NT's page size. */
+ round_heap (get_page_size ());
+
+ /* Open the undumped executable file. */
+ if (!open_input_file (&in_file, in_filename))
{
- i = GetLastError ();
+ printf ("Failed to open %s (%d)...bailing.\n",
+ in_filename, GetLastError ());
exit (1);
}
-
- size = GetFileSize (file, &upper_size);
- file_mapping = CreateFileMapping (file, NULL, PAGE_WRITECOPY,
- 0, size, NULL);
- if (!file_mapping)
+
+ /* Get the interesting section info, like start and size of .bss... */
+ get_section_info (&in_file);
+
+ /* 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))
{
- i = GetLastError ();
+ printf ("Failed to open %s (%d)...bailing.\n",
+ out_filename, GetLastError ());
exit (1);
}
-
- size = get_committed_heap_size ();
- file_base = MapViewOfFileEx (file_mapping, FILE_MAP_COPY, 0,
- heap_index_in_executable, size,
- get_heap_start ());
- if (file_base != 0)
- {
- return;
- }
- /* If we don't succeed with the mapping, then copy from the
- data into the heap. */
+ /* Set the flag (before dumping). */
+ using_dynamic_heap = TRUE;
- CloseHandle (file_mapping);
+ copy_executable_and_dump_data (&in_file, &out_file);
- if (VirtualAlloc (get_heap_start (), get_committed_heap_size (),
- MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE) == NULL)
- {
- i = GetLastError ();
- exit (1);
- }
+ /* 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;
- /* Seek to the location of the heap data in the executable. */
- i = heap_index_in_executable;
- if (SetFilePointer (file, i, NULL, FILE_BEGIN) == 0xFFFFFFFF)
- {
- i = GetLastError ();
- exit (1);
- }
+ dos_header = (PIMAGE_DOS_HEADER) out_file.file_base;
+ nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew);
- /* Read in the data. */
- if (!ReadFile (file, get_heap_start (),
- get_committed_heap_size (), &n_read, NULL))
- {
- i = GetLastError ();
- exit (1);
- }
+ nt_header->OptionalHeader.CheckSum = 0;
+// nt_header->FileHeader.TimeDateStamp = time (NULL);
+// dos_header->e_cp = size / 512;
+// nt_header->OptionalHeader.SizeOfImage = size;
- CloseHandle (file);
+ 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);
+ }
+
+ close_file_data (&in_file);
+ close_file_data (&out_file);
}
+
+/* eof */
+
+/* arch-tag: fe1d3d1c-ef88-4917-ab22-f12ab16b3254
+ (do not change this comment) */