1/* unexec() support for Cygwin; 2 complete rewrite of xemacs Cygwin unexec() code 3 4 Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc. 5 6This file is part of GNU Emacs. 7 8GNU Emacs is free software; you can redistribute it and/or modify 9it under the terms of the GNU General Public License as published by 10the Free Software Foundation; either version 2, or (at your option) 11any later version. 12 13GNU Emacs is distributed in the hope that it will be useful, 14but WITHOUT ANY WARRANTY; without even the implied warranty of 15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16GNU General Public License for more details. 17 18You should have received a copy of the GNU General Public License 19along with GNU Emacs; see the file COPYING. If not, write to 20the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21Boston, MA 02110-1301, USA. */ 22 23#include <config.h> 24#include <lisp.h> 25#include <stdio.h> 26#include <fcntl.h> 27#include <a.out.h> 28#include <unistd.h> 29#include <assert.h> 30 31#define DOTEXE ".exe" 32 33extern int bss_sbrk_did_unexec; 34 35/* emacs symbols that indicate where bss and data end for emacs internals */ 36extern char my_endbss[]; 37extern char my_edata[]; 38 39/* 40** header for Windows executable files 41*/ 42typedef struct 43{ 44 FILHDR file_header; 45 PEAOUTHDR file_optional_header; 46 SCNHDR section_header[32]; 47} exe_header_t; 48 49int debug_unexcw = 0; 50 51/* 52** Read the header from the executable into memory so we can more easily access it. 53*/ 54static exe_header_t * 55read_exe_header (int fd, exe_header_t * exe_header_buffer) 56{ 57 int i; 58 int ret; 59 60 assert (fd >= 0); 61 assert (exe_header_buffer != 0); 62 63 ret = lseek (fd, 0L, SEEK_SET); 64 assert (ret != -1); 65 66 ret = 67 read (fd, &exe_header_buffer->file_header, 68 sizeof (exe_header_buffer->file_header)); 69 assert (ret == sizeof (exe_header_buffer->file_header)); 70 71 assert (exe_header_buffer->file_header.e_magic == 0x5a4d); 72 assert (exe_header_buffer->file_header.nt_signature == 0x4550); 73 assert (exe_header_buffer->file_header.f_magic == 0x014c); 74 assert (exe_header_buffer->file_header.f_nscns > 0); 75 assert (exe_header_buffer->file_header.f_nscns <= 76 sizeof (exe_header_buffer->section_header) / 77 sizeof (exe_header_buffer->section_header[0])); 78 assert (exe_header_buffer->file_header.f_opthdr > 0); 79 80 ret = 81 read (fd, &exe_header_buffer->file_optional_header, 82 sizeof (exe_header_buffer->file_optional_header)); 83 assert (ret == sizeof (exe_header_buffer->file_optional_header)); 84 85 assert (exe_header_buffer->file_optional_header.magic == 0x010b); 86 87 for (i = 0; i < exe_header_buffer->file_header.f_nscns; ++i) 88 { 89 ret = 90 read (fd, &exe_header_buffer->section_header[i], 91 sizeof (exe_header_buffer->section_header[i])); 92 assert (ret == sizeof (exe_header_buffer->section_header[i])); 93 } 94 95 return (exe_header_buffer); 96} 97 98/* 99** Fix the dumped emacs executable: 100** 101** - copy .data section data of interest from running executable into 102** output .exe file 103** 104** - convert .bss section into an initialized data section (like 105** .data) and copy .bss section data of interest from running 106** executable into output .exe file 107*/ 108static void 109fixup_executable (int fd) 110{ 111 exe_header_t exe_header_buffer; 112 exe_header_t *exe_header; 113 int i; 114 int ret; 115 int found_data = 0; 116 int found_bss = 0; 117 118 exe_header = read_exe_header (fd, &exe_header_buffer); 119 assert (exe_header != 0); 120 121 assert (exe_header->file_header.f_nscns > 0); 122 for (i = 0; i < exe_header->file_header.f_nscns; ++i) 123 { 124 unsigned long start_address = 125 exe_header->section_header[i].s_vaddr + 126 exe_header->file_optional_header.ImageBase; 127 unsigned long end_address = 128 exe_header->section_header[i].s_vaddr + 129 exe_header->file_optional_header.ImageBase + 130 exe_header->section_header[i].s_paddr; 131 if (debug_unexcw) 132 printf ("%8s start 0x%08x end 0x%08x\n", 133 exe_header->section_header[i].s_name, 134 start_address, end_address); 135 if (my_edata >= (char *) start_address 136 && my_edata < (char *) end_address) 137 { 138 /* data section */ 139 ret = 140 lseek (fd, (long) (exe_header->section_header[i].s_scnptr), 141 SEEK_SET); 142 assert (ret != -1); 143 ret = 144 write (fd, (char *) start_address, 145 my_edata - (char *) start_address); 146 assert (ret == my_edata - (char *) start_address); 147 ++found_data; 148 if (debug_unexcw) 149 printf (" .data, mem start 0x%08x mem length %d\n", 150 start_address, my_edata - (char *) start_address); 151 if (debug_unexcw) 152 printf (" .data, file start %d file length %d\n", 153 (int) exe_header->section_header[i].s_scnptr, 154 (int) exe_header->section_header[i].s_paddr); 155 } 156 else if (my_endbss >= (char *) start_address 157 && my_endbss < (char *) end_address) 158 { 159 /* bss section */ 160 ++found_bss; 161 if (exe_header->section_header[i].s_flags & 0x00000080) 162 { 163 /* convert uninitialized data section to initialized data section */ 164 struct stat statbuf; 165 ret = fstat (fd, &statbuf); 166 assert (ret != -1); 167 168 exe_header->section_header[i].s_flags &= ~0x00000080; 169 exe_header->section_header[i].s_flags |= 0x00000040; 170 171 exe_header->section_header[i].s_scnptr = 172 (statbuf.st_size + 173 exe_header->file_optional_header.FileAlignment) / 174 exe_header->file_optional_header.FileAlignment * 175 exe_header->file_optional_header.FileAlignment; 176 177 exe_header->section_header[i].s_size = 178 (exe_header->section_header[i].s_paddr + 179 exe_header->file_optional_header.FileAlignment) / 180 exe_header->file_optional_header.FileAlignment * 181 exe_header->file_optional_header.FileAlignment; 182 183 ret = 184 lseek (fd, 185 (long) (exe_header->section_header[i].s_scnptr + 186 exe_header->section_header[i].s_size - 1), 187 SEEK_SET); 188 assert (ret != -1); 189 ret = write (fd, "", 1); 190 assert (ret == 1); 191 192 ret = 193 lseek (fd, 194 (long) ((char *) &exe_header->section_header[i] - 195 (char *) exe_header), SEEK_SET); 196 assert (ret != -1); 197 ret = 198 write (fd, &exe_header->section_header[i], 199 sizeof (exe_header->section_header[i])); 200 assert (ret == sizeof (exe_header->section_header[i])); 201 if (debug_unexcw) 202 printf (" seek to %ld, write %d\n", 203 (long) ((char *) &exe_header->section_header[i] - 204 (char *) exe_header), 205 sizeof (exe_header->section_header[i])); 206 } 207 /* write initialized data section */ 208 ret = 209 lseek (fd, (long) (exe_header->section_header[i].s_scnptr), 210 SEEK_SET); 211 assert (ret != -1); 212 ret = 213 write (fd, (char *) start_address, 214 my_endbss - (char *) start_address); 215 assert (ret == (my_endbss - (char *) start_address)); 216 if (debug_unexcw) 217 printf (" .bss, mem start 0x%08x mem length %d\n", 218 start_address, my_endbss - (char *) start_address); 219 if (debug_unexcw) 220 printf (" .bss, file start %d file length %d\n", 221 (int) exe_header->section_header[i].s_scnptr, 222 (int) exe_header->section_header[i].s_paddr); 223 } 224 } 225 assert (found_bss == 1); 226 assert (found_data == 1); 227} 228 229/* 230** Windows likes .exe suffixes on executables. 231*/ 232static char * 233add_exe_suffix_if_necessary (const char *name, char *modified) 234{ 235 int i = strlen (name); 236 if (i <= (sizeof (DOTEXE) - 1)) 237 { 238 sprintf (modified, "%s%s", name, DOTEXE); 239 } 240 else if (!strcasecmp (name + i - (sizeof (DOTEXE) - 1), DOTEXE)) 241 { 242 strcpy (modified, name); 243 } 244 else 245 { 246 sprintf (modified, "%s%s", name, DOTEXE); 247 } 248 return (modified); 249} 250 251int 252unexec (char *outfile, char *infile, unsigned start_data, unsigned d1, 253 unsigned d2) 254{ 255 char infile_buffer[FILENAME_MAX]; 256 char outfile_buffer[FILENAME_MAX]; 257 int fd_in; 258 int fd_out; 259 int ret; 260 int ret2; 261 262 if (bss_sbrk_did_unexec) 263 { 264 /* can only dump once */ 265 printf ("You can only dump Emacs once on this platform.\n"); 266 return (1); 267 } 268 269 report_sheap_usage (1); 270 271 infile = add_exe_suffix_if_necessary (infile, infile_buffer); 272 outfile = add_exe_suffix_if_necessary (outfile, outfile_buffer); 273 274 fd_in = open (infile, O_RDONLY | O_BINARY); 275 assert (fd_in >= 0); 276 fd_out = open (outfile, O_RDWR | O_TRUNC | O_CREAT | O_BINARY, 0755); 277 assert (fd_out >= 0); 278 for (;;) 279 { 280 char buffer[4096]; 281 ret = read (fd_in, buffer, sizeof (buffer)); 282 if (ret == 0) 283 { 284 /* eof */ 285 break; 286 } 287 assert (ret > 0); 288 /* data */ 289 ret2 = write (fd_out, buffer, ret); 290 assert (ret2 == ret); 291 } 292 ret = close (fd_in); 293 assert (ret == 0); 294 295 bss_sbrk_did_unexec = 1; 296 fixup_executable (fd_out); 297 bss_sbrk_did_unexec = 0; 298 299 ret = close (fd_out); 300 assert (ret == 0); 301 302 return (0); 303} 304 305/* arch-tag: fc44f6c3-ca0a-45e0-a5a2-58b6101b1e65 306 (do not change this comment) */ 307