]> code.delx.au - gnu-emacs/blob - src/unexaix.c
Update copyright notices for 2013.
[gnu-emacs] / src / unexaix.c
1 /* Dump an executable image.
2 Copyright (C) 1985-1988, 1999, 2001-2013 Free Software Foundation,
3 Inc.
4
5 This file is part of GNU Emacs.
6
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.
11
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.
16
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/>. */
19
20 /*
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! */
24
25
26 /* Originally based on the COFF unexec.c by Spencer W. Thomas.
27 *
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>
32 *
33 * Synopsis:
34 * unexec (const char *new_name, const *old_name);
35 *
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.
40 *
41 */
42
43 #include <config.h>
44 #include "unexec.h"
45
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
50 */
51 #include "getpagesize.h"
52
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>
59
60 #include "mem-limits.h"
61
62 char *start_of_text (void); /* Start of text */
63
64 extern int _data;
65 extern int _text;
66
67 #include <filehdr.h>
68 #include <aouthdr.h>
69 #include <scnhdr.h>
70 #include <syms.h>
71
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 */
76
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 *);
84
85 #ifndef MAX_SECTIONS
86 #define MAX_SECTIONS 10
87 #endif
88
89 static int adjust_lnnoptrs (int, int, const char *);
90
91 static int pagemask;
92
93 #include "lisp.h"
94
95 static void
96 report_error (const char *file, int fd)
97 {
98 if (fd)
99 close (fd);
100 report_file_error ("Cannot unexec", Fcons (build_string (file), Qnil));
101 }
102
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
106
107 static void
108 report_error_1 (int fd, const char *msg, int a1, int a2)
109 {
110 close (fd);
111 error (msg, a1, a2);
112 }
113
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 *);
119 \f
120 /* ****************************************************************
121 * unexec
122 *
123 * driving logic.
124 */
125 void
126 unexec (const char *new_name, const char *a_name)
127 {
128 int new = -1, a_out = -1;
129
130 if (a_name && (a_out = open (a_name, O_RDONLY)) < 0)
131 {
132 PERROR (a_name);
133 }
134 if ((new = creat (new_name, 0666)) < 0)
135 {
136 PERROR (new_name);
137 }
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)
144 {
145 close (new);
146 return;
147 }
148
149 close (new);
150 if (a_out >= 0)
151 close (a_out);
152 mark_x (new_name);
153 }
154
155 /* ****************************************************************
156 * make_hdr
157 *
158 * Make the header in the new a.out from the header in core.
159 * Modify the text and data sizes.
160 */
161 static int
162 make_hdr (int new, int a_out,
163 const char *a_name, const char *new_name)
164 {
165 int scns;
166 unsigned int bss_start;
167 unsigned int data_start;
168
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 */
177
178 load_scnptr = orig_load_scnptr = lnnoptr = 0;
179 pagemask = getpagesize () - 1;
180
181 /* Adjust text/data boundary. */
182 data_start = (long) start_of_data ();
183 data_start = ADDR_CORRECT (data_start);
184
185 data_start = data_start & ~pagemask; /* (Down) to page boundary. */
186
187 bss_start = ADDR_CORRECT (sbrk (0)) + pagemask;
188 bss_start &= ~ pagemask;
189
190 if (data_start > bss_start) /* Can't have negative data size. */
191 {
192 ERROR2 ("unexec: data_start (%u) can't be greater than bss_start (%u)",
193 data_start, bss_start);
194 }
195
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)
200 {
201 if (read (a_out, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr))
202 {
203 PERROR (a_name);
204 }
205 if (f_hdr.f_opthdr > 0)
206 {
207 if (read (a_out, &f_ohdr, sizeof (f_ohdr)) != sizeof (f_ohdr))
208 {
209 PERROR (a_name);
210 }
211 }
212 if (f_hdr.f_nscns > MAX_SECTIONS)
213 {
214 ERROR0 ("unexec: too many section headers -- increase MAX_SECTIONS");
215 }
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))
220 {
221 PERROR (a_name);
222 }
223
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); \
229 } \
230 if (ptr) { \
231 fprintf (stderr, "unexec: duplicate section header for section %s.\n", \
232 name); \
233 } \
234 ptr = s; \
235 }
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);
243 }
244
245 if (f_thdr == 0)
246 {
247 ERROR1 ("unexec: couldn't find \"%s\" section", (int) _TEXT);
248 }
249 if (f_dhdr == 0)
250 {
251 ERROR1 ("unexec: couldn't find \"%s\" section", (int) _DATA);
252 }
253 if (f_bhdr == 0)
254 {
255 ERROR1 ("unexec: couldn't find \"%s\" section", (int) _BSS);
256 }
257 }
258 else
259 {
260 ERROR0 ("can't build a COFF file from scratch yet");
261 }
262 orig_data_scnptr = f_dhdr->s_scnptr;
263 orig_load_scnptr = f_lhdr ? f_lhdr->s_scnptr : 0;
264
265 /* Now we alter the contents of all the f_*hdr variables
266 to correspond to what we want to dump. */
267
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);
271
272 f_ohdr.dsize = bss_start - f_ohdr.data_start;
273 f_ohdr.bsize = 0;
274
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;
279
280 /* fix scnptr's */
281 {
282 ulong ptr = section[0].s_scnptr;
283
284 bias = -1;
285 for (scns = 0; scns < f_hdr.f_nscns; scns++)
286 {
287 struct scnhdr *s = &section[scns];
288
289 if (s->s_flags & STYP_PAD) /* .pad sections omitted in AIX 4.1 */
290 {
291 /*
292 * the text_start should probably be o_algntext but that doesn't
293 * seem to change
294 */
295 if (f_ohdr.text_start != 0) /* && scns != 0 */
296 {
297 s->s_size = 512 - (ptr % 512);
298 if (s->s_size == 512)
299 s->s_size = 0;
300 }
301 s->s_scnptr = ptr;
302 }
303 else if (s->s_flags & STYP_DATA)
304 s->s_scnptr = ptr;
305 else if (!(s->s_flags & (STYP_TEXT | STYP_BSS)))
306 {
307 if (bias == -1) /* if first section after bss */
308 bias = ptr - s->s_scnptr;
309
310 s->s_scnptr += bias;
311 ptr = s->s_scnptr;
312 }
313
314 ptr = ptr + s->s_size;
315 }
316 }
317
318 /* fix other pointers */
319 for (scns = 0; scns < f_hdr.f_nscns; scns++)
320 {
321 struct scnhdr *s = &section[scns];
322
323 if (s->s_relptr != 0)
324 {
325 s->s_relptr += bias;
326 }
327 if (s->s_lnnoptr != 0)
328 {
329 if (lnnoptr == 0) lnnoptr = s->s_lnnoptr;
330 s->s_lnnoptr += bias;
331 }
332 }
333
334 if (f_hdr.f_symptr > 0L)
335 {
336 f_hdr.f_symptr += bias;
337 }
338
339 text_scnptr = f_thdr->s_scnptr;
340 data_scnptr = f_dhdr->s_scnptr;
341 load_scnptr = f_lhdr ? f_lhdr->s_scnptr : 0;
342
343 if (write (new, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr))
344 {
345 PERROR (new_name);
346 }
347
348 if (f_hdr.f_opthdr > 0)
349 {
350 if (write (new, &f_ohdr, sizeof (f_ohdr)) != sizeof (f_ohdr))
351 {
352 PERROR (new_name);
353 }
354 }
355
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))
359 {
360 PERROR (new_name);
361 }
362 }
363
364 return (0);
365 }
366 \f
367 /* ****************************************************************
368
369 *
370 * Copy the text and data segments from memory to the new a.out
371 */
372 static int
373 copy_text_and_data (int new)
374 {
375 char *end;
376 char *ptr;
377
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);
382
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);
387
388 return 0;
389 }
390
391 #define UnexBlockSz (1<<12) /* read/write block size */
392 static void
393 write_segment (int new, char *ptr, char *end)
394 {
395 int i, nwrite, ret;
396 char buf[80];
397 char zeros[UnexBlockSz];
398
399 for (i = 0; ptr < end;)
400 {
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)
411 {
412 memset (zeros, 0, nwrite);
413 write (new, zeros, nwrite);
414 }
415 else if (nwrite != ret)
416 {
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);
421 }
422 i += nwrite;
423 ptr += nwrite;
424 }
425 }
426 \f
427 /* ****************************************************************
428 * copy_sym
429 *
430 * Copy the relocation information and symbol table from the a.out to the new
431 */
432 static int
433 copy_sym (int new, int a_out, const char *a_name, const char *new_name)
434 {
435 char page[UnexBlockSz];
436 int n;
437
438 if (a_out < 0)
439 return 0;
440
441 if (orig_load_scnptr == 0L)
442 return 0;
443
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. */
448
449 while ((n = read (a_out, page, sizeof page)) > 0)
450 {
451 if (write (new, page, n) != n)
452 {
453 PERROR (new_name);
454 }
455 }
456 if (n < 0)
457 {
458 PERROR (a_name);
459 }
460 return 0;
461 }
462 \f
463 /* ****************************************************************
464 * mark_x
465 *
466 * After successfully building the new a.out, mark it executable
467 */
468 static void
469 mark_x (const char *name)
470 {
471 struct stat sbuf;
472 int um;
473 int new = 0; /* for PERROR */
474
475 um = umask (777);
476 umask (um);
477 if (stat (name, &sbuf) == -1)
478 {
479 PERROR (name);
480 }
481 sbuf.st_mode |= 0111 & ~um;
482 if (chmod (name, sbuf.st_mode) == -1)
483 PERROR (name);
484 }
485 \f
486 static int
487 adjust_lnnoptrs (int writedesc, int readdesc, const char *new_name)
488 {
489 int nsyms;
490 int naux;
491 int new;
492 struct syment symentry;
493 union auxent auxentry;
494
495 if (!lnnoptr || !f_hdr.f_symptr)
496 return 0;
497
498 if ((new = open (new_name, O_RDWR)) < 0)
499 {
500 PERROR (new_name);
501 return -1;
502 }
503
504 lseek (new, f_hdr.f_symptr, SEEK_SET);
505 for (nsyms = 0; nsyms < f_hdr.f_nsyms; nsyms++)
506 {
507 read (new, &symentry, SYMESZ);
508 if (symentry.n_sclass == C_BINCL || symentry.n_sclass == C_EINCL)
509 {
510 symentry.n_value += bias;
511 lseek (new, -SYMESZ, SEEK_CUR);
512 write (new, &symentry, SYMESZ);
513 }
514
515 for (naux = symentry.n_numaux; naux-- != 0; )
516 {
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))
521 {
522 auxentry.x_sym.x_fcnary.x_fcn.x_lnnoptr += bias;
523 lseek (new, -AUXESZ, SEEK_CUR);
524 write (new, &auxentry, AUXESZ);
525 }
526 }
527 }
528 close (new);
529
530 return 0;
531 }
532
533 static int
534 unrelocate_symbols (int new, int a_out,
535 const char *a_name, const char *new_name)
536 {
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;
549
550 if (load_scnptr == 0)
551 return 0;
552
553 lseek (a_out, orig_load_scnptr, SEEK_SET);
554 if (read (a_out, &ldhdr, sizeof (ldhdr)) != sizeof (ldhdr))
555 {
556 PERROR (new_name);
557 }
558
559 #define SYMNDX_TEXT 0
560 #define SYMNDX_DATA 1
561 #define SYMNDX_BSS 2
562
563 for (i = 0; i < ldhdr.l_nreloc; i++)
564 {
565 lseek (a_out,
566 orig_load_scnptr + LDHDRSZ + LDSYMSZ*ldhdr.l_nsyms + LDRELSZ*i,
567 SEEK_SET);
568
569 if (read (a_out, &ldrel, LDRELSZ) != LDRELSZ)
570 {
571 PERROR (a_name);
572 }
573
574 /* move the BSS loader symbols to the DATA segment */
575 if (ldrel.l_symndx == SYMNDX_BSS)
576 {
577 ldrel.l_symndx = SYMNDX_DATA;
578
579 lseek (new,
580 load_scnptr + LDHDRSZ + LDSYMSZ*ldhdr.l_nsyms + LDRELSZ*i,
581 SEEK_SET);
582
583 if (write (new, &ldrel, LDRELSZ) != LDRELSZ)
584 {
585 PERROR (new_name);
586 }
587 }
588
589 if (ldrel.l_rsecnm == f_ohdr.o_sndata)
590 {
591 int orig_int;
592
593 lseek (a_out,
594 orig_data_scnptr + (ldrel.l_vaddr - f_ohdr.data_start),
595 SEEK_SET);
596
597 if (read (a_out, (void *) &orig_int, sizeof (orig_int))
598 != sizeof (orig_int))
599 {
600 PERROR (a_name);
601 }
602
603 p = (int *) (ldrel.l_vaddr + d_reloc);
604
605 switch (ldrel.l_symndx) {
606 case SYMNDX_TEXT:
607 orig_int = * p - t_reloc;
608 break;
609
610 case SYMNDX_DATA:
611 case SYMNDX_BSS:
612 orig_int = * p - d_reloc;
613 break;
614 }
615
616 if (orig_int != * p)
617 {
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))
623 {
624 PERROR (new_name);
625 }
626 }
627 }
628 }
629 return 0;
630 }
631
632 /*
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.
636 *
637 */
638
639 char *
640 start_of_text (void)
641 {
642 return ((char *) 0x10000000);
643 }