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.
46 #define PERROR(file) report_error (file, new)
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>
62 #include "mem-limits.h"
72 static struct filehdr f_hdr
; /* File header */
73 static struct aouthdr f_ohdr
; /* Optional file header (a.out) */
74 static off_t bias
; /* Bias to add for growth */
75 static off_t lnnoptr
; /* Pointer to line-number info within file */
77 static off_t text_scnptr
;
78 static off_t data_scnptr
;
79 #define ALIGN(val, pwr) (((val) + ((1L<<(pwr))-1)) & ~((1L<<(pwr))-1))
80 static off_t load_scnptr
;
81 static off_t orig_load_scnptr
;
82 static off_t orig_data_scnptr
;
83 static int unrelocate_symbols (int, int, const char *, const char *);
86 #define MAX_SECTIONS 10
89 static int adjust_lnnoptrs (int, int, const char *);
96 report_error (const char *file
, int fd
)
100 int failed_errno
= errno
;
102 errno
= failed_errno
;
104 report_file_error ("Cannot unexec", Fcons (build_string (file
), Qnil
));
107 #define ERROR0(msg) report_error_1 (new, msg)
108 #define ERROR1(msg,x) report_error_1 (new, msg, x)
109 #define ERROR2(msg,x,y) report_error_1 (new, msg, x, y)
111 static _Noreturn
void ATTRIBUTE_FORMAT_PRINTF (2, 3)
112 report_error_1 (int fd
, const char *msg
, ...)
121 static int make_hdr (int, int, const char *, const char *);
122 static void mark_x (const char *);
123 static int copy_text_and_data (int);
124 static int copy_sym (int, int, const char *, const char *);
125 static void write_segment (int, char *, char *);
127 /* ****************************************************************
133 unexec (const char *new_name
, const char *a_name
)
135 int new = -1, a_out
= -1;
137 if (a_name
&& (a_out
= open (a_name
, O_RDONLY
)) < 0)
141 if ((new = creat (new_name
, 0666)) < 0)
145 if (make_hdr (new, a_out
,
146 a_name
, new_name
) < 0
147 || copy_text_and_data (new) < 0
148 || copy_sym (new, a_out
, a_name
, new_name
) < 0
149 || adjust_lnnoptrs (new, a_out
, new_name
) < 0
150 || unrelocate_symbols (new, a_out
, a_name
, new_name
) < 0)
162 /* ****************************************************************
165 * Make the header in the new a.out from the header in core.
166 * Modify the text and data sizes.
169 make_hdr (int new, int a_out
,
170 const char *a_name
, const char *new_name
)
174 uintptr_t data_start
;
176 struct scnhdr section
[MAX_SECTIONS
];
177 struct scnhdr
* f_thdr
; /* Text section header */
178 struct scnhdr
* f_dhdr
; /* Data section header */
179 struct scnhdr
* f_bhdr
; /* Bss section header */
180 struct scnhdr
* f_lhdr
; /* Loader section header */
181 struct scnhdr
* f_tchdr
; /* Typechk section header */
182 struct scnhdr
* f_dbhdr
; /* Debug section header */
183 struct scnhdr
* f_xhdr
; /* Except section header */
185 load_scnptr
= orig_load_scnptr
= lnnoptr
= 0;
186 pagemask
= getpagesize () - 1;
188 /* Adjust text/data boundary. */
189 data_start
= (uintptr_t) _data
;
191 data_start
= data_start
& ~pagemask
; /* (Down) to page boundary. */
193 bss_start
= (uintptr_t) sbrk (0) + pagemask
;
194 bss_start
&= ~ pagemask
;
196 if (data_start
> bss_start
) /* Can't have negative data size. */
198 ERROR2 (("unexec: data_start (0x%"PRIxPTR
199 ") can't be greater than bss_start (0x%"PRIxPTR
")"),
200 data_start
, bss_start
);
203 /* Salvage as much info from the existing file as possible */
204 f_thdr
= NULL
; f_dhdr
= NULL
; f_bhdr
= NULL
;
205 f_lhdr
= NULL
; f_tchdr
= NULL
; f_dbhdr
= NULL
; f_xhdr
= NULL
;
208 if (read (a_out
, &f_hdr
, sizeof (f_hdr
)) != sizeof (f_hdr
))
212 if (f_hdr
.f_opthdr
> 0)
214 if (read (a_out
, &f_ohdr
, sizeof (f_ohdr
)) != sizeof (f_ohdr
))
219 if (f_hdr
.f_nscns
> MAX_SECTIONS
)
221 ERROR0 ("unexec: too many section headers -- increase MAX_SECTIONS");
223 /* Loop through section headers */
224 for (scns
= 0; scns
< f_hdr
.f_nscns
; scns
++) {
225 struct scnhdr
*s
= §ion
[scns
];
226 if (read (a_out
, s
, sizeof (*s
)) != sizeof (*s
))
231 #define CHECK_SCNHDR(ptr, name, flags) \
232 if (strcmp (s->s_name, name) == 0) { \
233 if (s->s_flags != flags) { \
234 fprintf (stderr, "unexec: %lx flags where %x expected in %s section.\n", \
235 (unsigned long)s->s_flags, flags, name); \
238 fprintf (stderr, "unexec: duplicate section header for section %s.\n", \
243 CHECK_SCNHDR (f_thdr
, _TEXT
, STYP_TEXT
);
244 CHECK_SCNHDR (f_dhdr
, _DATA
, STYP_DATA
);
245 CHECK_SCNHDR (f_bhdr
, _BSS
, STYP_BSS
);
246 CHECK_SCNHDR (f_lhdr
, _LOADER
, STYP_LOADER
);
247 CHECK_SCNHDR (f_dbhdr
, _DEBUG
, STYP_DEBUG
);
248 CHECK_SCNHDR (f_tchdr
, _TYPCHK
, STYP_TYPCHK
);
249 CHECK_SCNHDR (f_xhdr
, _EXCEPT
, STYP_EXCEPT
);
254 ERROR1 ("unexec: couldn't find \"%s\" section", (int) _TEXT
);
258 ERROR1 ("unexec: couldn't find \"%s\" section", (int) _DATA
);
262 ERROR1 ("unexec: couldn't find \"%s\" section", (int) _BSS
);
267 ERROR0 ("can't build a COFF file from scratch yet");
269 orig_data_scnptr
= f_dhdr
->s_scnptr
;
270 orig_load_scnptr
= f_lhdr
? f_lhdr
->s_scnptr
: 0;
272 /* Now we alter the contents of all the f_*hdr variables
273 to correspond to what we want to dump. */
275 /* Indicate that the reloc information is no longer valid for ld (bind);
276 we only update it enough to fake out the exec-time loader. */
277 f_hdr
.f_flags
|= (F_RELFLG
| F_EXEC
);
279 f_ohdr
.dsize
= bss_start
- f_ohdr
.data_start
;
282 f_dhdr
->s_size
= f_ohdr
.dsize
;
283 f_bhdr
->s_size
= f_ohdr
.bsize
;
284 f_bhdr
->s_paddr
= f_ohdr
.data_start
+ f_ohdr
.dsize
;
285 f_bhdr
->s_vaddr
= f_ohdr
.data_start
+ f_ohdr
.dsize
;
289 off_t ptr
= section
[0].s_scnptr
;
292 for (scns
= 0; scns
< f_hdr
.f_nscns
; scns
++)
294 struct scnhdr
*s
= §ion
[scns
];
296 if (s
->s_flags
& STYP_PAD
) /* .pad sections omitted in AIX 4.1 */
299 * the text_start should probably be o_algntext but that doesn't
302 if (f_ohdr
.text_start
!= 0) /* && scns != 0 */
304 s
->s_size
= 512 - (ptr
% 512);
305 if (s
->s_size
== 512)
310 else if (s
->s_flags
& STYP_DATA
)
312 else if (!(s
->s_flags
& (STYP_TEXT
| STYP_BSS
)))
314 if (bias
== -1) /* if first section after bss */
315 bias
= ptr
- s
->s_scnptr
;
321 ptr
= ptr
+ s
->s_size
;
325 /* fix other pointers */
326 for (scns
= 0; scns
< f_hdr
.f_nscns
; scns
++)
328 struct scnhdr
*s
= §ion
[scns
];
330 if (s
->s_relptr
!= 0)
334 if (s
->s_lnnoptr
!= 0)
336 if (lnnoptr
== 0) lnnoptr
= s
->s_lnnoptr
;
337 s
->s_lnnoptr
+= bias
;
341 if (f_hdr
.f_symptr
> 0L)
343 f_hdr
.f_symptr
+= bias
;
346 text_scnptr
= f_thdr
->s_scnptr
;
347 data_scnptr
= f_dhdr
->s_scnptr
;
348 load_scnptr
= f_lhdr
? f_lhdr
->s_scnptr
: 0;
350 if (write (new, &f_hdr
, sizeof (f_hdr
)) != sizeof (f_hdr
))
355 if (f_hdr
.f_opthdr
> 0)
357 if (write (new, &f_ohdr
, sizeof (f_ohdr
)) != sizeof (f_ohdr
))
363 for (scns
= 0; scns
< f_hdr
.f_nscns
; scns
++) {
364 struct scnhdr
*s
= §ion
[scns
];
365 if (write (new, s
, sizeof (*s
)) != sizeof (*s
))
374 /* ****************************************************************
377 * Copy the text and data segments from memory to the new a.out
380 copy_text_and_data (int new)
385 lseek (new, text_scnptr
, SEEK_SET
);
386 ptr
= _text
+ text_scnptr
;
387 end
= ptr
+ f_ohdr
.tsize
;
388 write_segment (new, ptr
, end
);
390 lseek (new, data_scnptr
, SEEK_SET
);
391 ptr
= (char *) f_ohdr
.data_start
;
392 end
= ptr
+ f_ohdr
.dsize
;
393 write_segment (new, ptr
, end
);
398 #define UnexBlockSz (1<<12) /* read/write block size */
400 write_segment (int new, char *ptr
, char *end
)
403 char zeros
[UnexBlockSz
];
405 for (i
= 0; ptr
< end
;)
407 /* distance to next block. */
408 nwrite
= (((int) ptr
+ UnexBlockSz
) & -UnexBlockSz
) - (int) ptr
;
409 /* But not beyond specified end. */
410 if (nwrite
> end
- ptr
) nwrite
= end
- ptr
;
411 ret
= write (new, ptr
, nwrite
);
412 /* If write gets a page fault, it means we reached
413 a gap between the old text segment and the old data segment.
414 This gap has probably been remapped into part of the text segment.
415 So write zeros for it. */
416 if (ret
== -1 && errno
== EFAULT
)
418 memset (zeros
, 0, nwrite
);
419 write (new, zeros
, nwrite
);
421 else if (nwrite
!= ret
)
423 int write_errno
= errno
;
427 "unexec write failure: addr %p, fileno %d, size 0x%x, wrote 0x%x, errno %d",
428 addr
, new, nwrite
, ret
, errno
);
437 /* ****************************************************************
440 * Copy the relocation information and symbol table from the a.out to the new
443 copy_sym (int new, int a_out
, const char *a_name
, const char *new_name
)
445 char page
[UnexBlockSz
];
451 if (orig_load_scnptr
== 0L)
454 if (lnnoptr
&& lnnoptr
< orig_load_scnptr
) /* if there is line number info */
455 lseek (a_out
, lnnoptr
, SEEK_SET
); /* start copying from there */
457 lseek (a_out
, orig_load_scnptr
, SEEK_SET
); /* Position a.out to symtab. */
459 while ((n
= read (a_out
, page
, sizeof page
)) > 0)
461 if (write (new, page
, n
) != n
)
473 /* ****************************************************************
476 * After successfully building the new a.out, mark it executable
479 mark_x (const char *name
)
483 int new = 0; /* for PERROR */
487 if (stat (name
, &sbuf
) == -1)
491 sbuf
.st_mode
|= 0111 & ~um
;
492 if (chmod (name
, sbuf
.st_mode
) == -1)
497 adjust_lnnoptrs (int writedesc
, int readdesc
, const char *new_name
)
502 struct syment symentry
;
503 union auxent auxentry
;
505 if (!lnnoptr
|| !f_hdr
.f_symptr
)
508 if ((new = open (new_name
, O_RDWR
)) < 0)
514 lseek (new, f_hdr
.f_symptr
, SEEK_SET
);
515 for (nsyms
= 0; nsyms
< f_hdr
.f_nsyms
; nsyms
++)
517 read (new, &symentry
, SYMESZ
);
518 if (symentry
.n_sclass
== C_BINCL
|| symentry
.n_sclass
== C_EINCL
)
520 symentry
.n_value
+= bias
;
521 lseek (new, -SYMESZ
, SEEK_CUR
);
522 write (new, &symentry
, SYMESZ
);
525 for (naux
= symentry
.n_numaux
; naux
-- != 0; )
527 read (new, &auxentry
, AUXESZ
);
529 if (naux
!= 0 /* skip csect auxentry (last entry) */
530 && (symentry
.n_sclass
== C_EXT
|| symentry
.n_sclass
== C_HIDEXT
))
532 auxentry
.x_sym
.x_fcnary
.x_fcn
.x_lnnoptr
+= bias
;
533 lseek (new, -AUXESZ
, SEEK_CUR
);
534 write (new, &auxentry
, AUXESZ
);
544 unrelocate_symbols (int new, int a_out
,
545 const char *a_name
, const char *new_name
)
550 off_t t_reloc
= (intptr_t) _text
- f_ohdr
.text_start
;
551 #ifndef ALIGN_DATA_RELOC
552 off_t d_reloc
= (intptr_t) _data
- f_ohdr
.data_start
;
554 /* This worked (and was needed) before AIX 4.2.
555 I have no idea why. -- Mike */
556 off_t d_reloc
= (intptr_t) _data
- ALIGN (f_ohdr
.data_start
, 2);
560 if (load_scnptr
== 0)
563 lseek (a_out
, orig_load_scnptr
, SEEK_SET
);
564 if (read (a_out
, &ldhdr
, sizeof (ldhdr
)) != sizeof (ldhdr
))
569 #define SYMNDX_TEXT 0
570 #define SYMNDX_DATA 1
573 for (i
= 0; i
< ldhdr
.l_nreloc
; i
++)
576 orig_load_scnptr
+ LDHDRSZ
+ LDSYMSZ
*ldhdr
.l_nsyms
+ LDRELSZ
*i
,
579 if (read (a_out
, &ldrel
, LDRELSZ
) != LDRELSZ
)
584 /* move the BSS loader symbols to the DATA segment */
585 if (ldrel
.l_symndx
== SYMNDX_BSS
)
587 ldrel
.l_symndx
= SYMNDX_DATA
;
590 load_scnptr
+ LDHDRSZ
+ LDSYMSZ
*ldhdr
.l_nsyms
+ LDRELSZ
*i
,
593 if (write (new, &ldrel
, LDRELSZ
) != LDRELSZ
)
599 if (ldrel
.l_rsecnm
== f_ohdr
.o_sndata
)
604 orig_data_scnptr
+ (ldrel
.l_vaddr
- f_ohdr
.data_start
),
607 if (read (a_out
, (void *) &orig_int
, sizeof (orig_int
))
608 != sizeof (orig_int
))
613 p
= (int *) (ldrel
.l_vaddr
+ d_reloc
);
615 switch (ldrel
.l_symndx
) {
617 orig_int
= * p
- t_reloc
;
622 orig_int
= * p
- d_reloc
;
629 data_scnptr
+ (ldrel
.l_vaddr
- f_ohdr
.data_start
),
631 if (write (new, (void *) &orig_int
, sizeof (orig_int
))
632 != sizeof (orig_int
))