ChangeLog whitespace trivia
[emacs.git] / src / unexaix.c
blob92ebd2e3cebf019e9eaeb1efb11e13e256d53d0a
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"
46 #define PERROR(file) report_error (file, new)
47 #include <a.out.h>
48 /* Define getpagesize () if the system does not.
49 Note that this may depend on symbols defined in a.out.h
51 #include "getpagesize.h"
53 #include <sys/types.h>
54 #include <stdio.h>
55 #include <sys/stat.h>
56 #include <errno.h>
57 #include <unistd.h>
58 #include <fcntl.h>
60 #include "mem-limits.h"
62 char *start_of_text (void); /* Start of text */
64 extern int _data;
65 extern int _text;
67 #include <filehdr.h>
68 #include <aouthdr.h>
69 #include <scnhdr.h>
70 #include <syms.h>
72 static struct filehdr f_hdr; /* File header */
73 static struct aouthdr f_ohdr; /* Optional file header (a.out) */
74 static long bias; /* Bias to add for growth */
75 static long lnnoptr; /* Pointer to line-number info within file */
77 static long text_scnptr;
78 static long data_scnptr;
79 #define ALIGN(val, pwr) (((val) + ((1L<<(pwr))-1)) & ~((1L<<(pwr))-1))
80 static long load_scnptr;
81 static long orig_load_scnptr;
82 static long orig_data_scnptr;
83 static int unrelocate_symbols (int, int, const char *, const char *);
85 #ifndef MAX_SECTIONS
86 #define MAX_SECTIONS 10
87 #endif
89 static int adjust_lnnoptrs (int, int, const char *);
91 static int pagemask;
93 #include "lisp.h"
95 static void
96 report_error (const char *file, int fd)
98 if (fd)
99 close (fd);
100 report_file_error ("Cannot unexec", Fcons (build_string (file), Qnil));
103 #define ERROR0(msg) report_error_1 (new, msg, 0, 0); return -1
104 #define ERROR1(msg,x) report_error_1 (new, msg, x, 0); return -1
105 #define ERROR2(msg,x,y) report_error_1 (new, msg, x, y); return -1
107 static void
108 report_error_1 (int fd, const char *msg, int a1, int a2)
110 close (fd);
111 error (msg, a1, a2);
114 static int make_hdr (int, int, const char *, const char *);
115 static void mark_x (const char *);
116 static int copy_text_and_data (int);
117 static int copy_sym (int, int, const char *, const char *);
118 static void write_segment (int, char *, char *);
120 /* ****************************************************************
121 * unexec
123 * driving logic.
125 void
126 unexec (const char *new_name, const char *a_name)
128 int new = -1, a_out = -1;
130 if (a_name && (a_out = open (a_name, O_RDONLY)) < 0)
132 PERROR (a_name);
134 if ((new = creat (new_name, 0666)) < 0)
136 PERROR (new_name);
138 if (make_hdr (new, a_out,
139 a_name, new_name) < 0
140 || copy_text_and_data (new) < 0
141 || copy_sym (new, a_out, a_name, new_name) < 0
142 || adjust_lnnoptrs (new, a_out, new_name) < 0
143 || unrelocate_symbols (new, a_out, a_name, new_name) < 0)
145 close (new);
146 return;
149 close (new);
150 if (a_out >= 0)
151 close (a_out);
152 mark_x (new_name);
155 /* ****************************************************************
156 * make_hdr
158 * Make the header in the new a.out from the header in core.
159 * Modify the text and data sizes.
161 static int
162 make_hdr (int new, int a_out,
163 const char *a_name, const char *new_name)
165 int scns;
166 unsigned int bss_start;
167 unsigned int data_start;
169 struct scnhdr section[MAX_SECTIONS];
170 struct scnhdr * f_thdr; /* Text section header */
171 struct scnhdr * f_dhdr; /* Data section header */
172 struct scnhdr * f_bhdr; /* Bss section header */
173 struct scnhdr * f_lhdr; /* Loader section header */
174 struct scnhdr * f_tchdr; /* Typechk section header */
175 struct scnhdr * f_dbhdr; /* Debug section header */
176 struct scnhdr * f_xhdr; /* Except section header */
178 load_scnptr = orig_load_scnptr = lnnoptr = 0;
179 pagemask = getpagesize () - 1;
181 /* Adjust text/data boundary. */
182 data_start = (long) start_of_data ();
183 data_start = ADDR_CORRECT (data_start);
185 data_start = data_start & ~pagemask; /* (Down) to page boundary. */
187 bss_start = ADDR_CORRECT (sbrk (0)) + pagemask;
188 bss_start &= ~ pagemask;
190 if (data_start > bss_start) /* Can't have negative data size. */
192 ERROR2 ("unexec: data_start (%u) can't be greater than bss_start (%u)",
193 data_start, bss_start);
196 /* Salvage as much info from the existing file as possible */
197 f_thdr = NULL; f_dhdr = NULL; f_bhdr = NULL;
198 f_lhdr = NULL; f_tchdr = NULL; f_dbhdr = NULL; f_xhdr = NULL;
199 if (a_out >= 0)
201 if (read (a_out, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr))
203 PERROR (a_name);
205 if (f_hdr.f_opthdr > 0)
207 if (read (a_out, &f_ohdr, sizeof (f_ohdr)) != sizeof (f_ohdr))
209 PERROR (a_name);
212 if (f_hdr.f_nscns > MAX_SECTIONS)
214 ERROR0 ("unexec: too many section headers -- increase MAX_SECTIONS");
216 /* Loop through section headers */
217 for (scns = 0; scns < f_hdr.f_nscns; scns++) {
218 struct scnhdr *s = &section[scns];
219 if (read (a_out, s, sizeof (*s)) != sizeof (*s))
221 PERROR (a_name);
224 #define CHECK_SCNHDR(ptr, name, flags) \
225 if (strcmp (s->s_name, name) == 0) { \
226 if (s->s_flags != flags) { \
227 fprintf (stderr, "unexec: %lx flags where %x expected in %s section.\n", \
228 (unsigned long)s->s_flags, flags, name); \
230 if (ptr) { \
231 fprintf (stderr, "unexec: duplicate section header for section %s.\n", \
232 name); \
234 ptr = s; \
236 CHECK_SCNHDR (f_thdr, _TEXT, STYP_TEXT);
237 CHECK_SCNHDR (f_dhdr, _DATA, STYP_DATA);
238 CHECK_SCNHDR (f_bhdr, _BSS, STYP_BSS);
239 CHECK_SCNHDR (f_lhdr, _LOADER, STYP_LOADER);
240 CHECK_SCNHDR (f_dbhdr, _DEBUG, STYP_DEBUG);
241 CHECK_SCNHDR (f_tchdr, _TYPCHK, STYP_TYPCHK);
242 CHECK_SCNHDR (f_xhdr, _EXCEPT, STYP_EXCEPT);
245 if (f_thdr == 0)
247 ERROR1 ("unexec: couldn't find \"%s\" section", (int) _TEXT);
249 if (f_dhdr == 0)
251 ERROR1 ("unexec: couldn't find \"%s\" section", (int) _DATA);
253 if (f_bhdr == 0)
255 ERROR1 ("unexec: couldn't find \"%s\" section", (int) _BSS);
258 else
260 ERROR0 ("can't build a COFF file from scratch yet");
262 orig_data_scnptr = f_dhdr->s_scnptr;
263 orig_load_scnptr = f_lhdr ? f_lhdr->s_scnptr : 0;
265 /* Now we alter the contents of all the f_*hdr variables
266 to correspond to what we want to dump. */
268 /* Indicate that the reloc information is no longer valid for ld (bind);
269 we only update it enough to fake out the exec-time loader. */
270 f_hdr.f_flags |= (F_RELFLG | F_EXEC);
272 f_ohdr.dsize = bss_start - f_ohdr.data_start;
273 f_ohdr.bsize = 0;
275 f_dhdr->s_size = f_ohdr.dsize;
276 f_bhdr->s_size = f_ohdr.bsize;
277 f_bhdr->s_paddr = f_ohdr.data_start + f_ohdr.dsize;
278 f_bhdr->s_vaddr = f_ohdr.data_start + f_ohdr.dsize;
280 /* fix scnptr's */
282 ulong ptr = section[0].s_scnptr;
284 bias = -1;
285 for (scns = 0; scns < f_hdr.f_nscns; scns++)
287 struct scnhdr *s = &section[scns];
289 if (s->s_flags & STYP_PAD) /* .pad sections omitted in AIX 4.1 */
292 * the text_start should probably be o_algntext but that doesn't
293 * seem to change
295 if (f_ohdr.text_start != 0) /* && scns != 0 */
297 s->s_size = 512 - (ptr % 512);
298 if (s->s_size == 512)
299 s->s_size = 0;
301 s->s_scnptr = ptr;
303 else if (s->s_flags & STYP_DATA)
304 s->s_scnptr = ptr;
305 else if (!(s->s_flags & (STYP_TEXT | STYP_BSS)))
307 if (bias == -1) /* if first section after bss */
308 bias = ptr - s->s_scnptr;
310 s->s_scnptr += bias;
311 ptr = s->s_scnptr;
314 ptr = ptr + s->s_size;
318 /* fix other pointers */
319 for (scns = 0; scns < f_hdr.f_nscns; scns++)
321 struct scnhdr *s = &section[scns];
323 if (s->s_relptr != 0)
325 s->s_relptr += bias;
327 if (s->s_lnnoptr != 0)
329 if (lnnoptr == 0) lnnoptr = s->s_lnnoptr;
330 s->s_lnnoptr += bias;
334 if (f_hdr.f_symptr > 0L)
336 f_hdr.f_symptr += bias;
339 text_scnptr = f_thdr->s_scnptr;
340 data_scnptr = f_dhdr->s_scnptr;
341 load_scnptr = f_lhdr ? f_lhdr->s_scnptr : 0;
343 if (write (new, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr))
345 PERROR (new_name);
348 if (f_hdr.f_opthdr > 0)
350 if (write (new, &f_ohdr, sizeof (f_ohdr)) != sizeof (f_ohdr))
352 PERROR (new_name);
356 for (scns = 0; scns < f_hdr.f_nscns; scns++) {
357 struct scnhdr *s = &section[scns];
358 if (write (new, s, sizeof (*s)) != sizeof (*s))
360 PERROR (new_name);
364 return (0);
367 /* ****************************************************************
370 * Copy the text and data segments from memory to the new a.out
372 static int
373 copy_text_and_data (int new)
375 char *end;
376 char *ptr;
378 lseek (new, (long) text_scnptr, SEEK_SET);
379 ptr = start_of_text () + text_scnptr;
380 end = ptr + f_ohdr.tsize;
381 write_segment (new, ptr, end);
383 lseek (new, (long) data_scnptr, SEEK_SET);
384 ptr = (char *) f_ohdr.data_start;
385 end = ptr + f_ohdr.dsize;
386 write_segment (new, ptr, end);
388 return 0;
391 #define UnexBlockSz (1<<12) /* read/write block size */
392 static void
393 write_segment (int new, char *ptr, char *end)
395 int i, nwrite, ret;
396 char buf[80];
397 char zeros[UnexBlockSz];
399 for (i = 0; ptr < end;)
401 /* distance to next block. */
402 nwrite = (((int) ptr + UnexBlockSz) & -UnexBlockSz) - (int) ptr;
403 /* But not beyond specified end. */
404 if (nwrite > end - ptr) nwrite = end - ptr;
405 ret = write (new, ptr, nwrite);
406 /* If write gets a page fault, it means we reached
407 a gap between the old text segment and the old data segment.
408 This gap has probably been remapped into part of the text segment.
409 So write zeros for it. */
410 if (ret == -1 && errno == EFAULT)
412 memset (zeros, 0, nwrite);
413 write (new, zeros, nwrite);
415 else if (nwrite != ret)
417 sprintf (buf,
418 "unexec write failure: addr 0x%lx, fileno %d, size 0x%x, wrote 0x%x, errno %d",
419 (unsigned long)ptr, new, nwrite, ret, errno);
420 PERROR (buf);
422 i += nwrite;
423 ptr += nwrite;
427 /* ****************************************************************
428 * copy_sym
430 * Copy the relocation information and symbol table from the a.out to the new
432 static int
433 copy_sym (int new, int a_out, const char *a_name, const char *new_name)
435 char page[UnexBlockSz];
436 int n;
438 if (a_out < 0)
439 return 0;
441 if (orig_load_scnptr == 0L)
442 return 0;
444 if (lnnoptr && lnnoptr < orig_load_scnptr) /* if there is line number info */
445 lseek (a_out, lnnoptr, SEEK_SET); /* start copying from there */
446 else
447 lseek (a_out, orig_load_scnptr, SEEK_SET); /* Position a.out to symtab. */
449 while ((n = read (a_out, page, sizeof page)) > 0)
451 if (write (new, page, n) != n)
453 PERROR (new_name);
456 if (n < 0)
458 PERROR (a_name);
460 return 0;
463 /* ****************************************************************
464 * mark_x
466 * After successfully building the new a.out, mark it executable
468 static void
469 mark_x (const char *name)
471 struct stat sbuf;
472 int um;
473 int new = 0; /* for PERROR */
475 um = umask (777);
476 umask (um);
477 if (stat (name, &sbuf) == -1)
479 PERROR (name);
481 sbuf.st_mode |= 0111 & ~um;
482 if (chmod (name, sbuf.st_mode) == -1)
483 PERROR (name);
486 static int
487 adjust_lnnoptrs (int writedesc, int readdesc, const char *new_name)
489 int nsyms;
490 int naux;
491 int new;
492 struct syment symentry;
493 union auxent auxentry;
495 if (!lnnoptr || !f_hdr.f_symptr)
496 return 0;
498 if ((new = open (new_name, O_RDWR)) < 0)
500 PERROR (new_name);
501 return -1;
504 lseek (new, f_hdr.f_symptr, SEEK_SET);
505 for (nsyms = 0; nsyms < f_hdr.f_nsyms; nsyms++)
507 read (new, &symentry, SYMESZ);
508 if (symentry.n_sclass == C_BINCL || symentry.n_sclass == C_EINCL)
510 symentry.n_value += bias;
511 lseek (new, -SYMESZ, SEEK_CUR);
512 write (new, &symentry, SYMESZ);
515 for (naux = symentry.n_numaux; naux-- != 0; )
517 read (new, &auxentry, AUXESZ);
518 nsyms++;
519 if (naux != 0 /* skip csect auxentry (last entry) */
520 && (symentry.n_sclass == C_EXT || symentry.n_sclass == C_HIDEXT))
522 auxentry.x_sym.x_fcnary.x_fcn.x_lnnoptr += bias;
523 lseek (new, -AUXESZ, SEEK_CUR);
524 write (new, &auxentry, AUXESZ);
528 close (new);
530 return 0;
533 static int
534 unrelocate_symbols (int new, int a_out,
535 const char *a_name, const char *new_name)
537 int i;
538 LDHDR ldhdr;
539 LDREL ldrel;
540 ulong t_reloc = (ulong) &_text - f_ohdr.text_start;
541 #ifndef ALIGN_DATA_RELOC
542 ulong d_reloc = (ulong) &_data - f_ohdr.data_start;
543 #else
544 /* This worked (and was needed) before AIX 4.2.
545 I have no idea why. -- Mike */
546 ulong d_reloc = (ulong) &_data - ALIGN (f_ohdr.data_start, 2);
547 #endif
548 int * p;
550 if (load_scnptr == 0)
551 return 0;
553 lseek (a_out, orig_load_scnptr, SEEK_SET);
554 if (read (a_out, &ldhdr, sizeof (ldhdr)) != sizeof (ldhdr))
556 PERROR (new_name);
559 #define SYMNDX_TEXT 0
560 #define SYMNDX_DATA 1
561 #define SYMNDX_BSS 2
563 for (i = 0; i < ldhdr.l_nreloc; i++)
565 lseek (a_out,
566 orig_load_scnptr + LDHDRSZ + LDSYMSZ*ldhdr.l_nsyms + LDRELSZ*i,
567 SEEK_SET);
569 if (read (a_out, &ldrel, LDRELSZ) != LDRELSZ)
571 PERROR (a_name);
574 /* move the BSS loader symbols to the DATA segment */
575 if (ldrel.l_symndx == SYMNDX_BSS)
577 ldrel.l_symndx = SYMNDX_DATA;
579 lseek (new,
580 load_scnptr + LDHDRSZ + LDSYMSZ*ldhdr.l_nsyms + LDRELSZ*i,
581 SEEK_SET);
583 if (write (new, &ldrel, LDRELSZ) != LDRELSZ)
585 PERROR (new_name);
589 if (ldrel.l_rsecnm == f_ohdr.o_sndata)
591 int orig_int;
593 lseek (a_out,
594 orig_data_scnptr + (ldrel.l_vaddr - f_ohdr.data_start),
595 SEEK_SET);
597 if (read (a_out, (void *) &orig_int, sizeof (orig_int))
598 != sizeof (orig_int))
600 PERROR (a_name);
603 p = (int *) (ldrel.l_vaddr + d_reloc);
605 switch (ldrel.l_symndx) {
606 case SYMNDX_TEXT:
607 orig_int = * p - t_reloc;
608 break;
610 case SYMNDX_DATA:
611 case SYMNDX_BSS:
612 orig_int = * p - d_reloc;
613 break;
616 if (orig_int != * p)
618 lseek (new,
619 data_scnptr + (ldrel.l_vaddr - f_ohdr.data_start),
620 SEEK_SET);
621 if (write (new, (void *) &orig_int, sizeof (orig_int))
622 != sizeof (orig_int))
624 PERROR (new_name);
629 return 0;
633 * Return the address of the start of the text segment prior to
634 * doing an unexec. After unexec the return value is undefined.
635 * See crt0.c for further explanation and _start.
639 char *
640 start_of_text (void)
642 return ((char *) 0x10000000);