Geoff Voelker (voelker@cs.washington.edu) 8-12-94
*/
+#include <config.h>
+
#include <stdlib.h> /* _fmode */
#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 "ntheap.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;
+
+#include "w32heap.h"
-/* 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;
+#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;
-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);
HINSTANCE hprevinst = NULL;
LPSTR lpCmdLine = "";
int nCmdShow = 0;
-
-int __stdcall
-WinMain (_hinst, _hPrevInst, _lpCmdLine, _nCmdShow)
- HINSTANCE _hinst;
- HINSTANCE _hPrevInst;
- LPSTR _lpCmdLine;
- int _nCmdShow;
-{
- /* Need to parse command line */
-
- hinst = _hinst;
- hprevinst = _hPrevInst;
- lpCmdLine = _lpCmdLine;
- nCmdShow = _nCmdShow;
-
- return (main (__argc,__argv,_environ));
-}
#endif /* HAVE_NTGUI */
/* Startup code for running on NT. When we are running as the dumped
void
_start (void)
{
-#ifdef HAVE_NTGUI
- extern void WinMainCRTStartup (void);
-#else
extern void mainCRTStartup (void);
-#endif /* HAVE_NTGUI */
+
+#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 ();
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. */
/* Invoke the NT CRT startup routine now that our housecleaning
is finished. */
#ifdef HAVE_NTGUI
- WinMainCRTStartup ();
-#else
+ /* determine WinMain args like crt0.c does */
+ hinst = GetModuleHandle(NULL);
+ lpCmdLine = GetCommandLine();
+ nCmdShow = SW_SHOWDEFAULT;
+#endif
mainCRTStartup ();
-#endif /* HAVE_NTGUI */
}
/* Dump out .data and .bss sections into a new executable. */
round_heap (get_allocation_unit ());
/* Open the undumped executable file. */
- open_input_file (&in_file, in_filename);
+ 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);
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);
+ 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. */
-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);
- }
+ return FALSE;
size = GetFileSize (file, &upper_size);
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);
- }
+ 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);
- }
+ 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);
- }
-
+ 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);
- }
+ 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);
- }
+ 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);
/* 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)
*p_bss_start = (PUCHAR) start;
*p_bss_size = (DWORD) len;
}
+#endif
-static unsigned long
+unsigned long
get_section_size (PIMAGE_SECTION_HEADER p_section)
{
- /* The section size is in different locations in the different versions. */
- switch (get_nt_minor_version ())
+ /* 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++)
{
- case 10:
- return p_section->SizeOfRawData;
- default:
- return p_section->Misc.VirtualSize;
+ 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)
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. */
bss_start = ptr;
bss_size = get_section_size (section);
}
+#endif
+#if 0
if (!strcmp (section->Name, ".data"))
{
/* From lastfile.c */
/* The .data section. */
data_section = section;
- ptr = (char *) nt_header->OptionalHeader.ImageBase +
+ ptr = (char *) nt_header->OptionalHeader.ImageBase +
section->VirtualAddress;
data_start_va = ptr;
data_start_file = section->PointerToRawData;
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++;
}
- if (!bss_start && !bss_size)
+#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
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
}
/* 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 you think that you have installed\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
file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (file == INVALID_HANDLE_VALUE)
- {
- i = GetLastError ();
- exit (1);
- }
+ 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)
- {
- i = GetLastError ();
- exit (1);
- }
-
+ 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))
- {
- i = GetLastError ();
- exit (1);
- }
+ w32_fatal_reload_error ("Reading the saved .bss section.");
CloseHandle (file);
}
file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (file == INVALID_HANDLE_VALUE)
- {
- i = GetLastError ();
- exit (1);
- }
+ 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)
- {
- i = GetLastError ();
- exit (1);
- }
+ 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,
if (VirtualAlloc (get_heap_start (), get_committed_heap_size (),
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE) == NULL)
- {
- i = GetLastError ();
- exit (1);
- }
+ 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)
- {
- i = GetLastError ();
- exit (1);
- }
+ 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))
- {
- i = GetLastError ();
- exit (1);
- }
+ w32_fatal_reload_error ("Reading saved heap from executable file.");
CloseHandle (file);
}