Fix recently-introduced typos in Windows port.
[bpt/emacs.git] / src / unexcoff.c
CommitLineData
acaf905b 1/* Copyright (C) 1985-1988, 1992-1994, 2001-2012 Free Software Foundation, Inc.
7dd63af1
RS
2
3This file is part of GNU Emacs.
4
9ec0b715 5GNU Emacs is free software: you can redistribute it and/or modify
7dd63af1 6it under the terms of the GNU General Public License as published by
9ec0b715
GM
7the Free Software Foundation, either version 3 of the License, or
8(at your option) any later version.
7dd63af1
RS
9
10GNU Emacs is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
9ec0b715 16along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
7dd63af1
RS
17
18
19/*
29cf3e20
EZ
20 * unexcoff.c - Convert a running program into an a.out or COFF file.
21 *
22 * ==================================================================
23 * Note: This file is currently used only by the MSDOS (a.k.a. DJGPP)
24 * build of Emacs. If you are not interested in the MSDOS build, you
25 * are looking at the wrong version of unexec!
26 * ==================================================================
7dd63af1
RS
27 *
28 * Author: Spencer W. Thomas
29 * Computer Science Dept.
30 * University of Utah
31 * Date: Tue Mar 2 1982
29cf3e20 32 * Originally under the name unexec.c.
7dd63af1
RS
33 * Modified heavily since then.
34 *
35 * Synopsis:
dd5ecd6b 36 * unexec (const char *new_name, const char *old_name);
7dd63af1
RS
37 *
38 * Takes a snapshot of the program and makes an a.out format file in the
39 * file named by the string argument new_name.
40 * If a_name is non-NULL, the symbol table will be taken from the given file.
41 * On some machines, an existing a_name file is required.
42 *
7dd63af1
RS
43 * If you make improvements I'd like to get them too.
44 * harpo!utah-cs!thomas, thomas@Utah-20
45 *
46 */
47
48/* Modified to support SysVr3 shared libraries by James Van Artsdalen
49 * of Dell Computer Corporation. james@bigtex.cactus.org.
50 */
51
18160b98 52#include <config.h>
ce701a33
PE
53#include "unexec.h"
54
7dd63af1 55#define PERROR(file) report_error (file, new)
7dd63af1
RS
56
57#ifndef CANNOT_DUMP /* all rest of file! */
58
f914a6bf 59#ifdef HAVE_COFF_H
2a4487ac 60#include <coff.h>
3680bdc6 61#ifdef MSDOS
8eb2807f 62#include <fcntl.h> /* for O_RDONLY, O_RDWR */
c17a2102 63#include <crt0.h> /* for _crt0_startup_flags and its bits */
5f2f0bc1 64#include <sys/exceptn.h>
c17a2102 65static int save_djgpp_startup_flags;
3680bdc6
RS
66#define filehdr external_filehdr
67#define scnhdr external_scnhdr
68#define syment external_syment
69#define auxent external_auxent
70#define n_numaux e_numaux
71#define n_type e_type
72struct aouthdr
73{
234d3183
RS
74 unsigned short magic; /* type of file */
75 unsigned short vstamp; /* version stamp */
76 unsigned long tsize; /* text size in bytes, padded to FW bdry*/
77 unsigned long dsize; /* initialized data " " */
78 unsigned long bsize; /* uninitialized data " " */
79 unsigned long entry; /* entry pt. */
80 unsigned long text_start;/* base of text used for this file */
81 unsigned long data_start;/* base of data used for this file */
3680bdc6 82};
3680bdc6 83#endif /* not MSDOS */
f914a6bf 84#else /* not HAVE_COFF_H */
8d228cb0 85#include <a.out.h>
f914a6bf 86#endif /* not HAVE_COFF_H */
265a9e55 87
f34e2e18
RS
88/* Define getpagesize if the system does not.
89 Note that this may depend on symbols defined in a.out.h. */
7dd63af1
RS
90#include "getpagesize.h"
91
92#ifndef makedev /* Try to detect types.h already loaded */
93#include <sys/types.h>
265a9e55 94#endif /* makedev */
7dd63af1
RS
95#include <stdio.h>
96#include <sys/stat.h>
97#include <errno.h>
98
f914a6bf 99#include <sys/file.h>
2d30a233 100
5f2f0bc1 101extern char *start_of_data (void); /* Start of initialized data */
7dd63af1 102
7dd63af1
RS
103static long block_copy_start; /* Old executable start point */
104static struct filehdr f_hdr; /* File header */
105static struct aouthdr f_ohdr; /* Optional file header (a.out) */
106long bias; /* Bias to add for growth */
107long lnnoptr; /* Pointer to line-number info within file */
108#define SYMS_START block_copy_start
109
110static long text_scnptr;
111static long data_scnptr;
112
c8b14b5f
RS
113static long coff_offset;
114
7dd63af1
RS
115static int pagemask;
116
117/* Correct an int which is the bit pattern of a pointer to a byte
118 into an int which is the number of a byte.
119 This is a no-op on ordinary machines, but not on all. */
120
7dd63af1 121#define ADDR_CORRECT(x) ((char *)(x) - (char*)0)
7dd63af1 122
d7306fe6 123#include <setjmp.h>
2d30a233
RM
124#include "lisp.h"
125
5f2f0bc1
EZ
126static void
127report_error (const char *file, int fd)
7dd63af1
RS
128{
129 if (fd)
130 close (fd);
2d30a233 131 report_file_error ("Cannot unexec", Fcons (build_string (file), Qnil));
7dd63af1 132}
7dd63af1
RS
133
134#define ERROR0(msg) report_error_1 (new, msg, 0, 0); return -1
135#define ERROR1(msg,x) report_error_1 (new, msg, x, 0); return -1
136#define ERROR2(msg,x,y) report_error_1 (new, msg, x, y); return -1
137
5f2f0bc1
EZ
138static void
139report_error_1 (int fd, const char *msg, int a1, int a2)
7dd63af1
RS
140{
141 close (fd);
7dd63af1 142 error (msg, a1, a2);
7dd63af1
RS
143}
144\f
c3911ead 145static int make_hdr (int, int, const char *, const char *);
5f2f0bc1
EZ
146static int copy_text_and_data (int, int);
147static int copy_sym (int, int, const char *, const char *);
148static void mark_x (const char *);
7dd63af1 149
7dd63af1
RS
150/* ****************************************************************
151 * make_hdr
152 *
153 * Make the header in the new a.out from the header in core.
154 * Modify the text and data sizes.
155 */
156static int
dd5ecd6b
DN
157make_hdr (int new, int a_out,
158 const char *a_name, const char *new_name)
7dd63af1 159{
7dd63af1
RS
160 auto struct scnhdr f_thdr; /* Text section header */
161 auto struct scnhdr f_dhdr; /* Data section header */
162 auto struct scnhdr f_bhdr; /* Bss section header */
163 auto struct scnhdr scntemp; /* Temporary section header */
164 register int scns;
dd5ecd6b
DN
165 unsigned int bss_start;
166 unsigned int data_start;
7dd63af1
RS
167
168 pagemask = getpagesize () - 1;
169
170 /* Adjust text/data boundary. */
7dd63af1 171 data_start = (int) start_of_data ();
7dd63af1 172 data_start = ADDR_CORRECT (data_start);
7dd63af1 173 data_start = data_start & ~pagemask; /* (Down) to page boundary. */
7dd63af1 174
dd5ecd6b
DN
175 bss_start = ADDR_CORRECT (sbrk (0)) + pagemask;
176 bss_start &= ~ pagemask;
7dd63af1
RS
177
178 if (data_start > bss_start) /* Can't have negative data size. */
179 {
180 ERROR2 ("unexec: data_start (%u) can't be greater than bss_start (%u)",
181 data_start, bss_start);
182 }
183
c8b14b5f
RS
184 coff_offset = 0L; /* stays zero, except in DJGPP */
185
7dd63af1
RS
186 /* Salvage as much info from the existing file as possible */
187 if (a_out >= 0)
188 {
c8b14b5f 189#ifdef MSDOS
c8b14b5f
RS
190 /* Support the coff-go32-exe format with a prepended stub, since
191 this is what GCC 2.8.0 and later generates by default in DJGPP. */
192 unsigned short mz_header[3];
193
194 if (read (a_out, &mz_header, sizeof (mz_header)) != sizeof (mz_header))
195 {
196 PERROR (a_name);
197 }
198 if (mz_header[0] == 0x5a4d || mz_header[0] == 0x4d5a) /* "MZ" or "ZM" */
199 {
200 coff_offset = (long)mz_header[2] * 512L;
201 if (mz_header[1])
202 coff_offset += (long)mz_header[1] - 512L;
203 lseek (a_out, coff_offset, 0);
204 }
205 else
206 lseek (a_out, 0L, 0);
c8b14b5f 207#endif /* MSDOS */
7dd63af1
RS
208 if (read (a_out, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr))
209 {
210 PERROR (a_name);
211 }
212 block_copy_start += sizeof (f_hdr);
213 if (f_hdr.f_opthdr > 0)
214 {
215 if (read (a_out, &f_ohdr, sizeof (f_ohdr)) != sizeof (f_ohdr))
216 {
217 PERROR (a_name);
218 }
219 block_copy_start += sizeof (f_ohdr);
220 }
221 /* Loop through section headers, copying them in */
c8b14b5f 222 lseek (a_out, coff_offset + sizeof (f_hdr) + f_hdr.f_opthdr, 0);
7dd63af1
RS
223 for (scns = f_hdr.f_nscns; scns > 0; scns--) {
224 if (read (a_out, &scntemp, sizeof (scntemp)) != sizeof (scntemp))
225 {
226 PERROR (a_name);
227 }
228 if (scntemp.s_scnptr > 0L)
229 {
230 if (block_copy_start < scntemp.s_scnptr + scntemp.s_size)
231 block_copy_start = scntemp.s_scnptr + scntemp.s_size;
232 }
233 if (strcmp (scntemp.s_name, ".text") == 0)
234 {
235 f_thdr = scntemp;
236 }
237 else if (strcmp (scntemp.s_name, ".data") == 0)
238 {
239 f_dhdr = scntemp;
240 }
241 else if (strcmp (scntemp.s_name, ".bss") == 0)
242 {
243 f_bhdr = scntemp;
244 }
245 }
246 }
247 else
248 {
249 ERROR0 ("can't build a COFF file from scratch yet");
250 }
251
252 /* Now we alter the contents of all the f_*hdr variables
253 to correspond to what we want to dump. */
254
7dd63af1 255 f_hdr.f_flags |= (F_RELFLG | F_EXEC);
7dd63af1 256 f_ohdr.dsize = bss_start - f_ohdr.data_start;
dd5ecd6b 257 f_ohdr.bsize = 0;
7dd63af1
RS
258 f_thdr.s_size = f_ohdr.tsize;
259 f_thdr.s_scnptr = sizeof (f_hdr) + sizeof (f_ohdr);
260 f_thdr.s_scnptr += (f_hdr.f_nscns) * (sizeof (f_thdr));
7dd63af1 261 lnnoptr = f_thdr.s_lnnoptr;
7dd63af1 262 text_scnptr = f_thdr.s_scnptr;
7dd63af1 263 f_dhdr.s_paddr = f_ohdr.data_start;
7dd63af1
RS
264 f_dhdr.s_vaddr = f_ohdr.data_start;
265 f_dhdr.s_size = f_ohdr.dsize;
266 f_dhdr.s_scnptr = f_thdr.s_scnptr + f_thdr.s_size;
7dd63af1 267 data_scnptr = f_dhdr.s_scnptr;
7dd63af1 268 f_bhdr.s_paddr = f_ohdr.data_start + f_ohdr.dsize;
7dd63af1
RS
269 f_bhdr.s_vaddr = f_ohdr.data_start + f_ohdr.dsize;
270 f_bhdr.s_size = f_ohdr.bsize;
271 f_bhdr.s_scnptr = 0L;
7dd63af1 272 bias = f_dhdr.s_scnptr + f_dhdr.s_size - block_copy_start;
7dd63af1
RS
273
274 if (f_hdr.f_symptr > 0L)
275 {
276 f_hdr.f_symptr += bias;
277 }
278
279 if (f_thdr.s_lnnoptr > 0L)
280 {
281 f_thdr.s_lnnoptr += bias;
282 }
283
7dd63af1
RS
284 if (write (new, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr))
285 {
286 PERROR (new_name);
287 }
288
289 if (write (new, &f_ohdr, sizeof (f_ohdr)) != sizeof (f_ohdr))
290 {
291 PERROR (new_name);
292 }
293
7dd63af1
RS
294 if (write (new, &f_thdr, sizeof (f_thdr)) != sizeof (f_thdr))
295 {
296 PERROR (new_name);
297 }
298
299 if (write (new, &f_dhdr, sizeof (f_dhdr)) != sizeof (f_dhdr))
300 {
301 PERROR (new_name);
302 }
303
304 if (write (new, &f_bhdr, sizeof (f_bhdr)) != sizeof (f_bhdr))
305 {
306 PERROR (new_name);
307 }
308
7dd63af1
RS
309 return (0);
310
7dd63af1
RS
311}
312\f
5f2f0bc1
EZ
313void
314write_segment (int new, const char *ptr, const char *end)
730f4d72
EZ
315{
316 register int i, nwrite, ret;
730f4d72
EZ
317 /* This is the normal amount to write at once.
318 It is the size of block that NFS uses. */
319 int writesize = 1 << 13;
320 int pagesize = getpagesize ();
321 char zeros[1 << 13];
322
72af86bd 323 memset (zeros, 0, sizeof (zeros));
730f4d72
EZ
324
325 for (i = 0; ptr < end;)
326 {
327 /* Distance to next multiple of writesize. */
328 nwrite = (((int) ptr + writesize) & -writesize) - (int) ptr;
329 /* But not beyond specified end. */
330 if (nwrite > end - ptr) nwrite = end - ptr;
331 ret = write (new, ptr, nwrite);
332 /* If write gets a page fault, it means we reached
333 a gap between the old text segment and the old data segment.
334 This gap has probably been remapped into part of the text segment.
335 So write zeros for it. */
336 if (ret == -1
337#ifdef EFAULT
338 && errno == EFAULT
339#endif
340 )
341 {
342 /* Write only a page of zeros at once,
2b34df4e 343 so that we don't overshoot the start
730f4d72
EZ
344 of the valid memory in the old data segment. */
345 if (nwrite > pagesize)
346 nwrite = pagesize;
347 write (new, zeros, nwrite);
348 }
730f4d72
EZ
349 i += nwrite;
350 ptr += nwrite;
351 }
352}
7dd63af1
RS
353/* ****************************************************************
354 * copy_text_and_data
355 *
356 * Copy the text and data segments from memory to the new a.out
357 */
358static int
5f2f0bc1 359copy_text_and_data (int new, int a_out)
7dd63af1
RS
360{
361 register char *end;
362 register char *ptr;
363
8eb2807f 364#ifdef MSDOS
8eb2807f
RS
365 /* Dump the original table of exception handlers, not the one
366 where our exception hooks are registered. */
367 __djgpp_exception_toggle ();
c17a2102
KH
368
369 /* Switch off startup flags that might have been set at runtime
370 and which might change the way that dumped Emacs works. */
371 save_djgpp_startup_flags = _crt0_startup_flags;
372 _crt0_startup_flags &= ~(_CRT0_FLAG_NO_LFN | _CRT0_FLAG_NEARPTR);
8eb2807f
RS
373#endif
374
7dd63af1
RS
375 lseek (new, (long) text_scnptr, 0);
376 ptr = (char *) f_ohdr.text_start;
7dd63af1
RS
377 end = ptr + f_ohdr.tsize;
378 write_segment (new, ptr, end);
379
380 lseek (new, (long) data_scnptr, 0);
381 ptr = (char *) f_ohdr.data_start;
382 end = ptr + f_ohdr.dsize;
383 write_segment (new, ptr, end);
384
8eb2807f 385#ifdef MSDOS
8eb2807f
RS
386 /* Restore our exception hooks. */
387 __djgpp_exception_toggle ();
c17a2102
KH
388
389 /* Restore the startup flags. */
390 _crt0_startup_flags = save_djgpp_startup_flags;
8eb2807f 391#endif
8eb2807f 392
7dd63af1
RS
393
394 return 0;
395}
7dd63af1
RS
396\f
397/* ****************************************************************
398 * copy_sym
399 *
400 * Copy the relocation information and symbol table from the a.out to the new
401 */
402static int
5f2f0bc1 403copy_sym (int new, int a_out, const char *a_name, const char *new_name)
7dd63af1
RS
404{
405 char page[1024];
406 int n;
407
408 if (a_out < 0)
409 return 0;
410
7dd63af1
RS
411 if (SYMS_START == 0L)
412 return 0;
7dd63af1 413
7dd63af1 414 if (lnnoptr) /* if there is line number info */
c8b14b5f 415 lseek (a_out, coff_offset + lnnoptr, 0); /* start copying from there */
7dd63af1 416 else
c8b14b5f 417 lseek (a_out, coff_offset + SYMS_START, 0); /* Position a.out to symtab. */
7dd63af1
RS
418
419 while ((n = read (a_out, page, sizeof page)) > 0)
420 {
421 if (write (new, page, n) != n)
422 {
423 PERROR (new_name);
424 }
425 }
426 if (n < 0)
427 {
428 PERROR (a_name);
429 }
430 return 0;
431}
432\f
433/* ****************************************************************
434 * mark_x
435 *
eb8c3be9 436 * After successfully building the new a.out, mark it executable
7dd63af1
RS
437 */
438static void
5f2f0bc1 439mark_x (const char *name)
7dd63af1
RS
440{
441 struct stat sbuf;
442 int um;
443 int new = 0; /* for PERROR */
444
445 um = umask (777);
446 umask (um);
447 if (stat (name, &sbuf) == -1)
448 {
449 PERROR (name);
450 }
451 sbuf.st_mode |= 0111 & ~um;
452 if (chmod (name, sbuf.st_mode) == -1)
453 PERROR (name);
454}
455\f
7dd63af1
RS
456
457/*
458 * If the COFF file contains a symbol table and a line number section,
459 * then any auxiliary entries that have values for x_lnnoptr must
460 * be adjusted by the amount that the line number section has moved
461 * in the file (bias computed in make_hdr). The #@$%&* designers of
462 * the auxiliary entry structures used the absolute file offsets for
463 * the line number entry rather than an offset from the start of the
464 * line number section!
465 *
466 * When I figure out how to scan through the symbol table and pick out
467 * the auxiliary entries that need adjustment, this routine will
468 * be fixed. As it is now, all such entries are wrong and sdb
469 * will complain. Fred Fish, UniSoft Systems Inc.
470 */
471
472/* This function is probably very slow. Instead of reopening the new
473 file for input and output it should copy from the old to the new
474 using the two descriptors already open (WRITEDESC and READDESC).
475 Instead of reading one small structure at a time it should use
476 a reasonable size buffer. But I don't have time to work on such
477 things, so I am installing it as submitted to me. -- RMS. */
478
5f2f0bc1
EZ
479int
480adjust_lnnoptrs (int writedesc, int readdesc, const char *new_name)
7dd63af1
RS
481{
482 register int nsyms;
483 register int new;
7dd63af1
RS
484 struct syment symentry;
485 union auxent auxentry;
7dd63af1
RS
486
487 if (!lnnoptr || !f_hdr.f_symptr)
488 return 0;
489
3680bdc6
RS
490#ifdef MSDOS
491 if ((new = writedesc) < 0)
492#else
2d30a233 493 if ((new = open (new_name, O_RDWR)) < 0)
3680bdc6 494#endif
7dd63af1
RS
495 {
496 PERROR (new_name);
497 return -1;
498 }
499
500 lseek (new, f_hdr.f_symptr, 0);
501 for (nsyms = 0; nsyms < f_hdr.f_nsyms; nsyms++)
502 {
503 read (new, &symentry, SYMESZ);
504 if (symentry.n_numaux)
505 {
506 read (new, &auxentry, AUXESZ);
507 nsyms++;
1ba3de00
RS
508 if (ISFCN (symentry.n_type) || symentry.n_type == 0x2400)
509 {
510 auxentry.x_sym.x_fcnary.x_fcn.x_lnnoptr += bias;
511 lseek (new, -AUXESZ, 1);
512 write (new, &auxentry, AUXESZ);
513 }
7dd63af1
RS
514 }
515 }
3680bdc6 516#ifndef MSDOS
7dd63af1 517 close (new);
3680bdc6
RS
518#endif
519 return 0;
7dd63af1
RS
520}
521
730f4d72
EZ
522/* ****************************************************************
523 * unexec
524 *
525 * driving logic.
526 */
381259ef 527void
dd5ecd6b 528unexec (const char *new_name, const char *a_name)
730f4d72 529{
5f2f0bc1 530 int new = -1, a_out = -1;
730f4d72
EZ
531
532 if (a_name && (a_out = open (a_name, O_RDONLY)) < 0)
533 {
534 PERROR (a_name);
535 }
536 if ((new = creat (new_name, 0666)) < 0)
537 {
538 PERROR (new_name);
539 }
540
dd5ecd6b 541 if (make_hdr (new, a_out, a_name, new_name) < 0
730f4d72
EZ
542 || copy_text_and_data (new, a_out) < 0
543 || copy_sym (new, a_out, a_name, new_name) < 0
730f4d72 544 || adjust_lnnoptrs (new, a_out, new_name) < 0
730f4d72
EZ
545 )
546 {
547 close (new);
fffe2e14 548 return;
730f4d72
EZ
549 }
550
551 close (new);
552 if (a_out >= 0)
553 close (a_out);
554 mark_x (new_name);
730f4d72
EZ
555}
556
7dd63af1 557#endif /* not CANNOT_DUMP */