1 /* Dump an executable image.
2 Copyright (C) 1985-1988, 1999, 2001-2013 Free Software Foundation,
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>
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.
47 #define PERROR(file) report_error (file, new)
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>
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 *);
85 #define MAX_SECTIONS 10
88 static int adjust_lnnoptrs (int, int, const char *);
95 report_error (const char *file
, int fd
)
99 int failed_errno
= errno
;
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
, ...)
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 /* ****************************************************************
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)
140 if ((new = emacs_open (new_name
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0666)) < 0)
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)
161 /* ****************************************************************
164 * Make the header in the new a.out from the header in core.
165 * Modify the text and data sizes.
168 make_hdr (int new, int a_out
,
169 const char *a_name
, const char *new_name
)
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
;
207 if (read (a_out
, &f_hdr
, sizeof (f_hdr
)) != sizeof (f_hdr
))
211 if (f_hdr
.f_opthdr
> 0)
213 if (read (a_out
, &f_ohdr
, sizeof (f_ohdr
)) != sizeof (f_ohdr
))
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
= §ion
[scns
];
225 if (read (a_out
, s
, sizeof (*s
)) != sizeof (*s
))
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); \
237 fprintf (stderr, "unexec: duplicate section header for section %s.\n", \
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
);
253 ERROR1 ("unexec: couldn't find \"%s\" section", (int) _TEXT
);
257 ERROR1 ("unexec: couldn't find \"%s\" section", (int) _DATA
);
261 ERROR1 ("unexec: couldn't find \"%s\" section", (int) _BSS
);
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
;
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
;
288 off_t ptr
= section
[0].s_scnptr
;
291 for (scns
= 0; scns
< f_hdr
.f_nscns
; scns
++)
293 struct scnhdr
*s
= §ion
[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
301 if (f_ohdr
.text_start
!= 0) /* && scns != 0 */
303 s
->s_size
= 512 - (ptr
% 512);
304 if (s
->s_size
== 512)
309 else if (s
->s_flags
& STYP_DATA
)
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
;
320 ptr
= ptr
+ s
->s_size
;
324 /* fix other pointers */
325 for (scns
= 0; scns
< f_hdr
.f_nscns
; scns
++)
327 struct scnhdr
*s
= §ion
[scns
];
329 if (s
->s_relptr
!= 0)
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
))
354 if (f_hdr
.f_opthdr
> 0)
356 if (write (new, &f_ohdr
, sizeof (f_ohdr
)) != sizeof (f_ohdr
))
362 for (scns
= 0; scns
< f_hdr
.f_nscns
; scns
++) {
363 struct scnhdr
*s
= §ion
[scns
];
364 if (write (new, s
, sizeof (*s
)) != sizeof (*s
))
373 /* ****************************************************************
376 * Copy the text and data segments from memory to the new a.out
379 copy_text_and_data (int new)
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
);
397 #define UnexBlockSz (1<<12) /* read/write block size */
399 write_segment (int new, char *ptr
, char *end
)
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
;
426 "unexec write failure: addr %p, fileno %d, size 0x%x, wrote 0x%x, errno %d",
427 addr
, new, nwrite
, ret
, errno
);
436 /* ****************************************************************
439 * Copy the relocation information and symbol table from the a.out to the new
442 copy_sym (int new, int a_out
, const char *a_name
, const char *new_name
)
444 char page
[UnexBlockSz
];
450 if (orig_load_scnptr
== 0L)
453 if (lnnoptr
&& lnnoptr
< orig_load_scnptr
) /* if there is line number info */
454 lseek (a_out
, lnnoptr
, SEEK_SET
); /* start copying from there */
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
)
472 /* ****************************************************************
475 * After successfully building the new a.out, mark it executable
478 mark_x (const char *name
)
482 int new = 0; /* for PERROR */
486 if (stat (name
, &sbuf
) == -1)
490 sbuf
.st_mode
|= 0111 & ~um
;
491 if (chmod (name
, sbuf
.st_mode
) == -1)
496 adjust_lnnoptrs (int writedesc
, int readdesc
, const char *new_name
)
501 struct syment symentry
;
502 union auxent auxentry
;
504 if (!lnnoptr
|| !f_hdr
.f_symptr
)
507 if ((new = emacs_open (new_name
, O_RDWR
, 0)) < 0)
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
);
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
);
543 unrelocate_symbols (int new, int a_out
,
544 const char *a_name
, const char *new_name
)
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
;
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);
559 if (load_scnptr
== 0)
562 lseek (a_out
, orig_load_scnptr
, SEEK_SET
);
563 if (read (a_out
, &ldhdr
, sizeof (ldhdr
)) != sizeof (ldhdr
))
568 #define SYMNDX_TEXT 0
569 #define SYMNDX_DATA 1
572 for (i
= 0; i
< ldhdr
.l_nreloc
; i
++)
575 orig_load_scnptr
+ LDHDRSZ
+ LDSYMSZ
*ldhdr
.l_nsyms
+ LDRELSZ
*i
,
578 if (read (a_out
, &ldrel
, LDRELSZ
) != LDRELSZ
)
583 /* move the BSS loader symbols to the DATA segment */
584 if (ldrel
.l_symndx
== SYMNDX_BSS
)
586 ldrel
.l_symndx
= SYMNDX_DATA
;
589 load_scnptr
+ LDHDRSZ
+ LDSYMSZ
*ldhdr
.l_nsyms
+ LDRELSZ
*i
,
592 if (write (new, &ldrel
, LDRELSZ
) != LDRELSZ
)
598 if (ldrel
.l_rsecnm
== f_ohdr
.o_sndata
)
603 orig_data_scnptr
+ (ldrel
.l_vaddr
- f_ohdr
.data_start
),
606 if (read (a_out
, (void *) &orig_int
, sizeof (orig_int
))
607 != sizeof (orig_int
))
612 p
= (int *) (ldrel
.l_vaddr
+ d_reloc
);
614 switch (ldrel
.l_symndx
) {
616 orig_int
= * p
- t_reloc
;
621 orig_int
= * p
- d_reloc
;
628 data_scnptr
+ (ldrel
.l_vaddr
- f_ohdr
.data_start
),
630 if (write (new, (void *) &orig_int
, sizeof (orig_int
))
631 != sizeof (orig_int
))