X-Git-Url: http://git.hcoop.net/bpt/emacs.git/blobdiff_plain/556a263f578b576a971fcea01e55d830801d03a4..5109b06a473d53342cb577c6886f5e789a3ef0fa:/src/unexw32.c diff --git a/src/unexw32.c b/src/unexw32.c dissimilarity index 61% index 2363e7e73d..b183864496 100644 --- a/src/unexw32.c +++ b/src/unexw32.c @@ -1,733 +1,812 @@ -/* unexec for GNU Emacs on Windows NT. - Copyright (C) 1994 Free Software Foundation, Inc. - -This file is part of GNU Emacs. - -GNU Emacs is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -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. - -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. - - Geoff Voelker (voelker@cs.washington.edu) 8-12-94 -*/ - -#include - -#include /* _fmode */ -#include -#include -#include -#include - -/* 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); - -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; - -#include "w32heap.h" - -#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; - -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 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; -DWORD data_size = 0; - -/* Cached info about the .bss section in the executable. */ -PUCHAR bss_start = 0; -DWORD bss_size = 0; - -#ifdef HAVE_NTGUI -HINSTANCE hinst = NULL; -HINSTANCE hprevinst = NULL; -LPSTR lpCmdLine = ""; -int nCmdShow = 0; -#endif /* HAVE_NTGUI */ - -/* 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); - -#if 0 - /* 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); - } - -#if 1 - /* To allow profiling, make sure executable_path names the .exe - file, not the ._xe file created by the profiler which contains - extra code that makes the stored exe offsets incorrect. (This - will not be necessary when unexec properly extends the .bss (or - .data as appropriate) section to include the dumped bss data, - and dumps the heap into a proper section of its own.) */ - { - char * p = strrchr (executable_path, '.'); - if (p && p[1] == '_') - p[1] = 'e'; - } - - /* Using HiProf profiler, exe name is different still. */ - { - char * p = strrchr (executable_path, '\\'); - strcpy (p, "\\emacs.exe"); - } -#endif - - recreate_heap (executable_path); - need_to_recreate_heap = FALSE; - } - else - { - /* Grab our malloc arena space now, before CRT starts up. */ - sbrk (0); - } - - /* The default behavior is to treat files as binary and patch up - text files appropriately, in accordance with the MSDOS code. */ - _fmode = O_BINARY; - - /* This prevents ctrl-c's in shells running while we're suspended from - having us exit. */ - SetConsoleCtrlHandler ((PHANDLER_ROUTINE) ctrl_c_handler, TRUE); - - /* Invoke the NT CRT startup routine now that our housecleaning - is finished. */ -#ifdef HAVE_NTGUI - /* determine WinMain args like crt0.c does */ - hinst = GetModuleHandle(NULL); - lpCmdLine = GetCommandLine(); - nCmdShow = SW_SHOWDEFAULT; -#endif - 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. */ - if (!open_input_file (&in_file, in_filename)) - { - printf ("Failed to open %s (%d)...bailing.\n", - in_filename, GetLastError ()); - exit (1); - } - - /* 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; - if (!open_output_file (&out_file, out_filename, size)) - { - printf ("Failed to open %s (%d)...bailing.\n", - out_filename, GetLastError ()); - exit (1); - } - - /* 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); - - /* 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; - - dos_header = (PIMAGE_DOS_HEADER) out_file.file_base; - nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew); - - 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); - } - - close_file_data (&in_file); - close_file_data (&out_file); -} - - -/* File handling. */ - - -int -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); - if (file == INVALID_HANDLE_VALUE) - return FALSE; - - size = GetFileSize (file, &upper_size); - file_mapping = CreateFileMapping (file, NULL, PAGE_READONLY, - 0, size, NULL); - if (!file_mapping) - return FALSE; - - file_base = MapViewOfFile (file_mapping, FILE_MAP_READ, 0, 0, size); - 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; -} - -int -open_output_file (file_data *p_file, char *filename, unsigned long size) -{ - HANDLE file; - HANDLE file_mapping; - void *file_base; - - file = CreateFile (filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, - CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); - if (file == INVALID_HANDLE_VALUE) - return FALSE; - - file_mapping = CreateFileMapping (file, NULL, PAGE_READWRITE, - 0, size, NULL); - if (!file_mapping) - return FALSE; - - file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size); - 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. */ -void -close_file_data (file_data *p_file) -{ - UnmapViewOfFile (p_file->file_base); - CloseHandle (p_file->file_mapping); - CloseHandle (p_file->file); -} - - -/* Routines to manipulate NT executable file sections. */ - -#ifdef SEPARATE_BSS_SECTION -static void -get_bss_info_from_map_file (file_data *p_infile, PUCHAR *p_bss_start, - DWORD *p_bss_size) -{ - 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) - { - printf ("Failed to open map file %s, error %d...bailing out.\n", - map_filename, GetLastError ()); - exit (-1); - } - - while (fgets (buffer, sizeof (buffer), map)) - { - 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; - } - *p_bss_start = (PUCHAR) start; - *p_bss_size = (DWORD) len; -} -#endif - -unsigned long -get_section_size (PIMAGE_SECTION_HEADER p_section) -{ - /* The true section size, before rounding. Some linkers swap the - meaning of these two values. */ - return min (p_section->SizeOfRawData, - p_section->Misc.VirtualSize); -} - -/* 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++) - { - if (rva >= section->VirtualAddress - && rva < section->VirtualAddress + section->SizeOfRawData) - return section; - section++; - } - return NULL; -} - - -/* Flip through the executable and cache the info necessary for dumping. */ -static 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; - - dos_header = (PIMAGE_DOS_HEADER) p_infile->file_base; - 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) + - dos_header->e_lfanew); - if (nt_header == NULL) - { - 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) - { - printf ("Invalid IMAGE_NT_SIGNATURE 0x%x in %s...bailing.\n", - nt_header->Signature, p_infile->name); - } - - /* 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++) - { -#ifdef SEPARATE_BSS_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); - } -#endif -#if 0 - if (!strcmp (section->Name, ".data")) - { - /* 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; - } -#else - if (!strcmp (section->Name, "EMDATA")) - { - /* The Emacs initialized data section. */ - data_section = section; - ptr = (char *) nt_header->OptionalHeader.ImageBase + - section->VirtualAddress; - data_start_va = ptr; - data_start_file = section->PointerToRawData; - - /* Write back the full section. */ - data_size = get_section_size (section); - } -#endif - section++; - } - -#ifdef SEPARATE_BSS_SECTION - if (bss_start == UNINIT_PTR && bss_size == UNINIT_LONG) - { - /* 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; - } -#else -/* 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 should 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). However, in practice this doesn't - seem to matter, since presumably the system libraries always - reinitialize their bss variables. */ - bss_start = min (my_begbss, my_begbss_static); - bss_size = max (my_endbss, my_endbss_static) - bss_start; -#endif -} - - -/* 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); -} - - -/* Reload and remap routines. */ - -void -w32_fatal_reload_error (char *step) -{ - int error = GetLastError (); - char *buffer = alloca (4096); - - sprintf (buffer, - "Emacs failed to load its dumped heap back into its address space.\n" - "The error occurred during the following step:\n\n" - "%s\n\n" - "GetLastError = %d\n\n" - "Heap start: 0x%08x\n" - "Heap commit: 0x%08x\n" - "Heap end: 0x%08x\n\n" - "This error typically happens when the system loads a DLL into\n" - "the middle of Emacs' address space, preventing Emacs from\n" - "loading its heap there. If this happens only occasionally, then\n" - "you can probably ignore it. But if it happens so often that\n" - "you cannot get Emacs to start reliably, and you think that Emacs\n" - "is installed correctly, then you have a couple of options:\n\n" - "Emacs correctly, then you have two options:\n\n" - "1) You can dump Emacs yourself. By doing this, you ensure that\n" - "Emacs' heap fits around the DLLs in your system. To dump Emacs,\n" - "download the emacs-(version)-undump-(arch) distribution file\n" - "from the site where you downloaded the executable distribution.\n\n" - "2) You can build Emacs from source. This is just another way\n" - "to dump Emacs on your system.", - step, - error, - get_heap_start (), - get_heap_start () + get_committed_heap_size (), - get_heap_end ()); - - MessageBox (NULL, - buffer, - "Emacs Abort Dialog", - MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL); - - exit (-1); -} - -/* Load the dumped .bss section into the .bss area of our address space. */ -void -read_in_bss (char *filename) -{ - HANDLE file; - unsigned long size, index, n_read, total_read; - char buffer[512], *bss; - int i; - - file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); - if (file == INVALID_HANDLE_VALUE) - w32_fatal_reload_error ("Opening Emacs executable file for .bss."); - - /* 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) - w32_fatal_reload_error ("Seeking to the saved .bss section."); - - /* Ok, read in the saved .bss section and initialize all - uninitialized variables. */ - if (!ReadFile (file, bss_start, bss_size, &n_read, NULL)) - w32_fatal_reload_error ("Reading the saved .bss section."); - - CloseHandle (file); -} - -/* Map the heap dumped into the executable file into our address space. */ -void -map_in_heap (char *filename) -{ - HANDLE file; - HANDLE file_mapping; - void *file_base; - unsigned long size, upper_size, n_read; - int i; - - file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); - if (file == INVALID_HANDLE_VALUE) - w32_fatal_reload_error ("Opening Emacs executable file for heap."); - - size = GetFileSize (file, &upper_size); - file_mapping = CreateFileMapping (file, NULL, PAGE_WRITECOPY, - 0, size, NULL); - if (!file_mapping) - w32_fatal_reload_error ("Creating file mapping to heap in executable."); - - 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. */ - - CloseHandle (file_mapping); - - if (VirtualAlloc (get_heap_start (), get_committed_heap_size (), - MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE) == NULL) - w32_fatal_reload_error ("Allocating heap address space."); - - /* Seek to the location of the heap data in the executable. */ - i = heap_index_in_executable; - if (SetFilePointer (file, i, NULL, FILE_BEGIN) == 0xFFFFFFFF) - w32_fatal_reload_error ("Seeking to saved heap in executable file."); - - /* Read in the data. */ - if (!ReadFile (file, get_heap_start (), - get_committed_heap_size (), &n_read, NULL)) - w32_fatal_reload_error ("Reading saved heap from executable file."); - - CloseHandle (file); -} +/* unexec for GNU Emacs on Windows NT. + Copyright (C) 1994 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +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. + +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. + + Geoff Voelker (voelker@cs.washington.edu) 8-12-94 +*/ + +#include + +#include +#include +#include +#include + +/* 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); + +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; + +#include "w32heap.h" + +#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 using_dynamic_heap = FALSE; + +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 (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. */ +PIMAGE_SECTION_HEADER data_section; +PCHAR data_start = 0; +DWORD data_size = 0; + +/* Cached info about the .bss section in the executable. */ +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; +HINSTANCE hprevinst = NULL; +LPSTR lpCmdLine = ""; +int nCmdShow = 0; +#endif /* HAVE_NTGUI */ + +/* 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); + +#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 (); + + /* 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 + /* determine WinMain args like crt0.c does */ + hinst = GetModuleHandle(NULL); + lpCmdLine = GetCommandLine(); + nCmdShow = SW_SHOWDEFAULT; +#endif + mainCRTStartup (); +} + + +/* File handling. */ + +int +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); + if (file == INVALID_HANDLE_VALUE) + return FALSE; + + size = GetFileSize (file, &upper_size); + file_mapping = CreateFileMapping (file, NULL, PAGE_READONLY, + 0, size, NULL); + if (!file_mapping) + return FALSE; + + file_base = MapViewOfFile (file_mapping, FILE_MAP_READ, 0, 0, size); + 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; +} + +int +open_output_file (file_data *p_file, char *filename, unsigned long size) +{ + HANDLE file; + HANDLE file_mapping; + void *file_base; + + file = CreateFile (filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + if (file == INVALID_HANDLE_VALUE) + return FALSE; + + file_mapping = CreateFileMapping (file, NULL, PAGE_READWRITE, + 0, size, NULL); + if (!file_mapping) + return FALSE; + + file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size); + 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. */ +void +close_file_data (file_data *p_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. */ + +/* 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++) + { + /* 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; +} + +/* 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) + return section; + section++; + } + return NULL; +} + +/* 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) +{ + 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)) + +#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. */ +void +get_section_info (file_data *p_infile) +{ + PIMAGE_DOS_HEADER dos_header; + PIMAGE_NT_HEADERS nt_header; + PIMAGE_SECTION_HEADER section; + int overlap; + + dos_header = (PIMAGE_DOS_HEADER) p_infile->file_base; + 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) + + dos_header->e_lfanew); + if (nt_header == NULL) + { + 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) + { + printf ("Invalid IMAGE_NT_SIGNATURE 0x%x in %s...bailing.\n", + nt_header->Signature, p_infile->name); + exit (1); + } + + /* 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) + { + 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); + } + } + + /* 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. */ +#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; + } + + heap_section = rva_to_section (PTR_TO_RVA (get_heap_start ()), nt_header); +} + + +/* The dump routines. */ + +void +copy_executable_and_dump_data (file_data *p_infile, + 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; + +#define COPY_CHUNK(message, src, size) \ + do { \ + unsigned char *s = (void *)(src); \ + unsigned long count = (size); \ + 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) \ + do { \ + unsigned char *s = (void *)(src); \ + unsigned long count = (size); \ + 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. */ + + 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); + dst_nt_header = (PIMAGE_NT_HEADERS) dst; + COPY_CHUNK ("Copying NT header...", nt_header, + (DWORD) section - (DWORD) nt_header); + dst_section = (PIMAGE_SECTION_HEADER) dst; + COPY_CHUNK ("Copying section table...", section, + nt_header->FileHeader.NumberOfSections * sizeof (*section)); + + /* 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++) + { + char msg[100]; + sprintf (msg, "Copying raw data for %s...", 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); + /* 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); + 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); + 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); + 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); + 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; + } + + /* Align the section's raw data area. */ + ROUND_UP_DST (dst_nt_header->OptionalHeader.FileAlignment); + + section++; + dst_section++; + } + + /* 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); + + /* 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++) + { + /* 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); + } + + 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); + } + } +} + + +/* 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 *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"); + + 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)) + { + printf ("Failed to open %s (%d)...bailing.\n", + in_filename, GetLastError ()); + exit (1); + } + + /* 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)) + { + printf ("Failed to open %s (%d)...bailing.\n", + out_filename, GetLastError ()); + exit (1); + } + + /* Set the flag (before dumping). */ + using_dynamic_heap = TRUE; + + copy_executable_and_dump_data (&in_file, &out_file); + + /* 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; + + dos_header = (PIMAGE_DOS_HEADER) out_file.file_base; + nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew); + + 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); + } + + close_file_data (&in_file); + close_file_data (&out_file); +} + +/* eof */ + +/* arch-tag: fe1d3d1c-ef88-4917-ab22-f12ab16b3254 + (do not change this comment) */