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>
60 #include "mem-limits.h"
62 char *start_of_text (void); /* Start of text */
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 *);
86 #define MAX_SECTIONS 10
89 static int adjust_lnnoptrs (int, int, const char *);
96 report_error (const char *file
, int 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
108 report_error_1 (int fd
, const char *msg
, int a1
, int 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 /* ****************************************************************
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)
134 if ((new = creat (new_name
, 0666)) < 0)
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)
155 /* ****************************************************************
158 * Make the header in the new a.out from the header in core.
159 * Modify the text and data sizes.
162 make_hdr (int new, int a_out
,
163 const char *a_name
, const char *new_name
)
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
;
201 if (read (a_out
, &f_hdr
, sizeof (f_hdr
)) != sizeof (f_hdr
))
205 if (f_hdr
.f_opthdr
> 0)
207 if (read (a_out
, &f_ohdr
, sizeof (f_ohdr
)) != sizeof (f_ohdr
))
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
= §ion
[scns
];
219 if (read (a_out
, s
, sizeof (*s
)) != sizeof (*s
))
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); \
231 fprintf (stderr, "unexec: duplicate section header for section %s.\n", \
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
);
247 ERROR1 ("unexec: couldn't find \"%s\" section", (int) _TEXT
);
251 ERROR1 ("unexec: couldn't find \"%s\" section", (int) _DATA
);
255 ERROR1 ("unexec: couldn't find \"%s\" section", (int) _BSS
);
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
;
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
;
282 ulong ptr
= section
[0].s_scnptr
;
285 for (scns
= 0; scns
< f_hdr
.f_nscns
; scns
++)
287 struct scnhdr
*s
= §ion
[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
295 if (f_ohdr
.text_start
!= 0) /* && scns != 0 */
297 s
->s_size
= 512 - (ptr
% 512);
298 if (s
->s_size
== 512)
303 else if (s
->s_flags
& STYP_DATA
)
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
;
314 ptr
= ptr
+ s
->s_size
;
318 /* fix other pointers */
319 for (scns
= 0; scns
< f_hdr
.f_nscns
; scns
++)
321 struct scnhdr
*s
= §ion
[scns
];
323 if (s
->s_relptr
!= 0)
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
))
348 if (f_hdr
.f_opthdr
> 0)
350 if (write (new, &f_ohdr
, sizeof (f_ohdr
)) != sizeof (f_ohdr
))
356 for (scns
= 0; scns
< f_hdr
.f_nscns
; scns
++) {
357 struct scnhdr
*s
= §ion
[scns
];
358 if (write (new, s
, sizeof (*s
)) != sizeof (*s
))
367 /* ****************************************************************
370 * Copy the text and data segments from memory to the new a.out
373 copy_text_and_data (int new)
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
);
391 #define UnexBlockSz (1<<12) /* read/write block size */
393 write_segment (int new, char *ptr
, char *end
)
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
)
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
);
427 /* ****************************************************************
430 * Copy the relocation information and symbol table from the a.out to the new
433 copy_sym (int new, int a_out
, const char *a_name
, const char *new_name
)
435 char page
[UnexBlockSz
];
441 if (orig_load_scnptr
== 0L)
444 if (lnnoptr
&& lnnoptr
< orig_load_scnptr
) /* if there is line number info */
445 lseek (a_out
, lnnoptr
, SEEK_SET
); /* start copying from there */
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
)
463 /* ****************************************************************
466 * After successfully building the new a.out, mark it executable
469 mark_x (const char *name
)
473 int new = 0; /* for PERROR */
477 if (stat (name
, &sbuf
) == -1)
481 sbuf
.st_mode
|= 0111 & ~um
;
482 if (chmod (name
, sbuf
.st_mode
) == -1)
487 adjust_lnnoptrs (int writedesc
, int readdesc
, const char *new_name
)
492 struct syment symentry
;
493 union auxent auxentry
;
495 if (!lnnoptr
|| !f_hdr
.f_symptr
)
498 if ((new = open (new_name
, O_RDWR
)) < 0)
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
);
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
);
534 unrelocate_symbols (int new, int a_out
,
535 const char *a_name
, const char *new_name
)
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
;
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);
550 if (load_scnptr
== 0)
553 lseek (a_out
, orig_load_scnptr
, SEEK_SET
);
554 if (read (a_out
, &ldhdr
, sizeof (ldhdr
)) != sizeof (ldhdr
))
559 #define SYMNDX_TEXT 0
560 #define SYMNDX_DATA 1
563 for (i
= 0; i
< ldhdr
.l_nreloc
; i
++)
566 orig_load_scnptr
+ LDHDRSZ
+ LDSYMSZ
*ldhdr
.l_nsyms
+ LDRELSZ
*i
,
569 if (read (a_out
, &ldrel
, LDRELSZ
) != LDRELSZ
)
574 /* move the BSS loader symbols to the DATA segment */
575 if (ldrel
.l_symndx
== SYMNDX_BSS
)
577 ldrel
.l_symndx
= SYMNDX_DATA
;
580 load_scnptr
+ LDHDRSZ
+ LDSYMSZ
*ldhdr
.l_nsyms
+ LDRELSZ
*i
,
583 if (write (new, &ldrel
, LDRELSZ
) != LDRELSZ
)
589 if (ldrel
.l_rsecnm
== f_ohdr
.o_sndata
)
594 orig_data_scnptr
+ (ldrel
.l_vaddr
- f_ohdr
.data_start
),
597 if (read (a_out
, (void *) &orig_int
, sizeof (orig_int
))
598 != sizeof (orig_int
))
603 p
= (int *) (ldrel
.l_vaddr
+ d_reloc
);
605 switch (ldrel
.l_symndx
) {
607 orig_int
= * p
- t_reloc
;
612 orig_int
= * p
- d_reloc
;
619 data_scnptr
+ (ldrel
.l_vaddr
- f_ohdr
.data_start
),
621 if (write (new, (void *) &orig_int
, sizeof (orig_int
))
622 != sizeof (orig_int
))
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.
642 return ((char *) 0x10000000);