(Qforeground_color, Qbackground_color): Declare.
[bpt/emacs.git] / src / unexw32.c
index 67555b2..ebd934c 100644 (file)
@@ -21,23 +21,38 @@ Boston, MA 02111-1307, USA.
    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;
@@ -45,8 +60,8 @@ 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);
@@ -67,23 +82,6 @@ HINSTANCE hinst = NULL;
 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
@@ -93,11 +91,14 @@ WinMain (_hinst, _hPrevInst, _lpCmdLine, _nCmdShow)
 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 ();
@@ -116,9 +117,35 @@ _start (void)
          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.  */
@@ -131,10 +158,12 @@ _start (void)
   /* 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.  */
@@ -166,7 +195,12 @@ unexec (char *new_name, char *old_name, void *start_data, void *start_bss,
   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);
@@ -176,7 +210,12 @@ unexec (char *new_name, char *old_name, void *start_data, void *start_bss,
   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;
@@ -184,6 +223,35 @@ unexec (char *new_name, char *old_name, void *start_data, void *start_bss,
   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);
 }
@@ -192,7 +260,7 @@ unexec (char *new_name, char *old_name, void *start_data, void *start_bss,
 /* File handling.  */
 
 
-void 
+int
 open_input_file (file_data *p_file, char *filename)
 {
   HANDLE file;
@@ -203,83 +271,59 @@ open_input_file (file_data *p_file, char *filename)
   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);
@@ -290,6 +334,7 @@ close_file_data (file_data *p_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)
@@ -328,20 +373,56 @@ get_bss_info_from_map_file (file_data *p_infile, PUCHAR *p_bss_start,
   *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)
@@ -378,6 +459,7 @@ 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.  */
@@ -386,6 +468,8 @@ get_section_info (file_data *p_infile)
          bss_start = ptr;
          bss_size = get_section_size (section);
        }
+#endif
+#if 0
       if (!strcmp (section->Name, ".data")) 
        {
          /* From lastfile.c  */
@@ -393,7 +477,7 @@ get_section_info (file_data *p_infile)
 
          /* 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;
@@ -404,10 +488,25 @@ get_section_info (file_data *p_infile)
             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
@@ -421,6 +520,22 @@ get_section_info (file_data *p_infile)
       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
 }
 
 
@@ -497,6 +612,43 @@ dump_bss_and_heap (file_data *p_infile, file_data *p_outfile)
 
 /* 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
@@ -510,27 +662,17 @@ read_in_bss (char *filename)
   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);
 }
@@ -548,19 +690,13 @@ map_in_heap (char *filename)
   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, 
@@ -578,26 +714,17 @@ map_in_heap (char *filename)
 
   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);
 }