merge from trunk
[emacs.git] / src / unexaix.c
blob204f6cf4ad3787c32d01596a349889f9c1e20bc4
1 /* Dump an executable image.
2 Copyright (C) 1985-1988, 1999, 2001-2013 Free Software Foundation,
3 Inc.
5 This file is part of GNU Emacs.
7 GNU Emacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
21 In other words, you are welcome to use, share and improve this program.
22 You are forbidden to forbid anyone else to use, share and improve
23 what you give them. Help stamp out software-hoarding! */
26 /* Originally based on the COFF unexec.c by Spencer W. Thomas.
28 * Subsequently hacked on by
29 * Bill Mann <Bill_Man@praxisint.com>
30 * Andrew Vignaux <Andrew.Vignaux@comp.vuw.ac.nz>
31 * Mike Sperber <sperber@informatik.uni-tuebingen.de>
33 * Synopsis:
34 * unexec (const char *new_name, const *old_name);
36 * Takes a snapshot of the program and makes an a.out format file in the
37 * file named by the string argument new_name.
38 * If a_name is non-NULL, the symbol table will be taken from the given file.
39 * On some machines, an existing a_name file is required.
43 #include <config.h>
44 #include "unexec.h"
45 #include "lisp.h"
47 #define PERROR(file) report_error (file, new)
48 #include <a.out.h>
49 /* Define getpagesize () if the system does not.
50 Note that this may depend on symbols defined in a.out.h
52 #include "getpagesize.h"
54 #include <sys/types.h>
55 #include <inttypes.h>
56 #include <stdarg.h>
57 #include <stdio.h>
58 #include <sys/stat.h>
59 #include <errno.h>
60 #include <unistd.h>
61 #include <fcntl.h>
63 extern char _data[];
64 extern char _text[];
66 #include <filehdr.h>
67 #include <aouthdr.h>
68 #include <scnhdr.h>
69 #include <syms.h>
71 static struct filehdr f_hdr; /* File header */
72 static struct aouthdr f_ohdr; /* Optional file header (a.out) */
73 static off_t bias; /* Bias to add for growth */
74 static off_t lnnoptr; /* Pointer to line-number info within file */
76 static off_t text_scnptr;
77 static off_t data_scnptr;
78 #define ALIGN(val, pwr) (((val) + ((1L<<(pwr))-1)) & ~((1L<<(pwr))-1))
79 static off_t load_scnptr;
80 static off_t orig_load_scnptr;
81 static off_t orig_data_scnptr;
82 static int unrelocate_symbols (int, int, const char *, const char *);
84 #ifndef MAX_SECTIONS
85 #define MAX_SECTIONS 10
86 #endif
88 static int adjust_lnnoptrs (int, int, const char *);
90 static int pagemask;
92 #include "lisp.h"
94 static _Noreturn void
95 report_error (const char *file, int fd)
97 if (fd)
99 int failed_errno = errno;
100 close (fd);
101 errno = failed_errno;
103 report_file_error ("Cannot unexec", Fcons (build_string (file), Qnil));
106 #define ERROR0(msg) report_error_1 (new, msg)
107 #define ERROR1(msg,x) report_error_1 (new, msg, x)
108 #define ERROR2(msg,x,y) report_error_1 (new, msg, x, y)
110 static _Noreturn void ATTRIBUTE_FORMAT_PRINTF (2, 3)
111 report_error_1 (int fd, const char *msg, ...)
113 va_list ap;
114 close (fd);
115 va_start (ap, msg);
116 verror (msg, ap);
117 va_end (ap);
120 static int make_hdr (int, int, const char *, const char *);
121 static void mark_x (const char *);
122 static int copy_text_and_data (int);
123 static int copy_sym (int, int, const char *, const char *);
124 static void write_segment (int, char *, char *);
126 /* ****************************************************************
127 * unexec
129 * driving logic.
131 void
132 unexec (const char *new_name, const char *a_name)
134 int new = -1, a_out = -1;
136 if (a_name && (a_out = emacs_open (a_name, O_RDONLY, 0)) < 0)
138 PERROR (a_name);
140 if ((new = emacs_open (new_name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0)
142 PERROR (new_name);
144 if (make_hdr (new, a_out,
145 a_name, new_name) < 0
146 || copy_text_and_data (new) < 0
147 || copy_sym (new, a_out, a_name, new_name) < 0
148 || adjust_lnnoptrs (new, a_out, new_name) < 0
149 || unrelocate_symbols (new, a_out, a_name, new_name) < 0)
151 close (new);
152 return;
155 close (new);
156 if (a_out >= 0)
157 close (a_out);
158 mark_x (new_name);
161 /* ****************************************************************
162 * make_hdr
164 * Make the header in the new a.out from the header in core.
165 * Modify the text and data sizes.
167 static int
168 make_hdr (int new, int a_out,
169 const char *a_name, const char *new_name)
171 int scns;
172 uintptr_t bss_start;
173 uintptr_t data_start;
175 struct scnhdr section[MAX_SECTIONS];
176 struct scnhdr * f_thdr; /* Text section header */
177 struct scnhdr * f_dhdr; /* Data section header */
178 struct scnhdr * f_bhdr; /* Bss section header */
179 struct scnhdr * f_lhdr; /* Loader section header */
180 struct scnhdr * f_tchdr; /* Typechk section header */
181 struct scnhdr * f_dbhdr; /* Debug section header */
182 struct scnhdr * f_xhdr; /* Except section header */
184 load_scnptr = orig_load_scnptr = lnnoptr = 0;
185 pagemask = getpagesize () - 1;
187 /* Adjust text/data boundary. */
188 data_start = (uintptr_t) _data;
190 data_start = data_start & ~pagemask; /* (Down) to page boundary. */
192 bss_start = (uintptr_t) sbrk (0) + pagemask;
193 bss_start &= ~ pagemask;
195 if (data_start > bss_start) /* Can't have negative data size. */
197 ERROR2 (("unexec: data_start (0x%"PRIxPTR
198 ") can't be greater than bss_start (0x%"PRIxPTR")"),
199 data_start, bss_start);
202 /* Salvage as much info from the existing file as possible */
203 f_thdr = NULL; f_dhdr = NULL; f_bhdr = NULL;
204 f_lhdr = NULL; f_tchdr = NULL; f_dbhdr = NULL; f_xhdr = NULL;
205 if (a_out >= 0)
207 if (read (a_out, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr))
209 PERROR (a_name);
211 if (f_hdr.f_opthdr > 0)
213 if (read (a_out, &f_ohdr, sizeof (f_ohdr)) != sizeof (f_ohdr))
215 PERROR (a_name);
218 if (f_hdr.f_nscns > MAX_SECTIONS)
220 ERROR0 ("unexec: too many section headers -- increase MAX_SECTIONS");
222 /* Loop through section headers */
223 for (scns = 0; scns < f_hdr.f_nscns; scns++) {
224 struct scnhdr *s = &section[scns];
225 if (read (a_out, s, sizeof (*s)) != sizeof (*s))
227 PERROR (a_name);
230 #define CHECK_SCNHDR(ptr, name, flags) \
231 if (strcmp (s->s_name, name) == 0) { \
232 if (s->s_flags != flags) { \
233 fprintf (stderr, "unexec: %lx flags where %x expected in %s section.\n", \
234 (unsigned long)s->s_flags, flags, name); \
236 if (ptr) { \
237 fprintf (stderr, "unexec: duplicate section header for section %s.\n", \
238 name); \
240 ptr = s; \
242 CHECK_SCNHDR (f_thdr, _TEXT, STYP_TEXT);
243 CHECK_SCNHDR (f_dhdr, _DATA, STYP_DATA);
244 CHECK_SCNHDR (f_bhdr, _BSS, STYP_BSS);
245 CHECK_SCNHDR (f_lhdr, _LOADER, STYP_LOADER);
246 CHECK_SCNHDR (f_dbhdr, _DEBUG, STYP_DEBUG);
247 CHECK_SCNHDR (f_tchdr, _TYPCHK, STYP_TYPCHK);
248 CHECK_SCNHDR (f_xhdr, _EXCEPT, STYP_EXCEPT);
251 if (f_thdr == 0)
253 ERROR1 ("unexec: couldn't find \"%s\" section", (int) _TEXT);
255 if (f_dhdr == 0)
257 ERROR1 ("unexec: couldn't find \"%s\" section", (int) _DATA);
259 if (f_bhdr == 0)
261 ERROR1 ("unexec: couldn't find \"%s\" section", (int) _BSS);
264 else
266 ERROR0 ("can't build a COFF file from scratch yet");
268 orig_data_scnptr = f_dhdr->s_scnptr;
269 orig_load_scnptr = f_lhdr ? f_lhdr->s_scnptr : 0;
271 /* Now we alter the contents of all the f_*hdr variables
272 to correspond to what we want to dump. */
274 /* Indicate that the reloc information is no longer valid for ld (bind);
275 we only update it enough to fake out the exec-time loader. */
276 f_hdr.f_flags |= (F_RELFLG | F_EXEC);
278 f_ohdr.dsize = bss_start - f_ohdr.data_start;
279 f_ohdr.bsize = 0;
281 f_dhdr->s_size = f_ohdr.dsize;
282 f_bhdr->s_size = f_ohdr.bsize;
283 f_bhdr->s_paddr = f_ohdr.data_start + f_ohdr.dsize;
284 f_bhdr->s_vaddr = f_ohdr.data_start + f_ohdr.dsize;
286 /* fix scnptr's */
288 off_t ptr = section[0].s_scnptr;
290 bias = -1;
291 for (scns = 0; scns < f_hdr.f_nscns; scns++)
293 struct scnhdr *s = &section[scns];
295 if (s->s_flags & STYP_PAD) /* .pad sections omitted in AIX 4.1 */
298 * the text_start should probably be o_algntext but that doesn't
299 * seem to change
301 if (f_ohdr.text_start != 0) /* && scns != 0 */
303 s->s_size = 512 - (ptr % 512);
304 if (s->s_size == 512)
305 s->s_size = 0;
307 s->s_scnptr = ptr;
309 else if (s->s_flags & STYP_DATA)
310 s->s_scnptr = ptr;
311 else if (!(s->s_flags & (STYP_TEXT | STYP_BSS)))
313 if (bias == -1) /* if first section after bss */
314 bias = ptr - s->s_scnptr;
316 s->s_scnptr += bias;
317 ptr = s->s_scnptr;
320 ptr = ptr + s->s_size;
324 /* fix other pointers */
325 for (scns = 0; scns < f_hdr.f_nscns; scns++)
327 struct scnhdr *s = &section[scns];
329 if (s->s_relptr != 0)
331 s->s_relptr += bias;
333 if (s->s_lnnoptr != 0)
335 if (lnnoptr == 0) lnnoptr = s->s_lnnoptr;
336 s->s_lnnoptr += bias;
340 if (f_hdr.f_symptr > 0L)
342 f_hdr.f_symptr += bias;
345 text_scnptr = f_thdr->s_scnptr;
346 data_scnptr = f_dhdr->s_scnptr;
347 load_scnptr = f_lhdr ? f_lhdr->s_scnptr : 0;
349 if (write (new, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr))
351 PERROR (new_name);
354 if (f_hdr.f_opthdr > 0)
356 if (write (new, &f_ohdr, sizeof (f_ohdr)) != sizeof (f_ohdr))
358 PERROR (new_name);
362 for (scns = 0; scns < f_hdr.f_nscns; scns++) {
363 struct scnhdr *s = &section[scns];
364 if (write (new, s, sizeof (*s)) != sizeof (*s))
366 PERROR (new_name);
370 return (0);
373 /* ****************************************************************
376 * Copy the text and data segments from memory to the new a.out
378 static int
379 copy_text_and_data (int new)
381 char *end;
382 char *ptr;
384 lseek (new, text_scnptr, SEEK_SET);
385 ptr = _text + text_scnptr;
386 end = ptr + f_ohdr.tsize;
387 write_segment (new, ptr, end);
389 lseek (new, data_scnptr, SEEK_SET);
390 ptr = (char *) f_ohdr.data_start;
391 end = ptr + f_ohdr.dsize;
392 write_segment (new, ptr, end);
394 return 0;
397 #define UnexBlockSz (1<<12) /* read/write block size */
398 static void
399 write_segment (int new, char *ptr, char *end)
401 int i, nwrite, ret;
402 char zeros[UnexBlockSz];
404 for (i = 0; ptr < end;)
406 /* distance to next block. */
407 nwrite = (((int) ptr + UnexBlockSz) & -UnexBlockSz) - (int) ptr;
408 /* But not beyond specified end. */
409 if (nwrite > end - ptr) nwrite = end - ptr;
410 ret = write (new, ptr, nwrite);
411 /* If write gets a page fault, it means we reached
412 a gap between the old text segment and the old data segment.
413 This gap has probably been remapped into part of the text segment.
414 So write zeros for it. */
415 if (ret == -1 && errno == EFAULT)
417 memset (zeros, 0, nwrite);
418 write (new, zeros, nwrite);
420 else if (nwrite != ret)
422 int write_errno = errno;
423 char buf[1000];
424 void *addr = ptr;
425 sprintf (buf,
426 "unexec write failure: addr %p, fileno %d, size 0x%x, wrote 0x%x, errno %d",
427 addr, new, nwrite, ret, errno);
428 errno = write_errno;
429 PERROR (buf);
431 i += nwrite;
432 ptr += nwrite;
436 /* ****************************************************************
437 * copy_sym
439 * Copy the relocation information and symbol table from the a.out to the new
441 static int
442 copy_sym (int new, int a_out, const char *a_name, const char *new_name)
444 char page[UnexBlockSz];
445 int n;
447 if (a_out < 0)
448 return 0;
450 if (orig_load_scnptr == 0L)
451 return 0;
453 if (lnnoptr && lnnoptr < orig_load_scnptr) /* if there is line number info */
454 lseek (a_out, lnnoptr, SEEK_SET); /* start copying from there */
455 else
456 lseek (a_out, orig_load_scnptr, SEEK_SET); /* Position a.out to symtab. */
458 while ((n = read (a_out, page, sizeof page)) > 0)
460 if (write (new, page, n) != n)
462 PERROR (new_name);
465 if (n < 0)
467 PERROR (a_name);
469 return 0;
472 /* ****************************************************************
473 * mark_x
475 * After successfully building the new a.out, mark it executable
477 static void
478 mark_x (const char *name)
480 struct stat sbuf;
481 int um;
482 int new = 0; /* for PERROR */
484 um = umask (777);
485 umask (um);
486 if (stat (name, &sbuf) == -1)
488 PERROR (name);
490 sbuf.st_mode |= 0111 & ~um;
491 if (chmod (name, sbuf.st_mode) == -1)
492 PERROR (name);
495 static int
496 adjust_lnnoptrs (int writedesc, int readdesc, const char *new_name)
498 int nsyms;
499 int naux;
500 int new;
501 struct syment symentry;
502 union auxent auxentry;
504 if (!lnnoptr || !f_hdr.f_symptr)
505 return 0;
507 if ((new = emacs_open (new_name, O_RDWR, 0)) < 0)
509 PERROR (new_name);
510 return -1;
513 lseek (new, f_hdr.f_symptr, SEEK_SET);
514 for (nsyms = 0; nsyms < f_hdr.f_nsyms; nsyms++)
516 read (new, &symentry, SYMESZ);
517 if (symentry.n_sclass == C_BINCL || symentry.n_sclass == C_EINCL)
519 symentry.n_value += bias;
520 lseek (new, -SYMESZ, SEEK_CUR);
521 write (new, &symentry, SYMESZ);
524 for (naux = symentry.n_numaux; naux-- != 0; )
526 read (new, &auxentry, AUXESZ);
527 nsyms++;
528 if (naux != 0 /* skip csect auxentry (last entry) */
529 && (symentry.n_sclass == C_EXT || symentry.n_sclass == C_HIDEXT))
531 auxentry.x_sym.x_fcnary.x_fcn.x_lnnoptr += bias;
532 lseek (new, -AUXESZ, SEEK_CUR);
533 write (new, &auxentry, AUXESZ);
537 close (new);
539 return 0;
542 static int
543 unrelocate_symbols (int new, int a_out,
544 const char *a_name, const char *new_name)
546 int i;
547 LDHDR ldhdr;
548 LDREL ldrel;
549 off_t t_reloc = (intptr_t) _text - f_ohdr.text_start;
550 #ifndef ALIGN_DATA_RELOC
551 off_t d_reloc = (intptr_t) _data - f_ohdr.data_start;
552 #else
553 /* This worked (and was needed) before AIX 4.2.
554 I have no idea why. -- Mike */
555 off_t d_reloc = (intptr_t) _data - ALIGN (f_ohdr.data_start, 2);
556 #endif
557 int * p;
559 if (load_scnptr == 0)
560 return 0;
562 lseek (a_out, orig_load_scnptr, SEEK_SET);
563 if (read (a_out, &ldhdr, sizeof (ldhdr)) != sizeof (ldhdr))
565 PERROR (new_name);
568 #define SYMNDX_TEXT 0
569 #define SYMNDX_DATA 1
570 #define SYMNDX_BSS 2
572 for (i = 0; i < ldhdr.l_nreloc; i++)
574 lseek (a_out,
575 orig_load_scnptr + LDHDRSZ + LDSYMSZ*ldhdr.l_nsyms + LDRELSZ*i,
576 SEEK_SET);
578 if (read (a_out, &ldrel, LDRELSZ) != LDRELSZ)
580 PERROR (a_name);
583 /* move the BSS loader symbols to the DATA segment */
584 if (ldrel.l_symndx == SYMNDX_BSS)
586 ldrel.l_symndx = SYMNDX_DATA;
588 lseek (new,
589 load_scnptr + LDHDRSZ + LDSYMSZ*ldhdr.l_nsyms + LDRELSZ*i,
590 SEEK_SET);
592 if (write (new, &ldrel, LDRELSZ) != LDRELSZ)
594 PERROR (new_name);
598 if (ldrel.l_rsecnm == f_ohdr.o_sndata)
600 int orig_int;
602 lseek (a_out,
603 orig_data_scnptr + (ldrel.l_vaddr - f_ohdr.data_start),
604 SEEK_SET);
606 if (read (a_out, (void *) &orig_int, sizeof (orig_int))
607 != sizeof (orig_int))
609 PERROR (a_name);
612 p = (int *) (ldrel.l_vaddr + d_reloc);
614 switch (ldrel.l_symndx) {
615 case SYMNDX_TEXT:
616 orig_int = * p - t_reloc;
617 break;
619 case SYMNDX_DATA:
620 case SYMNDX_BSS:
621 orig_int = * p - d_reloc;
622 break;
625 if (orig_int != * p)
627 lseek (new,
628 data_scnptr + (ldrel.l_vaddr - f_ohdr.data_start),
629 SEEK_SET);
630 if (write (new, (void *) &orig_int, sizeof (orig_int))
631 != sizeof (orig_int))
633 PERROR (new_name);
638 return 0;