diff options
| author | Pip Cet | 2024-08-20 18:29:53 +0000 |
|---|---|---|
| committer | Stefan Kangas | 2024-12-12 22:48:10 +0100 |
| commit | 7ce34a3bcf5ed277ef37aa75e1ccbd858543b6cf (patch) | |
| tree | 4654efb303767c4f67d68d94ac4bb23d8f3e5f2d /src | |
| parent | 9ccd459e8452cc9e6e81e53f26bbeef20d2d5bb7 (diff) | |
| download | emacs-7ce34a3bcf5ed277ef37aa75e1ccbd858543b6cf.tar.gz emacs-7ce34a3bcf5ed277ef37aa75e1ccbd858543b6cf.zip | |
Unexec removal: Remove obsolete files
* src/sheap.c, src/sheap.h, src/unexec.h, src/unexaix.c, unexcoff.c:
* src/unexcw.c, src/unexelf.c, src/unexhp9k800.c, src/unexmacosx.c:
* src/unexsol.c, src/unexw32.c: Remove files.
Diffstat (limited to 'src')
| -rw-r--r-- | src/sheap.c | 79 | ||||
| -rw-r--r-- | src/sheap.h | 30 | ||||
| -rw-r--r-- | src/unexaix.c | 611 | ||||
| -rw-r--r-- | src/unexcoff.c | 540 | ||||
| -rw-r--r-- | src/unexcw.c | 302 | ||||
| -rw-r--r-- | src/unexec.h | 4 | ||||
| -rw-r--r-- | src/unexelf.c | 658 | ||||
| -rw-r--r-- | src/unexhp9k800.c | 324 | ||||
| -rw-r--r-- | src/unexmacosx.c | 1406 | ||||
| -rw-r--r-- | src/unexsol.c | 28 | ||||
| -rw-r--r-- | src/unexw32.c | 684 |
11 files changed, 0 insertions, 4666 deletions
diff --git a/src/sheap.c b/src/sheap.c deleted file mode 100644 index bab70c4e343..00000000000 --- a/src/sheap.c +++ /dev/null | |||
| @@ -1,79 +0,0 @@ | |||
| 1 | /* simulate `sbrk' with an array in .bss, for `unexec' support for Cygwin; | ||
| 2 | complete rewrite of xemacs Cygwin `unexec' code | ||
| 3 | |||
| 4 | Copyright (C) 2004-2024 Free Software Foundation, Inc. | ||
| 5 | |||
| 6 | This file is part of GNU Emacs. | ||
| 7 | |||
| 8 | GNU Emacs is free software: you can redistribute it and/or modify | ||
| 9 | it under the terms of the GNU General Public License as published by | ||
| 10 | the Free Software Foundation, either version 3 of the License, or (at | ||
| 11 | your option) any later version. | ||
| 12 | |||
| 13 | GNU Emacs is distributed in the hope that it will be useful, | ||
| 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | GNU General Public License for more details. | ||
| 17 | |||
| 18 | You should have received a copy of the GNU General Public License | ||
| 19 | along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | ||
| 20 | |||
| 21 | #include <config.h> | ||
| 22 | |||
| 23 | #include "sheap.h" | ||
| 24 | |||
| 25 | #include <stdio.h> | ||
| 26 | #include "lisp.h" | ||
| 27 | #include <unistd.h> | ||
| 28 | #include <stdlib.h> /* for exit */ | ||
| 29 | |||
| 30 | static int debug_sheap; | ||
| 31 | |||
| 32 | char bss_sbrk_buffer[STATIC_HEAP_SIZE]; | ||
| 33 | char *max_bss_sbrk_ptr; | ||
| 34 | |||
| 35 | void * | ||
| 36 | bss_sbrk (ptrdiff_t request_size) | ||
| 37 | { | ||
| 38 | static char *bss_sbrk_ptr; | ||
| 39 | |||
| 40 | if (!bss_sbrk_ptr) | ||
| 41 | { | ||
| 42 | max_bss_sbrk_ptr = bss_sbrk_ptr = bss_sbrk_buffer; | ||
| 43 | #ifdef CYGWIN | ||
| 44 | /* Force space for fork to work. */ | ||
| 45 | sbrk (4096); | ||
| 46 | #endif | ||
| 47 | } | ||
| 48 | |||
| 49 | int used = bss_sbrk_ptr - bss_sbrk_buffer; | ||
| 50 | |||
| 51 | if (request_size < -used) | ||
| 52 | { | ||
| 53 | printf (("attempt to free too much: " | ||
| 54 | "avail %d used %d failed request %"pD"d\n"), | ||
| 55 | STATIC_HEAP_SIZE, used, request_size); | ||
| 56 | exit (-1); | ||
| 57 | return 0; | ||
| 58 | } | ||
| 59 | else if (STATIC_HEAP_SIZE - used < request_size) | ||
| 60 | { | ||
| 61 | printf ("static heap exhausted: avail %d used %d failed request %"pD"d\n", | ||
| 62 | STATIC_HEAP_SIZE, used, request_size); | ||
| 63 | exit (-1); | ||
| 64 | return 0; | ||
| 65 | } | ||
| 66 | |||
| 67 | void *ret = bss_sbrk_ptr; | ||
| 68 | bss_sbrk_ptr += request_size; | ||
| 69 | if (max_bss_sbrk_ptr < bss_sbrk_ptr) | ||
| 70 | max_bss_sbrk_ptr = bss_sbrk_ptr; | ||
| 71 | if (debug_sheap) | ||
| 72 | { | ||
| 73 | if (request_size < 0) | ||
| 74 | printf ("freed size %"pD"d\n", request_size); | ||
| 75 | else | ||
| 76 | printf ("allocated %p size %"pD"d\n", ret, request_size); | ||
| 77 | } | ||
| 78 | return ret; | ||
| 79 | } | ||
diff --git a/src/sheap.h b/src/sheap.h deleted file mode 100644 index 92f7ba5e857..00000000000 --- a/src/sheap.h +++ /dev/null | |||
| @@ -1,30 +0,0 @@ | |||
| 1 | /* Static heap allocation for GNU Emacs. | ||
| 2 | |||
| 3 | Copyright 2016-2024 Free Software Foundation, 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 <https://www.gnu.org/licenses/>. */ | ||
| 19 | |||
| 20 | #include <stddef.h> | ||
| 21 | #include "lisp.h" | ||
| 22 | |||
| 23 | /* Size of the static heap. Guess a value that is probably too large, | ||
| 24 | by up to a factor of four or so. Typically the unused part is not | ||
| 25 | paged in and so does not cost much. */ | ||
| 26 | enum { STATIC_HEAP_SIZE = sizeof (Lisp_Object) << 24 }; | ||
| 27 | |||
| 28 | extern char bss_sbrk_buffer[STATIC_HEAP_SIZE]; | ||
| 29 | extern char *max_bss_sbrk_ptr; | ||
| 30 | extern void *bss_sbrk (ptrdiff_t); | ||
diff --git a/src/unexaix.c b/src/unexaix.c deleted file mode 100644 index f9bc39cf927..00000000000 --- a/src/unexaix.c +++ /dev/null | |||
| @@ -1,611 +0,0 @@ | |||
| 1 | /* Dump an executable file. | ||
| 2 | Copyright (C) 1985-1988, 1999, 2001-2024 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 (at | ||
| 10 | 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 <https://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 | #include "lisp.h" | ||
| 46 | |||
| 47 | #define PERROR(file) report_error (file, new) | ||
| 48 | #include <a.out.h> | ||
| 49 | /* Define getpagesize () if the system does not. | ||
| 50 | Note that this may depend on symbols defined in a.out.h | ||
| 51 | */ | ||
| 52 | #include "getpagesize.h" | ||
| 53 | |||
| 54 | #include <sys/types.h> | ||
| 55 | #include <inttypes.h> | ||
| 56 | #include <stdarg.h> | ||
| 57 | #include <stdio.h> | ||
| 58 | #include <errno.h> | ||
| 59 | #include <unistd.h> | ||
| 60 | #include <fcntl.h> | ||
| 61 | |||
| 62 | extern char _data[]; | ||
| 63 | extern char _text[]; | ||
| 64 | |||
| 65 | #include <filehdr.h> | ||
| 66 | #include <aouthdr.h> | ||
| 67 | #include <scnhdr.h> | ||
| 68 | #include <syms.h> | ||
| 69 | |||
| 70 | static struct filehdr f_hdr; /* File header */ | ||
| 71 | static struct aouthdr f_ohdr; /* Optional file header (a.out) */ | ||
| 72 | static off_t bias; /* Bias to add for growth */ | ||
| 73 | static off_t lnnoptr; /* Pointer to line-number info within file */ | ||
| 74 | |||
| 75 | static off_t text_scnptr; | ||
| 76 | static off_t data_scnptr; | ||
| 77 | #define ALIGN(val, pwr) (((val) + ((1L<<(pwr))-1)) & ~((1L<<(pwr))-1)) | ||
| 78 | static off_t load_scnptr; | ||
| 79 | static off_t orig_load_scnptr; | ||
| 80 | static off_t orig_data_scnptr; | ||
| 81 | static int unrelocate_symbols (int, int, const char *, const char *); | ||
| 82 | |||
| 83 | #ifndef MAX_SECTIONS | ||
| 84 | #define MAX_SECTIONS 10 | ||
| 85 | #endif | ||
| 86 | |||
| 87 | static int adjust_lnnoptrs (int, int, const char *); | ||
| 88 | |||
| 89 | static int pagemask; | ||
| 90 | |||
| 91 | #include "lisp.h" | ||
| 92 | |||
| 93 | static _Noreturn void | ||
| 94 | report_error (const char *file, int fd) | ||
| 95 | { | ||
| 96 | int err = errno; | ||
| 97 | if (fd) | ||
| 98 | emacs_close (fd); | ||
| 99 | report_file_errno ("Cannot unexec", build_string (file), err); | ||
| 100 | } | ||
| 101 | |||
| 102 | #define ERROR0(msg) report_error_1 (new, msg) | ||
| 103 | #define ERROR1(msg,x) report_error_1 (new, msg, x) | ||
| 104 | #define ERROR2(msg,x,y) report_error_1 (new, msg, x, y) | ||
| 105 | |||
| 106 | static _Noreturn void ATTRIBUTE_FORMAT_PRINTF (2, 3) | ||
| 107 | report_error_1 (int fd, const char *msg, ...) | ||
| 108 | { | ||
| 109 | va_list ap; | ||
| 110 | emacs_close (fd); | ||
| 111 | va_start (ap, msg); | ||
| 112 | verror (msg, ap); | ||
| 113 | va_end (ap); | ||
| 114 | } | ||
| 115 | |||
| 116 | static int make_hdr (int, int, const char *, const char *); | ||
| 117 | static void mark_x (const char *); | ||
| 118 | static int copy_text_and_data (int); | ||
| 119 | static int copy_sym (int, int, const char *, const char *); | ||
| 120 | static void write_segment (int, char *, char *); | ||
| 121 | |||
| 122 | /* **************************************************************** | ||
| 123 | * unexec | ||
| 124 | * | ||
| 125 | * driving logic. | ||
| 126 | */ | ||
| 127 | void | ||
| 128 | unexec (const char *new_name, const char *a_name) | ||
| 129 | { | ||
| 130 | int new = -1, a_out = -1; | ||
| 131 | |||
| 132 | if (a_name && (a_out = emacs_open (a_name, O_RDONLY, 0)) < 0) | ||
| 133 | { | ||
| 134 | PERROR (a_name); | ||
| 135 | } | ||
| 136 | if ((new = emacs_open (new_name, O_WRONLY | O_CREAT | O_TRUNC, 0777)) < 0) | ||
| 137 | { | ||
| 138 | PERROR (new_name); | ||
| 139 | } | ||
| 140 | if (make_hdr (new, a_out, | ||
| 141 | a_name, new_name) < 0 | ||
| 142 | || copy_text_and_data (new) < 0 | ||
| 143 | || copy_sym (new, a_out, a_name, new_name) < 0 | ||
| 144 | || adjust_lnnoptrs (new, a_out, new_name) < 0 | ||
| 145 | || unrelocate_symbols (new, a_out, a_name, new_name) < 0) | ||
| 146 | { | ||
| 147 | emacs_close (new); | ||
| 148 | return; | ||
| 149 | } | ||
| 150 | |||
| 151 | emacs_close (new); | ||
| 152 | if (a_out >= 0) | ||
| 153 | emacs_close (a_out); | ||
| 154 | } | ||
| 155 | |||
| 156 | /* **************************************************************** | ||
| 157 | * make_hdr | ||
| 158 | * | ||
| 159 | * Make the header in the new a.out from the header in core. | ||
| 160 | * Modify the text and data sizes. | ||
| 161 | */ | ||
| 162 | static int | ||
| 163 | make_hdr (int new, int a_out, | ||
| 164 | const char *a_name, const char *new_name) | ||
| 165 | { | ||
| 166 | int scns; | ||
| 167 | uintptr_t bss_start; | ||
| 168 | uintptr_t data_start; | ||
| 169 | |||
| 170 | struct scnhdr section[MAX_SECTIONS]; | ||
| 171 | struct scnhdr * f_thdr; /* Text section header */ | ||
| 172 | struct scnhdr * f_dhdr; /* Data section header */ | ||
| 173 | struct scnhdr * f_bhdr; /* Bss section header */ | ||
| 174 | struct scnhdr * f_lhdr; /* Loader section header */ | ||
| 175 | struct scnhdr * f_tchdr; /* Typechk section header */ | ||
| 176 | struct scnhdr * f_dbhdr; /* Debug section header */ | ||
| 177 | struct scnhdr * f_xhdr; /* Except section header */ | ||
| 178 | |||
| 179 | load_scnptr = orig_load_scnptr = lnnoptr = 0; | ||
| 180 | pagemask = getpagesize () - 1; | ||
| 181 | |||
| 182 | /* Adjust text/data boundary. */ | ||
| 183 | data_start = (uintptr_t) _data; | ||
| 184 | |||
| 185 | data_start = data_start & ~pagemask; /* (Down) to page boundary. */ | ||
| 186 | |||
| 187 | bss_start = (uintptr_t) 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 (0x%"PRIxPTR | ||
| 193 | ") can't be greater than bss_start (0x%"PRIxPTR")"), | ||
| 194 | data_start, bss_start); | ||
| 195 | } | ||
| 196 | |||
| 197 | /* Salvage as much info from the existing file as possible */ | ||
| 198 | f_thdr = NULL; f_dhdr = NULL; f_bhdr = NULL; | ||
| 199 | f_lhdr = NULL; f_tchdr = NULL; f_dbhdr = NULL; f_xhdr = NULL; | ||
| 200 | if (a_out >= 0) | ||
| 201 | { | ||
| 202 | if (read (a_out, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr)) | ||
| 203 | { | ||
| 204 | PERROR (a_name); | ||
| 205 | } | ||
| 206 | if (f_hdr.f_opthdr > 0) | ||
| 207 | { | ||
| 208 | if (read (a_out, &f_ohdr, sizeof (f_ohdr)) != sizeof (f_ohdr)) | ||
| 209 | { | ||
| 210 | PERROR (a_name); | ||
| 211 | } | ||
| 212 | } | ||
| 213 | if (f_hdr.f_nscns > MAX_SECTIONS) | ||
| 214 | { | ||
| 215 | ERROR0 ("unexec: too many section headers -- increase MAX_SECTIONS"); | ||
| 216 | } | ||
| 217 | /* Loop through section headers */ | ||
| 218 | for (scns = 0; scns < f_hdr.f_nscns; scns++) { | ||
| 219 | struct scnhdr *s = §ion[scns]; | ||
| 220 | if (read (a_out, s, sizeof (*s)) != sizeof (*s)) | ||
| 221 | { | ||
| 222 | PERROR (a_name); | ||
| 223 | } | ||
| 224 | |||
| 225 | #define CHECK_SCNHDR(ptr, name, flags) \ | ||
| 226 | if (strcmp (s->s_name, name) == 0) { \ | ||
| 227 | if (s->s_flags != flags) { \ | ||
| 228 | fprintf (stderr, "unexec: %lx flags where %x expected in %s section.\n", \ | ||
| 229 | (unsigned long)s->s_flags, flags, name); \ | ||
| 230 | } \ | ||
| 231 | if (ptr) { \ | ||
| 232 | fprintf (stderr, "unexec: duplicate section header for section %s.\n", \ | ||
| 233 | name); \ | ||
| 234 | } \ | ||
| 235 | ptr = s; \ | ||
| 236 | } | ||
| 237 | CHECK_SCNHDR (f_thdr, _TEXT, STYP_TEXT); | ||
| 238 | CHECK_SCNHDR (f_dhdr, _DATA, STYP_DATA); | ||
| 239 | CHECK_SCNHDR (f_bhdr, _BSS, STYP_BSS); | ||
| 240 | CHECK_SCNHDR (f_lhdr, _LOADER, STYP_LOADER); | ||
| 241 | CHECK_SCNHDR (f_dbhdr, _DEBUG, STYP_DEBUG); | ||
| 242 | CHECK_SCNHDR (f_tchdr, _TYPCHK, STYP_TYPCHK); | ||
| 243 | CHECK_SCNHDR (f_xhdr, _EXCEPT, STYP_EXCEPT); | ||
| 244 | } | ||
| 245 | |||
| 246 | if (f_thdr == 0) | ||
| 247 | { | ||
| 248 | ERROR1 ("unexec: couldn't find \"%s\" section", _TEXT); | ||
| 249 | } | ||
| 250 | if (f_dhdr == 0) | ||
| 251 | { | ||
| 252 | ERROR1 ("unexec: couldn't find \"%s\" section", _DATA); | ||
| 253 | } | ||
| 254 | if (f_bhdr == 0) | ||
| 255 | { | ||
| 256 | ERROR1 ("unexec: couldn't find \"%s\" section", _BSS); | ||
| 257 | } | ||
| 258 | } | ||
| 259 | else | ||
| 260 | { | ||
| 261 | ERROR0 ("can't build a COFF file from scratch yet"); | ||
| 262 | } | ||
| 263 | orig_data_scnptr = f_dhdr->s_scnptr; | ||
| 264 | orig_load_scnptr = f_lhdr ? f_lhdr->s_scnptr : 0; | ||
| 265 | |||
| 266 | /* Now we alter the contents of all the f_*hdr variables | ||
| 267 | to correspond to what we want to dump. */ | ||
| 268 | |||
| 269 | /* Indicate that the reloc information is no longer valid for ld (bind); | ||
| 270 | we only update it enough to fake out the exec-time loader. */ | ||
| 271 | f_hdr.f_flags |= (F_RELFLG | F_EXEC); | ||
| 272 | |||
| 273 | f_ohdr.dsize = bss_start - f_ohdr.data_start; | ||
| 274 | f_ohdr.bsize = 0; | ||
| 275 | |||
| 276 | f_dhdr->s_size = f_ohdr.dsize; | ||
| 277 | f_bhdr->s_size = f_ohdr.bsize; | ||
| 278 | f_bhdr->s_paddr = f_ohdr.data_start + f_ohdr.dsize; | ||
| 279 | f_bhdr->s_vaddr = f_ohdr.data_start + f_ohdr.dsize; | ||
| 280 | |||
| 281 | /* fix scnptr's */ | ||
| 282 | { | ||
| 283 | off_t ptr = section[0].s_scnptr; | ||
| 284 | |||
| 285 | bias = -1; | ||
| 286 | for (scns = 0; scns < f_hdr.f_nscns; scns++) | ||
| 287 | { | ||
| 288 | struct scnhdr *s = §ion[scns]; | ||
| 289 | |||
| 290 | if (s->s_flags & STYP_PAD) /* .pad sections omitted in AIX 4.1 */ | ||
| 291 | { | ||
| 292 | /* | ||
| 293 | * the text_start should probably be o_algntext but that doesn't | ||
| 294 | * seem to change | ||
| 295 | */ | ||
| 296 | if (f_ohdr.text_start != 0) /* && scns != 0 */ | ||
| 297 | { | ||
| 298 | s->s_size = 512 - (ptr % 512); | ||
| 299 | if (s->s_size == 512) | ||
| 300 | s->s_size = 0; | ||
| 301 | } | ||
| 302 | s->s_scnptr = ptr; | ||
| 303 | } | ||
| 304 | else if (s->s_flags & STYP_DATA) | ||
| 305 | s->s_scnptr = ptr; | ||
| 306 | else if (!(s->s_flags & (STYP_TEXT | STYP_BSS))) | ||
| 307 | { | ||
| 308 | if (bias == -1) /* if first section after bss */ | ||
| 309 | bias = ptr - s->s_scnptr; | ||
| 310 | |||
| 311 | s->s_scnptr += bias; | ||
| 312 | ptr = s->s_scnptr; | ||
| 313 | } | ||
| 314 | |||
| 315 | ptr = ptr + s->s_size; | ||
| 316 | } | ||
| 317 | } | ||
| 318 | |||
| 319 | /* fix other pointers */ | ||
| 320 | for (scns = 0; scns < f_hdr.f_nscns; scns++) | ||
| 321 | { | ||
| 322 | struct scnhdr *s = §ion[scns]; | ||
| 323 | |||
| 324 | if (s->s_relptr != 0) | ||
| 325 | { | ||
| 326 | s->s_relptr += bias; | ||
| 327 | } | ||
| 328 | if (s->s_lnnoptr != 0) | ||
| 329 | { | ||
| 330 | if (lnnoptr == 0) lnnoptr = s->s_lnnoptr; | ||
| 331 | s->s_lnnoptr += bias; | ||
| 332 | } | ||
| 333 | } | ||
| 334 | |||
| 335 | if (f_hdr.f_symptr > 0L) | ||
| 336 | { | ||
| 337 | f_hdr.f_symptr += bias; | ||
| 338 | } | ||
| 339 | |||
| 340 | text_scnptr = f_thdr->s_scnptr; | ||
| 341 | data_scnptr = f_dhdr->s_scnptr; | ||
| 342 | load_scnptr = f_lhdr ? f_lhdr->s_scnptr : 0; | ||
| 343 | |||
| 344 | if (write (new, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr)) | ||
| 345 | { | ||
| 346 | PERROR (new_name); | ||
| 347 | } | ||
| 348 | |||
| 349 | if (f_hdr.f_opthdr > 0) | ||
| 350 | { | ||
| 351 | if (write (new, &f_ohdr, sizeof (f_ohdr)) != sizeof (f_ohdr)) | ||
| 352 | { | ||
| 353 | PERROR (new_name); | ||
| 354 | } | ||
| 355 | } | ||
| 356 | |||
| 357 | for (scns = 0; scns < f_hdr.f_nscns; scns++) { | ||
| 358 | struct scnhdr *s = §ion[scns]; | ||
| 359 | if (write (new, s, sizeof (*s)) != sizeof (*s)) | ||
| 360 | { | ||
| 361 | PERROR (new_name); | ||
| 362 | } | ||
| 363 | } | ||
| 364 | |||
| 365 | return (0); | ||
| 366 | } | ||
| 367 | |||
| 368 | /* **************************************************************** | ||
| 369 | |||
| 370 | * | ||
| 371 | * Copy the text and data segments from memory to the new a.out | ||
| 372 | */ | ||
| 373 | static int | ||
| 374 | copy_text_and_data (int new) | ||
| 375 | { | ||
| 376 | char *end; | ||
| 377 | char *ptr; | ||
| 378 | |||
| 379 | lseek (new, text_scnptr, SEEK_SET); | ||
| 380 | ptr = _text; | ||
| 381 | end = ptr + f_ohdr.tsize; | ||
| 382 | write_segment (new, ptr, end); | ||
| 383 | |||
| 384 | lseek (new, data_scnptr, SEEK_SET); | ||
| 385 | ptr = (char *) (ptrdiff_t) f_ohdr.data_start; | ||
| 386 | end = ptr + f_ohdr.dsize; | ||
| 387 | write_segment (new, ptr, end); | ||
| 388 | |||
| 389 | return 0; | ||
| 390 | } | ||
| 391 | |||
| 392 | #define UnexBlockSz (1<<12) /* read/write block size */ | ||
| 393 | static void | ||
| 394 | write_segment (int new, char *ptr, char *end) | ||
| 395 | { | ||
| 396 | int i, nwrite, ret; | ||
| 397 | char zeros[UnexBlockSz]; | ||
| 398 | |||
| 399 | for (i = 0; ptr < end;) | ||
| 400 | { | ||
| 401 | /* distance to next block. */ | ||
| 402 | nwrite = (((ptrdiff_t) ptr + UnexBlockSz) & -UnexBlockSz) - (ptrdiff_t) 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 | int write_errno = errno; | ||
| 418 | char buf[1000]; | ||
| 419 | void *addr = ptr; | ||
| 420 | sprintf (buf, | ||
| 421 | "unexec write failure: addr %p, fileno %d, size 0x%x, wrote 0x%x, errno %d", | ||
| 422 | addr, new, nwrite, ret, errno); | ||
| 423 | errno = write_errno; | ||
| 424 | PERROR (buf); | ||
| 425 | } | ||
| 426 | i += nwrite; | ||
| 427 | ptr += nwrite; | ||
| 428 | } | ||
| 429 | } | ||
| 430 | |||
| 431 | /* **************************************************************** | ||
| 432 | * copy_sym | ||
| 433 | * | ||
| 434 | * Copy the relocation information and symbol table from the a.out to the new | ||
| 435 | */ | ||
| 436 | static int | ||
| 437 | copy_sym (int new, int a_out, const char *a_name, const char *new_name) | ||
| 438 | { | ||
| 439 | char page[UnexBlockSz]; | ||
| 440 | int n; | ||
| 441 | |||
| 442 | if (a_out < 0) | ||
| 443 | return 0; | ||
| 444 | |||
| 445 | if (orig_load_scnptr == 0L) | ||
| 446 | return 0; | ||
| 447 | |||
| 448 | if (lnnoptr && lnnoptr < orig_load_scnptr) /* if there is line number info */ | ||
| 449 | lseek (a_out, lnnoptr, SEEK_SET); /* start copying from there */ | ||
| 450 | else | ||
| 451 | lseek (a_out, orig_load_scnptr, SEEK_SET); /* Position a.out to symtab. */ | ||
| 452 | |||
| 453 | while ((n = read (a_out, page, sizeof page)) > 0) | ||
| 454 | { | ||
| 455 | if (write (new, page, n) != n) | ||
| 456 | { | ||
| 457 | PERROR (new_name); | ||
| 458 | } | ||
| 459 | } | ||
| 460 | if (n < 0) | ||
| 461 | { | ||
| 462 | PERROR (a_name); | ||
| 463 | } | ||
| 464 | return 0; | ||
| 465 | } | ||
| 466 | |||
| 467 | static int | ||
| 468 | adjust_lnnoptrs (int writedesc, int readdesc, const char *new_name) | ||
| 469 | { | ||
| 470 | int nsyms; | ||
| 471 | int naux; | ||
| 472 | int new; | ||
| 473 | struct syment symentry; | ||
| 474 | union auxent auxentry; | ||
| 475 | |||
| 476 | if (!lnnoptr || !f_hdr.f_symptr) | ||
| 477 | return 0; | ||
| 478 | |||
| 479 | if ((new = emacs_open (new_name, O_RDWR, 0)) < 0) | ||
| 480 | { | ||
| 481 | PERROR (new_name); | ||
| 482 | return -1; | ||
| 483 | } | ||
| 484 | |||
| 485 | lseek (new, f_hdr.f_symptr, SEEK_SET); | ||
| 486 | for (nsyms = 0; nsyms < f_hdr.f_nsyms; nsyms++) | ||
| 487 | { | ||
| 488 | read (new, &symentry, SYMESZ); | ||
| 489 | if (symentry.n_sclass == C_BINCL || symentry.n_sclass == C_EINCL) | ||
| 490 | { | ||
| 491 | symentry.n_value += bias; | ||
| 492 | lseek (new, -SYMESZ, SEEK_CUR); | ||
| 493 | write (new, &symentry, SYMESZ); | ||
| 494 | } | ||
| 495 | |||
| 496 | for (naux = symentry.n_numaux; naux-- != 0; ) | ||
| 497 | { | ||
| 498 | read (new, &auxentry, AUXESZ); | ||
| 499 | nsyms++; | ||
| 500 | if (naux != 0 /* skip csect auxentry (last entry) */ | ||
| 501 | && (symentry.n_sclass == C_EXT || symentry.n_sclass == C_HIDEXT)) | ||
| 502 | { | ||
| 503 | auxentry.x_sym.x_fcnary.x_fcn.x_lnnoptr += bias; | ||
| 504 | lseek (new, -AUXESZ, SEEK_CUR); | ||
| 505 | write (new, &auxentry, AUXESZ); | ||
| 506 | } | ||
| 507 | } | ||
| 508 | } | ||
| 509 | emacs_close (new); | ||
| 510 | |||
| 511 | return 0; | ||
| 512 | } | ||
| 513 | |||
| 514 | static int | ||
| 515 | unrelocate_symbols (int new, int a_out, | ||
| 516 | const char *a_name, const char *new_name) | ||
| 517 | { | ||
| 518 | int i; | ||
| 519 | LDHDR ldhdr; | ||
| 520 | LDREL ldrel; | ||
| 521 | off_t t_reloc = (intptr_t) _text - f_ohdr.text_start; | ||
| 522 | #ifndef ALIGN_DATA_RELOC | ||
| 523 | off_t d_reloc = (intptr_t) _data - f_ohdr.data_start; | ||
| 524 | #else | ||
| 525 | /* This worked (and was needed) before AIX 4.2. | ||
| 526 | I have no idea why. -- Mike */ | ||
| 527 | off_t d_reloc = (intptr_t) _data - ALIGN (f_ohdr.data_start, 2); | ||
| 528 | #endif | ||
| 529 | int * p; | ||
| 530 | |||
| 531 | if (load_scnptr == 0) | ||
| 532 | return 0; | ||
| 533 | |||
| 534 | lseek (a_out, orig_load_scnptr, SEEK_SET); | ||
| 535 | if (read (a_out, &ldhdr, sizeof (ldhdr)) != sizeof (ldhdr)) | ||
| 536 | { | ||
| 537 | PERROR (new_name); | ||
| 538 | } | ||
| 539 | |||
| 540 | #define SYMNDX_TEXT 0 | ||
| 541 | #define SYMNDX_DATA 1 | ||
| 542 | #define SYMNDX_BSS 2 | ||
| 543 | |||
| 544 | for (i = 0; i < ldhdr.l_nreloc; i++) | ||
| 545 | { | ||
| 546 | lseek (a_out, | ||
| 547 | orig_load_scnptr + LDHDRSZ + LDSYMSZ*ldhdr.l_nsyms + LDRELSZ*i, | ||
| 548 | SEEK_SET); | ||
| 549 | |||
| 550 | if (read (a_out, &ldrel, LDRELSZ) != LDRELSZ) | ||
| 551 | { | ||
| 552 | PERROR (a_name); | ||
| 553 | } | ||
| 554 | |||
| 555 | /* move the BSS loader symbols to the DATA segment */ | ||
| 556 | if (ldrel.l_symndx == SYMNDX_BSS) | ||
| 557 | { | ||
| 558 | ldrel.l_symndx = SYMNDX_DATA; | ||
| 559 | |||
| 560 | lseek (new, | ||
| 561 | load_scnptr + LDHDRSZ + LDSYMSZ*ldhdr.l_nsyms + LDRELSZ*i, | ||
| 562 | SEEK_SET); | ||
| 563 | |||
| 564 | if (write (new, &ldrel, LDRELSZ) != LDRELSZ) | ||
| 565 | { | ||
| 566 | PERROR (new_name); | ||
| 567 | } | ||
| 568 | } | ||
| 569 | |||
| 570 | if (ldrel.l_rsecnm == f_ohdr.o_sndata) | ||
| 571 | { | ||
| 572 | int orig_int; | ||
| 573 | |||
| 574 | lseek (a_out, | ||
| 575 | orig_data_scnptr + (ldrel.l_vaddr - f_ohdr.data_start), | ||
| 576 | SEEK_SET); | ||
| 577 | |||
| 578 | if (read (a_out, (void *) &orig_int, sizeof (orig_int)) | ||
| 579 | != sizeof (orig_int)) | ||
| 580 | { | ||
| 581 | PERROR (a_name); | ||
| 582 | } | ||
| 583 | |||
| 584 | p = (int *) (intptr_t) (ldrel.l_vaddr + d_reloc); | ||
| 585 | |||
| 586 | switch (ldrel.l_symndx) { | ||
| 587 | case SYMNDX_TEXT: | ||
| 588 | orig_int = * p - t_reloc; | ||
| 589 | break; | ||
| 590 | |||
| 591 | case SYMNDX_DATA: | ||
| 592 | case SYMNDX_BSS: | ||
| 593 | orig_int = * p - d_reloc; | ||
| 594 | break; | ||
| 595 | } | ||
| 596 | |||
| 597 | if (orig_int != * p) | ||
| 598 | { | ||
| 599 | lseek (new, | ||
| 600 | data_scnptr + (ldrel.l_vaddr - f_ohdr.data_start), | ||
| 601 | SEEK_SET); | ||
| 602 | if (write (new, (void *) &orig_int, sizeof (orig_int)) | ||
| 603 | != sizeof (orig_int)) | ||
| 604 | { | ||
| 605 | PERROR (new_name); | ||
| 606 | } | ||
| 607 | } | ||
| 608 | } | ||
| 609 | } | ||
| 610 | return 0; | ||
| 611 | } | ||
diff --git a/src/unexcoff.c b/src/unexcoff.c deleted file mode 100644 index 4a981da4a04..00000000000 --- a/src/unexcoff.c +++ /dev/null | |||
| @@ -1,540 +0,0 @@ | |||
| 1 | /* Copyright (C) 1985-1988, 1992-1994, 2001-2024 Free Software | ||
| 2 | * Foundation, Inc. | ||
| 3 | |||
| 4 | This file is part of GNU Emacs. | ||
| 5 | |||
| 6 | GNU Emacs is free software: you can redistribute it and/or modify | ||
| 7 | it under the terms of the GNU General Public License as published by | ||
| 8 | the Free Software Foundation, either version 3 of the License, or (at | ||
| 9 | your option) any later version. | ||
| 10 | |||
| 11 | GNU Emacs is distributed in the hope that it will be useful, | ||
| 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | GNU General Public License for more details. | ||
| 15 | |||
| 16 | You should have received a copy of the GNU General Public License | ||
| 17 | along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | ||
| 18 | |||
| 19 | |||
| 20 | /* | ||
| 21 | * unexcoff.c - Convert a running program into an a.out or COFF file. | ||
| 22 | * | ||
| 23 | * ================================================================== | ||
| 24 | * Note: This file is currently used only by the MSDOS (a.k.a. DJGPP) | ||
| 25 | * build of Emacs. If you are not interested in the MSDOS build, you | ||
| 26 | * are looking at the wrong version of unexec! | ||
| 27 | * ================================================================== | ||
| 28 | * | ||
| 29 | * Author: Spencer W. Thomas | ||
| 30 | * Computer Science Dept. | ||
| 31 | * University of Utah | ||
| 32 | * Date: Tue Mar 2 1982 | ||
| 33 | * Originally under the name unexec.c. | ||
| 34 | * Modified heavily since then. | ||
| 35 | * | ||
| 36 | * Synopsis: | ||
| 37 | * unexec (const char *new_name, const char *old_name); | ||
| 38 | * | ||
| 39 | * Takes a snapshot of the program and makes an a.out format file in the | ||
| 40 | * file named by the string argument new_name. | ||
| 41 | * If a_name is non-NULL, the symbol table will be taken from the given file. | ||
| 42 | * On some machines, an existing a_name file is required. | ||
| 43 | * | ||
| 44 | * If you make improvements I'd like to get them too. | ||
| 45 | * harpo!utah-cs!thomas, thomas@Utah-20 | ||
| 46 | * | ||
| 47 | */ | ||
| 48 | |||
| 49 | /* Modified to support SysVr3 shared libraries by James Van Artsdalen | ||
| 50 | * of Dell Computer Corporation. james@bigtex.cactus.org. | ||
| 51 | */ | ||
| 52 | |||
| 53 | #include <config.h> | ||
| 54 | #include "unexec.h" | ||
| 55 | #include "lisp.h" | ||
| 56 | |||
| 57 | #define PERROR(file) report_error (file, new) | ||
| 58 | |||
| 59 | #ifdef HAVE_UNEXEC /* all rest of file! */ | ||
| 60 | |||
| 61 | #ifdef HAVE_COFF_H | ||
| 62 | #include <coff.h> | ||
| 63 | #ifdef MSDOS | ||
| 64 | #include <fcntl.h> /* for O_RDONLY, O_RDWR */ | ||
| 65 | #include <crt0.h> /* for _crt0_startup_flags and its bits */ | ||
| 66 | #include <sys/exceptn.h> | ||
| 67 | static int save_djgpp_startup_flags; | ||
| 68 | #include <libc/atexit.h> | ||
| 69 | static struct __atexit *save_atexit_ptr; | ||
| 70 | #define filehdr external_filehdr | ||
| 71 | #define scnhdr external_scnhdr | ||
| 72 | #define syment external_syment | ||
| 73 | #define auxent external_auxent | ||
| 74 | #define n_numaux e_numaux | ||
| 75 | #define n_type e_type | ||
| 76 | struct aouthdr | ||
| 77 | { | ||
| 78 | unsigned short magic; /* type of file */ | ||
| 79 | unsigned short vstamp; /* version stamp */ | ||
| 80 | unsigned long tsize; /* text size in bytes, padded to FW bdry*/ | ||
| 81 | unsigned long dsize; /* initialized data " " */ | ||
| 82 | unsigned long bsize; /* uninitialized data " " */ | ||
| 83 | unsigned long entry; /* entry pt. */ | ||
| 84 | unsigned long text_start;/* base of text used for this file */ | ||
| 85 | unsigned long data_start;/* base of data used for this file */ | ||
| 86 | }; | ||
| 87 | #endif /* MSDOS */ | ||
| 88 | #else /* not HAVE_COFF_H */ | ||
| 89 | #include <a.out.h> | ||
| 90 | #endif /* not HAVE_COFF_H */ | ||
| 91 | |||
| 92 | /* Define getpagesize if the system does not. | ||
| 93 | Note that this may depend on symbols defined in a.out.h. */ | ||
| 94 | #include "getpagesize.h" | ||
| 95 | |||
| 96 | #ifndef makedev /* Try to detect types.h already loaded */ | ||
| 97 | #include <sys/types.h> | ||
| 98 | #endif /* makedev */ | ||
| 99 | #include <errno.h> | ||
| 100 | |||
| 101 | #include <sys/file.h> | ||
| 102 | |||
| 103 | extern int etext; | ||
| 104 | |||
| 105 | static long block_copy_start; /* Old executable start point */ | ||
| 106 | static struct filehdr f_hdr; /* File header */ | ||
| 107 | static struct aouthdr f_ohdr; /* Optional file header (a.out) */ | ||
| 108 | long bias; /* Bias to add for growth */ | ||
| 109 | long lnnoptr; /* Pointer to line-number info within file */ | ||
| 110 | #define SYMS_START block_copy_start | ||
| 111 | |||
| 112 | static long text_scnptr; | ||
| 113 | static long data_scnptr; | ||
| 114 | |||
| 115 | static long coff_offset; | ||
| 116 | |||
| 117 | static int pagemask; | ||
| 118 | |||
| 119 | /* Correct an int which is the bit pattern of a pointer to a byte | ||
| 120 | into an int which is the number of a byte. | ||
| 121 | This is a no-op on ordinary machines, but not on all. */ | ||
| 122 | |||
| 123 | #define ADDR_CORRECT(x) ((char *) (x) - (char *) 0) | ||
| 124 | |||
| 125 | #include "lisp.h" | ||
| 126 | |||
| 127 | static void | ||
| 128 | report_error (const char *file, int fd) | ||
| 129 | { | ||
| 130 | int err = errno; | ||
| 131 | if (fd) | ||
| 132 | emacs_close (fd); | ||
| 133 | report_file_errno ("Cannot unexec", build_string (file), err); | ||
| 134 | } | ||
| 135 | |||
| 136 | #define ERROR0(msg) report_error_1 (new, msg, 0, 0); return -1 | ||
| 137 | #define ERROR1(msg,x) report_error_1 (new, msg, x, 0); return -1 | ||
| 138 | #define ERROR2(msg,x,y) report_error_1 (new, msg, x, y); return -1 | ||
| 139 | |||
| 140 | static void | ||
| 141 | report_error_1 (int fd, const char *msg, int a1, int a2) | ||
| 142 | { | ||
| 143 | emacs_close (fd); | ||
| 144 | error (msg, a1, a2); | ||
| 145 | } | ||
| 146 | |||
| 147 | static int make_hdr (int, int, const char *, const char *); | ||
| 148 | static int copy_text_and_data (int, int); | ||
| 149 | static int copy_sym (int, int, const char *, const char *); | ||
| 150 | static void mark_x (const char *); | ||
| 151 | |||
| 152 | /* **************************************************************** | ||
| 153 | * make_hdr | ||
| 154 | * | ||
| 155 | * Make the header in the new a.out from the header in core. | ||
| 156 | * Modify the text and data sizes. | ||
| 157 | */ | ||
| 158 | static int | ||
| 159 | make_hdr (int new, int a_out, | ||
| 160 | const char *a_name, const char *new_name) | ||
| 161 | { | ||
| 162 | auto struct scnhdr f_thdr; /* Text section header */ | ||
| 163 | auto struct scnhdr f_dhdr; /* Data section header */ | ||
| 164 | auto struct scnhdr f_bhdr; /* Bss section header */ | ||
| 165 | auto struct scnhdr scntemp; /* Temporary section header */ | ||
| 166 | register int scns; | ||
| 167 | unsigned int bss_start; | ||
| 168 | unsigned int data_start; | ||
| 169 | |||
| 170 | pagemask = getpagesize () - 1; | ||
| 171 | |||
| 172 | /* Adjust text/data boundary. */ | ||
| 173 | data_start = (int) DATA_START; | ||
| 174 | data_start = ADDR_CORRECT (data_start); | ||
| 175 | data_start = data_start & ~pagemask; /* (Down) to page boundary. */ | ||
| 176 | |||
| 177 | bss_start = ADDR_CORRECT (sbrk (0)) + pagemask; | ||
| 178 | bss_start &= ~ pagemask; | ||
| 179 | |||
| 180 | if (data_start > bss_start) /* Can't have negative data size. */ | ||
| 181 | { | ||
| 182 | ERROR2 ("unexec: data_start (%u) can't be greater than bss_start (%u)", | ||
| 183 | data_start, bss_start); | ||
| 184 | } | ||
| 185 | |||
| 186 | coff_offset = 0L; /* stays zero, except in DJGPP */ | ||
| 187 | |||
| 188 | /* Salvage as much info from the existing file as possible */ | ||
| 189 | if (a_out >= 0) | ||
| 190 | { | ||
| 191 | #ifdef MSDOS | ||
| 192 | /* Support the coff-go32-exe format with a prepended stub, since | ||
| 193 | this is what GCC 2.8.0 and later generates by default in DJGPP. */ | ||
| 194 | unsigned short mz_header[3]; | ||
| 195 | |||
| 196 | if (read (a_out, &mz_header, sizeof (mz_header)) != sizeof (mz_header)) | ||
| 197 | { | ||
| 198 | PERROR (a_name); | ||
| 199 | } | ||
| 200 | if (mz_header[0] == 0x5a4d || mz_header[0] == 0x4d5a) /* "MZ" or "ZM" */ | ||
| 201 | { | ||
| 202 | coff_offset = (long)mz_header[2] * 512L; | ||
| 203 | if (mz_header[1]) | ||
| 204 | coff_offset += (long)mz_header[1] - 512L; | ||
| 205 | lseek (a_out, coff_offset, 0); | ||
| 206 | } | ||
| 207 | else | ||
| 208 | lseek (a_out, 0L, 0); | ||
| 209 | #endif /* MSDOS */ | ||
| 210 | if (read (a_out, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr)) | ||
| 211 | { | ||
| 212 | PERROR (a_name); | ||
| 213 | } | ||
| 214 | block_copy_start += sizeof (f_hdr); | ||
| 215 | if (f_hdr.f_opthdr > 0) | ||
| 216 | { | ||
| 217 | if (read (a_out, &f_ohdr, sizeof (f_ohdr)) != sizeof (f_ohdr)) | ||
| 218 | { | ||
| 219 | PERROR (a_name); | ||
| 220 | } | ||
| 221 | block_copy_start += sizeof (f_ohdr); | ||
| 222 | } | ||
| 223 | /* Loop through section headers, copying them in */ | ||
| 224 | lseek (a_out, coff_offset + sizeof (f_hdr) + f_hdr.f_opthdr, 0); | ||
| 225 | for (scns = f_hdr.f_nscns; scns > 0; scns--) { | ||
| 226 | if (read (a_out, &scntemp, sizeof (scntemp)) != sizeof (scntemp)) | ||
| 227 | { | ||
| 228 | PERROR (a_name); | ||
| 229 | } | ||
| 230 | if (scntemp.s_scnptr > 0L) | ||
| 231 | { | ||
| 232 | if (block_copy_start < scntemp.s_scnptr + scntemp.s_size) | ||
| 233 | block_copy_start = scntemp.s_scnptr + scntemp.s_size; | ||
| 234 | } | ||
| 235 | if (strcmp (scntemp.s_name, ".text") == 0) | ||
| 236 | { | ||
| 237 | f_thdr = scntemp; | ||
| 238 | } | ||
| 239 | else if (strcmp (scntemp.s_name, ".data") == 0) | ||
| 240 | { | ||
| 241 | f_dhdr = scntemp; | ||
| 242 | } | ||
| 243 | else if (strcmp (scntemp.s_name, ".bss") == 0) | ||
| 244 | { | ||
| 245 | f_bhdr = scntemp; | ||
| 246 | } | ||
| 247 | } | ||
| 248 | } | ||
| 249 | else | ||
| 250 | { | ||
| 251 | ERROR0 ("can't build a COFF file from scratch yet"); | ||
| 252 | } | ||
| 253 | |||
| 254 | /* Now we alter the contents of all the f_*hdr variables | ||
| 255 | to correspond to what we want to dump. */ | ||
| 256 | |||
| 257 | f_hdr.f_flags |= (F_RELFLG | F_EXEC); | ||
| 258 | f_ohdr.dsize = bss_start - f_ohdr.data_start; | ||
| 259 | f_ohdr.bsize = 0; | ||
| 260 | f_thdr.s_size = f_ohdr.tsize; | ||
| 261 | f_thdr.s_scnptr = sizeof (f_hdr) + sizeof (f_ohdr); | ||
| 262 | f_thdr.s_scnptr += (f_hdr.f_nscns) * (sizeof (f_thdr)); | ||
| 263 | lnnoptr = f_thdr.s_lnnoptr; | ||
| 264 | text_scnptr = f_thdr.s_scnptr; | ||
| 265 | f_dhdr.s_paddr = f_ohdr.data_start; | ||
| 266 | f_dhdr.s_vaddr = f_ohdr.data_start; | ||
| 267 | f_dhdr.s_size = f_ohdr.dsize; | ||
| 268 | f_dhdr.s_scnptr = f_thdr.s_scnptr + f_thdr.s_size; | ||
| 269 | data_scnptr = f_dhdr.s_scnptr; | ||
| 270 | f_bhdr.s_paddr = f_ohdr.data_start + f_ohdr.dsize; | ||
| 271 | f_bhdr.s_vaddr = f_ohdr.data_start + f_ohdr.dsize; | ||
| 272 | f_bhdr.s_size = f_ohdr.bsize; | ||
| 273 | f_bhdr.s_scnptr = 0L; | ||
| 274 | bias = f_dhdr.s_scnptr + f_dhdr.s_size - block_copy_start; | ||
| 275 | |||
| 276 | if (f_hdr.f_symptr > 0L) | ||
| 277 | { | ||
| 278 | f_hdr.f_symptr += bias; | ||
| 279 | } | ||
| 280 | |||
| 281 | if (f_thdr.s_lnnoptr > 0L) | ||
| 282 | { | ||
| 283 | f_thdr.s_lnnoptr += bias; | ||
| 284 | } | ||
| 285 | |||
| 286 | if (write (new, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr)) | ||
| 287 | { | ||
| 288 | PERROR (new_name); | ||
| 289 | } | ||
| 290 | |||
| 291 | if (write (new, &f_ohdr, sizeof (f_ohdr)) != sizeof (f_ohdr)) | ||
| 292 | { | ||
| 293 | PERROR (new_name); | ||
| 294 | } | ||
| 295 | |||
| 296 | if (write (new, &f_thdr, sizeof (f_thdr)) != sizeof (f_thdr)) | ||
| 297 | { | ||
| 298 | PERROR (new_name); | ||
| 299 | } | ||
| 300 | |||
| 301 | if (write (new, &f_dhdr, sizeof (f_dhdr)) != sizeof (f_dhdr)) | ||
| 302 | { | ||
| 303 | PERROR (new_name); | ||
| 304 | } | ||
| 305 | |||
| 306 | if (write (new, &f_bhdr, sizeof (f_bhdr)) != sizeof (f_bhdr)) | ||
| 307 | { | ||
| 308 | PERROR (new_name); | ||
| 309 | } | ||
| 310 | |||
| 311 | return (0); | ||
| 312 | |||
| 313 | } | ||
| 314 | |||
| 315 | void | ||
| 316 | write_segment (int new, const char *ptr, const char *end) | ||
| 317 | { | ||
| 318 | register int i, nwrite, ret; | ||
| 319 | /* This is the normal amount to write at once. | ||
| 320 | It is the size of block that NFS uses. */ | ||
| 321 | int writesize = 1 << 13; | ||
| 322 | int pagesize = getpagesize (); | ||
| 323 | char zeros[1 << 13]; | ||
| 324 | |||
| 325 | memset (zeros, 0, sizeof (zeros)); | ||
| 326 | |||
| 327 | for (i = 0; ptr < end;) | ||
| 328 | { | ||
| 329 | /* Distance to next multiple of writesize. */ | ||
| 330 | nwrite = (((int) ptr + writesize) & -writesize) - (int) ptr; | ||
| 331 | /* But not beyond specified end. */ | ||
| 332 | if (nwrite > end - ptr) nwrite = end - ptr; | ||
| 333 | ret = write (new, ptr, nwrite); | ||
| 334 | /* If write gets a page fault, it means we reached | ||
| 335 | a gap between the old text segment and the old data segment. | ||
| 336 | This gap has probably been remapped into part of the text segment. | ||
| 337 | So write zeros for it. */ | ||
| 338 | if (ret == -1 && errno == EFAULT) | ||
| 339 | { | ||
| 340 | /* Write only a page of zeros at once, | ||
| 341 | so that we don't overshoot the start | ||
| 342 | of the valid memory in the old data segment. */ | ||
| 343 | if (nwrite > pagesize) | ||
| 344 | nwrite = pagesize; | ||
| 345 | write (new, zeros, nwrite); | ||
| 346 | } | ||
| 347 | i += nwrite; | ||
| 348 | ptr += nwrite; | ||
| 349 | } | ||
| 350 | } | ||
| 351 | /* **************************************************************** | ||
| 352 | * copy_text_and_data | ||
| 353 | * | ||
| 354 | * Copy the text and data segments from memory to the new a.out | ||
| 355 | */ | ||
| 356 | static int | ||
| 357 | copy_text_and_data (int new, int a_out) | ||
| 358 | { | ||
| 359 | register char *end; | ||
| 360 | register char *ptr; | ||
| 361 | |||
| 362 | #ifdef MSDOS | ||
| 363 | /* Dump the original table of exception handlers, not the one | ||
| 364 | where our exception hooks are registered. */ | ||
| 365 | __djgpp_exception_toggle (); | ||
| 366 | |||
| 367 | /* Switch off startup flags that might have been set at runtime | ||
| 368 | and which might change the way that dumped Emacs works. */ | ||
| 369 | save_djgpp_startup_flags = _crt0_startup_flags; | ||
| 370 | _crt0_startup_flags &= ~(_CRT0_FLAG_NO_LFN | _CRT0_FLAG_NEARPTR); | ||
| 371 | |||
| 372 | /* Zero out the 'atexit' chain in the dumped executable, to avoid | ||
| 373 | calling the atexit functions twice. (emacs.c:main installs an | ||
| 374 | atexit function.) */ | ||
| 375 | save_atexit_ptr = __atexit_ptr; | ||
| 376 | __atexit_ptr = NULL; | ||
| 377 | #endif | ||
| 378 | |||
| 379 | lseek (new, (long) text_scnptr, 0); | ||
| 380 | ptr = (char *) f_ohdr.text_start; | ||
| 381 | end = ptr + f_ohdr.tsize; | ||
| 382 | write_segment (new, ptr, end); | ||
| 383 | |||
| 384 | lseek (new, (long) data_scnptr, 0); | ||
| 385 | ptr = (char *) f_ohdr.data_start; | ||
| 386 | end = ptr + f_ohdr.dsize; | ||
| 387 | write_segment (new, ptr, end); | ||
| 388 | |||
| 389 | #ifdef MSDOS | ||
| 390 | /* Restore our exception hooks. */ | ||
| 391 | __djgpp_exception_toggle (); | ||
| 392 | |||
| 393 | /* Restore the startup flags. */ | ||
| 394 | _crt0_startup_flags = save_djgpp_startup_flags; | ||
| 395 | |||
| 396 | /* Restore the atexit chain. */ | ||
| 397 | __atexit_ptr = save_atexit_ptr; | ||
| 398 | #endif | ||
| 399 | |||
| 400 | |||
| 401 | return 0; | ||
| 402 | } | ||
| 403 | |||
| 404 | /* **************************************************************** | ||
| 405 | * copy_sym | ||
| 406 | * | ||
| 407 | * Copy the relocation information and symbol table from the a.out to the new | ||
| 408 | */ | ||
| 409 | static int | ||
| 410 | copy_sym (int new, int a_out, const char *a_name, const char *new_name) | ||
| 411 | { | ||
| 412 | char page[1024]; | ||
| 413 | int n; | ||
| 414 | |||
| 415 | if (a_out < 0) | ||
| 416 | return 0; | ||
| 417 | |||
| 418 | if (SYMS_START == 0L) | ||
| 419 | return 0; | ||
| 420 | |||
| 421 | if (lnnoptr) /* if there is line number info */ | ||
| 422 | lseek (a_out, coff_offset + lnnoptr, 0); /* start copying from there */ | ||
| 423 | else | ||
| 424 | lseek (a_out, coff_offset + SYMS_START, 0); /* Position a.out to symtab. */ | ||
| 425 | |||
| 426 | while ((n = read (a_out, page, sizeof page)) > 0) | ||
| 427 | { | ||
| 428 | if (write (new, page, n) != n) | ||
| 429 | { | ||
| 430 | PERROR (new_name); | ||
| 431 | } | ||
| 432 | } | ||
| 433 | if (n < 0) | ||
| 434 | { | ||
| 435 | PERROR (a_name); | ||
| 436 | } | ||
| 437 | return 0; | ||
| 438 | } | ||
| 439 | |||
| 440 | |||
| 441 | /* | ||
| 442 | * If the COFF file contains a symbol table and a line number section, | ||
| 443 | * then any auxiliary entries that have values for x_lnnoptr must | ||
| 444 | * be adjusted by the amount that the line number section has moved | ||
| 445 | * in the file (bias computed in make_hdr). The #@$%&* designers of | ||
| 446 | * the auxiliary entry structures used the absolute file offsets for | ||
| 447 | * the line number entry rather than an offset from the start of the | ||
| 448 | * line number section! | ||
| 449 | * | ||
| 450 | * When I figure out how to scan through the symbol table and pick out | ||
| 451 | * the auxiliary entries that need adjustment, this routine will | ||
| 452 | * be fixed. As it is now, all such entries are wrong and sdb | ||
| 453 | * will complain. Fred Fish, UniSoft Systems Inc. | ||
| 454 | */ | ||
| 455 | |||
| 456 | /* This function is probably very slow. Instead of reopening the new | ||
| 457 | file for input and output it should copy from the old to the new | ||
| 458 | using the two descriptors already open (WRITEDESC and READDESC). | ||
| 459 | Instead of reading one small structure at a time it should use | ||
| 460 | a reasonable size buffer. But I don't have time to work on such | ||
| 461 | things, so I am installing it as submitted to me. -- RMS. */ | ||
| 462 | |||
| 463 | int | ||
| 464 | adjust_lnnoptrs (int writedesc, int readdesc, const char *new_name) | ||
| 465 | { | ||
| 466 | register int nsyms; | ||
| 467 | register int new; | ||
| 468 | struct syment symentry; | ||
| 469 | union auxent auxentry; | ||
| 470 | |||
| 471 | if (!lnnoptr || !f_hdr.f_symptr) | ||
| 472 | return 0; | ||
| 473 | |||
| 474 | #ifdef MSDOS | ||
| 475 | if ((new = writedesc) < 0) | ||
| 476 | #else | ||
| 477 | if ((new = emacs_open (new_name, O_RDWR, 0)) < 0) | ||
| 478 | #endif | ||
| 479 | { | ||
| 480 | PERROR (new_name); | ||
| 481 | return -1; | ||
| 482 | } | ||
| 483 | |||
| 484 | lseek (new, f_hdr.f_symptr, 0); | ||
| 485 | for (nsyms = 0; nsyms < f_hdr.f_nsyms; nsyms++) | ||
| 486 | { | ||
| 487 | read (new, &symentry, SYMESZ); | ||
| 488 | if (symentry.n_numaux) | ||
| 489 | { | ||
| 490 | read (new, &auxentry, AUXESZ); | ||
| 491 | nsyms++; | ||
| 492 | if (ISFCN (symentry.n_type) || symentry.n_type == 0x2400) | ||
| 493 | { | ||
| 494 | auxentry.x_sym.x_fcnary.x_fcn.x_lnnoptr += bias; | ||
| 495 | lseek (new, -AUXESZ, 1); | ||
| 496 | write (new, &auxentry, AUXESZ); | ||
| 497 | } | ||
| 498 | } | ||
| 499 | } | ||
| 500 | #ifndef MSDOS | ||
| 501 | emacs_close (new); | ||
| 502 | #endif | ||
| 503 | return 0; | ||
| 504 | } | ||
| 505 | |||
| 506 | /* **************************************************************** | ||
| 507 | * unexec | ||
| 508 | * | ||
| 509 | * driving logic. | ||
| 510 | */ | ||
| 511 | void | ||
| 512 | unexec (const char *new_name, const char *a_name) | ||
| 513 | { | ||
| 514 | int new = -1, a_out = -1; | ||
| 515 | |||
| 516 | if (a_name && (a_out = emacs_open (a_name, O_RDONLY, 0)) < 0) | ||
| 517 | { | ||
| 518 | PERROR (a_name); | ||
| 519 | } | ||
| 520 | if ((new = emacs_open (new_name, O_WRONLY | O_CREAT | O_TRUNC, 0777)) < 0) | ||
| 521 | { | ||
| 522 | PERROR (new_name); | ||
| 523 | } | ||
| 524 | |||
| 525 | if (make_hdr (new, a_out, a_name, new_name) < 0 | ||
| 526 | || copy_text_and_data (new, a_out) < 0 | ||
| 527 | || copy_sym (new, a_out, a_name, new_name) < 0 | ||
| 528 | || adjust_lnnoptrs (new, a_out, new_name) < 0 | ||
| 529 | ) | ||
| 530 | { | ||
| 531 | emacs_close (new); | ||
| 532 | return; | ||
| 533 | } | ||
| 534 | |||
| 535 | emacs_close (new); | ||
| 536 | if (a_out >= 0) | ||
| 537 | emacs_close (a_out); | ||
| 538 | } | ||
| 539 | |||
| 540 | #endif /* HAVE_UNEXEC */ | ||
diff --git a/src/unexcw.c b/src/unexcw.c deleted file mode 100644 index 5c91498cc6c..00000000000 --- a/src/unexcw.c +++ /dev/null | |||
| @@ -1,302 +0,0 @@ | |||
| 1 | /* unexec() support for Cygwin; | ||
| 2 | complete rewrite of xemacs Cygwin unexec() code | ||
| 3 | |||
| 4 | Copyright (C) 2004-2024 Free Software Foundation, Inc. | ||
| 5 | |||
| 6 | This file is part of GNU Emacs. | ||
| 7 | |||
| 8 | GNU Emacs is free software: you can redistribute it and/or modify | ||
| 9 | it under the terms of the GNU General Public License as published by | ||
| 10 | the Free Software Foundation, either version 3 of the License, or (at | ||
| 11 | your option) any later version. | ||
| 12 | |||
| 13 | GNU Emacs is distributed in the hope that it will be useful, | ||
| 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | GNU General Public License for more details. | ||
| 17 | |||
| 18 | You should have received a copy of the GNU General Public License | ||
| 19 | along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | ||
| 20 | |||
| 21 | #include <config.h> | ||
| 22 | #include "unexec.h" | ||
| 23 | #include "lisp.h" | ||
| 24 | #include <stdio.h> | ||
| 25 | #include <fcntl.h> | ||
| 26 | #include <a.out.h> | ||
| 27 | #include <unistd.h> | ||
| 28 | #include <assert.h> | ||
| 29 | |||
| 30 | #define DOTEXE ".exe" | ||
| 31 | |||
| 32 | /* | ||
| 33 | ** header for Windows executable files | ||
| 34 | */ | ||
| 35 | typedef struct | ||
| 36 | { | ||
| 37 | FILHDR file_header; | ||
| 38 | PEAOUTHDR file_optional_header; | ||
| 39 | SCNHDR section_header[32]; | ||
| 40 | } exe_header_t; | ||
| 41 | |||
| 42 | int debug_unexcw = 0; | ||
| 43 | |||
| 44 | /* | ||
| 45 | ** Read the header from the executable into memory so we can more easily access it. | ||
| 46 | */ | ||
| 47 | static exe_header_t * | ||
| 48 | read_exe_header (int fd, exe_header_t * exe_header_buffer) | ||
| 49 | { | ||
| 50 | int i; | ||
| 51 | MAYBE_UNUSED int ret; | ||
| 52 | |||
| 53 | assert (fd >= 0); | ||
| 54 | assert (exe_header_buffer != 0); | ||
| 55 | |||
| 56 | ret = lseek (fd, 0L, SEEK_SET); | ||
| 57 | assert (ret != -1); | ||
| 58 | |||
| 59 | ret = | ||
| 60 | read (fd, &exe_header_buffer->file_header, | ||
| 61 | sizeof (exe_header_buffer->file_header)); | ||
| 62 | assert (ret == sizeof (exe_header_buffer->file_header)); | ||
| 63 | |||
| 64 | assert (exe_header_buffer->file_header.e_magic == 0x5a4d); | ||
| 65 | assert (exe_header_buffer->file_header.nt_signature == 0x4550); | ||
| 66 | #ifdef __x86_64__ | ||
| 67 | assert (exe_header_buffer->file_header.f_magic == 0x8664); | ||
| 68 | #else | ||
| 69 | assert (exe_header_buffer->file_header.f_magic == 0x014c); | ||
| 70 | #endif | ||
| 71 | assert (exe_header_buffer->file_header.f_nscns > 0); | ||
| 72 | assert (exe_header_buffer->file_header.f_nscns <= | ||
| 73 | ARRAYELTS (exe_header_buffer->section_header)); | ||
| 74 | assert (exe_header_buffer->file_header.f_opthdr > 0); | ||
| 75 | |||
| 76 | ret = | ||
| 77 | read (fd, &exe_header_buffer->file_optional_header, | ||
| 78 | sizeof (exe_header_buffer->file_optional_header)); | ||
| 79 | assert (ret == sizeof (exe_header_buffer->file_optional_header)); | ||
| 80 | |||
| 81 | #ifdef __x86_64__ | ||
| 82 | assert (exe_header_buffer->file_optional_header.magic == 0x020b); | ||
| 83 | #else | ||
| 84 | assert (exe_header_buffer->file_optional_header.magic == 0x010b); | ||
| 85 | #endif | ||
| 86 | |||
| 87 | for (i = 0; i < exe_header_buffer->file_header.f_nscns; ++i) | ||
| 88 | { | ||
| 89 | ret = | ||
| 90 | read (fd, &exe_header_buffer->section_header[i], | ||
| 91 | sizeof (exe_header_buffer->section_header[i])); | ||
| 92 | assert (ret == sizeof (exe_header_buffer->section_header[i])); | ||
| 93 | } | ||
| 94 | |||
| 95 | return (exe_header_buffer); | ||
| 96 | } | ||
| 97 | |||
| 98 | /* | ||
| 99 | ** Fix the dumped emacs executable: | ||
| 100 | ** | ||
| 101 | ** - copy .data section data of interest from running executable into | ||
| 102 | ** output .exe file | ||
| 103 | ** | ||
| 104 | ** - convert .bss section into an initialized data section (like | ||
| 105 | ** .data) and copy .bss section data of interest from running | ||
| 106 | ** executable into output .exe file | ||
| 107 | */ | ||
| 108 | static void | ||
| 109 | fixup_executable (int fd) | ||
| 110 | { | ||
| 111 | exe_header_t exe_header_buffer; | ||
| 112 | exe_header_t *exe_header; | ||
| 113 | int i; | ||
| 114 | MAYBE_UNUSED int ret; | ||
| 115 | int found_data = 0; | ||
| 116 | int found_bss = 0; | ||
| 117 | |||
| 118 | exe_header = read_exe_header (fd, &exe_header_buffer); | ||
| 119 | assert (exe_header != 0); | ||
| 120 | |||
| 121 | assert (exe_header->file_header.f_nscns > 0); | ||
| 122 | for (i = 0; i < exe_header->file_header.f_nscns; ++i) | ||
| 123 | { | ||
| 124 | unsigned long start_address = | ||
| 125 | exe_header->section_header[i].s_vaddr + | ||
| 126 | exe_header->file_optional_header.ImageBase; | ||
| 127 | unsigned long end_address = | ||
| 128 | exe_header->section_header[i].s_vaddr + | ||
| 129 | exe_header->file_optional_header.ImageBase + | ||
| 130 | exe_header->section_header[i].s_paddr; | ||
| 131 | if (debug_unexcw) | ||
| 132 | printf ("%8s start %#lx end %#lx\n", | ||
| 133 | exe_header->section_header[i].s_name, | ||
| 134 | start_address, end_address); | ||
| 135 | if (my_edata >= (char *) start_address | ||
| 136 | && my_edata < (char *) end_address) | ||
| 137 | { | ||
| 138 | /* data section */ | ||
| 139 | ret = | ||
| 140 | lseek (fd, (long) (exe_header->section_header[i].s_scnptr), | ||
| 141 | SEEK_SET); | ||
| 142 | assert (ret != -1); | ||
| 143 | ret = | ||
| 144 | write (fd, (char *) start_address, | ||
| 145 | my_edata - (char *) start_address); | ||
| 146 | assert (ret == my_edata - (char *) start_address); | ||
| 147 | ++found_data; | ||
| 148 | if (debug_unexcw) | ||
| 149 | printf (" .data, mem start %#lx mem length %td\n", | ||
| 150 | start_address, my_edata - (char *) start_address); | ||
| 151 | if (debug_unexcw) | ||
| 152 | printf (" .data, file start %d file length %d\n", | ||
| 153 | (int) exe_header->section_header[i].s_scnptr, | ||
| 154 | (int) exe_header->section_header[i].s_paddr); | ||
| 155 | } | ||
| 156 | else if (my_endbss >= (char *) start_address | ||
| 157 | && my_endbss < (char *) end_address) | ||
| 158 | { | ||
| 159 | /* bss section */ | ||
| 160 | ++found_bss; | ||
| 161 | if (exe_header->section_header[i].s_flags & 0x00000080) | ||
| 162 | { | ||
| 163 | /* convert uninitialized data section to initialized data section */ | ||
| 164 | struct stat statbuf; | ||
| 165 | ret = fstat (fd, &statbuf); | ||
| 166 | assert (ret != -1); | ||
| 167 | |||
| 168 | exe_header->section_header[i].s_flags &= ~0x00000080; | ||
| 169 | exe_header->section_header[i].s_flags |= 0x00000040; | ||
| 170 | |||
| 171 | exe_header->section_header[i].s_scnptr = | ||
| 172 | (statbuf.st_size + | ||
| 173 | exe_header->file_optional_header.FileAlignment) / | ||
| 174 | exe_header->file_optional_header.FileAlignment * | ||
| 175 | exe_header->file_optional_header.FileAlignment; | ||
| 176 | |||
| 177 | exe_header->section_header[i].s_size = | ||
| 178 | (exe_header->section_header[i].s_paddr + | ||
| 179 | exe_header->file_optional_header.FileAlignment) / | ||
| 180 | exe_header->file_optional_header.FileAlignment * | ||
| 181 | exe_header->file_optional_header.FileAlignment; | ||
| 182 | |||
| 183 | /* Make sure the generated bootstrap binary isn't | ||
| 184 | * sparse. NT doesn't use a file cache for sparse | ||
| 185 | * executables, so if we bootstrap Emacs using a sparse | ||
| 186 | * bootstrap-emacs.exe, bootstrap takes about twenty | ||
| 187 | * times longer than it would otherwise. */ | ||
| 188 | |||
| 189 | ret = posix_fallocate (fd, | ||
| 190 | ( exe_header->section_header[i].s_scnptr + | ||
| 191 | exe_header->section_header[i].s_size ), | ||
| 192 | 1); | ||
| 193 | |||
| 194 | assert (ret != -1); | ||
| 195 | |||
| 196 | ret = | ||
| 197 | lseek (fd, | ||
| 198 | (long) (exe_header->section_header[i].s_scnptr + | ||
| 199 | exe_header->section_header[i].s_size - 1), | ||
| 200 | SEEK_SET); | ||
| 201 | assert (ret != -1); | ||
| 202 | ret = write (fd, "", 1); | ||
| 203 | assert (ret == 1); | ||
| 204 | |||
| 205 | ret = | ||
| 206 | lseek (fd, | ||
| 207 | (long) ((char *) &exe_header->section_header[i] - | ||
| 208 | (char *) exe_header), SEEK_SET); | ||
| 209 | assert (ret != -1); | ||
| 210 | ret = | ||
| 211 | write (fd, &exe_header->section_header[i], | ||
| 212 | sizeof (exe_header->section_header[i])); | ||
| 213 | assert (ret == sizeof (exe_header->section_header[i])); | ||
| 214 | if (debug_unexcw) | ||
| 215 | printf (" seek to %ld, write %zu\n", | ||
| 216 | (long) ((char *) &exe_header->section_header[i] - | ||
| 217 | (char *) exe_header), | ||
| 218 | sizeof (exe_header->section_header[i])); | ||
| 219 | } | ||
| 220 | /* write initialized data section */ | ||
| 221 | ret = | ||
| 222 | lseek (fd, (long) (exe_header->section_header[i].s_scnptr), | ||
| 223 | SEEK_SET); | ||
| 224 | assert (ret != -1); | ||
| 225 | ret = | ||
| 226 | write (fd, (char *) start_address, | ||
| 227 | my_endbss - (char *) start_address); | ||
| 228 | assert (ret == (my_endbss - (char *) start_address)); | ||
| 229 | if (debug_unexcw) | ||
| 230 | printf (" .bss, mem start %#lx mem length %td\n", | ||
| 231 | start_address, my_endbss - (char *) start_address); | ||
| 232 | if (debug_unexcw) | ||
| 233 | printf (" .bss, file start %d file length %d\n", | ||
| 234 | (int) exe_header->section_header[i].s_scnptr, | ||
| 235 | (int) exe_header->section_header[i].s_paddr); | ||
| 236 | } | ||
| 237 | } | ||
| 238 | assert (found_bss == 1); | ||
| 239 | assert (found_data == 1); | ||
| 240 | } | ||
| 241 | |||
| 242 | /* | ||
| 243 | ** Windows likes .exe suffixes on executables. | ||
| 244 | */ | ||
| 245 | static char * | ||
| 246 | add_exe_suffix_if_necessary (const char *name, char *modified) | ||
| 247 | { | ||
| 248 | int i = strlen (name); | ||
| 249 | if (i <= (sizeof (DOTEXE) - 1)) | ||
| 250 | { | ||
| 251 | sprintf (modified, "%s%s", name, DOTEXE); | ||
| 252 | } | ||
| 253 | else if (!strcasecmp (name + i - (sizeof (DOTEXE) - 1), DOTEXE)) | ||
| 254 | { | ||
| 255 | strcpy (modified, name); | ||
| 256 | } | ||
| 257 | else | ||
| 258 | { | ||
| 259 | sprintf (modified, "%s%s", name, DOTEXE); | ||
| 260 | } | ||
| 261 | return (modified); | ||
| 262 | } | ||
| 263 | |||
| 264 | void | ||
| 265 | unexec (const char *outfile, const char *infile) | ||
| 266 | { | ||
| 267 | char infile_buffer[FILENAME_MAX]; | ||
| 268 | char outfile_buffer[FILENAME_MAX]; | ||
| 269 | int fd_in; | ||
| 270 | int fd_out; | ||
| 271 | int ret; | ||
| 272 | MAYBE_UNUSED int ret2; | ||
| 273 | |||
| 274 | infile = add_exe_suffix_if_necessary (infile, infile_buffer); | ||
| 275 | outfile = add_exe_suffix_if_necessary (outfile, outfile_buffer); | ||
| 276 | |||
| 277 | fd_in = emacs_open (infile, O_RDONLY, 0); | ||
| 278 | assert (fd_in >= 0); | ||
| 279 | fd_out = emacs_open (outfile, O_RDWR | O_TRUNC | O_CREAT, 0755); | ||
| 280 | assert (fd_out >= 0); | ||
| 281 | for (;;) | ||
| 282 | { | ||
| 283 | char buffer[4096]; | ||
| 284 | ret = read (fd_in, buffer, sizeof (buffer)); | ||
| 285 | if (ret == 0) | ||
| 286 | { | ||
| 287 | /* eof */ | ||
| 288 | break; | ||
| 289 | } | ||
| 290 | assert (ret > 0); | ||
| 291 | /* data */ | ||
| 292 | ret2 = write (fd_out, buffer, ret); | ||
| 293 | assert (ret2 == ret); | ||
| 294 | } | ||
| 295 | ret = emacs_close (fd_in); | ||
| 296 | assert (ret == 0); | ||
| 297 | |||
| 298 | fixup_executable (fd_out); | ||
| 299 | |||
| 300 | ret = emacs_close (fd_out); | ||
| 301 | assert (ret == 0); | ||
| 302 | } | ||
diff --git a/src/unexec.h b/src/unexec.h deleted file mode 100644 index cdb2e8016ea..00000000000 --- a/src/unexec.h +++ /dev/null | |||
| @@ -1,4 +0,0 @@ | |||
| 1 | #ifndef EMACS_UNEXEC_H | ||
| 2 | #define EMACS_UNEXEC_H | ||
| 3 | void unexec (const char *, const char *); | ||
| 4 | #endif /* EMACS_UNEXEC_H */ | ||
diff --git a/src/unexelf.c b/src/unexelf.c deleted file mode 100644 index 4b109470066..00000000000 --- a/src/unexelf.c +++ /dev/null | |||
| @@ -1,658 +0,0 @@ | |||
| 1 | /* Copyright (C) 1985-1988, 1990, 1992, 1999-2024 Free Software | ||
| 2 | Foundation, Inc. | ||
| 3 | |||
| 4 | This file is part of GNU Emacs. | ||
| 5 | |||
| 6 | GNU Emacs is free software: you can redistribute it and/or modify | ||
| 7 | it under the terms of the GNU General Public License as published by | ||
| 8 | the Free Software Foundation, either version 3 of the License, or (at | ||
| 9 | your option) any later version. | ||
| 10 | |||
| 11 | GNU Emacs is distributed in the hope that it will be useful, | ||
| 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | GNU General Public License for more details. | ||
| 15 | |||
| 16 | You should have received a copy of the GNU General Public License | ||
| 17 | along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | ||
| 18 | |||
| 19 | /* | ||
| 20 | In other words, you are welcome to use, share and improve this program. | ||
| 21 | You are forbidden to forbid anyone else to use, share and improve | ||
| 22 | what you give them. Help stamp out software-hoarding! */ | ||
| 23 | |||
| 24 | |||
| 25 | /* | ||
| 26 | * unexec.c - Convert a running program into an a.out file. | ||
| 27 | * | ||
| 28 | * Author: Spencer W. Thomas | ||
| 29 | * Computer Science Dept. | ||
| 30 | * University of Utah | ||
| 31 | * Date: Tue Mar 2 1982 | ||
| 32 | * Modified heavily since then. | ||
| 33 | * | ||
| 34 | * Synopsis: | ||
| 35 | * unexec (const char *new_name, const char *old_name); | ||
| 36 | * | ||
| 37 | * Takes a snapshot of the program and makes an a.out format file in the | ||
| 38 | * file named by the string argument new_name. | ||
| 39 | * If old_name is non-NULL, the symbol table will be taken from the given file. | ||
| 40 | * On some machines, an existing old_name file is required. | ||
| 41 | * | ||
| 42 | */ | ||
| 43 | |||
| 44 | /* We do not use mmap because that fails with NFS. | ||
| 45 | Instead we read the whole file, modify it, and write it out. */ | ||
| 46 | |||
| 47 | #include <config.h> | ||
| 48 | #include "unexec.h" | ||
| 49 | #include "lisp.h" | ||
| 50 | |||
| 51 | #include <errno.h> | ||
| 52 | #include <fcntl.h> | ||
| 53 | #include <limits.h> | ||
| 54 | #include <memory.h> | ||
| 55 | #include <stdint.h> | ||
| 56 | #include <stdio.h> | ||
| 57 | #include <sys/stat.h> | ||
| 58 | #include <sys/types.h> | ||
| 59 | #include <unistd.h> | ||
| 60 | |||
| 61 | #ifdef __QNX__ | ||
| 62 | # include <sys/elf.h> | ||
| 63 | #elif !defined __NetBSD__ && !defined __OpenBSD__ | ||
| 64 | # include <elf.h> | ||
| 65 | #endif | ||
| 66 | #include <sys/mman.h> | ||
| 67 | #if defined (_SYSTYPE_SYSV) | ||
| 68 | #include <sys/elf_mips.h> | ||
| 69 | #include <sym.h> | ||
| 70 | #endif /* _SYSTYPE_SYSV */ | ||
| 71 | |||
| 72 | #ifndef MAP_ANON | ||
| 73 | #ifdef MAP_ANONYMOUS | ||
| 74 | #define MAP_ANON MAP_ANONYMOUS | ||
| 75 | #else | ||
| 76 | #define MAP_ANON 0 | ||
| 77 | #endif | ||
| 78 | #endif | ||
| 79 | |||
| 80 | #ifndef MAP_FAILED | ||
| 81 | #define MAP_FAILED ((void *) -1) | ||
| 82 | #endif | ||
| 83 | |||
| 84 | #if defined (__alpha__) && !defined (__NetBSD__) && !defined (__OpenBSD__) | ||
| 85 | /* Declare COFF debugging symbol table. This used to be in | ||
| 86 | /usr/include/sym.h, but this file is no longer included in Red Hat | ||
| 87 | 5.0 and presumably in any other glibc 2.x based distribution. */ | ||
| 88 | typedef struct { | ||
| 89 | short magic; | ||
| 90 | short vstamp; | ||
| 91 | int ilineMax; | ||
| 92 | int idnMax; | ||
| 93 | int ipdMax; | ||
| 94 | int isymMax; | ||
| 95 | int ioptMax; | ||
| 96 | int iauxMax; | ||
| 97 | int issMax; | ||
| 98 | int issExtMax; | ||
| 99 | int ifdMax; | ||
| 100 | int crfd; | ||
| 101 | int iextMax; | ||
| 102 | long cbLine; | ||
| 103 | long cbLineOffset; | ||
| 104 | long cbDnOffset; | ||
| 105 | long cbPdOffset; | ||
| 106 | long cbSymOffset; | ||
| 107 | long cbOptOffset; | ||
| 108 | long cbAuxOffset; | ||
| 109 | long cbSsOffset; | ||
| 110 | long cbSsExtOffset; | ||
| 111 | long cbFdOffset; | ||
| 112 | long cbRfdOffset; | ||
| 113 | long cbExtOffset; | ||
| 114 | } HDRR, *pHDRR; | ||
| 115 | #define cbHDRR sizeof (HDRR) | ||
| 116 | #define hdrNil ((pHDRR)0) | ||
| 117 | #endif | ||
| 118 | |||
| 119 | #ifdef __NetBSD__ | ||
| 120 | /* | ||
| 121 | * NetBSD does not have normal-looking user-land ELF support. | ||
| 122 | */ | ||
| 123 | # if defined __alpha__ || defined __sparc_v9__ || defined _LP64 | ||
| 124 | # define ELFSIZE 64 | ||
| 125 | # else | ||
| 126 | # define ELFSIZE 32 | ||
| 127 | # endif | ||
| 128 | # include <sys/exec_elf.h> | ||
| 129 | |||
| 130 | # ifndef PT_LOAD | ||
| 131 | # define PT_LOAD Elf_pt_load | ||
| 132 | # if 0 /* was in pkgsrc patches for 20.7 */ | ||
| 133 | # define SHT_PROGBITS Elf_sht_progbits | ||
| 134 | # endif | ||
| 135 | # define SHT_SYMTAB Elf_sht_symtab | ||
| 136 | # define SHT_DYNSYM Elf_sht_dynsym | ||
| 137 | # define SHT_NULL Elf_sht_null | ||
| 138 | # define SHT_NOBITS Elf_sht_nobits | ||
| 139 | # define SHT_REL Elf_sht_rel | ||
| 140 | # define SHT_RELA Elf_sht_rela | ||
| 141 | |||
| 142 | # define SHN_UNDEF Elf_eshn_undefined | ||
| 143 | # define SHN_ABS Elf_eshn_absolute | ||
| 144 | # define SHN_COMMON Elf_eshn_common | ||
| 145 | # endif /* !PT_LOAD */ | ||
| 146 | |||
| 147 | # ifdef __alpha__ | ||
| 148 | # include <sys/exec_ecoff.h> | ||
| 149 | # define HDRR struct ecoff_symhdr | ||
| 150 | # define pHDRR HDRR * | ||
| 151 | # endif /* __alpha__ */ | ||
| 152 | |||
| 153 | #ifdef __mips__ /* was in pkgsrc patches for 20.7 */ | ||
| 154 | # define SHT_MIPS_DEBUG DT_MIPS_FLAGS | ||
| 155 | # define HDRR struct Elf_Shdr | ||
| 156 | #endif /* __mips__ */ | ||
| 157 | #endif /* __NetBSD__ */ | ||
| 158 | |||
| 159 | #ifdef __OpenBSD__ | ||
| 160 | # include <sys/exec_elf.h> | ||
| 161 | #endif | ||
| 162 | |||
| 163 | #if __GNU_LIBRARY__ - 0 >= 6 | ||
| 164 | # include <link.h> /* get ElfW etc */ | ||
| 165 | #endif | ||
| 166 | |||
| 167 | #ifndef ElfW | ||
| 168 | # define ElfBitsW(bits, type) Elf##bits##_##type | ||
| 169 | # ifndef ELFSIZE | ||
| 170 | # ifdef _LP64 | ||
| 171 | # define ELFSIZE 64 | ||
| 172 | # else | ||
| 173 | # define ELFSIZE 32 | ||
| 174 | # endif | ||
| 175 | # endif | ||
| 176 | /* This macro expands `bits' before invoking ElfBitsW. */ | ||
| 177 | # define ElfExpandBitsW(bits, type) ElfBitsW (bits, type) | ||
| 178 | # define ElfW(type) ElfExpandBitsW (ELFSIZE, type) | ||
| 179 | #endif | ||
| 180 | |||
| 181 | /* The code often converts ElfW (Half) values like e_shentsize to ptrdiff_t; | ||
| 182 | check that this doesn't lose information. */ | ||
| 183 | #include <intprops.h> | ||
| 184 | static_assert ((! TYPE_SIGNED (ElfW (Half)) | ||
| 185 | || PTRDIFF_MIN <= TYPE_MINIMUM (ElfW (Half))) | ||
| 186 | && TYPE_MAXIMUM (ElfW (Half)) <= PTRDIFF_MAX); | ||
| 187 | |||
| 188 | #ifdef UNEXELF_DEBUG | ||
| 189 | # define DEBUG_LOG(expr) fprintf (stderr, #expr " 0x%"PRIxMAX"\n", \ | ||
| 190 | (uintmax_t) (expr)) | ||
| 191 | #endif | ||
| 192 | |||
| 193 | /* Get the address of a particular section or program header entry, | ||
| 194 | * accounting for the size of the entries. | ||
| 195 | */ | ||
| 196 | |||
| 197 | static void * | ||
| 198 | entry_address (void *section_h, ptrdiff_t idx, ptrdiff_t entsize) | ||
| 199 | { | ||
| 200 | char *h = section_h; | ||
| 201 | return h + idx * entsize; | ||
| 202 | } | ||
| 203 | |||
| 204 | #define OLD_SECTION_H(n) \ | ||
| 205 | (*(ElfW (Shdr) *) entry_address (old_section_h, n, old_file_h->e_shentsize)) | ||
| 206 | #define NEW_SECTION_H(n) \ | ||
| 207 | (*(ElfW (Shdr) *) entry_address (new_section_h, n, new_file_h->e_shentsize)) | ||
| 208 | #define OLD_PROGRAM_H(n) \ | ||
| 209 | (*(ElfW (Phdr) *) entry_address (old_program_h, n, old_file_h->e_phentsize)) | ||
| 210 | |||
| 211 | typedef unsigned char byte; | ||
| 212 | |||
| 213 | /* **************************************************************** | ||
| 214 | * unexec | ||
| 215 | * | ||
| 216 | * driving logic. | ||
| 217 | * | ||
| 218 | * In ELF, this works by replacing the old bss SHT_NOBITS section with | ||
| 219 | * a new, larger, SHT_PROGBITS section. | ||
| 220 | * | ||
| 221 | */ | ||
| 222 | void | ||
| 223 | unexec (const char *new_name, const char *old_name) | ||
| 224 | { | ||
| 225 | int new_file, old_file; | ||
| 226 | off_t new_file_size; | ||
| 227 | |||
| 228 | /* Pointers to the base of the image of the two files. */ | ||
| 229 | caddr_t old_base, new_base; | ||
| 230 | |||
| 231 | #if MAP_ANON == 0 | ||
| 232 | int mmap_fd; | ||
| 233 | #else | ||
| 234 | # define mmap_fd -1 | ||
| 235 | #endif | ||
| 236 | |||
| 237 | /* Pointers to the file, program and section headers for the old and | ||
| 238 | new files. */ | ||
| 239 | ElfW (Ehdr) *old_file_h, *new_file_h; | ||
| 240 | ElfW (Phdr) *old_program_h, *new_program_h; | ||
| 241 | ElfW (Shdr) *old_section_h, *new_section_h; | ||
| 242 | |||
| 243 | /* Point to the section name table. */ | ||
| 244 | char *old_section_names, *new_section_names; | ||
| 245 | |||
| 246 | ElfW (Phdr) *old_bss_seg, *new_bss_seg; | ||
| 247 | ElfW (Addr) old_bss_addr, new_bss_addr; | ||
| 248 | ElfW (Word) old_bss_size, bss_size_growth, new_data2_size; | ||
| 249 | ElfW (Off) old_bss_offset, new_data2_offset; | ||
| 250 | |||
| 251 | ptrdiff_t n; | ||
| 252 | ptrdiff_t old_bss_index; | ||
| 253 | struct stat stat_buf; | ||
| 254 | off_t old_file_size; | ||
| 255 | |||
| 256 | /* Open the old file, allocate a buffer of the right size, and read | ||
| 257 | in the file contents. */ | ||
| 258 | |||
| 259 | old_file = emacs_open (old_name, O_RDONLY, 0); | ||
| 260 | |||
| 261 | if (old_file < 0) | ||
| 262 | fatal ("Can't open %s for reading: %s", old_name, strerror (errno)); | ||
| 263 | |||
| 264 | if (fstat (old_file, &stat_buf) != 0) | ||
| 265 | fatal ("Can't fstat (%s): %s", old_name, strerror (errno)); | ||
| 266 | |||
| 267 | #if MAP_ANON == 0 | ||
| 268 | mmap_fd = emacs_open ("/dev/zero", O_RDONLY, 0); | ||
| 269 | if (mmap_fd < 0) | ||
| 270 | fatal ("Can't open /dev/zero for reading: %s", strerror (errno)); | ||
| 271 | #endif | ||
| 272 | |||
| 273 | /* We cannot use malloc here because that may use sbrk. If it does, | ||
| 274 | we'd dump our temporary buffers with Emacs, and we'd have to be | ||
| 275 | extra careful to use the correct value of sbrk(0) after | ||
| 276 | allocating all buffers in the code below, which we aren't. */ | ||
| 277 | old_file_size = stat_buf.st_size; | ||
| 278 | if (! (0 <= old_file_size && old_file_size <= SIZE_MAX)) | ||
| 279 | fatal ("File size out of range"); | ||
| 280 | old_base = mmap (NULL, old_file_size, PROT_READ | PROT_WRITE, | ||
| 281 | MAP_ANON | MAP_PRIVATE, mmap_fd, 0); | ||
| 282 | if (old_base == MAP_FAILED) | ||
| 283 | fatal ("Can't allocate buffer for %s: %s", old_name, strerror (errno)); | ||
| 284 | |||
| 285 | if (read (old_file, old_base, old_file_size) != old_file_size) | ||
| 286 | fatal ("Didn't read all of %s: %s", old_name, strerror (errno)); | ||
| 287 | |||
| 288 | /* Get pointers to headers & section names */ | ||
| 289 | |||
| 290 | old_file_h = (ElfW (Ehdr) *) old_base; | ||
| 291 | old_program_h = (ElfW (Phdr) *) ((byte *) old_base + old_file_h->e_phoff); | ||
| 292 | old_section_h = (ElfW (Shdr) *) ((byte *) old_base + old_file_h->e_shoff); | ||
| 293 | old_section_names = (char *) old_base | ||
| 294 | + OLD_SECTION_H (old_file_h->e_shstrndx).sh_offset; | ||
| 295 | |||
| 296 | /* Find the PT_LOAD header covering the highest address. This | ||
| 297 | segment will be where bss sections are located, past p_filesz. */ | ||
| 298 | old_bss_seg = 0; | ||
| 299 | for (n = old_file_h->e_phnum; --n >= 0; ) | ||
| 300 | { | ||
| 301 | ElfW (Phdr) *seg = &OLD_PROGRAM_H (n); | ||
| 302 | if (seg->p_type == PT_LOAD | ||
| 303 | && (old_bss_seg == 0 | ||
| 304 | || seg->p_vaddr > old_bss_seg->p_vaddr)) | ||
| 305 | old_bss_seg = seg; | ||
| 306 | } | ||
| 307 | eassume (old_bss_seg); | ||
| 308 | if (!old_bss_seg) | ||
| 309 | emacs_abort (); | ||
| 310 | |||
| 311 | /* Note that old_bss_addr may be lower than the first bss section | ||
| 312 | address, since the section may need aligning. */ | ||
| 313 | old_bss_addr = old_bss_seg->p_vaddr + old_bss_seg->p_filesz; | ||
| 314 | old_bss_offset = old_bss_seg->p_offset + old_bss_seg->p_filesz; | ||
| 315 | old_bss_size = old_bss_seg->p_memsz - old_bss_seg->p_filesz; | ||
| 316 | |||
| 317 | /* Find the last bss style section in the bss segment range. */ | ||
| 318 | old_bss_index = -1; | ||
| 319 | for (n = old_file_h->e_shnum; --n > 0; ) | ||
| 320 | { | ||
| 321 | ElfW (Shdr) *shdr = &OLD_SECTION_H (n); | ||
| 322 | if (shdr->sh_type == SHT_NOBITS | ||
| 323 | && shdr->sh_addr >= old_bss_addr | ||
| 324 | && shdr->sh_addr + shdr->sh_size <= old_bss_addr + old_bss_size | ||
| 325 | && (old_bss_index == -1 | ||
| 326 | || OLD_SECTION_H (old_bss_index).sh_addr < shdr->sh_addr)) | ||
| 327 | old_bss_index = n; | ||
| 328 | } | ||
| 329 | |||
| 330 | if (old_bss_index == -1) | ||
| 331 | fatal ("no bss section found"); | ||
| 332 | |||
| 333 | void *no_break = (void *) (intptr_t) -1; | ||
| 334 | void *new_break = no_break; | ||
| 335 | #ifdef HAVE_SBRK | ||
| 336 | new_break = sbrk (0); | ||
| 337 | #endif | ||
| 338 | if (new_break == no_break) | ||
| 339 | new_break = (byte *) old_bss_addr + old_bss_size; | ||
| 340 | new_bss_addr = (ElfW (Addr)) new_break; | ||
| 341 | bss_size_growth = new_bss_addr - old_bss_addr; | ||
| 342 | new_data2_size = bss_size_growth; | ||
| 343 | new_data2_size += alignof (ElfW (Shdr)) - 1; | ||
| 344 | new_data2_size -= new_data2_size % alignof (ElfW (Shdr)); | ||
| 345 | |||
| 346 | new_data2_offset = old_bss_offset; | ||
| 347 | |||
| 348 | #ifdef UNEXELF_DEBUG | ||
| 349 | fprintf (stderr, "old_bss_index %td\n", old_bss_index); | ||
| 350 | DEBUG_LOG (old_bss_addr); | ||
| 351 | DEBUG_LOG (old_bss_size); | ||
| 352 | DEBUG_LOG (old_bss_offset); | ||
| 353 | DEBUG_LOG (new_bss_addr); | ||
| 354 | DEBUG_LOG (new_data2_size); | ||
| 355 | DEBUG_LOG (new_data2_offset); | ||
| 356 | #endif | ||
| 357 | |||
| 358 | if (new_bss_addr < old_bss_addr + old_bss_size) | ||
| 359 | fatal (".bss shrank when undumping"); | ||
| 360 | |||
| 361 | /* Set the output file to the right size. Allocate a buffer to hold | ||
| 362 | the image of the new file. Set pointers to various interesting | ||
| 363 | objects. */ | ||
| 364 | |||
| 365 | new_file = emacs_open (new_name, O_RDWR | O_CREAT, 0777); | ||
| 366 | if (new_file < 0) | ||
| 367 | fatal ("Can't creat (%s): %s", new_name, strerror (errno)); | ||
| 368 | |||
| 369 | new_file_size = old_file_size + new_data2_size; | ||
| 370 | |||
| 371 | if (ftruncate (new_file, new_file_size)) | ||
| 372 | fatal ("Can't ftruncate (%s): %s", new_name, strerror (errno)); | ||
| 373 | |||
| 374 | new_base = mmap (NULL, new_file_size, PROT_READ | PROT_WRITE, | ||
| 375 | MAP_ANON | MAP_PRIVATE, mmap_fd, 0); | ||
| 376 | if (new_base == MAP_FAILED) | ||
| 377 | fatal ("Can't allocate buffer for %s: %s", old_name, strerror (errno)); | ||
| 378 | |||
| 379 | /* Make our new file, program and section headers as copies of the | ||
| 380 | originals. */ | ||
| 381 | |||
| 382 | new_file_h = (ElfW (Ehdr) *) new_base; | ||
| 383 | memcpy (new_file_h, old_file_h, old_file_h->e_ehsize); | ||
| 384 | |||
| 385 | /* Fix up file header. Section header is further away now. */ | ||
| 386 | |||
| 387 | if (new_file_h->e_shoff >= old_bss_offset) | ||
| 388 | new_file_h->e_shoff += new_data2_size; | ||
| 389 | |||
| 390 | new_program_h = (ElfW (Phdr) *) ((byte *) new_base + new_file_h->e_phoff); | ||
| 391 | new_section_h = (ElfW (Shdr) *) ((byte *) new_base + new_file_h->e_shoff); | ||
| 392 | |||
| 393 | memcpy (new_program_h, old_program_h, | ||
| 394 | old_file_h->e_phnum * old_file_h->e_phentsize); | ||
| 395 | memcpy (new_section_h, old_section_h, | ||
| 396 | old_file_h->e_shnum * old_file_h->e_shentsize); | ||
| 397 | |||
| 398 | #ifdef UNEXELF_DEBUG | ||
| 399 | DEBUG_LOG (old_file_h->e_shoff); | ||
| 400 | fprintf (stderr, "Old section count %td\n", (ptrdiff_t) old_file_h->e_shnum); | ||
| 401 | DEBUG_LOG (new_file_h->e_shoff); | ||
| 402 | fprintf (stderr, "New section count %td\n", (ptrdiff_t) new_file_h->e_shnum); | ||
| 403 | #endif | ||
| 404 | |||
| 405 | /* Fix up program header. Extend the writable data segment so | ||
| 406 | that the bss area is covered too. */ | ||
| 407 | |||
| 408 | new_bss_seg = new_program_h + (old_bss_seg - old_program_h); | ||
| 409 | new_bss_seg->p_filesz = new_bss_addr - new_bss_seg->p_vaddr; | ||
| 410 | new_bss_seg->p_memsz = new_bss_seg->p_filesz; | ||
| 411 | |||
| 412 | /* Copy over what we have in memory now for the bss area. */ | ||
| 413 | memcpy (new_base + new_data2_offset, (caddr_t) old_bss_addr, | ||
| 414 | bss_size_growth); | ||
| 415 | |||
| 416 | /* Walk through all section headers, copying data and updating. */ | ||
| 417 | for (n = 1; n < old_file_h->e_shnum; n++) | ||
| 418 | { | ||
| 419 | caddr_t src; | ||
| 420 | ElfW (Shdr) *old_shdr = &OLD_SECTION_H (n); | ||
| 421 | ElfW (Shdr) *new_shdr = &NEW_SECTION_H (n); | ||
| 422 | |||
| 423 | if (new_shdr->sh_type == SHT_NOBITS | ||
| 424 | && new_shdr->sh_addr >= old_bss_addr | ||
| 425 | && (new_shdr->sh_addr + new_shdr->sh_size | ||
| 426 | <= old_bss_addr + old_bss_size)) | ||
| 427 | { | ||
| 428 | /* This section now has file backing. */ | ||
| 429 | new_shdr->sh_type = SHT_PROGBITS; | ||
| 430 | |||
| 431 | /* SHT_NOBITS sections do not need a valid sh_offset, so it | ||
| 432 | might be incorrect. Write the correct value. */ | ||
| 433 | new_shdr->sh_offset = (new_shdr->sh_addr - new_bss_seg->p_vaddr | ||
| 434 | + new_bss_seg->p_offset); | ||
| 435 | |||
| 436 | /* If this is was a SHT_NOBITS .plt section, then it is | ||
| 437 | probably a PowerPC PLT. If it is PowerPC64 ELFv1 then | ||
| 438 | glibc ld.so doesn't initialize the toc pointer word. A | ||
| 439 | non-zero toc pointer word can defeat Power7 thread safety | ||
| 440 | during lazy update of a PLT entry. This only matters if | ||
| 441 | emacs becomes multi-threaded. */ | ||
| 442 | if (strcmp (old_section_names + new_shdr->sh_name, ".plt") == 0) | ||
| 443 | memset (new_shdr->sh_offset + new_base, 0, new_shdr->sh_size); | ||
| 444 | |||
| 445 | /* Extend the size of the last bss section to cover dumped | ||
| 446 | data. */ | ||
| 447 | if (n == old_bss_index) | ||
| 448 | new_shdr->sh_size = new_bss_addr - new_shdr->sh_addr; | ||
| 449 | |||
| 450 | /* We have already copied this section from the current | ||
| 451 | process. */ | ||
| 452 | continue; | ||
| 453 | } | ||
| 454 | |||
| 455 | /* Any section that was originally placed after the .bss | ||
| 456 | section should now be offset by NEW_DATA2_SIZE. */ | ||
| 457 | if (new_shdr->sh_offset >= old_bss_offset) | ||
| 458 | new_shdr->sh_offset += new_data2_size; | ||
| 459 | |||
| 460 | /* Now, start to copy the content of sections. */ | ||
| 461 | if (new_shdr->sh_type == SHT_NULL | ||
| 462 | || new_shdr->sh_type == SHT_NOBITS) | ||
| 463 | continue; | ||
| 464 | |||
| 465 | /* Some sections are copied from the current process instead of | ||
| 466 | the old file. */ | ||
| 467 | if (!strcmp (old_section_names + new_shdr->sh_name, ".data") | ||
| 468 | || !strcmp (old_section_names + new_shdr->sh_name, ".sdata") | ||
| 469 | || !strcmp (old_section_names + new_shdr->sh_name, ".lit4") | ||
| 470 | || !strcmp (old_section_names + new_shdr->sh_name, ".lit8") | ||
| 471 | || !strcmp (old_section_names + new_shdr->sh_name, ".sdata1") | ||
| 472 | || !strcmp (old_section_names + new_shdr->sh_name, ".data1")) | ||
| 473 | src = (caddr_t) old_shdr->sh_addr; | ||
| 474 | else | ||
| 475 | src = old_base + old_shdr->sh_offset; | ||
| 476 | |||
| 477 | memcpy (new_shdr->sh_offset + new_base, src, new_shdr->sh_size); | ||
| 478 | |||
| 479 | #if (defined __alpha__ && !defined __OpenBSD__) || defined _SYSTYPE_SYSV | ||
| 480 | /* Update Alpha and MIPS COFF debug symbol table. */ | ||
| 481 | if (strcmp (old_section_names + new_shdr->sh_name, ".mdebug") == 0 | ||
| 482 | && new_shdr->sh_offset - old_shdr->sh_offset != 0 | ||
| 483 | #if defined _SYSTYPE_SYSV | ||
| 484 | && new_shdr->sh_type == SHT_MIPS_DEBUG | ||
| 485 | #endif | ||
| 486 | ) | ||
| 487 | { | ||
| 488 | ptrdiff_t diff = new_shdr->sh_offset - old_shdr->sh_offset; | ||
| 489 | HDRR *phdr = (HDRR *) (new_shdr->sh_offset + new_base); | ||
| 490 | |||
| 491 | phdr->cbLineOffset += diff; | ||
| 492 | phdr->cbDnOffset += diff; | ||
| 493 | phdr->cbPdOffset += diff; | ||
| 494 | phdr->cbSymOffset += diff; | ||
| 495 | phdr->cbOptOffset += diff; | ||
| 496 | phdr->cbAuxOffset += diff; | ||
| 497 | phdr->cbSsOffset += diff; | ||
| 498 | phdr->cbSsExtOffset += diff; | ||
| 499 | phdr->cbFdOffset += diff; | ||
| 500 | phdr->cbRfdOffset += diff; | ||
| 501 | phdr->cbExtOffset += diff; | ||
| 502 | } | ||
| 503 | #endif /* __alpha__ || _SYSTYPE_SYSV */ | ||
| 504 | } | ||
| 505 | |||
| 506 | /* Update the symbol values of _edata and _end. */ | ||
| 507 | for (n = new_file_h->e_shnum; 0 < --n; ) | ||
| 508 | { | ||
| 509 | byte *symnames; | ||
| 510 | ElfW (Sym) *symp, *symendp; | ||
| 511 | ElfW (Shdr) *sym_shdr = &NEW_SECTION_H (n); | ||
| 512 | |||
| 513 | if (sym_shdr->sh_type != SHT_DYNSYM | ||
| 514 | && sym_shdr->sh_type != SHT_SYMTAB) | ||
| 515 | continue; | ||
| 516 | |||
| 517 | symnames = ((byte *) new_base | ||
| 518 | + NEW_SECTION_H (sym_shdr->sh_link).sh_offset); | ||
| 519 | symp = (ElfW (Sym) *) (sym_shdr->sh_offset + new_base); | ||
| 520 | symendp = (ElfW (Sym) *) ((byte *) symp + sym_shdr->sh_size); | ||
| 521 | |||
| 522 | for (; symp < symendp; symp ++) | ||
| 523 | { | ||
| 524 | if (strcmp ((char *) (symnames + symp->st_name), "_end") == 0 | ||
| 525 | || strcmp ((char *) (symnames + symp->st_name), "end") == 0 | ||
| 526 | || strcmp ((char *) (symnames + symp->st_name), "_edata") == 0 | ||
| 527 | || strcmp ((char *) (symnames + symp->st_name), "edata") == 0) | ||
| 528 | memcpy (&symp->st_value, &new_bss_addr, sizeof (new_bss_addr)); | ||
| 529 | |||
| 530 | /* Strictly speaking, #ifdef below is not necessary. But we | ||
| 531 | keep it to indicate that this kind of change may also be | ||
| 532 | necessary for other unexecs to support GNUstep. */ | ||
| 533 | #ifdef NS_IMPL_GNUSTEP | ||
| 534 | /* ObjC runtime modifies the values of some data structures | ||
| 535 | such as classes and selectors in the .data section after | ||
| 536 | loading. As the dump process copies the .data section | ||
| 537 | from the current process, that causes problems when the | ||
| 538 | modified classes are reinitialized in the dumped | ||
| 539 | executable. We copy such data from the old file, not | ||
| 540 | from the current process. */ | ||
| 541 | if (strncmp ((char *) (symnames + symp->st_name), | ||
| 542 | "_OBJC_", sizeof ("_OBJC_") - 1) == 0) | ||
| 543 | { | ||
| 544 | ElfW (Shdr) *new_shdr = &NEW_SECTION_H (symp->st_shndx); | ||
| 545 | if (new_shdr->sh_type != SHT_NOBITS) | ||
| 546 | { | ||
| 547 | ElfW (Shdr) *old_shdr = &OLD_SECTION_H (symp->st_shndx); | ||
| 548 | ptrdiff_t reladdr = symp->st_value - new_shdr->sh_addr; | ||
| 549 | ptrdiff_t newoff = reladdr + new_shdr->sh_offset; | ||
| 550 | |||
| 551 | if (old_shdr->sh_type == SHT_NOBITS) | ||
| 552 | memset (new_base + newoff, 0, symp->st_size); | ||
| 553 | else | ||
| 554 | { | ||
| 555 | ptrdiff_t oldoff = reladdr + old_shdr->sh_offset; | ||
| 556 | memcpy (new_base + newoff, old_base + oldoff, | ||
| 557 | symp->st_size); | ||
| 558 | } | ||
| 559 | } | ||
| 560 | } | ||
| 561 | #endif | ||
| 562 | } | ||
| 563 | } | ||
| 564 | |||
| 565 | /* Modify the names of sections we changed from SHT_NOBITS to | ||
| 566 | SHT_PROGBITS. This is really just cosmetic, but some tools that | ||
| 567 | (wrongly) operate on section names rather than types might be | ||
| 568 | confused by a SHT_PROGBITS .bss section. */ | ||
| 569 | new_section_names = ((char *) new_base | ||
| 570 | + NEW_SECTION_H (new_file_h->e_shstrndx).sh_offset); | ||
| 571 | for (n = new_file_h->e_shnum; 0 < --n; ) | ||
| 572 | { | ||
| 573 | ElfW (Shdr) *old_shdr = &OLD_SECTION_H (n); | ||
| 574 | ElfW (Shdr) *new_shdr = &NEW_SECTION_H (n); | ||
| 575 | |||
| 576 | /* Replace the leading '.' with ','. When .shstrtab is string | ||
| 577 | merged this will rename both .bss and .rela.bss to ,bss and | ||
| 578 | .rela,bss. */ | ||
| 579 | if (old_shdr->sh_type == SHT_NOBITS | ||
| 580 | && new_shdr->sh_type == SHT_PROGBITS) | ||
| 581 | *(new_section_names + new_shdr->sh_name) = ','; | ||
| 582 | } | ||
| 583 | |||
| 584 | /* This loop seeks out relocation sections for the data section, so | ||
| 585 | that it can undo relocations performed by the runtime loader. | ||
| 586 | |||
| 587 | The following approach does not work on x86 platforms that use | ||
| 588 | the GNU Gold linker, which can generate .rel.dyn relocation | ||
| 589 | sections containing R_386_32 entries that the following code does | ||
| 590 | not grok. Emacs works around this problem by avoiding C | ||
| 591 | constructs that generate such entries, which is horrible hack. | ||
| 592 | |||
| 593 | FIXME: Presumably more problems like this will crop up as linkers | ||
| 594 | get fancier. We really need to stop assuming that Emacs can grok | ||
| 595 | arbitrary linker output. See Bug#27248. */ | ||
| 596 | for (n = new_file_h->e_shnum; 0 < --n; ) | ||
| 597 | { | ||
| 598 | ElfW (Shdr) *rel_shdr = &NEW_SECTION_H (n); | ||
| 599 | ElfW (Shdr) *shdr; | ||
| 600 | |||
| 601 | switch (rel_shdr->sh_type) | ||
| 602 | { | ||
| 603 | default: | ||
| 604 | break; | ||
| 605 | case SHT_REL: | ||
| 606 | case SHT_RELA: | ||
| 607 | /* This code handles two different size structs, but there should | ||
| 608 | be no harm in that provided that r_offset is always the first | ||
| 609 | member. */ | ||
| 610 | shdr = &NEW_SECTION_H (rel_shdr->sh_info); | ||
| 611 | if (!strcmp (old_section_names + shdr->sh_name, ".data") | ||
| 612 | || !strcmp (old_section_names + shdr->sh_name, ".sdata") | ||
| 613 | || !strcmp (old_section_names + shdr->sh_name, ".lit4") | ||
| 614 | || !strcmp (old_section_names + shdr->sh_name, ".lit8") | ||
| 615 | || !strcmp (old_section_names + shdr->sh_name, ".sdata1") | ||
| 616 | || !strcmp (old_section_names + shdr->sh_name, ".data1")) | ||
| 617 | { | ||
| 618 | ElfW (Addr) offset = shdr->sh_addr - shdr->sh_offset; | ||
| 619 | caddr_t reloc = old_base + rel_shdr->sh_offset, end; | ||
| 620 | for (end = reloc + rel_shdr->sh_size; | ||
| 621 | reloc < end; | ||
| 622 | reloc += rel_shdr->sh_entsize) | ||
| 623 | { | ||
| 624 | ElfW (Addr) addr = ((ElfW (Rel) *) reloc)->r_offset - offset; | ||
| 625 | /* Ignore R_*_NONE relocs. */ | ||
| 626 | if (((ElfW (Rel) *) reloc)->r_offset == 0) | ||
| 627 | continue; | ||
| 628 | /* Assume reloc applies to a word. | ||
| 629 | ??? This is not always true, eg. TLS module/index | ||
| 630 | pair in .got which occupies two words. */ | ||
| 631 | memcpy (new_base + addr, old_base + addr, | ||
| 632 | sizeof (ElfW (Addr))); | ||
| 633 | } | ||
| 634 | } | ||
| 635 | break; | ||
| 636 | } | ||
| 637 | } | ||
| 638 | |||
| 639 | /* Write out new_file, and free the buffers. */ | ||
| 640 | |||
| 641 | if (write (new_file, new_base, new_file_size) != new_file_size) | ||
| 642 | fatal ("Didn't write %lu bytes to %s: %s", | ||
| 643 | (unsigned long) new_file_size, new_name, strerror (errno)); | ||
| 644 | munmap (old_base, old_file_size); | ||
| 645 | munmap (new_base, new_file_size); | ||
| 646 | |||
| 647 | /* Close the files and make the new file executable. */ | ||
| 648 | |||
| 649 | #if MAP_ANON == 0 | ||
| 650 | emacs_close (mmap_fd); | ||
| 651 | #endif | ||
| 652 | |||
| 653 | if (emacs_close (old_file) != 0) | ||
| 654 | fatal ("Can't close (%s): %s", old_name, strerror (errno)); | ||
| 655 | |||
| 656 | if (emacs_close (new_file) != 0) | ||
| 657 | fatal ("Can't close (%s): %s", new_name, strerror (errno)); | ||
| 658 | } | ||
diff --git a/src/unexhp9k800.c b/src/unexhp9k800.c deleted file mode 100644 index d2943eb18c9..00000000000 --- a/src/unexhp9k800.c +++ /dev/null | |||
| @@ -1,324 +0,0 @@ | |||
| 1 | /* Unexec for HP 9000 Series 800 machines. | ||
| 2 | |||
| 3 | This file is in the public domain. | ||
| 4 | |||
| 5 | Author: John V. Morris | ||
| 6 | |||
| 7 | This file was written by John V. Morris at Hewlett Packard. | ||
| 8 | Both the author and Hewlett Packard Co. have disclaimed the | ||
| 9 | copyright on this file, and it is therefore in the public domain. | ||
| 10 | (Search for "hp9k800" in copyright.list.) | ||
| 11 | */ | ||
| 12 | |||
| 13 | /* | ||
| 14 | Bob Desinger <hpsemc!bd@hplabs.hp.com> | ||
| 15 | |||
| 16 | Note that the GNU project considers support for HP operation a | ||
| 17 | peripheral activity which should not be allowed to divert effort | ||
| 18 | from development of the GNU system. Changes in this code will be | ||
| 19 | installed when users send them in, but aside from that we don't | ||
| 20 | plan to think about it, or about whether other Emacs maintenance | ||
| 21 | might break it. | ||
| 22 | |||
| 23 | |||
| 24 | Unexec creates a copy of the old a.out file, and replaces the old data | ||
| 25 | area with the current data area. When the new file is executed, the | ||
| 26 | process will see the same data structures and data values that the | ||
| 27 | original process had when unexec was called. | ||
| 28 | |||
| 29 | Unlike other versions of unexec, this one copies symbol table and | ||
| 30 | debug information to the new a.out file. Thus, the new a.out file | ||
| 31 | may be debugged with symbolic debuggers. | ||
| 32 | |||
| 33 | If you fix any bugs in this, I'd like to incorporate your fixes. | ||
| 34 | Send them to uunet!hpda!hpsemc!jmorris or jmorris%hpsemc@hplabs.HP.COM. | ||
| 35 | |||
| 36 | CAVEATS: | ||
| 37 | This routine saves the current value of all static and external | ||
| 38 | variables. This means that any data structure that needs to be | ||
| 39 | initialized must be explicitly reset. Variables will not have their | ||
| 40 | expected default values. | ||
| 41 | |||
| 42 | Unfortunately, the HP-UX signal handler has internal initialization | ||
| 43 | flags which are not explicitly reset. Thus, for signals to work in | ||
| 44 | conjunction with this routine, the following code must executed when | ||
| 45 | the new process starts up. | ||
| 46 | |||
| 47 | void _sigreturn (); | ||
| 48 | ... | ||
| 49 | sigsetreturn (_sigreturn); | ||
| 50 | */ | ||
| 51 | |||
| 52 | #include <config.h> | ||
| 53 | #include "unexec.h" | ||
| 54 | #include "lisp.h" | ||
| 55 | #include "sysstdio.h" | ||
| 56 | |||
| 57 | #include <fcntl.h> | ||
| 58 | #include <errno.h> | ||
| 59 | #include <a.out.h> | ||
| 60 | #include <dl.h> | ||
| 61 | |||
| 62 | /* brk value to restore, stored as a global. | ||
| 63 | This is really used only if we used shared libraries. */ | ||
| 64 | static long brk_on_dump = 0; | ||
| 65 | |||
| 66 | /* Called from main, if we use shared libraries. */ | ||
| 67 | int | ||
| 68 | run_time_remap (char *ignored) | ||
| 69 | { | ||
| 70 | brk ((char *) brk_on_dump); | ||
| 71 | } | ||
| 72 | |||
| 73 | #undef roundup | ||
| 74 | #define roundup(x,n) (((x) + ((n) - 1)) & ~((n) - 1)) /* n is power of 2 */ | ||
| 75 | |||
| 76 | /* Report a fatal error and exit. */ | ||
| 77 | static _Noreturn void | ||
| 78 | unexec_error (char const *msg) | ||
| 79 | { | ||
| 80 | perror (msg); | ||
| 81 | exit (1); | ||
| 82 | } | ||
| 83 | |||
| 84 | /* Do an lseek and check the result. */ | ||
| 85 | static void | ||
| 86 | check_lseek (int fd, off_t offset, int whence) | ||
| 87 | { | ||
| 88 | if (lseek (fd, offset, whence) < 0) | ||
| 89 | unexec_error ("Cannot lseek"); | ||
| 90 | } | ||
| 91 | |||
| 92 | /* Save current data space in the file, update header. */ | ||
| 93 | |||
| 94 | static void | ||
| 95 | save_data_space (int file, struct header *hdr, struct som_exec_auxhdr *auxhdr, | ||
| 96 | int size) | ||
| 97 | { | ||
| 98 | /* Write the entire data space out to the file */ | ||
| 99 | if (write (file, auxhdr->exec_dmem, size) != size) | ||
| 100 | unexec_error ("Can't save new data space"); | ||
| 101 | |||
| 102 | /* Update the header to reflect the new data size */ | ||
| 103 | auxhdr->exec_dsize = size; | ||
| 104 | auxhdr->exec_bsize = 0; | ||
| 105 | } | ||
| 106 | |||
| 107 | /* Update the values of file pointers when something is inserted. */ | ||
| 108 | |||
| 109 | static void | ||
| 110 | update_file_ptrs (int file, struct header *hdr, struct som_exec_auxhdr *auxhdr, | ||
| 111 | unsigned int location, int offset) | ||
| 112 | { | ||
| 113 | struct subspace_dictionary_record subspace; | ||
| 114 | int i; | ||
| 115 | |||
| 116 | /* Increase the overall size of the module */ | ||
| 117 | hdr->som_length += offset; | ||
| 118 | |||
| 119 | /* Update the various file pointers in the header */ | ||
| 120 | #define update(ptr) if (ptr > location) ptr = ptr + offset | ||
| 121 | update (hdr->aux_header_location); | ||
| 122 | update (hdr->space_strings_location); | ||
| 123 | update (hdr->init_array_location); | ||
| 124 | update (hdr->compiler_location); | ||
| 125 | update (hdr->symbol_location); | ||
| 126 | update (hdr->fixup_request_location); | ||
| 127 | update (hdr->symbol_strings_location); | ||
| 128 | update (hdr->unloadable_sp_location); | ||
| 129 | update (auxhdr->exec_tfile); | ||
| 130 | update (auxhdr->exec_dfile); | ||
| 131 | |||
| 132 | /* Do for each subspace dictionary entry */ | ||
| 133 | check_lseek (file, hdr->subspace_location, 0); | ||
| 134 | for (i = 0; i < hdr->subspace_total; i++) | ||
| 135 | { | ||
| 136 | ptrdiff_t subspace_size = sizeof subspace; | ||
| 137 | if (read (file, &subspace, subspace_size) != subspace_size) | ||
| 138 | unexec_error ("Can't read subspace record"); | ||
| 139 | |||
| 140 | /* If subspace has a file location, update it */ | ||
| 141 | if (subspace.initialization_length > 0 | ||
| 142 | && subspace.file_loc_init_value > location) | ||
| 143 | { | ||
| 144 | subspace.file_loc_init_value += offset; | ||
| 145 | check_lseek (file, -subspace_size, 1); | ||
| 146 | if (write (file, &subspace, subspace_size) != subspace_size) | ||
| 147 | unexec_error ("Can't update subspace record"); | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | /* Do for each initialization pointer record */ | ||
| 152 | /* (I don't think it applies to executable files, only relocatables) */ | ||
| 153 | #undef update | ||
| 154 | } | ||
| 155 | |||
| 156 | /* Read in the header records from an a.out file. */ | ||
| 157 | |||
| 158 | static void | ||
| 159 | read_header (int file, struct header *hdr, struct som_exec_auxhdr *auxhdr) | ||
| 160 | { | ||
| 161 | |||
| 162 | /* Read the header in */ | ||
| 163 | check_lseek (file, 0, 0); | ||
| 164 | if (read (file, hdr, sizeof (*hdr)) != sizeof (*hdr)) | ||
| 165 | unexec_error ("Couldn't read header from a.out file"); | ||
| 166 | |||
| 167 | if (hdr->a_magic != EXEC_MAGIC && hdr->a_magic != SHARE_MAGIC | ||
| 168 | && hdr->a_magic != DEMAND_MAGIC) | ||
| 169 | { | ||
| 170 | fputs ("a.out file doesn't have valid magic number\n", stderr); | ||
| 171 | exit (1); | ||
| 172 | } | ||
| 173 | |||
| 174 | check_lseek (file, hdr->aux_header_location, 0); | ||
| 175 | if (read (file, auxhdr, sizeof (*auxhdr)) != sizeof (*auxhdr)) | ||
| 176 | unexec_error ("Couldn't read auxiliary header from a.out file"); | ||
| 177 | } | ||
| 178 | |||
| 179 | /* Write out the header records into an a.out file. */ | ||
| 180 | |||
| 181 | static void | ||
| 182 | write_header (int file, struct header *hdr, struct som_exec_auxhdr *auxhdr) | ||
| 183 | { | ||
| 184 | /* Update the checksum */ | ||
| 185 | hdr->checksum = calculate_checksum (hdr); | ||
| 186 | |||
| 187 | /* Write the header back into the a.out file */ | ||
| 188 | check_lseek (file, 0, 0); | ||
| 189 | if (write (file, hdr, sizeof (*hdr)) != sizeof (*hdr)) | ||
| 190 | unexec_error ("Couldn't write header to a.out file"); | ||
| 191 | check_lseek (file, hdr->aux_header_location, 0); | ||
| 192 | if (write (file, auxhdr, sizeof (*auxhdr)) != sizeof (*auxhdr)) | ||
| 193 | unexec_error ("Couldn't write auxiliary header to a.out file"); | ||
| 194 | } | ||
| 195 | |||
| 196 | /* Calculate the checksum of a SOM header record. */ | ||
| 197 | |||
| 198 | static int | ||
| 199 | calculate_checksum (struct header *hdr) | ||
| 200 | { | ||
| 201 | int checksum, i, *ptr; | ||
| 202 | |||
| 203 | checksum = 0; ptr = (int *) hdr; | ||
| 204 | |||
| 205 | for (i = 0; i < sizeof (*hdr) / sizeof (int) - 1; i++) | ||
| 206 | checksum ^= ptr[i]; | ||
| 207 | |||
| 208 | return (checksum); | ||
| 209 | } | ||
| 210 | |||
| 211 | /* Copy size bytes from the old file to the new one. */ | ||
| 212 | |||
| 213 | static void | ||
| 214 | copy_file (int old, int new, int size) | ||
| 215 | { | ||
| 216 | int len; | ||
| 217 | int buffer[8192]; /* word aligned will be faster */ | ||
| 218 | |||
| 219 | for (; size > 0; size -= len) | ||
| 220 | { | ||
| 221 | len = min (size, sizeof (buffer)); | ||
| 222 | if (read (old, buffer, len) != len) | ||
| 223 | unexec_error ("Read failure on a.out file"); | ||
| 224 | if (write (new, buffer, len) != len) | ||
| 225 | unexec_error ("Write failure in a.out file"); | ||
| 226 | } | ||
| 227 | } | ||
| 228 | |||
| 229 | /* Copy the rest of the file, up to EOF. */ | ||
| 230 | |||
| 231 | static void | ||
| 232 | copy_rest (int old, int new) | ||
| 233 | { | ||
| 234 | int buffer[4096]; | ||
| 235 | int len; | ||
| 236 | |||
| 237 | /* Copy bytes until end of file or error */ | ||
| 238 | while ((len = read (old, buffer, sizeof (buffer))) > 0) | ||
| 239 | if (write (new, buffer, len) != len) break; | ||
| 240 | |||
| 241 | if (len != 0) | ||
| 242 | unexec_error ("Unable to copy the rest of the file"); | ||
| 243 | } | ||
| 244 | |||
| 245 | #ifdef DEBUG | ||
| 246 | static void | ||
| 247 | display_header (struct header *hdr, struct som_exec_auxhdr *auxhdr) | ||
| 248 | { | ||
| 249 | /* Display the header information (debug) */ | ||
| 250 | printf ("\n\nFILE HEADER\n"); | ||
| 251 | printf ("magic number %d \n", hdr->a_magic); | ||
| 252 | printf ("text loc %.8x size %d \n", auxhdr->exec_tmem, auxhdr->exec_tsize); | ||
| 253 | printf ("data loc %.8x size %d \n", auxhdr->exec_dmem, auxhdr->exec_dsize); | ||
| 254 | printf ("entry %x \n", auxhdr->exec_entry); | ||
| 255 | printf ("Bss segment size %u\n", auxhdr->exec_bsize); | ||
| 256 | printf ("\n"); | ||
| 257 | printf ("data file loc %d size %d\n", | ||
| 258 | auxhdr->exec_dfile, auxhdr->exec_dsize); | ||
| 259 | printf ("som_length %d\n", hdr->som_length); | ||
| 260 | printf ("unloadable sploc %d size %d\n", | ||
| 261 | hdr->unloadable_sp_location, hdr->unloadable_sp_size); | ||
| 262 | } | ||
| 263 | #endif /* DEBUG */ | ||
| 264 | |||
| 265 | |||
| 266 | /* Create a new a.out file, same as old but with current data space */ | ||
| 267 | void | ||
| 268 | unexec (const char *new_name, /* name of the new a.out file to be created */ | ||
| 269 | const char *old_name) /* name of the old a.out file */ | ||
| 270 | { | ||
| 271 | int old, new; | ||
| 272 | int old_size, new_size; | ||
| 273 | struct header hdr; | ||
| 274 | struct som_exec_auxhdr auxhdr; | ||
| 275 | long i; | ||
| 276 | |||
| 277 | /* For the greatest flexibility, should create a temporary file in | ||
| 278 | the same directory as the new file. When everything is complete, | ||
| 279 | rename the temp file to the new name. | ||
| 280 | This way, a program could update its own a.out file even while | ||
| 281 | it is still executing. If problems occur, everything is still | ||
| 282 | intact. NOT implemented. */ | ||
| 283 | |||
| 284 | /* Open the input and output a.out files. */ | ||
| 285 | old = emacs_open (old_name, O_RDONLY, 0); | ||
| 286 | if (old < 0) | ||
| 287 | unexec_error (old_name); | ||
| 288 | new = emacs_open (new_name, O_CREAT | O_RDWR | O_TRUNC, 0777); | ||
| 289 | if (new < 0) | ||
| 290 | unexec_error (new_name); | ||
| 291 | |||
| 292 | /* Read the old headers. */ | ||
| 293 | read_header (old, &hdr, &auxhdr); | ||
| 294 | |||
| 295 | brk_on_dump = (long) sbrk (0); | ||
| 296 | |||
| 297 | /* Decide how large the new and old data areas are. */ | ||
| 298 | old_size = auxhdr.exec_dsize; | ||
| 299 | /* I suspect these two statements are separate | ||
| 300 | to avoid a compiler bug in hpux version 8. */ | ||
| 301 | i = (long) sbrk (0); | ||
| 302 | new_size = i - auxhdr.exec_dmem; | ||
| 303 | |||
| 304 | /* Copy the old file to the new, up to the data space. */ | ||
| 305 | check_lseek (old, 0, 0); | ||
| 306 | copy_file (old, new, auxhdr.exec_dfile); | ||
| 307 | |||
| 308 | /* Skip the old data segment and write a new one. */ | ||
| 309 | check_lseek (old, old_size, 1); | ||
| 310 | save_data_space (new, &hdr, &auxhdr, new_size); | ||
| 311 | |||
| 312 | /* Copy the rest of the file. */ | ||
| 313 | copy_rest (old, new); | ||
| 314 | |||
| 315 | /* Update file pointers since we probably changed size of data area. */ | ||
| 316 | update_file_ptrs (new, &hdr, &auxhdr, auxhdr.exec_dfile, new_size-old_size); | ||
| 317 | |||
| 318 | /* Save the modified header. */ | ||
| 319 | write_header (new, &hdr, &auxhdr); | ||
| 320 | |||
| 321 | /* Close the binary file. */ | ||
| 322 | emacs_close (old); | ||
| 323 | emacs_close (new); | ||
| 324 | } | ||
diff --git a/src/unexmacosx.c b/src/unexmacosx.c deleted file mode 100644 index 7b2326441b4..00000000000 --- a/src/unexmacosx.c +++ /dev/null | |||
| @@ -1,1406 +0,0 @@ | |||
| 1 | /* Dump Emacs in Mach-O format for use on macOS. | ||
| 2 | Copyright (C) 2001-2024 Free Software Foundation, Inc. | ||
| 3 | |||
| 4 | This file is part of GNU Emacs. | ||
| 5 | |||
| 6 | GNU Emacs is free software: you can redistribute it and/or modify | ||
| 7 | it under the terms of the GNU General Public License as published by | ||
| 8 | the Free Software Foundation, either version 3 of the License, or (at | ||
| 9 | your option) any later version. | ||
| 10 | |||
| 11 | GNU Emacs is distributed in the hope that it will be useful, | ||
| 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | GNU General Public License for more details. | ||
| 15 | |||
| 16 | You should have received a copy of the GNU General Public License | ||
| 17 | along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | ||
| 18 | |||
| 19 | /* Contributed by Andrew Choi (akochoi@mac.com). */ | ||
| 20 | |||
| 21 | /* Documentation note. | ||
| 22 | |||
| 23 | Consult the following documents/files for a description of the | ||
| 24 | Mach-O format: the file loader.h, man pages for Mach-O and ld, old | ||
| 25 | NEXTSTEP documents of the Mach-O format. The tool otool dumps the | ||
| 26 | mach header (-h option) and the load commands (-l option) in a | ||
| 27 | Mach-O file. The tool nm on macOS displays the symbol table in | ||
| 28 | a Mach-O file. For examples of unexec for the Mach-O format, see | ||
| 29 | the file unexnext.c in the GNU Emacs distribution, the file | ||
| 30 | unexdyld.c in the Darwin port of GNU Emacs 20.7, and unexdyld.c in | ||
| 31 | the Darwin port of XEmacs 21.1. Also the Darwin Libc source | ||
| 32 | contains the source code for malloc_freezedry and malloc_jumpstart. | ||
| 33 | Read that to see what they do. This file was written completely | ||
| 34 | from scratch, making use of information from the above sources. */ | ||
| 35 | |||
| 36 | /* The macOS implementation of unexec makes use of Darwin's `zone' | ||
| 37 | memory allocator. All calls to malloc, realloc, and free in Emacs | ||
| 38 | are redirected to unexec_malloc, unexec_realloc, and unexec_free in | ||
| 39 | this file. When temacs is run, all memory requests are handled in | ||
| 40 | the zone EmacsZone. The Darwin memory allocator library calls | ||
| 41 | maintain the data structures to manage this zone. Dumping writes | ||
| 42 | its contents to data segments of the executable file. When emacs | ||
| 43 | is run, the loader recreates the contents of the zone in memory. | ||
| 44 | However since the initialization routine of the zone memory | ||
| 45 | allocator is run again, this `zone' can no longer be used as a | ||
| 46 | heap. That is why emacs uses the ordinary malloc system call to | ||
| 47 | allocate memory. Also, when a block of memory needs to be | ||
| 48 | reallocated and the new size is larger than the old one, a new | ||
| 49 | block must be obtained by malloc and the old contents copied to | ||
| 50 | it. */ | ||
| 51 | |||
| 52 | /* Peculiarity of the Mach-O files generated by ld in macOS | ||
| 53 | (possible causes of future bugs if changed). | ||
| 54 | |||
| 55 | The file offset of the start of the __TEXT segment is zero. Since | ||
| 56 | the Mach header and load commands are located at the beginning of a | ||
| 57 | Mach-O file, copying the contents of the __TEXT segment from the | ||
| 58 | input file overwrites them in the output file. Despite this, | ||
| 59 | unexec works fine as written below because the segment load command | ||
| 60 | for __TEXT appears, and is therefore processed, before all other | ||
| 61 | load commands except the segment load command for __PAGEZERO, which | ||
| 62 | remains unchanged. | ||
| 63 | |||
| 64 | Although the file offset of the start of the __TEXT segment is | ||
| 65 | zero, none of the sections it contains actually start there. In | ||
| 66 | fact, the earliest one starts a few hundred bytes beyond the end of | ||
| 67 | the last load command. The linker option -headerpad controls the | ||
| 68 | minimum size of this padding. Its setting can be changed in | ||
| 69 | s/darwin.h. A value of 0x690, e.g., leaves room for 30 additional | ||
| 70 | load commands for the newly created __DATA segments (at 56 bytes | ||
| 71 | each). Unexec fails if there is not enough room for these new | ||
| 72 | segments. | ||
| 73 | |||
| 74 | The __TEXT segment contains the sections __text, __cstring, | ||
| 75 | __picsymbol_stub, and __const and the __DATA segment contains the | ||
| 76 | sections __data, __la_symbol_ptr, __nl_symbol_ptr, __dyld, __bss, | ||
| 77 | and __common. The other segments do not contain any sections. | ||
| 78 | These sections are copied from the input file to the output file, | ||
| 79 | except for __data, __bss, and __common, which are dumped from | ||
| 80 | memory. The types of the sections __bss and __common are changed | ||
| 81 | from S_ZEROFILL to S_REGULAR. Note that the number of sections and | ||
| 82 | their relative order in the input and output files remain | ||
| 83 | unchanged. Otherwise all n_sect fields in the nlist records in the | ||
| 84 | symbol table (specified by the LC_SYMTAB load command) will have to | ||
| 85 | be changed accordingly. | ||
| 86 | */ | ||
| 87 | |||
| 88 | #include <config.h> | ||
| 89 | |||
| 90 | /* Although <config.h> redefines malloc to unexec_malloc, etc., this | ||
| 91 | file wants stdlib.h to declare the originals. */ | ||
| 92 | #undef malloc | ||
| 93 | #undef realloc | ||
| 94 | #undef free | ||
| 95 | |||
| 96 | #include <stdlib.h> | ||
| 97 | |||
| 98 | #include "unexec.h" | ||
| 99 | #include "lisp.h" | ||
| 100 | #include "sysstdio.h" | ||
| 101 | |||
| 102 | #include <errno.h> | ||
| 103 | #include <fcntl.h> | ||
| 104 | #include <stdarg.h> | ||
| 105 | #include <stdint.h> | ||
| 106 | #include <sys/types.h> | ||
| 107 | #include <unistd.h> | ||
| 108 | #include <mach/mach.h> | ||
| 109 | #include <mach/vm_map.h> | ||
| 110 | #include <mach-o/loader.h> | ||
| 111 | #include <mach-o/reloc.h> | ||
| 112 | #ifdef HAVE_MALLOC_MALLOC_H | ||
| 113 | #include <malloc/malloc.h> | ||
| 114 | #else | ||
| 115 | #include <objc/malloc.h> | ||
| 116 | #endif | ||
| 117 | |||
| 118 | #include <assert.h> | ||
| 119 | |||
| 120 | /* LC_DATA_IN_CODE is not defined in mach-o/loader.h on Mac OS X 10.7. | ||
| 121 | But it is used if we build with "Command Line Tools for Xcode 4.5 | ||
| 122 | (Mac OS X Lion) - September 2012". */ | ||
| 123 | #ifndef LC_DATA_IN_CODE | ||
| 124 | #define LC_DATA_IN_CODE 0x29 /* table of non-instructions in __text */ | ||
| 125 | #endif | ||
| 126 | |||
| 127 | #ifdef _LP64 | ||
| 128 | #define mach_header mach_header_64 | ||
| 129 | #define segment_command segment_command_64 | ||
| 130 | #undef VM_REGION_BASIC_INFO_COUNT | ||
| 131 | #define VM_REGION_BASIC_INFO_COUNT VM_REGION_BASIC_INFO_COUNT_64 | ||
| 132 | #undef VM_REGION_BASIC_INFO | ||
| 133 | #define VM_REGION_BASIC_INFO VM_REGION_BASIC_INFO_64 | ||
| 134 | #undef LC_SEGMENT | ||
| 135 | #define LC_SEGMENT LC_SEGMENT_64 | ||
| 136 | #define vm_region vm_region_64 | ||
| 137 | #define section section_64 | ||
| 138 | #undef MH_MAGIC | ||
| 139 | #define MH_MAGIC MH_MAGIC_64 | ||
| 140 | #endif | ||
| 141 | |||
| 142 | #define VERBOSE 1 | ||
| 143 | |||
| 144 | /* Size of buffer used to copy data from the input file to the output | ||
| 145 | file in function unexec_copy. */ | ||
| 146 | #define UNEXEC_COPY_BUFSZ 1024 | ||
| 147 | |||
| 148 | /* Regions with memory addresses above this value are assumed to be | ||
| 149 | mapped to dynamically loaded libraries and will not be dumped. */ | ||
| 150 | #define VM_DATA_TOP (20 * 1024 * 1024) | ||
| 151 | |||
| 152 | /* Type of an element on the list of regions to be dumped. */ | ||
| 153 | struct region_t { | ||
| 154 | vm_address_t address; | ||
| 155 | vm_size_t size; | ||
| 156 | vm_prot_t protection; | ||
| 157 | vm_prot_t max_protection; | ||
| 158 | |||
| 159 | struct region_t *next; | ||
| 160 | }; | ||
| 161 | |||
| 162 | /* Head and tail of the list of regions to be dumped. */ | ||
| 163 | static struct region_t *region_list_head = 0; | ||
| 164 | static struct region_t *region_list_tail = 0; | ||
| 165 | |||
| 166 | /* Pointer to array of load commands. */ | ||
| 167 | static struct load_command **lca; | ||
| 168 | |||
| 169 | /* Number of load commands. */ | ||
| 170 | static int nlc; | ||
| 171 | |||
| 172 | /* The highest VM address of segments loaded by the input file. | ||
| 173 | Regions with addresses beyond this are assumed to be allocated | ||
| 174 | dynamically and thus require dumping. */ | ||
| 175 | static vm_address_t infile_lc_highest_addr = 0; | ||
| 176 | |||
| 177 | /* The lowest file offset used by the all sections in the __TEXT | ||
| 178 | segments. This leaves room at the beginning of the file to store | ||
| 179 | the Mach-O header. Check this value against header size to ensure | ||
| 180 | the added load commands for the new __DATA segments did not | ||
| 181 | overwrite any of the sections in the __TEXT segment. */ | ||
| 182 | static unsigned long text_seg_lowest_offset = 0x10000000; | ||
| 183 | |||
| 184 | /* Mach header. */ | ||
| 185 | static struct mach_header mh; | ||
| 186 | |||
| 187 | /* Offset at which the next load command should be written. */ | ||
| 188 | static unsigned long curr_header_offset = sizeof (struct mach_header); | ||
| 189 | |||
| 190 | /* Offset at which the next segment should be written. */ | ||
| 191 | static unsigned long curr_file_offset = 0; | ||
| 192 | |||
| 193 | static unsigned long pagesize; | ||
| 194 | #define ROUNDUP_TO_PAGE_BOUNDARY(x) (((x) + pagesize - 1) & ~(pagesize - 1)) | ||
| 195 | |||
| 196 | static int infd, outfd; | ||
| 197 | |||
| 198 | static int in_dumped_exec = 0; | ||
| 199 | |||
| 200 | static malloc_zone_t *emacs_zone; | ||
| 201 | |||
| 202 | /* file offset of input file's data segment */ | ||
| 203 | static off_t data_segment_old_fileoff = 0; | ||
| 204 | |||
| 205 | static struct segment_command *data_segment_scp; | ||
| 206 | |||
| 207 | /* Read N bytes from infd into memory starting at address DEST. | ||
| 208 | Return true if successful, false otherwise. */ | ||
| 209 | static int | ||
| 210 | unexec_read (void *dest, size_t n) | ||
| 211 | { | ||
| 212 | return n == read (infd, dest, n); | ||
| 213 | } | ||
| 214 | |||
| 215 | /* Write COUNT bytes from memory starting at address SRC to outfd | ||
| 216 | starting at offset DEST. Return true if successful, false | ||
| 217 | otherwise. */ | ||
| 218 | static int | ||
| 219 | unexec_write (off_t dest, const void *src, size_t count) | ||
| 220 | { | ||
| 221 | task_t task = mach_task_self(); | ||
| 222 | if (task == MACH_PORT_NULL || task == MACH_PORT_DEAD) | ||
| 223 | return false; | ||
| 224 | |||
| 225 | if (lseek (outfd, dest, SEEK_SET) != dest) | ||
| 226 | return 0; | ||
| 227 | |||
| 228 | /* We use the Mach virtual memory API to read our process memory | ||
| 229 | because using src directly would be undefined behavior and fails | ||
| 230 | under Address Sanitizer. */ | ||
| 231 | bool success = false; | ||
| 232 | vm_offset_t data; | ||
| 233 | mach_msg_type_number_t data_count; | ||
| 234 | if (vm_read (task, (uintptr_t) src, count, &data, &data_count) | ||
| 235 | == KERN_SUCCESS) | ||
| 236 | { | ||
| 237 | success = | ||
| 238 | write (outfd, (const void *) (uintptr_t) data, data_count) == count; | ||
| 239 | vm_deallocate (task, data, data_count); | ||
| 240 | } | ||
| 241 | return success; | ||
| 242 | } | ||
| 243 | |||
| 244 | /* Write COUNT bytes of zeros to outfd starting at offset DEST. | ||
| 245 | Return true if successful, false otherwise. */ | ||
| 246 | static int | ||
| 247 | unexec_write_zero (off_t dest, size_t count) | ||
| 248 | { | ||
| 249 | char buf[UNEXEC_COPY_BUFSZ]; | ||
| 250 | ssize_t bytes; | ||
| 251 | |||
| 252 | memset (buf, 0, UNEXEC_COPY_BUFSZ); | ||
| 253 | if (lseek (outfd, dest, SEEK_SET) != dest) | ||
| 254 | return 0; | ||
| 255 | |||
| 256 | while (count > 0) | ||
| 257 | { | ||
| 258 | bytes = count > UNEXEC_COPY_BUFSZ ? UNEXEC_COPY_BUFSZ : count; | ||
| 259 | if (write (outfd, buf, bytes) != bytes) | ||
| 260 | return 0; | ||
| 261 | count -= bytes; | ||
| 262 | } | ||
| 263 | |||
| 264 | return 1; | ||
| 265 | } | ||
| 266 | |||
| 267 | /* Copy COUNT bytes from starting offset SRC in infd to starting | ||
| 268 | offset DEST in outfd. Return true if successful, false | ||
| 269 | otherwise. */ | ||
| 270 | static int | ||
| 271 | unexec_copy (off_t dest, off_t src, ssize_t count) | ||
| 272 | { | ||
| 273 | ssize_t bytes_read; | ||
| 274 | ssize_t bytes_to_read; | ||
| 275 | |||
| 276 | char buf[UNEXEC_COPY_BUFSZ]; | ||
| 277 | |||
| 278 | if (lseek (infd, src, SEEK_SET) != src) | ||
| 279 | return 0; | ||
| 280 | |||
| 281 | if (lseek (outfd, dest, SEEK_SET) != dest) | ||
| 282 | return 0; | ||
| 283 | |||
| 284 | while (count > 0) | ||
| 285 | { | ||
| 286 | bytes_to_read = count > UNEXEC_COPY_BUFSZ ? UNEXEC_COPY_BUFSZ : count; | ||
| 287 | bytes_read = read (infd, buf, bytes_to_read); | ||
| 288 | if (bytes_read <= 0) | ||
| 289 | return 0; | ||
| 290 | if (write (outfd, buf, bytes_read) != bytes_read) | ||
| 291 | return 0; | ||
| 292 | count -= bytes_read; | ||
| 293 | } | ||
| 294 | |||
| 295 | return 1; | ||
| 296 | } | ||
| 297 | |||
| 298 | /* Debugging and informational messages routines. */ | ||
| 299 | |||
| 300 | static _Noreturn void | ||
| 301 | unexec_error (const char *format, ...) | ||
| 302 | { | ||
| 303 | va_list ap; | ||
| 304 | |||
| 305 | va_start (ap, format); | ||
| 306 | fputs ("unexec: ", stderr); | ||
| 307 | vfprintf (stderr, format, ap); | ||
| 308 | putc ('\n', stderr); | ||
| 309 | va_end (ap); | ||
| 310 | exit (1); | ||
| 311 | } | ||
| 312 | |||
| 313 | static void | ||
| 314 | print_prot (vm_prot_t prot) | ||
| 315 | { | ||
| 316 | if (prot == VM_PROT_NONE) | ||
| 317 | printf ("none"); | ||
| 318 | else | ||
| 319 | { | ||
| 320 | putchar (prot & VM_PROT_READ ? 'r' : ' '); | ||
| 321 | putchar (prot & VM_PROT_WRITE ? 'w' : ' '); | ||
| 322 | putchar (prot & VM_PROT_EXECUTE ? 'x' : ' '); | ||
| 323 | putchar (' '); | ||
| 324 | } | ||
| 325 | } | ||
| 326 | |||
| 327 | static void | ||
| 328 | print_region (vm_address_t address, vm_size_t size, vm_prot_t prot, | ||
| 329 | vm_prot_t max_prot) | ||
| 330 | { | ||
| 331 | printf ("%#10lx %#8lx ", (long) address, (long) size); | ||
| 332 | print_prot (prot); | ||
| 333 | putchar (' '); | ||
| 334 | print_prot (max_prot); | ||
| 335 | putchar ('\n'); | ||
| 336 | } | ||
| 337 | |||
| 338 | static void | ||
| 339 | print_region_list (void) | ||
| 340 | { | ||
| 341 | struct region_t *r; | ||
| 342 | |||
| 343 | printf (" address size prot maxp\n"); | ||
| 344 | |||
| 345 | for (r = region_list_head; r; r = r->next) | ||
| 346 | print_region (r->address, r->size, r->protection, r->max_protection); | ||
| 347 | } | ||
| 348 | |||
| 349 | /* Build the list of regions that need to be dumped. Regions with | ||
| 350 | addresses above VM_DATA_TOP are omitted. Adjacent regions with | ||
| 351 | identical protection are merged. Note that non-writable regions | ||
| 352 | cannot be omitted because they some regions created at run time are | ||
| 353 | read-only. */ | ||
| 354 | static void | ||
| 355 | build_region_list (void) | ||
| 356 | { | ||
| 357 | task_t target_task = mach_task_self (); | ||
| 358 | vm_address_t address = (vm_address_t) 0; | ||
| 359 | vm_size_t size; | ||
| 360 | struct vm_region_basic_info info; | ||
| 361 | mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT; | ||
| 362 | mach_port_t object_name; | ||
| 363 | struct region_t *r; | ||
| 364 | |||
| 365 | #if VERBOSE | ||
| 366 | printf ("--- List of All Regions ---\n"); | ||
| 367 | printf (" address size prot maxp\n"); | ||
| 368 | #endif | ||
| 369 | |||
| 370 | while (vm_region (target_task, &address, &size, VM_REGION_BASIC_INFO, | ||
| 371 | (vm_region_info_t) &info, &info_count, &object_name) | ||
| 372 | == KERN_SUCCESS && info_count == VM_REGION_BASIC_INFO_COUNT) | ||
| 373 | { | ||
| 374 | /* Done when we reach addresses of shared libraries, which are | ||
| 375 | loaded in high memory. */ | ||
| 376 | if (address >= VM_DATA_TOP) | ||
| 377 | break; | ||
| 378 | |||
| 379 | #if VERBOSE | ||
| 380 | print_region (address, size, info.protection, info.max_protection); | ||
| 381 | #endif | ||
| 382 | |||
| 383 | /* If a region immediately follows the previous one (the one | ||
| 384 | most recently added to the list) and has identical | ||
| 385 | protection, merge it with the latter. Otherwise create a | ||
| 386 | new list element for it. */ | ||
| 387 | if (region_list_tail | ||
| 388 | && info.protection == region_list_tail->protection | ||
| 389 | && info.max_protection == region_list_tail->max_protection | ||
| 390 | && region_list_tail->address + region_list_tail->size == address) | ||
| 391 | { | ||
| 392 | region_list_tail->size += size; | ||
| 393 | } | ||
| 394 | else | ||
| 395 | { | ||
| 396 | r = malloc (sizeof *r); | ||
| 397 | |||
| 398 | if (!r) | ||
| 399 | unexec_error ("cannot allocate region structure"); | ||
| 400 | |||
| 401 | r->address = address; | ||
| 402 | r->size = size; | ||
| 403 | r->protection = info.protection; | ||
| 404 | r->max_protection = info.max_protection; | ||
| 405 | |||
| 406 | r->next = 0; | ||
| 407 | if (region_list_head == 0) | ||
| 408 | { | ||
| 409 | region_list_head = r; | ||
| 410 | region_list_tail = r; | ||
| 411 | } | ||
| 412 | else | ||
| 413 | { | ||
| 414 | region_list_tail->next = r; | ||
| 415 | region_list_tail = r; | ||
| 416 | } | ||
| 417 | |||
| 418 | /* Deallocate (unused) object name returned by | ||
| 419 | vm_region. */ | ||
| 420 | if (object_name != MACH_PORT_NULL) | ||
| 421 | mach_port_deallocate (target_task, object_name); | ||
| 422 | } | ||
| 423 | |||
| 424 | address += size; | ||
| 425 | } | ||
| 426 | |||
| 427 | printf ("--- List of Regions to be Dumped ---\n"); | ||
| 428 | print_region_list (); | ||
| 429 | } | ||
| 430 | |||
| 431 | |||
| 432 | #define MAX_UNEXEC_REGIONS 400 | ||
| 433 | |||
| 434 | static int num_unexec_regions; | ||
| 435 | typedef struct { | ||
| 436 | vm_range_t range; | ||
| 437 | vm_size_t filesize; | ||
| 438 | } unexec_region_info; | ||
| 439 | static unexec_region_info unexec_regions[MAX_UNEXEC_REGIONS]; | ||
| 440 | |||
| 441 | static void | ||
| 442 | unexec_regions_recorder (task_t task, void *rr, unsigned type, | ||
| 443 | vm_range_t *ranges, unsigned num) | ||
| 444 | { | ||
| 445 | vm_address_t p; | ||
| 446 | vm_size_t filesize; | ||
| 447 | |||
| 448 | while (num && num_unexec_regions < MAX_UNEXEC_REGIONS) | ||
| 449 | { | ||
| 450 | /* Subtract the size of trailing null bytes from filesize. It | ||
| 451 | can be smaller than vmsize in segment commands. In such a | ||
| 452 | case, trailing bytes are initialized with zeros. */ | ||
| 453 | for (p = ranges->address + ranges->size; p > ranges->address; p--) | ||
| 454 | if (*(((char *) p)-1)) | ||
| 455 | break; | ||
| 456 | filesize = p - ranges->address; | ||
| 457 | |||
| 458 | unexec_regions[num_unexec_regions].filesize = filesize; | ||
| 459 | unexec_regions[num_unexec_regions++].range = *ranges; | ||
| 460 | printf ("%#10lx (sz: %#8lx/%#8lx)\n", (long) (ranges->address), | ||
| 461 | (long) filesize, (long) (ranges->size)); | ||
| 462 | ranges++; num--; | ||
| 463 | } | ||
| 464 | } | ||
| 465 | |||
| 466 | static kern_return_t | ||
| 467 | unexec_reader (task_t task, vm_address_t address, vm_size_t size, void **ptr) | ||
| 468 | { | ||
| 469 | *ptr = (void *) address; | ||
| 470 | return KERN_SUCCESS; | ||
| 471 | } | ||
| 472 | |||
| 473 | static void | ||
| 474 | find_emacs_zone_regions (void) | ||
| 475 | { | ||
| 476 | num_unexec_regions = 0; | ||
| 477 | |||
| 478 | emacs_zone->introspect->enumerator (mach_task_self (), 0, | ||
| 479 | MALLOC_PTR_REGION_RANGE_TYPE | ||
| 480 | | MALLOC_ADMIN_REGION_RANGE_TYPE, | ||
| 481 | (vm_address_t) emacs_zone, | ||
| 482 | unexec_reader, | ||
| 483 | unexec_regions_recorder); | ||
| 484 | |||
| 485 | if (num_unexec_regions == MAX_UNEXEC_REGIONS) | ||
| 486 | unexec_error ("find_emacs_zone_regions: too many regions"); | ||
| 487 | } | ||
| 488 | |||
| 489 | static int | ||
| 490 | unexec_regions_sort_compare (const void *a, const void *b) | ||
| 491 | { | ||
| 492 | vm_address_t aa = ((unexec_region_info *) a)->range.address; | ||
| 493 | vm_address_t bb = ((unexec_region_info *) b)->range.address; | ||
| 494 | |||
| 495 | if (aa < bb) | ||
| 496 | return -1; | ||
| 497 | else if (aa > bb) | ||
| 498 | return 1; | ||
| 499 | else | ||
| 500 | return 0; | ||
| 501 | } | ||
| 502 | |||
| 503 | static void | ||
| 504 | unexec_regions_merge (void) | ||
| 505 | { | ||
| 506 | qsort (unexec_regions, num_unexec_regions, sizeof (unexec_regions[0]), | ||
| 507 | &unexec_regions_sort_compare); | ||
| 508 | |||
| 509 | /* Align each region start address to a page boundary. */ | ||
| 510 | for (unexec_region_info *cur = unexec_regions; | ||
| 511 | cur < unexec_regions + num_unexec_regions; cur++) | ||
| 512 | { | ||
| 513 | vm_size_t padsize = cur->range.address & (pagesize - 1); | ||
| 514 | if (padsize) | ||
| 515 | { | ||
| 516 | cur->range.address -= padsize; | ||
| 517 | cur->range.size += padsize; | ||
| 518 | cur->filesize += padsize; | ||
| 519 | |||
| 520 | unexec_region_info *prev = cur == unexec_regions ? NULL : cur - 1; | ||
| 521 | if (prev | ||
| 522 | && prev->range.address + prev->range.size > cur->range.address) | ||
| 523 | { | ||
| 524 | prev->range.size = cur->range.address - prev->range.address; | ||
| 525 | if (prev->filesize > prev->range.size) | ||
| 526 | prev->filesize = prev->range.size; | ||
| 527 | } | ||
| 528 | } | ||
| 529 | } | ||
| 530 | |||
| 531 | int n = 0; | ||
| 532 | unexec_region_info r = unexec_regions[0]; | ||
| 533 | for (int i = 1; i < num_unexec_regions; i++) | ||
| 534 | { | ||
| 535 | if (r.range.address + r.range.size == unexec_regions[i].range.address | ||
| 536 | && r.range.size - r.filesize < 2 * pagesize) | ||
| 537 | { | ||
| 538 | r.filesize = r.range.size + unexec_regions[i].filesize; | ||
| 539 | r.range.size += unexec_regions[i].range.size; | ||
| 540 | } | ||
| 541 | else | ||
| 542 | { | ||
| 543 | unexec_regions[n++] = r; | ||
| 544 | r = unexec_regions[i]; | ||
| 545 | } | ||
| 546 | } | ||
| 547 | unexec_regions[n++] = r; | ||
| 548 | num_unexec_regions = n; | ||
| 549 | } | ||
| 550 | |||
| 551 | |||
| 552 | /* More informational messages routines. */ | ||
| 553 | |||
| 554 | static void | ||
| 555 | print_load_command_name (int lc) | ||
| 556 | { | ||
| 557 | switch (lc) | ||
| 558 | { | ||
| 559 | case LC_SEGMENT: | ||
| 560 | #ifndef _LP64 | ||
| 561 | printf ("LC_SEGMENT "); | ||
| 562 | #else | ||
| 563 | printf ("LC_SEGMENT_64 "); | ||
| 564 | #endif | ||
| 565 | break; | ||
| 566 | case LC_LOAD_DYLINKER: | ||
| 567 | printf ("LC_LOAD_DYLINKER "); | ||
| 568 | break; | ||
| 569 | case LC_LOAD_DYLIB: | ||
| 570 | printf ("LC_LOAD_DYLIB "); | ||
| 571 | break; | ||
| 572 | case LC_SYMTAB: | ||
| 573 | printf ("LC_SYMTAB "); | ||
| 574 | break; | ||
| 575 | case LC_DYSYMTAB: | ||
| 576 | printf ("LC_DYSYMTAB "); | ||
| 577 | break; | ||
| 578 | case LC_UNIXTHREAD: | ||
| 579 | printf ("LC_UNIXTHREAD "); | ||
| 580 | break; | ||
| 581 | case LC_PREBOUND_DYLIB: | ||
| 582 | printf ("LC_PREBOUND_DYLIB"); | ||
| 583 | break; | ||
| 584 | case LC_TWOLEVEL_HINTS: | ||
| 585 | printf ("LC_TWOLEVEL_HINTS"); | ||
| 586 | break; | ||
| 587 | #ifdef LC_UUID | ||
| 588 | case LC_UUID: | ||
| 589 | printf ("LC_UUID "); | ||
| 590 | break; | ||
| 591 | #endif | ||
| 592 | #ifdef LC_DYLD_INFO | ||
| 593 | case LC_DYLD_INFO: | ||
| 594 | printf ("LC_DYLD_INFO "); | ||
| 595 | break; | ||
| 596 | case LC_DYLD_INFO_ONLY: | ||
| 597 | printf ("LC_DYLD_INFO_ONLY"); | ||
| 598 | break; | ||
| 599 | #endif | ||
| 600 | #ifdef LC_VERSION_MIN_MACOSX | ||
| 601 | case LC_VERSION_MIN_MACOSX: | ||
| 602 | printf ("LC_VERSION_MIN_MACOSX"); | ||
| 603 | break; | ||
| 604 | #endif | ||
| 605 | #ifdef LC_FUNCTION_STARTS | ||
| 606 | case LC_FUNCTION_STARTS: | ||
| 607 | printf ("LC_FUNCTION_STARTS"); | ||
| 608 | break; | ||
| 609 | #endif | ||
| 610 | #ifdef LC_MAIN | ||
| 611 | case LC_MAIN: | ||
| 612 | printf ("LC_MAIN "); | ||
| 613 | break; | ||
| 614 | #endif | ||
| 615 | #ifdef LC_DATA_IN_CODE | ||
| 616 | case LC_DATA_IN_CODE: | ||
| 617 | printf ("LC_DATA_IN_CODE "); | ||
| 618 | break; | ||
| 619 | #endif | ||
| 620 | #ifdef LC_SOURCE_VERSION | ||
| 621 | case LC_SOURCE_VERSION: | ||
| 622 | printf ("LC_SOURCE_VERSION"); | ||
| 623 | break; | ||
| 624 | #endif | ||
| 625 | #ifdef LC_DYLIB_CODE_SIGN_DRS | ||
| 626 | case LC_DYLIB_CODE_SIGN_DRS: | ||
| 627 | printf ("LC_DYLIB_CODE_SIGN_DRS"); | ||
| 628 | break; | ||
| 629 | #endif | ||
| 630 | default: | ||
| 631 | printf ("unknown "); | ||
| 632 | } | ||
| 633 | } | ||
| 634 | |||
| 635 | static void | ||
| 636 | print_load_command (struct load_command *lc) | ||
| 637 | { | ||
| 638 | print_load_command_name (lc->cmd); | ||
| 639 | printf ("%8d", lc->cmdsize); | ||
| 640 | |||
| 641 | if (lc->cmd == LC_SEGMENT) | ||
| 642 | { | ||
| 643 | struct segment_command *scp; | ||
| 644 | struct section *sectp; | ||
| 645 | int j; | ||
| 646 | |||
| 647 | scp = (struct segment_command *) lc; | ||
| 648 | printf (" %-16.16s %#10lx %#8lx\n", | ||
| 649 | scp->segname, (long) (scp->vmaddr), (long) (scp->vmsize)); | ||
| 650 | |||
| 651 | sectp = (struct section *) (scp + 1); | ||
| 652 | for (j = 0; j < scp->nsects; j++) | ||
| 653 | { | ||
| 654 | printf (" %-16.16s %#10lx %#8lx\n", | ||
| 655 | sectp->sectname, (long) (sectp->addr), (long) (sectp->size)); | ||
| 656 | sectp++; | ||
| 657 | } | ||
| 658 | } | ||
| 659 | else | ||
| 660 | printf ("\n"); | ||
| 661 | } | ||
| 662 | |||
| 663 | /* Read header and load commands from input file. Store the latter in | ||
| 664 | the global array lca. Store the total number of load commands in | ||
| 665 | global variable nlc. */ | ||
| 666 | static void | ||
| 667 | read_load_commands (void) | ||
| 668 | { | ||
| 669 | int i; | ||
| 670 | |||
| 671 | if (!unexec_read (&mh, sizeof (struct mach_header))) | ||
| 672 | unexec_error ("cannot read mach-o header"); | ||
| 673 | |||
| 674 | if (mh.magic != MH_MAGIC) | ||
| 675 | unexec_error ("input file not in Mach-O format"); | ||
| 676 | |||
| 677 | if (mh.filetype != MH_EXECUTE) | ||
| 678 | unexec_error ("input Mach-O file is not an executable object file"); | ||
| 679 | |||
| 680 | #if VERBOSE | ||
| 681 | printf ("--- Header Information ---\n"); | ||
| 682 | printf ("Magic = 0x%08x\n", mh.magic); | ||
| 683 | printf ("CPUType = %d\n", mh.cputype); | ||
| 684 | printf ("CPUSubType = %d\n", mh.cpusubtype); | ||
| 685 | printf ("FileType = 0x%x\n", mh.filetype); | ||
| 686 | printf ("NCmds = %d\n", mh.ncmds); | ||
| 687 | printf ("SizeOfCmds = %d\n", mh.sizeofcmds); | ||
| 688 | printf ("Flags = 0x%08x\n", mh.flags); | ||
| 689 | #endif | ||
| 690 | |||
| 691 | nlc = mh.ncmds; | ||
| 692 | lca = malloc (nlc * sizeof *lca); | ||
| 693 | |||
| 694 | for (i = 0; i < nlc; i++) | ||
| 695 | { | ||
| 696 | struct load_command lc; | ||
| 697 | /* Load commands are variable-size: so read the command type and | ||
| 698 | size first and then read the rest. */ | ||
| 699 | if (!unexec_read (&lc, sizeof (struct load_command))) | ||
| 700 | unexec_error ("cannot read load command"); | ||
| 701 | lca[i] = malloc (lc.cmdsize); | ||
| 702 | memcpy (lca[i], &lc, sizeof (struct load_command)); | ||
| 703 | if (!unexec_read (lca[i] + 1, lc.cmdsize - sizeof (struct load_command))) | ||
| 704 | unexec_error ("cannot read content of load command"); | ||
| 705 | if (lc.cmd == LC_SEGMENT) | ||
| 706 | { | ||
| 707 | struct segment_command *scp = (struct segment_command *) lca[i]; | ||
| 708 | |||
| 709 | if (scp->vmaddr + scp->vmsize > infile_lc_highest_addr) | ||
| 710 | infile_lc_highest_addr = scp->vmaddr + scp->vmsize; | ||
| 711 | |||
| 712 | if (strncmp (scp->segname, SEG_TEXT, 16) == 0) | ||
| 713 | { | ||
| 714 | struct section *sectp = (struct section *) (scp + 1); | ||
| 715 | int j; | ||
| 716 | |||
| 717 | for (j = 0; j < scp->nsects; j++) | ||
| 718 | if (sectp->offset < text_seg_lowest_offset) | ||
| 719 | text_seg_lowest_offset = sectp->offset; | ||
| 720 | } | ||
| 721 | } | ||
| 722 | } | ||
| 723 | |||
| 724 | printf ("Highest address of load commands in input file: %#8lx\n", | ||
| 725 | (unsigned long)infile_lc_highest_addr); | ||
| 726 | |||
| 727 | printf ("Lowest offset of all sections in __TEXT segment: %#8lx\n", | ||
| 728 | text_seg_lowest_offset); | ||
| 729 | |||
| 730 | printf ("--- List of Load Commands in Input File ---\n"); | ||
| 731 | printf ("# cmd cmdsize name address size\n"); | ||
| 732 | |||
| 733 | for (i = 0; i < nlc; i++) | ||
| 734 | { | ||
| 735 | printf ("%1d ", i); | ||
| 736 | print_load_command (lca[i]); | ||
| 737 | } | ||
| 738 | } | ||
| 739 | |||
| 740 | /* Copy a LC_SEGMENT load command other than the __DATA segment from | ||
| 741 | the input file to the output file, adjusting the file offset of the | ||
| 742 | segment and the file offsets of sections contained in it. */ | ||
| 743 | static void | ||
| 744 | copy_segment (struct load_command *lc) | ||
| 745 | { | ||
| 746 | struct segment_command *scp = (struct segment_command *) lc; | ||
| 747 | unsigned long old_fileoff = scp->fileoff; | ||
| 748 | struct section *sectp; | ||
| 749 | int j; | ||
| 750 | |||
| 751 | scp->fileoff = curr_file_offset; | ||
| 752 | |||
| 753 | sectp = (struct section *) (scp + 1); | ||
| 754 | for (j = 0; j < scp->nsects; j++) | ||
| 755 | { | ||
| 756 | sectp->offset += curr_file_offset - old_fileoff; | ||
| 757 | sectp++; | ||
| 758 | } | ||
| 759 | |||
| 760 | printf ("Writing segment %-16.16s @ %#8lx (%#8lx/%#8lx @ %#10lx)\n", | ||
| 761 | scp->segname, (long) (scp->fileoff), (long) (scp->filesize), | ||
| 762 | (long) (scp->vmsize), (long) (scp->vmaddr)); | ||
| 763 | |||
| 764 | if (!unexec_copy (scp->fileoff, old_fileoff, scp->filesize)) | ||
| 765 | unexec_error ("cannot copy segment from input to output file"); | ||
| 766 | curr_file_offset += ROUNDUP_TO_PAGE_BOUNDARY (scp->filesize); | ||
| 767 | |||
| 768 | if (!unexec_write (curr_header_offset, lc, lc->cmdsize)) | ||
| 769 | unexec_error ("cannot write load command to header"); | ||
| 770 | |||
| 771 | curr_header_offset += lc->cmdsize; | ||
| 772 | } | ||
| 773 | |||
| 774 | /* Copy a LC_SEGMENT load command for the __DATA segment in the input | ||
| 775 | file to the output file. We assume that only one such segment load | ||
| 776 | command exists in the input file and it contains the sections | ||
| 777 | __data, __bss, __common, __la_symbol_ptr, __nl_symbol_ptr, and | ||
| 778 | __dyld. The first three of these should be dumped from memory and | ||
| 779 | the rest should be copied from the input file. Note that the | ||
| 780 | sections __bss and __common contain no data in the input file | ||
| 781 | because their flag fields have the value S_ZEROFILL. Dumping these | ||
| 782 | from memory makes it necessary to adjust file offset fields in | ||
| 783 | subsequently dumped load commands. Then, create new __DATA segment | ||
| 784 | load commands for regions on the region list other than the one | ||
| 785 | corresponding to the __DATA segment in the input file. */ | ||
| 786 | static void | ||
| 787 | copy_data_segment (struct load_command *lc) | ||
| 788 | { | ||
| 789 | struct segment_command *scp = (struct segment_command *) lc; | ||
| 790 | struct section *sectp; | ||
| 791 | int j; | ||
| 792 | unsigned long header_offset, old_file_offset; | ||
| 793 | |||
| 794 | /* The new filesize of the segment is set to its vmsize because data | ||
| 795 | blocks for segments must start at region boundaries. Note that | ||
| 796 | this may leave unused locations at the end of the segment data | ||
| 797 | block because the total of the sizes of all sections in the | ||
| 798 | segment is generally smaller than vmsize. */ | ||
| 799 | scp->filesize = scp->vmsize; | ||
| 800 | |||
| 801 | printf ("Writing segment %-16.16s @ %#8lx (%#8lx/%#8lx @ %#10lx)\n", | ||
| 802 | scp->segname, curr_file_offset, (long)(scp->filesize), | ||
| 803 | (long)(scp->vmsize), (long) (scp->vmaddr)); | ||
| 804 | |||
| 805 | /* Offsets in the output file for writing the next section structure | ||
| 806 | and segment data block, respectively. */ | ||
| 807 | header_offset = curr_header_offset + sizeof (struct segment_command); | ||
| 808 | |||
| 809 | sectp = (struct section *) (scp + 1); | ||
| 810 | for (j = 0; j < scp->nsects; j++) | ||
| 811 | { | ||
| 812 | old_file_offset = sectp->offset; | ||
| 813 | sectp->offset = sectp->addr - scp->vmaddr + curr_file_offset; | ||
| 814 | /* The __data section is dumped from memory. The __bss and | ||
| 815 | __common sections are also dumped from memory but their flag | ||
| 816 | fields require changing (from S_ZEROFILL to S_REGULAR). The | ||
| 817 | other three kinds of sections are just copied from the input | ||
| 818 | file. */ | ||
| 819 | if (strncmp (sectp->sectname, SECT_DATA, 16) == 0) | ||
| 820 | { | ||
| 821 | unsigned long my_size; | ||
| 822 | |||
| 823 | /* The __data section is basically dumped from memory. But | ||
| 824 | initialized data in statically linked libraries are | ||
| 825 | copied from the input file. In particular, | ||
| 826 | add_image_hook.names and add_image_hook.pointers stored | ||
| 827 | by libarclite_macosx.a, are restored so that they will be | ||
| 828 | reinitialized when the dumped binary is executed. */ | ||
| 829 | my_size = (unsigned long)my_edata - sectp->addr; | ||
| 830 | if (!(sectp->addr <= (unsigned long)my_edata | ||
| 831 | && my_size <= sectp->size)) | ||
| 832 | unexec_error ("my_edata is not in section %s", SECT_DATA); | ||
| 833 | if (!unexec_write (sectp->offset, (void *) sectp->addr, my_size)) | ||
| 834 | unexec_error ("cannot write section %s", SECT_DATA); | ||
| 835 | if (!unexec_copy (sectp->offset + my_size, old_file_offset + my_size, | ||
| 836 | sectp->size - my_size)) | ||
| 837 | unexec_error ("cannot copy section %s", SECT_DATA); | ||
| 838 | if (!unexec_write (header_offset, sectp, sizeof (struct section))) | ||
| 839 | unexec_error ("cannot write section %s's header", SECT_DATA); | ||
| 840 | } | ||
| 841 | else if (strncmp (sectp->sectname, SECT_COMMON, 16) == 0) | ||
| 842 | { | ||
| 843 | sectp->flags = S_REGULAR; | ||
| 844 | if (!unexec_write (sectp->offset, (void *) sectp->addr, sectp->size)) | ||
| 845 | unexec_error ("cannot write section %.16s", sectp->sectname); | ||
| 846 | if (!unexec_write (header_offset, sectp, sizeof (struct section))) | ||
| 847 | unexec_error ("cannot write section %.16s's header", sectp->sectname); | ||
| 848 | } | ||
| 849 | else if (strncmp (sectp->sectname, SECT_BSS, 16) == 0) | ||
| 850 | { | ||
| 851 | unsigned long my_size; | ||
| 852 | |||
| 853 | sectp->flags = S_REGULAR; | ||
| 854 | |||
| 855 | /* Clear uninitialized local variables in statically linked | ||
| 856 | libraries. In particular, function pointers stored by | ||
| 857 | libSystemStub.a, which is introduced in Mac OS X 10.4 for | ||
| 858 | binary compatibility with respect to long double, are | ||
| 859 | cleared so that they will be reinitialized when the | ||
| 860 | dumped binary is executed on other versions of OS. */ | ||
| 861 | my_size = (unsigned long)my_endbss_static - sectp->addr; | ||
| 862 | if (!(sectp->addr <= (unsigned long)my_endbss_static | ||
| 863 | && my_size <= sectp->size)) | ||
| 864 | unexec_error ("my_endbss_static is not in section %.16s", | ||
| 865 | sectp->sectname); | ||
| 866 | if (!unexec_write (sectp->offset, (void *) sectp->addr, my_size)) | ||
| 867 | unexec_error ("cannot write section %.16s", sectp->sectname); | ||
| 868 | if (!unexec_write_zero (sectp->offset + my_size, | ||
| 869 | sectp->size - my_size)) | ||
| 870 | unexec_error ("cannot write section %.16s", sectp->sectname); | ||
| 871 | if (!unexec_write (header_offset, sectp, sizeof (struct section))) | ||
| 872 | unexec_error ("cannot write section %.16s's header", sectp->sectname); | ||
| 873 | } | ||
| 874 | else if (strncmp (sectp->sectname, "__bss", 5) == 0 | ||
| 875 | || strncmp (sectp->sectname, "__pu_bss", 8) == 0) | ||
| 876 | { | ||
| 877 | sectp->flags = S_REGULAR; | ||
| 878 | |||
| 879 | /* These sections are produced by GCC 4.6+. | ||
| 880 | |||
| 881 | FIXME: We possibly ought to clear uninitialized local | ||
| 882 | variables in statically linked libraries like for | ||
| 883 | SECT_BSS (__bss) above, but setting up the markers we | ||
| 884 | need in lastfile.c would be rather messy. See | ||
| 885 | darwin_output_aligned_bss () in gcc/config/darwin.c for | ||
| 886 | the root of the problem, keeping in mind that the | ||
| 887 | sections are numbered by their alignment in GCC 4.6, but | ||
| 888 | by log2(alignment) in GCC 4.7. */ | ||
| 889 | |||
| 890 | if (!unexec_write (sectp->offset, (void *) sectp->addr, sectp->size)) | ||
| 891 | unexec_error ("cannot copy section %.16s", sectp->sectname); | ||
| 892 | if (!unexec_write (header_offset, sectp, sizeof (struct section))) | ||
| 893 | unexec_error ("cannot write section %.16s's header", sectp->sectname); | ||
| 894 | } | ||
| 895 | else if (strncmp (sectp->sectname, "__la_symbol_ptr", 16) == 0 | ||
| 896 | || strncmp (sectp->sectname, "__nl_symbol_ptr", 16) == 0 | ||
| 897 | || strncmp (sectp->sectname, "__got", 16) == 0 | ||
| 898 | || strncmp (sectp->sectname, "__la_sym_ptr2", 16) == 0 | ||
| 899 | || strncmp (sectp->sectname, "__dyld", 16) == 0 | ||
| 900 | || strncmp (sectp->sectname, "__const", 16) == 0 | ||
| 901 | || strncmp (sectp->sectname, "__cfstring", 16) == 0 | ||
| 902 | || strncmp (sectp->sectname, "__gcc_except_tab", 16) == 0 | ||
| 903 | || strncmp (sectp->sectname, "__program_vars", 16) == 0 | ||
| 904 | || strncmp (sectp->sectname, "__mod_init_func", 16) == 0 | ||
| 905 | || strncmp (sectp->sectname, "__mod_term_func", 16) == 0 | ||
| 906 | || strncmp (sectp->sectname, "__static_data", 16) == 0 | ||
| 907 | || strncmp (sectp->sectname, "__objc_", 7) == 0) | ||
| 908 | { | ||
| 909 | if (!unexec_copy (sectp->offset, old_file_offset, sectp->size)) | ||
| 910 | unexec_error ("cannot copy section %.16s", sectp->sectname); | ||
| 911 | if (!unexec_write (header_offset, sectp, sizeof (struct section))) | ||
| 912 | unexec_error ("cannot write section %.16s's header", sectp->sectname); | ||
| 913 | } | ||
| 914 | else | ||
| 915 | unexec_error ("unrecognized section %.16s in __DATA segment", | ||
| 916 | sectp->sectname); | ||
| 917 | |||
| 918 | printf (" section %-16.16s at %#8lx - %#8lx (sz: %#8lx)\n", | ||
| 919 | sectp->sectname, (long) (sectp->offset), | ||
| 920 | (long) (sectp->offset + sectp->size), (long) (sectp->size)); | ||
| 921 | |||
| 922 | header_offset += sizeof (struct section); | ||
| 923 | sectp++; | ||
| 924 | } | ||
| 925 | |||
| 926 | curr_file_offset += ROUNDUP_TO_PAGE_BOUNDARY (scp->filesize); | ||
| 927 | |||
| 928 | if (!unexec_write (curr_header_offset, scp, sizeof (struct segment_command))) | ||
| 929 | unexec_error ("cannot write header of __DATA segment"); | ||
| 930 | curr_header_offset += lc->cmdsize; | ||
| 931 | |||
| 932 | /* Create new __DATA segment load commands for regions on the region | ||
| 933 | list that do not corresponding to any segment load commands in | ||
| 934 | the input file. | ||
| 935 | */ | ||
| 936 | for (j = 0; j < num_unexec_regions; j++) | ||
| 937 | { | ||
| 938 | struct segment_command sc; | ||
| 939 | |||
| 940 | sc.cmd = LC_SEGMENT; | ||
| 941 | sc.cmdsize = sizeof (struct segment_command); | ||
| 942 | strncpy (sc.segname, SEG_DATA, 16); | ||
| 943 | sc.vmaddr = unexec_regions[j].range.address; | ||
| 944 | sc.vmsize = unexec_regions[j].range.size; | ||
| 945 | sc.fileoff = curr_file_offset; | ||
| 946 | sc.filesize = unexec_regions[j].filesize; | ||
| 947 | sc.maxprot = VM_PROT_READ | VM_PROT_WRITE; | ||
| 948 | sc.initprot = VM_PROT_READ | VM_PROT_WRITE; | ||
| 949 | sc.nsects = 0; | ||
| 950 | sc.flags = 0; | ||
| 951 | |||
| 952 | printf ("Writing segment %-16.16s @ %#8lx (%#8lx/%#8lx @ %#10lx)\n", | ||
| 953 | sc.segname, (long) (sc.fileoff), (long) (sc.filesize), | ||
| 954 | (long) (sc.vmsize), (long) (sc.vmaddr)); | ||
| 955 | |||
| 956 | if (!unexec_write (sc.fileoff, (void *) sc.vmaddr, sc.filesize)) | ||
| 957 | unexec_error ("cannot write new __DATA segment"); | ||
| 958 | curr_file_offset += ROUNDUP_TO_PAGE_BOUNDARY (sc.filesize); | ||
| 959 | |||
| 960 | if (!unexec_write (curr_header_offset, &sc, sc.cmdsize)) | ||
| 961 | unexec_error ("cannot write new __DATA segment's header"); | ||
| 962 | curr_header_offset += sc.cmdsize; | ||
| 963 | mh.ncmds++; | ||
| 964 | } | ||
| 965 | } | ||
| 966 | |||
| 967 | /* Copy a LC_SYMTAB load command from the input file to the output | ||
| 968 | file, adjusting the file offset fields. */ | ||
| 969 | static void | ||
| 970 | copy_symtab (struct load_command *lc, long delta) | ||
| 971 | { | ||
| 972 | struct symtab_command *stp = (struct symtab_command *) lc; | ||
| 973 | |||
| 974 | stp->symoff += delta; | ||
| 975 | stp->stroff += delta; | ||
| 976 | |||
| 977 | printf ("Writing LC_SYMTAB command\n"); | ||
| 978 | |||
| 979 | if (!unexec_write (curr_header_offset, lc, lc->cmdsize)) | ||
| 980 | unexec_error ("cannot write symtab command to header"); | ||
| 981 | |||
| 982 | curr_header_offset += lc->cmdsize; | ||
| 983 | } | ||
| 984 | |||
| 985 | /* Fix up relocation entries. */ | ||
| 986 | static void | ||
| 987 | unrelocate (const char *name, off_t reloff, int nrel, vm_address_t base) | ||
| 988 | { | ||
| 989 | int i, unreloc_count; | ||
| 990 | struct relocation_info reloc_info; | ||
| 991 | struct scattered_relocation_info *sc_reloc_info | ||
| 992 | = (struct scattered_relocation_info *) &reloc_info; | ||
| 993 | vm_address_t location; | ||
| 994 | |||
| 995 | for (unreloc_count = 0, i = 0; i < nrel; i++) | ||
| 996 | { | ||
| 997 | if (lseek (infd, reloff, L_SET) != reloff) | ||
| 998 | unexec_error ("unrelocate: %s:%d cannot seek to reloc_info", name, i); | ||
| 999 | if (!unexec_read (&reloc_info, sizeof (reloc_info))) | ||
| 1000 | unexec_error ("unrelocate: %s:%d cannot read reloc_info", name, i); | ||
| 1001 | reloff += sizeof (reloc_info); | ||
| 1002 | |||
| 1003 | if (sc_reloc_info->r_scattered == 0) | ||
| 1004 | switch (reloc_info.r_type) | ||
| 1005 | { | ||
| 1006 | case GENERIC_RELOC_VANILLA: | ||
| 1007 | location = base + reloc_info.r_address; | ||
| 1008 | if (location >= data_segment_scp->vmaddr | ||
| 1009 | && location < (data_segment_scp->vmaddr | ||
| 1010 | + data_segment_scp->vmsize)) | ||
| 1011 | { | ||
| 1012 | off_t src_off = data_segment_old_fileoff | ||
| 1013 | + (location - data_segment_scp->vmaddr); | ||
| 1014 | off_t dst_off = data_segment_scp->fileoff | ||
| 1015 | + (location - data_segment_scp->vmaddr); | ||
| 1016 | |||
| 1017 | if (!unexec_copy (dst_off, src_off, 1 << reloc_info.r_length)) | ||
| 1018 | unexec_error ("unrelocate: %s:%d cannot copy original value", | ||
| 1019 | name, i); | ||
| 1020 | unreloc_count++; | ||
| 1021 | } | ||
| 1022 | break; | ||
| 1023 | default: | ||
| 1024 | unexec_error ("unrelocate: %s:%d cannot handle type = %d", | ||
| 1025 | name, i, reloc_info.r_type); | ||
| 1026 | } | ||
| 1027 | else | ||
| 1028 | unexec_error ("unrelocate: %s:%d cannot handle scattered type = %d", | ||
| 1029 | name, i, sc_reloc_info->r_type); | ||
| 1030 | } | ||
| 1031 | |||
| 1032 | if (nrel > 0) | ||
| 1033 | printf ("Fixed up %d/%d %s relocation entries in data segment.\n", | ||
| 1034 | unreloc_count, nrel, name); | ||
| 1035 | } | ||
| 1036 | |||
| 1037 | /* Copy a LC_DYSYMTAB load command from the input file to the output | ||
| 1038 | file, adjusting the file offset fields. */ | ||
| 1039 | static void | ||
| 1040 | copy_dysymtab (struct load_command *lc, long delta) | ||
| 1041 | { | ||
| 1042 | struct dysymtab_command *dstp = (struct dysymtab_command *) lc; | ||
| 1043 | vm_address_t base; | ||
| 1044 | |||
| 1045 | #ifdef _LP64 | ||
| 1046 | /* First writable segment address. */ | ||
| 1047 | base = data_segment_scp->vmaddr; | ||
| 1048 | #else | ||
| 1049 | /* First segment address in the file (unless MH_SPLIT_SEGS set). */ | ||
| 1050 | base = 0; | ||
| 1051 | #endif | ||
| 1052 | |||
| 1053 | unrelocate ("local", dstp->locreloff, dstp->nlocrel, base); | ||
| 1054 | unrelocate ("external", dstp->extreloff, dstp->nextrel, base); | ||
| 1055 | |||
| 1056 | if (dstp->nextrel > 0) { | ||
| 1057 | dstp->extreloff += delta; | ||
| 1058 | } | ||
| 1059 | |||
| 1060 | if (dstp->nlocrel > 0) { | ||
| 1061 | dstp->locreloff += delta; | ||
| 1062 | } | ||
| 1063 | |||
| 1064 | if (dstp->nindirectsyms > 0) | ||
| 1065 | dstp->indirectsymoff += delta; | ||
| 1066 | |||
| 1067 | printf ("Writing LC_DYSYMTAB command\n"); | ||
| 1068 | |||
| 1069 | if (!unexec_write (curr_header_offset, lc, lc->cmdsize)) | ||
| 1070 | unexec_error ("cannot write symtab command to header"); | ||
| 1071 | |||
| 1072 | curr_header_offset += lc->cmdsize; | ||
| 1073 | } | ||
| 1074 | |||
| 1075 | /* Copy a LC_TWOLEVEL_HINTS load command from the input file to the output | ||
| 1076 | file, adjusting the file offset fields. */ | ||
| 1077 | static void | ||
| 1078 | copy_twolevelhints (struct load_command *lc, long delta) | ||
| 1079 | { | ||
| 1080 | struct twolevel_hints_command *tlhp = (struct twolevel_hints_command *) lc; | ||
| 1081 | |||
| 1082 | if (tlhp->nhints > 0) { | ||
| 1083 | tlhp->offset += delta; | ||
| 1084 | } | ||
| 1085 | |||
| 1086 | printf ("Writing LC_TWOLEVEL_HINTS command\n"); | ||
| 1087 | |||
| 1088 | if (!unexec_write (curr_header_offset, lc, lc->cmdsize)) | ||
| 1089 | unexec_error ("cannot write two level hint command to header"); | ||
| 1090 | |||
| 1091 | curr_header_offset += lc->cmdsize; | ||
| 1092 | } | ||
| 1093 | |||
| 1094 | #ifdef LC_DYLD_INFO | ||
| 1095 | /* Copy a LC_DYLD_INFO(_ONLY) load command from the input file to the output | ||
| 1096 | file, adjusting the file offset fields. */ | ||
| 1097 | static void | ||
| 1098 | copy_dyld_info (struct load_command *lc, long delta) | ||
| 1099 | { | ||
| 1100 | struct dyld_info_command *dip = (struct dyld_info_command *) lc; | ||
| 1101 | |||
| 1102 | if (dip->rebase_off > 0) | ||
| 1103 | dip->rebase_off += delta; | ||
| 1104 | if (dip->bind_off > 0) | ||
| 1105 | dip->bind_off += delta; | ||
| 1106 | if (dip->weak_bind_off > 0) | ||
| 1107 | dip->weak_bind_off += delta; | ||
| 1108 | if (dip->lazy_bind_off > 0) | ||
| 1109 | dip->lazy_bind_off += delta; | ||
| 1110 | if (dip->export_off > 0) | ||
| 1111 | dip->export_off += delta; | ||
| 1112 | |||
| 1113 | printf ("Writing "); | ||
| 1114 | print_load_command_name (lc->cmd); | ||
| 1115 | printf (" command\n"); | ||
| 1116 | |||
| 1117 | if (!unexec_write (curr_header_offset, lc, lc->cmdsize)) | ||
| 1118 | unexec_error ("cannot write dyld info command to header"); | ||
| 1119 | |||
| 1120 | curr_header_offset += lc->cmdsize; | ||
| 1121 | } | ||
| 1122 | #endif | ||
| 1123 | |||
| 1124 | #ifdef LC_FUNCTION_STARTS | ||
| 1125 | /* Copy a LC_FUNCTION_STARTS/LC_DATA_IN_CODE/LC_DYLIB_CODE_SIGN_DRS | ||
| 1126 | load command from the input file to the output file, adjusting the | ||
| 1127 | data offset field. */ | ||
| 1128 | static void | ||
| 1129 | copy_linkedit_data (struct load_command *lc, long delta) | ||
| 1130 | { | ||
| 1131 | struct linkedit_data_command *ldp = (struct linkedit_data_command *) lc; | ||
| 1132 | |||
| 1133 | if (ldp->dataoff > 0) | ||
| 1134 | ldp->dataoff += delta; | ||
| 1135 | |||
| 1136 | printf ("Writing "); | ||
| 1137 | print_load_command_name (lc->cmd); | ||
| 1138 | printf (" command\n"); | ||
| 1139 | |||
| 1140 | if (!unexec_write (curr_header_offset, lc, lc->cmdsize)) | ||
| 1141 | unexec_error ("cannot write linkedit data command to header"); | ||
| 1142 | |||
| 1143 | curr_header_offset += lc->cmdsize; | ||
| 1144 | } | ||
| 1145 | #endif | ||
| 1146 | |||
| 1147 | /* Copy other kinds of load commands from the input file to the output | ||
| 1148 | file, ones that do not require adjustments of file offsets. */ | ||
| 1149 | static void | ||
| 1150 | copy_other (struct load_command *lc) | ||
| 1151 | { | ||
| 1152 | printf ("Writing "); | ||
| 1153 | print_load_command_name (lc->cmd); | ||
| 1154 | printf (" command\n"); | ||
| 1155 | |||
| 1156 | if (!unexec_write (curr_header_offset, lc, lc->cmdsize)) | ||
| 1157 | unexec_error ("cannot write symtab command to header"); | ||
| 1158 | |||
| 1159 | curr_header_offset += lc->cmdsize; | ||
| 1160 | } | ||
| 1161 | |||
| 1162 | /* Loop through all load commands and dump them. Then write the Mach | ||
| 1163 | header. */ | ||
| 1164 | static void | ||
| 1165 | dump_it (void) | ||
| 1166 | { | ||
| 1167 | int i; | ||
| 1168 | long linkedit_delta = 0; | ||
| 1169 | |||
| 1170 | printf ("--- Load Commands written to Output File ---\n"); | ||
| 1171 | |||
| 1172 | for (i = 0; i < nlc; i++) | ||
| 1173 | switch (lca[i]->cmd) | ||
| 1174 | { | ||
| 1175 | case LC_SEGMENT: | ||
| 1176 | { | ||
| 1177 | struct segment_command *scp = (struct segment_command *) lca[i]; | ||
| 1178 | if (strncmp (scp->segname, SEG_DATA, 16) == 0) | ||
| 1179 | { | ||
| 1180 | /* save data segment file offset and segment_command for | ||
| 1181 | unrelocate */ | ||
| 1182 | if (data_segment_old_fileoff) | ||
| 1183 | unexec_error ("cannot handle multiple DATA segments" | ||
| 1184 | " in input file"); | ||
| 1185 | data_segment_old_fileoff = scp->fileoff; | ||
| 1186 | data_segment_scp = scp; | ||
| 1187 | |||
| 1188 | copy_data_segment (lca[i]); | ||
| 1189 | } | ||
| 1190 | else | ||
| 1191 | { | ||
| 1192 | if (strncmp (scp->segname, SEG_LINKEDIT, 16) == 0) | ||
| 1193 | { | ||
| 1194 | if (linkedit_delta) | ||
| 1195 | unexec_error ("cannot handle multiple LINKEDIT segments" | ||
| 1196 | " in input file"); | ||
| 1197 | linkedit_delta = curr_file_offset - scp->fileoff; | ||
| 1198 | } | ||
| 1199 | |||
| 1200 | copy_segment (lca[i]); | ||
| 1201 | } | ||
| 1202 | } | ||
| 1203 | break; | ||
| 1204 | case LC_SYMTAB: | ||
| 1205 | copy_symtab (lca[i], linkedit_delta); | ||
| 1206 | break; | ||
| 1207 | case LC_DYSYMTAB: | ||
| 1208 | copy_dysymtab (lca[i], linkedit_delta); | ||
| 1209 | break; | ||
| 1210 | case LC_TWOLEVEL_HINTS: | ||
| 1211 | copy_twolevelhints (lca[i], linkedit_delta); | ||
| 1212 | break; | ||
| 1213 | #ifdef LC_DYLD_INFO | ||
| 1214 | case LC_DYLD_INFO: | ||
| 1215 | case LC_DYLD_INFO_ONLY: | ||
| 1216 | copy_dyld_info (lca[i], linkedit_delta); | ||
| 1217 | break; | ||
| 1218 | #endif | ||
| 1219 | #ifdef LC_FUNCTION_STARTS | ||
| 1220 | case LC_FUNCTION_STARTS: | ||
| 1221 | #ifdef LC_DATA_IN_CODE | ||
| 1222 | case LC_DATA_IN_CODE: | ||
| 1223 | #endif | ||
| 1224 | #ifdef LC_DYLIB_CODE_SIGN_DRS | ||
| 1225 | case LC_DYLIB_CODE_SIGN_DRS: | ||
| 1226 | #endif | ||
| 1227 | copy_linkedit_data (lca[i], linkedit_delta); | ||
| 1228 | break; | ||
| 1229 | #endif | ||
| 1230 | default: | ||
| 1231 | copy_other (lca[i]); | ||
| 1232 | break; | ||
| 1233 | } | ||
| 1234 | |||
| 1235 | if (curr_header_offset > text_seg_lowest_offset) | ||
| 1236 | unexec_error ("not enough room for load commands for new __DATA segments" | ||
| 1237 | " (increase headerpad_extra in configure.in to at least %lX)", | ||
| 1238 | num_unexec_regions * sizeof (struct segment_command)); | ||
| 1239 | |||
| 1240 | printf ("%ld unused bytes follow Mach-O header\n", | ||
| 1241 | text_seg_lowest_offset - curr_header_offset); | ||
| 1242 | |||
| 1243 | mh.sizeofcmds = curr_header_offset - sizeof (struct mach_header); | ||
| 1244 | if (!unexec_write (0, &mh, sizeof (struct mach_header))) | ||
| 1245 | unexec_error ("cannot write final header contents"); | ||
| 1246 | } | ||
| 1247 | |||
| 1248 | /* Take a snapshot of Emacs and make a Mach-O format executable file | ||
| 1249 | from it. The file names of the output and input files are outfile | ||
| 1250 | and infile, respectively. The three other parameters are | ||
| 1251 | ignored. */ | ||
| 1252 | void | ||
| 1253 | unexec (const char *outfile, const char *infile) | ||
| 1254 | { | ||
| 1255 | if (in_dumped_exec) | ||
| 1256 | unexec_error ("Unexec from a dumped executable is not supported."); | ||
| 1257 | |||
| 1258 | pagesize = getpagesize (); | ||
| 1259 | infd = emacs_open (infile, O_RDONLY, 0); | ||
| 1260 | if (infd < 0) | ||
| 1261 | { | ||
| 1262 | unexec_error ("%s: %s", infile, strerror (errno)); | ||
| 1263 | } | ||
| 1264 | |||
| 1265 | outfd = emacs_open (outfile, O_WRONLY | O_TRUNC | O_CREAT, 0777); | ||
| 1266 | if (outfd < 0) | ||
| 1267 | { | ||
| 1268 | emacs_close (infd); | ||
| 1269 | unexec_error ("%s: %s", outfile, strerror (errno)); | ||
| 1270 | } | ||
| 1271 | |||
| 1272 | build_region_list (); | ||
| 1273 | read_load_commands (); | ||
| 1274 | |||
| 1275 | find_emacs_zone_regions (); | ||
| 1276 | unexec_regions_merge (); | ||
| 1277 | |||
| 1278 | in_dumped_exec = 1; | ||
| 1279 | |||
| 1280 | dump_it (); | ||
| 1281 | |||
| 1282 | emacs_close (outfd); | ||
| 1283 | } | ||
| 1284 | |||
| 1285 | |||
| 1286 | void | ||
| 1287 | unexec_init_emacs_zone (void) | ||
| 1288 | { | ||
| 1289 | emacs_zone = malloc_create_zone (0, 0); | ||
| 1290 | malloc_set_zone_name (emacs_zone, "EmacsZone"); | ||
| 1291 | } | ||
| 1292 | |||
| 1293 | #ifndef MACOSX_MALLOC_MULT16 | ||
| 1294 | #define MACOSX_MALLOC_MULT16 1 | ||
| 1295 | #endif | ||
| 1296 | |||
| 1297 | typedef struct unexec_malloc_header { | ||
| 1298 | union { | ||
| 1299 | char c[8]; | ||
| 1300 | size_t size; | ||
| 1301 | } u; | ||
| 1302 | } unexec_malloc_header_t; | ||
| 1303 | |||
| 1304 | #if MACOSX_MALLOC_MULT16 | ||
| 1305 | |||
| 1306 | #define ptr_in_unexec_regions(p) ((((vm_address_t) (p)) & 8) != 0) | ||
| 1307 | |||
| 1308 | #else | ||
| 1309 | |||
| 1310 | int | ||
| 1311 | ptr_in_unexec_regions (void *ptr) | ||
| 1312 | { | ||
| 1313 | int i; | ||
| 1314 | |||
| 1315 | for (i = 0; i < num_unexec_regions; i++) | ||
| 1316 | if ((vm_address_t) ptr - unexec_regions[i].range.address | ||
| 1317 | < unexec_regions[i].range.size) | ||
| 1318 | return 1; | ||
| 1319 | |||
| 1320 | return 0; | ||
| 1321 | } | ||
| 1322 | |||
| 1323 | #endif | ||
| 1324 | |||
| 1325 | void * | ||
| 1326 | unexec_malloc (size_t size) | ||
| 1327 | { | ||
| 1328 | if (in_dumped_exec) | ||
| 1329 | { | ||
| 1330 | void *p; | ||
| 1331 | |||
| 1332 | p = malloc (size); | ||
| 1333 | #if MACOSX_MALLOC_MULT16 | ||
| 1334 | assert (((vm_address_t) p % 16) == 0); | ||
| 1335 | #endif | ||
| 1336 | return p; | ||
| 1337 | } | ||
| 1338 | else | ||
| 1339 | { | ||
| 1340 | unexec_malloc_header_t *ptr; | ||
| 1341 | |||
| 1342 | ptr = (unexec_malloc_header_t *) | ||
| 1343 | malloc_zone_malloc (emacs_zone, size + sizeof (unexec_malloc_header_t)); | ||
| 1344 | ptr->u.size = size; | ||
| 1345 | ptr++; | ||
| 1346 | #if MACOSX_MALLOC_MULT16 | ||
| 1347 | assert (((vm_address_t) ptr % 16) == 8); | ||
| 1348 | #endif | ||
| 1349 | return (void *) ptr; | ||
| 1350 | } | ||
| 1351 | } | ||
| 1352 | |||
| 1353 | void * | ||
| 1354 | unexec_realloc (void *old_ptr, size_t new_size) | ||
| 1355 | { | ||
| 1356 | if (in_dumped_exec) | ||
| 1357 | { | ||
| 1358 | void *p; | ||
| 1359 | |||
| 1360 | if (ptr_in_unexec_regions (old_ptr)) | ||
| 1361 | { | ||
| 1362 | size_t old_size = ((unexec_malloc_header_t *) old_ptr)[-1].u.size; | ||
| 1363 | size_t size = new_size > old_size ? old_size : new_size; | ||
| 1364 | |||
| 1365 | p = malloc (new_size); | ||
| 1366 | if (size) | ||
| 1367 | memcpy (p, old_ptr, size); | ||
| 1368 | } | ||
| 1369 | else | ||
| 1370 | { | ||
| 1371 | p = realloc (old_ptr, new_size); | ||
| 1372 | } | ||
| 1373 | #if MACOSX_MALLOC_MULT16 | ||
| 1374 | assert (((vm_address_t) p % 16) == 0); | ||
| 1375 | #endif | ||
| 1376 | return p; | ||
| 1377 | } | ||
| 1378 | else | ||
| 1379 | { | ||
| 1380 | unexec_malloc_header_t *ptr; | ||
| 1381 | |||
| 1382 | ptr = (unexec_malloc_header_t *) | ||
| 1383 | malloc_zone_realloc (emacs_zone, (unexec_malloc_header_t *) old_ptr - 1, | ||
| 1384 | new_size + sizeof (unexec_malloc_header_t)); | ||
| 1385 | ptr->u.size = new_size; | ||
| 1386 | ptr++; | ||
| 1387 | #if MACOSX_MALLOC_MULT16 | ||
| 1388 | assert (((vm_address_t) ptr % 16) == 8); | ||
| 1389 | #endif | ||
| 1390 | return (void *) ptr; | ||
| 1391 | } | ||
| 1392 | } | ||
| 1393 | |||
| 1394 | void | ||
| 1395 | unexec_free (void *ptr) | ||
| 1396 | { | ||
| 1397 | if (ptr == NULL) | ||
| 1398 | return; | ||
| 1399 | if (in_dumped_exec) | ||
| 1400 | { | ||
| 1401 | if (!ptr_in_unexec_regions (ptr)) | ||
| 1402 | free (ptr); | ||
| 1403 | } | ||
| 1404 | else | ||
| 1405 | malloc_zone_free (emacs_zone, (unexec_malloc_header_t *) ptr - 1); | ||
| 1406 | } | ||
diff --git a/src/unexsol.c b/src/unexsol.c deleted file mode 100644 index 0f84099d39e..00000000000 --- a/src/unexsol.c +++ /dev/null | |||
| @@ -1,28 +0,0 @@ | |||
| 1 | /* Trivial unexec for Solaris. */ | ||
| 2 | |||
| 3 | #include <config.h> | ||
| 4 | #include "unexec.h" | ||
| 5 | |||
| 6 | #include <dlfcn.h> | ||
| 7 | |||
| 8 | #include "lisp.h" | ||
| 9 | #include "buffer.h" | ||
| 10 | #include "coding.h" | ||
| 11 | |||
| 12 | void | ||
| 13 | unexec (const char *new_name, const char *old_name) | ||
| 14 | { | ||
| 15 | Lisp_Object data; | ||
| 16 | Lisp_Object errstring; | ||
| 17 | |||
| 18 | if (! dldump (0, new_name, RTLD_MEMORY)) | ||
| 19 | return; | ||
| 20 | |||
| 21 | data = list1 (build_string (new_name)); | ||
| 22 | synchronize_system_messages_locale (); | ||
| 23 | errstring = code_convert_string_norecord (build_string (dlerror ()), | ||
| 24 | Vlocale_coding_system, 0); | ||
| 25 | |||
| 26 | xsignal (Qfile_error, | ||
| 27 | Fcons (build_string ("Cannot unexec"), Fcons (errstring, data))); | ||
| 28 | } | ||
diff --git a/src/unexw32.c b/src/unexw32.c deleted file mode 100644 index f0a910781cc..00000000000 --- a/src/unexw32.c +++ /dev/null | |||
| @@ -1,684 +0,0 @@ | |||
| 1 | /* unexec for GNU Emacs on Windows NT. | ||
| 2 | Copyright (C) 1994, 2001-2024 Free Software Foundation, Inc. | ||
| 3 | |||
| 4 | This file is part of GNU Emacs. | ||
| 5 | |||
| 6 | GNU Emacs is free software: you can redistribute it and/or modify | ||
| 7 | it under the terms of the GNU General Public License as published by | ||
| 8 | the Free Software Foundation, either version 3 of the License, or (at | ||
| 9 | your option) any later version. | ||
| 10 | |||
| 11 | GNU Emacs is distributed in the hope that it will be useful, | ||
| 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | GNU General Public License for more details. | ||
| 15 | |||
| 16 | You should have received a copy of the GNU General Public License | ||
| 17 | along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | ||
| 18 | |||
| 19 | /* | ||
| 20 | Geoff Voelker (voelker@cs.washington.edu) 8-12-94 | ||
| 21 | */ | ||
| 22 | |||
| 23 | #include <config.h> | ||
| 24 | #include "unexec.h" | ||
| 25 | #include "lisp.h" | ||
| 26 | #include "w32common.h" | ||
| 27 | #include "w32.h" | ||
| 28 | |||
| 29 | #include <stdio.h> | ||
| 30 | #include <fcntl.h> | ||
| 31 | #include <time.h> | ||
| 32 | #include <windows.h> | ||
| 33 | |||
| 34 | /* Include relevant definitions from IMAGEHLP.H, which can be found | ||
| 35 | in \\win32sdk\mstools\samples\image\include\imagehlp.h. */ | ||
| 36 | |||
| 37 | PIMAGE_NT_HEADERS (__stdcall * pfnCheckSumMappedFile) (LPVOID BaseAddress, | ||
| 38 | DWORD FileLength, | ||
| 39 | LPDWORD HeaderSum, | ||
| 40 | LPDWORD CheckSum); | ||
| 41 | |||
| 42 | extern char my_begdata[]; | ||
| 43 | extern char my_begbss[]; | ||
| 44 | extern char *my_begbss_static; | ||
| 45 | |||
| 46 | #include "w32heap.h" | ||
| 47 | |||
| 48 | void get_section_info (file_data *p_file); | ||
| 49 | void copy_executable_and_dump_data (file_data *, file_data *); | ||
| 50 | void dump_bss_and_heap (file_data *p_infile, file_data *p_outfile); | ||
| 51 | |||
| 52 | /* Cached info about the .data section in the executable. */ | ||
| 53 | PIMAGE_SECTION_HEADER data_section; | ||
| 54 | PCHAR data_start = 0; | ||
| 55 | DWORD_PTR data_size = 0; | ||
| 56 | |||
| 57 | /* Cached info about the .bss section in the executable. */ | ||
| 58 | PIMAGE_SECTION_HEADER bss_section; | ||
| 59 | PCHAR bss_start = 0; | ||
| 60 | DWORD_PTR bss_size = 0; | ||
| 61 | DWORD_PTR extra_bss_size = 0; | ||
| 62 | /* bss data that is static might be discontiguous from non-static. */ | ||
| 63 | PIMAGE_SECTION_HEADER bss_section_static; | ||
| 64 | PCHAR bss_start_static = 0; | ||
| 65 | DWORD_PTR bss_size_static = 0; | ||
| 66 | DWORD_PTR extra_bss_size_static = 0; | ||
| 67 | |||
| 68 | /* File handling. */ | ||
| 69 | |||
| 70 | /* Implementation note: this and the next functions work with ANSI | ||
| 71 | codepage encoded file names! */ | ||
| 72 | |||
| 73 | int | ||
| 74 | open_output_file (file_data *p_file, char *filename, unsigned long size) | ||
| 75 | { | ||
| 76 | HANDLE file; | ||
| 77 | HANDLE file_mapping; | ||
| 78 | void *file_base; | ||
| 79 | |||
| 80 | /* We delete any existing FILENAME because loadup.el will create a | ||
| 81 | hard link to it under the name emacs-XX.YY.ZZ.nn.exe. Evidently, | ||
| 82 | overwriting a file on Unix breaks any hard links to it, but that | ||
| 83 | doesn't happen on Windows. If we don't delete the file before | ||
| 84 | creating it, all the emacs-XX.YY.ZZ.nn.exe end up being hard | ||
| 85 | links to the same file, which defeats the purpose of these hard | ||
| 86 | links: being able to run previous builds. */ | ||
| 87 | DeleteFileA (filename); | ||
| 88 | file = CreateFileA (filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, | ||
| 89 | CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); | ||
| 90 | if (file == INVALID_HANDLE_VALUE) | ||
| 91 | return FALSE; | ||
| 92 | |||
| 93 | file_mapping = CreateFileMapping (file, NULL, PAGE_READWRITE, | ||
| 94 | 0, size, NULL); | ||
| 95 | if (!file_mapping) | ||
| 96 | return FALSE; | ||
| 97 | |||
| 98 | file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size); | ||
| 99 | if (file_base == 0) | ||
| 100 | return FALSE; | ||
| 101 | |||
| 102 | p_file->name = filename; | ||
| 103 | p_file->size = size; | ||
| 104 | p_file->file = file; | ||
| 105 | p_file->file_mapping = file_mapping; | ||
| 106 | p_file->file_base = file_base; | ||
| 107 | |||
| 108 | return TRUE; | ||
| 109 | } | ||
| 110 | |||
| 111 | |||
| 112 | /* Routines to manipulate NT executable file sections. */ | ||
| 113 | |||
| 114 | /* Return pointer to section header for named section. */ | ||
| 115 | IMAGE_SECTION_HEADER * | ||
| 116 | find_section (const char * name, IMAGE_NT_HEADERS * nt_header) | ||
| 117 | { | ||
| 118 | PIMAGE_SECTION_HEADER section; | ||
| 119 | int i; | ||
| 120 | |||
| 121 | section = IMAGE_FIRST_SECTION (nt_header); | ||
| 122 | |||
| 123 | for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++) | ||
| 124 | { | ||
| 125 | if (strcmp ((char *)section->Name, name) == 0) | ||
| 126 | return section; | ||
| 127 | section++; | ||
| 128 | } | ||
| 129 | return NULL; | ||
| 130 | } | ||
| 131 | |||
| 132 | #if 0 /* unused */ | ||
| 133 | /* Return pointer to section header for section containing the given | ||
| 134 | offset in its raw data area. */ | ||
| 135 | static IMAGE_SECTION_HEADER * | ||
| 136 | offset_to_section (DWORD_PTR offset, IMAGE_NT_HEADERS * nt_header) | ||
| 137 | { | ||
| 138 | PIMAGE_SECTION_HEADER section; | ||
| 139 | int i; | ||
| 140 | |||
| 141 | section = IMAGE_FIRST_SECTION (nt_header); | ||
| 142 | |||
| 143 | for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++) | ||
| 144 | { | ||
| 145 | if (offset >= section->PointerToRawData | ||
| 146 | && offset < section->PointerToRawData + section->SizeOfRawData) | ||
| 147 | return section; | ||
| 148 | section++; | ||
| 149 | } | ||
| 150 | return NULL; | ||
| 151 | } | ||
| 152 | #endif | ||
| 153 | |||
| 154 | /* Return offset to an object in dst, given offset in src. We assume | ||
| 155 | there is at least one section in both src and dst images, and that | ||
| 156 | the some sections may have been added to dst (after sections in src). */ | ||
| 157 | static DWORD_PTR | ||
| 158 | relocate_offset (DWORD_PTR offset, | ||
| 159 | IMAGE_NT_HEADERS * src_nt_header, | ||
| 160 | IMAGE_NT_HEADERS * dst_nt_header) | ||
| 161 | { | ||
| 162 | PIMAGE_SECTION_HEADER src_section = IMAGE_FIRST_SECTION (src_nt_header); | ||
| 163 | PIMAGE_SECTION_HEADER dst_section = IMAGE_FIRST_SECTION (dst_nt_header); | ||
| 164 | int i = 0; | ||
| 165 | |||
| 166 | while (offset >= src_section->PointerToRawData) | ||
| 167 | { | ||
| 168 | if (offset < src_section->PointerToRawData + src_section->SizeOfRawData) | ||
| 169 | break; | ||
| 170 | i++; | ||
| 171 | if (i == src_nt_header->FileHeader.NumberOfSections) | ||
| 172 | { | ||
| 173 | /* Handle offsets after the last section. */ | ||
| 174 | dst_section = IMAGE_FIRST_SECTION (dst_nt_header); | ||
| 175 | dst_section += dst_nt_header->FileHeader.NumberOfSections - 1; | ||
| 176 | while (dst_section->PointerToRawData == 0) | ||
| 177 | dst_section--; | ||
| 178 | while (src_section->PointerToRawData == 0) | ||
| 179 | src_section--; | ||
| 180 | return offset | ||
| 181 | + (dst_section->PointerToRawData + dst_section->SizeOfRawData) | ||
| 182 | - (src_section->PointerToRawData + src_section->SizeOfRawData); | ||
| 183 | } | ||
| 184 | src_section++; | ||
| 185 | dst_section++; | ||
| 186 | } | ||
| 187 | return offset + | ||
| 188 | (dst_section->PointerToRawData - src_section->PointerToRawData); | ||
| 189 | } | ||
| 190 | |||
| 191 | #define RVA_TO_OFFSET(rva, section) \ | ||
| 192 | ((section)->PointerToRawData + ((DWORD_PTR)(rva) - (section)->VirtualAddress)) | ||
| 193 | |||
| 194 | #define RVA_TO_SECTION_OFFSET(rva, section) \ | ||
| 195 | ((DWORD_PTR)(rva) - (section)->VirtualAddress) | ||
| 196 | |||
| 197 | /* Convert address in executing image to RVA. */ | ||
| 198 | #define PTR_TO_RVA(ptr) ((DWORD_PTR)(ptr) - (DWORD_PTR) GetModuleHandle (NULL)) | ||
| 199 | |||
| 200 | #define PTR_TO_OFFSET(ptr, pfile_data) \ | ||
| 201 | ((unsigned char *)(ptr) - (pfile_data)->file_base) | ||
| 202 | |||
| 203 | #define OFFSET_TO_PTR(offset, pfile_data) \ | ||
| 204 | ((pfile_data)->file_base + (DWORD_PTR)(offset)) | ||
| 205 | |||
| 206 | #if 0 /* unused */ | ||
| 207 | #define OFFSET_TO_RVA(offset, section) \ | ||
| 208 | ((section)->VirtualAddress + ((DWORD_PTR)(offset) - (section)->PointerToRawData)) | ||
| 209 | |||
| 210 | #define RVA_TO_PTR(var,section,filedata) \ | ||
| 211 | ((unsigned char *)(RVA_TO_OFFSET (var,section) + (filedata).file_base)) | ||
| 212 | #endif | ||
| 213 | |||
| 214 | |||
| 215 | /* Flip through the executable and cache the info necessary for dumping. */ | ||
| 216 | void | ||
| 217 | get_section_info (file_data *p_infile) | ||
| 218 | { | ||
| 219 | PIMAGE_DOS_HEADER dos_header; | ||
| 220 | PIMAGE_NT_HEADERS nt_header; | ||
| 221 | int overlap; | ||
| 222 | |||
| 223 | dos_header = (PIMAGE_DOS_HEADER) p_infile->file_base; | ||
| 224 | if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) | ||
| 225 | { | ||
| 226 | printf ("Unknown EXE header in %s...bailing.\n", p_infile->name); | ||
| 227 | exit (1); | ||
| 228 | } | ||
| 229 | nt_header = (PIMAGE_NT_HEADERS) (((DWORD_PTR) dos_header) + | ||
| 230 | dos_header->e_lfanew); | ||
| 231 | if (nt_header == NULL) | ||
| 232 | { | ||
| 233 | printf ("Failed to find IMAGE_NT_HEADER in %s...bailing.\n", | ||
| 234 | p_infile->name); | ||
| 235 | exit (1); | ||
| 236 | } | ||
| 237 | |||
| 238 | /* Check the NT header signature ... */ | ||
| 239 | if (nt_header->Signature != IMAGE_NT_SIGNATURE) | ||
| 240 | { | ||
| 241 | printf ("Invalid IMAGE_NT_SIGNATURE 0x%lx in %s...bailing.\n", | ||
| 242 | nt_header->Signature, p_infile->name); | ||
| 243 | exit (1); | ||
| 244 | } | ||
| 245 | |||
| 246 | /* Locate the ".data" and ".bss" sections for Emacs. (Note that the | ||
| 247 | actual section names are probably different from these, and might | ||
| 248 | actually be the same section.) | ||
| 249 | |||
| 250 | We do this as follows: first we determine the virtual address | ||
| 251 | ranges in this process for the data and bss variables that we wish | ||
| 252 | to preserve. Then we map these VAs to the section entries in the | ||
| 253 | source image. Finally, we determine the new size of the raw data | ||
| 254 | area for the bss section, so we can make the new image the correct | ||
| 255 | size. */ | ||
| 256 | |||
| 257 | /* We arrange for the Emacs initialized data to be in a separate | ||
| 258 | section if possible, because we cannot rely on my_begdata and | ||
| 259 | my_edata marking out the full extent of the initialized data, at | ||
| 260 | least on the Alpha where the linker freely reorders variables | ||
| 261 | across libraries. If we can arrange for this, all we need to do is | ||
| 262 | find the start and size of the EMDATA section. */ | ||
| 263 | data_section = find_section ("EMDATA", nt_header); | ||
| 264 | if (data_section) | ||
| 265 | { | ||
| 266 | data_start = (char *) nt_header->OptionalHeader.ImageBase + | ||
| 267 | data_section->VirtualAddress; | ||
| 268 | data_size = data_section->Misc.VirtualSize; | ||
| 269 | } | ||
| 270 | else | ||
| 271 | { | ||
| 272 | /* Fallback on the old method if compiler doesn't support the | ||
| 273 | data_set #pragma (or its equivalent). */ | ||
| 274 | data_start = my_begdata; | ||
| 275 | data_size = my_edata - my_begdata; | ||
| 276 | data_section = rva_to_section (PTR_TO_RVA (my_begdata), nt_header); | ||
| 277 | if (data_section != rva_to_section (PTR_TO_RVA (my_edata), nt_header)) | ||
| 278 | { | ||
| 279 | printf ("Initialized data is not in a single section...bailing\n"); | ||
| 280 | exit (1); | ||
| 281 | } | ||
| 282 | } | ||
| 283 | |||
| 284 | /* As noted in lastfile.c, the Alpha (but not the Intel) MSVC linker | ||
| 285 | globally segregates all static and public bss data (ie. across all | ||
| 286 | linked modules, not just per module), so we must take both static | ||
| 287 | and public bss areas into account to determine the true extent of | ||
| 288 | the bss area used by Emacs. | ||
| 289 | |||
| 290 | To be strictly correct, we dump the static and public bss areas | ||
| 291 | used by Emacs separately if non-overlapping (since otherwise we are | ||
| 292 | dumping bss data belonging to system libraries, eg. the static bss | ||
| 293 | system data on the Alpha). */ | ||
| 294 | |||
| 295 | bss_start = my_begbss; | ||
| 296 | bss_size = my_endbss - my_begbss; | ||
| 297 | bss_section = rva_to_section (PTR_TO_RVA (my_begbss), nt_header); | ||
| 298 | if (bss_section != rva_to_section (PTR_TO_RVA (my_endbss), nt_header)) | ||
| 299 | { | ||
| 300 | printf ("Uninitialized data is not in a single section...bailing\n"); | ||
| 301 | exit (1); | ||
| 302 | } | ||
| 303 | /* Compute how much the .bss section's raw data will grow. */ | ||
| 304 | extra_bss_size = | ||
| 305 | ROUND_UP (RVA_TO_SECTION_OFFSET (PTR_TO_RVA (my_endbss), bss_section), | ||
| 306 | nt_header->OptionalHeader.FileAlignment) | ||
| 307 | - bss_section->SizeOfRawData; | ||
| 308 | |||
| 309 | bss_start_static = my_begbss_static; | ||
| 310 | bss_size_static = my_endbss_static - my_begbss_static; | ||
| 311 | bss_section_static = rva_to_section (PTR_TO_RVA (my_begbss_static), nt_header); | ||
| 312 | if (bss_section_static != rva_to_section (PTR_TO_RVA (my_endbss_static), nt_header)) | ||
| 313 | { | ||
| 314 | printf ("Uninitialized static data is not in a single section...bailing\n"); | ||
| 315 | exit (1); | ||
| 316 | } | ||
| 317 | /* Compute how much the static .bss section's raw data will grow. */ | ||
| 318 | extra_bss_size_static = | ||
| 319 | ROUND_UP (RVA_TO_SECTION_OFFSET (PTR_TO_RVA (my_endbss_static), bss_section_static), | ||
| 320 | nt_header->OptionalHeader.FileAlignment) | ||
| 321 | - bss_section_static->SizeOfRawData; | ||
| 322 | |||
| 323 | /* Combine the bss sections into one if they overlap. */ | ||
| 324 | #ifdef _ALPHA_ | ||
| 325 | overlap = 1; /* force all bss data to be dumped */ | ||
| 326 | #else | ||
| 327 | overlap = 0; | ||
| 328 | #endif | ||
| 329 | if (bss_start < bss_start_static) | ||
| 330 | { | ||
| 331 | if (bss_start_static < bss_start + bss_size) | ||
| 332 | overlap = 1; | ||
| 333 | } | ||
| 334 | else | ||
| 335 | { | ||
| 336 | if (bss_start < bss_start_static + bss_size_static) | ||
| 337 | overlap = 1; | ||
| 338 | } | ||
| 339 | if (overlap) | ||
| 340 | { | ||
| 341 | if (bss_section != bss_section_static) | ||
| 342 | { | ||
| 343 | printf ("BSS data not in a single section...bailing\n"); | ||
| 344 | exit (1); | ||
| 345 | } | ||
| 346 | bss_start = min (bss_start, bss_start_static); | ||
| 347 | bss_size = max (my_endbss, my_endbss_static) - bss_start; | ||
| 348 | bss_section_static = 0; | ||
| 349 | extra_bss_size = max (extra_bss_size, extra_bss_size_static); | ||
| 350 | extra_bss_size_static = 0; | ||
| 351 | } | ||
| 352 | } | ||
| 353 | |||
| 354 | /* Format to print a DWORD_PTR value. */ | ||
| 355 | #if defined MINGW_W64 && defined _WIN64 | ||
| 356 | # define pDWP "16llx" | ||
| 357 | #else | ||
| 358 | # define pDWP "08lx" | ||
| 359 | #endif | ||
| 360 | |||
| 361 | /* The dump routines. */ | ||
| 362 | |||
| 363 | void | ||
| 364 | copy_executable_and_dump_data (file_data *p_infile, | ||
| 365 | file_data *p_outfile) | ||
| 366 | { | ||
| 367 | unsigned char *dst, *dst_save; | ||
| 368 | PIMAGE_DOS_HEADER dos_header; | ||
| 369 | PIMAGE_NT_HEADERS nt_header; | ||
| 370 | PIMAGE_NT_HEADERS dst_nt_header; | ||
| 371 | PIMAGE_SECTION_HEADER section; | ||
| 372 | PIMAGE_SECTION_HEADER dst_section; | ||
| 373 | DWORD_PTR offset; | ||
| 374 | int i; | ||
| 375 | int be_verbose = GetEnvironmentVariable ("DEBUG_DUMP", NULL, 0) > 0; | ||
| 376 | |||
| 377 | #define COPY_CHUNK(message, src, size, verbose) \ | ||
| 378 | do { \ | ||
| 379 | unsigned char *s = (void *)(src); \ | ||
| 380 | DWORD_PTR count = (size); \ | ||
| 381 | if (verbose) \ | ||
| 382 | { \ | ||
| 383 | printf ("%s\n", (message)); \ | ||
| 384 | printf ("\t0x%"pDWP" Offset in input file.\n", (DWORD_PTR)(s - p_infile->file_base)); \ | ||
| 385 | printf ("\t0x%"pDWP" Offset in output file.\n", (DWORD_PTR)(dst - p_outfile->file_base)); \ | ||
| 386 | printf ("\t0x%"pDWP" Size in bytes.\n", count); \ | ||
| 387 | } \ | ||
| 388 | memcpy (dst, s, count); \ | ||
| 389 | dst += count; \ | ||
| 390 | } while (0) | ||
| 391 | |||
| 392 | #define COPY_PROC_CHUNK(message, src, size, verbose) \ | ||
| 393 | do { \ | ||
| 394 | unsigned char *s = (void *)(src); \ | ||
| 395 | DWORD_PTR count = (size); \ | ||
| 396 | if (verbose) \ | ||
| 397 | { \ | ||
| 398 | printf ("%s\n", (message)); \ | ||
| 399 | printf ("\t0x%p Address in process.\n", s); \ | ||
| 400 | printf ("\t0x%p Base output file.\n", p_outfile->file_base); \ | ||
| 401 | printf ("\t0x%"pDWP" Offset in output file.\n", (DWORD_PTR)(dst - p_outfile->file_base)); \ | ||
| 402 | printf ("\t0x%p Address in output file.\n", dst); \ | ||
| 403 | printf ("\t0x%"pDWP" Size in bytes.\n", count); \ | ||
| 404 | } \ | ||
| 405 | memcpy (dst, s, count); \ | ||
| 406 | dst += count; \ | ||
| 407 | } while (0) | ||
| 408 | |||
| 409 | #define DST_TO_OFFSET() PTR_TO_OFFSET (dst, p_outfile) | ||
| 410 | #define ROUND_UP_DST(align) \ | ||
| 411 | (dst = p_outfile->file_base + ROUND_UP (DST_TO_OFFSET (), (align))) | ||
| 412 | #define ROUND_UP_DST_AND_ZERO(align) \ | ||
| 413 | do { \ | ||
| 414 | unsigned char *newdst = p_outfile->file_base \ | ||
| 415 | + ROUND_UP (DST_TO_OFFSET (), (align)); \ | ||
| 416 | /* Zero the alignment slop; it may actually initialize real data. */ \ | ||
| 417 | memset (dst, 0, newdst - dst); \ | ||
| 418 | dst = newdst; \ | ||
| 419 | } while (0) | ||
| 420 | |||
| 421 | /* Copy the source image sequentially, ie. section by section after | ||
| 422 | copying the headers and section table, to simplify the process of | ||
| 423 | dumping the raw data for the bss and heap sections. | ||
| 424 | |||
| 425 | Note that dst is updated implicitly by each COPY_CHUNK. */ | ||
| 426 | |||
| 427 | dos_header = (PIMAGE_DOS_HEADER) p_infile->file_base; | ||
| 428 | nt_header = (PIMAGE_NT_HEADERS) (((DWORD_PTR) dos_header) + | ||
| 429 | dos_header->e_lfanew); | ||
| 430 | section = IMAGE_FIRST_SECTION (nt_header); | ||
| 431 | |||
| 432 | dst = (unsigned char *) p_outfile->file_base; | ||
| 433 | |||
| 434 | COPY_CHUNK ("Copying DOS header...", dos_header, | ||
| 435 | (DWORD_PTR) nt_header - (DWORD_PTR) dos_header, be_verbose); | ||
| 436 | dst_nt_header = (PIMAGE_NT_HEADERS) dst; | ||
| 437 | COPY_CHUNK ("Copying NT header...", nt_header, | ||
| 438 | (DWORD_PTR) section - (DWORD_PTR) nt_header, be_verbose); | ||
| 439 | dst_section = (PIMAGE_SECTION_HEADER) dst; | ||
| 440 | COPY_CHUNK ("Copying section table...", section, | ||
| 441 | nt_header->FileHeader.NumberOfSections * sizeof (*section), | ||
| 442 | be_verbose); | ||
| 443 | |||
| 444 | /* Align the first section's raw data area, and set the header size | ||
| 445 | field accordingly. */ | ||
| 446 | ROUND_UP_DST_AND_ZERO (dst_nt_header->OptionalHeader.FileAlignment); | ||
| 447 | dst_nt_header->OptionalHeader.SizeOfHeaders = DST_TO_OFFSET (); | ||
| 448 | |||
| 449 | for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++) | ||
| 450 | { | ||
| 451 | char msg[100]; | ||
| 452 | /* Windows section names are fixed 8-char strings, only | ||
| 453 | zero-terminated if the name is shorter than 8 characters. */ | ||
| 454 | sprintf (msg, "Copying raw data for %.8s...", section->Name); | ||
| 455 | |||
| 456 | dst_save = dst; | ||
| 457 | |||
| 458 | /* Update the file-relative offset for this section's raw data (if | ||
| 459 | it has any) in case things have been relocated; we will update | ||
| 460 | the other offsets below once we know where everything is. */ | ||
| 461 | if (dst_section->PointerToRawData) | ||
| 462 | dst_section->PointerToRawData = DST_TO_OFFSET (); | ||
| 463 | |||
| 464 | /* Can always copy the original raw data. */ | ||
| 465 | COPY_CHUNK | ||
| 466 | (msg, OFFSET_TO_PTR (section->PointerToRawData, p_infile), | ||
| 467 | section->SizeOfRawData, be_verbose); | ||
| 468 | /* Ensure alignment slop is zeroed. */ | ||
| 469 | ROUND_UP_DST_AND_ZERO (dst_nt_header->OptionalHeader.FileAlignment); | ||
| 470 | |||
| 471 | /* Note that various sections below may be aliases. */ | ||
| 472 | if (section == data_section) | ||
| 473 | { | ||
| 474 | dst = dst_save | ||
| 475 | + RVA_TO_SECTION_OFFSET (PTR_TO_RVA (data_start), dst_section); | ||
| 476 | COPY_PROC_CHUNK ("Dumping initialized data...", | ||
| 477 | data_start, data_size, be_verbose); | ||
| 478 | dst = dst_save + dst_section->SizeOfRawData; | ||
| 479 | } | ||
| 480 | if (section == bss_section) | ||
| 481 | { | ||
| 482 | /* Dump contents of bss variables, adjusting the section's raw | ||
| 483 | data size as necessary. */ | ||
| 484 | dst = dst_save | ||
| 485 | + RVA_TO_SECTION_OFFSET (PTR_TO_RVA (bss_start), dst_section); | ||
| 486 | COPY_PROC_CHUNK ("Dumping bss data...", bss_start, | ||
| 487 | bss_size, be_verbose); | ||
| 488 | ROUND_UP_DST (dst_nt_header->OptionalHeader.FileAlignment); | ||
| 489 | dst_section->PointerToRawData = PTR_TO_OFFSET (dst_save, p_outfile); | ||
| 490 | /* Determine new size of raw data area. */ | ||
| 491 | dst = max (dst, dst_save + dst_section->SizeOfRawData); | ||
| 492 | dst_section->SizeOfRawData = dst - dst_save; | ||
| 493 | dst_section->Characteristics &= ~IMAGE_SCN_CNT_UNINITIALIZED_DATA; | ||
| 494 | dst_section->Characteristics |= IMAGE_SCN_CNT_INITIALIZED_DATA; | ||
| 495 | } | ||
| 496 | if (section == bss_section_static) | ||
| 497 | { | ||
| 498 | /* Dump contents of static bss variables, adjusting the | ||
| 499 | section's raw data size as necessary. */ | ||
| 500 | dst = dst_save | ||
| 501 | + RVA_TO_SECTION_OFFSET (PTR_TO_RVA (bss_start_static), dst_section); | ||
| 502 | COPY_PROC_CHUNK ("Dumping static bss data...", bss_start_static, | ||
| 503 | bss_size_static, be_verbose); | ||
| 504 | ROUND_UP_DST (dst_nt_header->OptionalHeader.FileAlignment); | ||
| 505 | dst_section->PointerToRawData = PTR_TO_OFFSET (dst_save, p_outfile); | ||
| 506 | /* Determine new size of raw data area. */ | ||
| 507 | dst = max (dst, dst_save + dst_section->SizeOfRawData); | ||
| 508 | dst_section->SizeOfRawData = dst - dst_save; | ||
| 509 | dst_section->Characteristics &= ~IMAGE_SCN_CNT_UNINITIALIZED_DATA; | ||
| 510 | dst_section->Characteristics |= IMAGE_SCN_CNT_INITIALIZED_DATA; | ||
| 511 | } | ||
| 512 | |||
| 513 | /* Align the section's raw data area. */ | ||
| 514 | ROUND_UP_DST (dst_nt_header->OptionalHeader.FileAlignment); | ||
| 515 | |||
| 516 | section++; | ||
| 517 | dst_section++; | ||
| 518 | } | ||
| 519 | |||
| 520 | /* Copy remainder of source image. */ | ||
| 521 | do | ||
| 522 | section--; | ||
| 523 | while (section->PointerToRawData == 0); | ||
| 524 | offset = ROUND_UP (section->PointerToRawData + section->SizeOfRawData, | ||
| 525 | nt_header->OptionalHeader.FileAlignment); | ||
| 526 | COPY_CHUNK | ||
| 527 | ("Copying remainder of executable...", | ||
| 528 | OFFSET_TO_PTR (offset, p_infile), | ||
| 529 | p_infile->size - offset, be_verbose); | ||
| 530 | |||
| 531 | /* Final size for new image. */ | ||
| 532 | p_outfile->size = DST_TO_OFFSET (); | ||
| 533 | |||
| 534 | /* Now patch up remaining file-relative offsets. */ | ||
| 535 | section = IMAGE_FIRST_SECTION (nt_header); | ||
| 536 | dst_section = IMAGE_FIRST_SECTION (dst_nt_header); | ||
| 537 | |||
| 538 | #define ADJUST_OFFSET(var) \ | ||
| 539 | do { \ | ||
| 540 | if ((var) != 0) \ | ||
| 541 | (var) = relocate_offset ((var), nt_header, dst_nt_header); \ | ||
| 542 | } while (0) | ||
| 543 | |||
| 544 | dst_nt_header->OptionalHeader.SizeOfInitializedData = 0; | ||
| 545 | dst_nt_header->OptionalHeader.SizeOfUninitializedData = 0; | ||
| 546 | for (i = 0; i < dst_nt_header->FileHeader.NumberOfSections; i++) | ||
| 547 | { | ||
| 548 | /* Recompute data sizes for completeness. */ | ||
| 549 | if (dst_section[i].Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) | ||
| 550 | dst_nt_header->OptionalHeader.SizeOfInitializedData += | ||
| 551 | ROUND_UP (dst_section[i].Misc.VirtualSize, dst_nt_header->OptionalHeader.FileAlignment); | ||
| 552 | else if (dst_section[i].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) | ||
| 553 | dst_nt_header->OptionalHeader.SizeOfUninitializedData += | ||
| 554 | ROUND_UP (dst_section[i].Misc.VirtualSize, dst_nt_header->OptionalHeader.FileAlignment); | ||
| 555 | |||
| 556 | ADJUST_OFFSET (dst_section[i].PointerToLinenumbers); | ||
| 557 | } | ||
| 558 | |||
| 559 | ADJUST_OFFSET (dst_nt_header->FileHeader.PointerToSymbolTable); | ||
| 560 | |||
| 561 | /* Update offsets in debug directory entries. */ | ||
| 562 | { | ||
| 563 | IMAGE_DATA_DIRECTORY debug_dir = | ||
| 564 | dst_nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]; | ||
| 565 | PIMAGE_DEBUG_DIRECTORY debug_entry; | ||
| 566 | |||
| 567 | section = rva_to_section (debug_dir.VirtualAddress, dst_nt_header); | ||
| 568 | if (section) | ||
| 569 | { | ||
| 570 | debug_entry = (PIMAGE_DEBUG_DIRECTORY) | ||
| 571 | (RVA_TO_OFFSET (debug_dir.VirtualAddress, section) + p_outfile->file_base); | ||
| 572 | debug_dir.Size /= sizeof (IMAGE_DEBUG_DIRECTORY); | ||
| 573 | |||
| 574 | for (i = 0; i < debug_dir.Size; i++, debug_entry++) | ||
| 575 | ADJUST_OFFSET (debug_entry->PointerToRawData); | ||
| 576 | } | ||
| 577 | } | ||
| 578 | } | ||
| 579 | |||
| 580 | |||
| 581 | /* Dump out .data and .bss sections into a new executable. */ | ||
| 582 | void | ||
| 583 | unexec (const char *new_name, const char *old_name) | ||
| 584 | { | ||
| 585 | file_data in_file, out_file; | ||
| 586 | char out_filename[MAX_PATH], in_filename[MAX_PATH], new_name_a[MAX_PATH]; | ||
| 587 | unsigned long size; | ||
| 588 | char *p; | ||
| 589 | char *q; | ||
| 590 | |||
| 591 | /* Ignore old_name, and get our actual location from the OS. */ | ||
| 592 | if (!GetModuleFileNameA (NULL, in_filename, MAX_PATH)) | ||
| 593 | abort (); | ||
| 594 | |||
| 595 | /* Can't use dostounix_filename here, since that needs its file name | ||
| 596 | argument encoded in UTF-8. */ | ||
| 597 | for (p = in_filename; *p; p = CharNextA (p)) | ||
| 598 | if (*p == '\\') | ||
| 599 | *p = '/'; | ||
| 600 | |||
| 601 | strcpy (out_filename, in_filename); | ||
| 602 | filename_to_ansi (new_name, new_name_a); | ||
| 603 | |||
| 604 | /* Change the base of the output filename to match the requested name. */ | ||
| 605 | if ((p = strrchr (out_filename, '/')) == NULL) | ||
| 606 | abort (); | ||
| 607 | /* The filenames have already been expanded, and will be in Unix | ||
| 608 | format, so it is safe to expect an absolute name. */ | ||
| 609 | if ((q = strrchr (new_name_a, '/')) == NULL) | ||
| 610 | abort (); | ||
| 611 | strcpy (p, q); | ||
| 612 | |||
| 613 | #ifdef ENABLE_CHECKING | ||
| 614 | report_temacs_memory_usage (); | ||
| 615 | #endif | ||
| 616 | |||
| 617 | /* Make sure that the output filename has the ".exe" extension...patch | ||
| 618 | it up if not. */ | ||
| 619 | p = out_filename + strlen (out_filename) - 4; | ||
| 620 | if (strcmp (p, ".exe")) | ||
| 621 | strcat (out_filename, ".exe"); | ||
| 622 | |||
| 623 | printf ("Dumping from %s\n", in_filename); | ||
| 624 | printf (" to %s\n", out_filename); | ||
| 625 | |||
| 626 | /* Open the undumped executable file. */ | ||
| 627 | if (!open_input_file (&in_file, in_filename)) | ||
| 628 | { | ||
| 629 | printf ("Failed to open %s (%lu)...bailing.\n", | ||
| 630 | in_filename, GetLastError ()); | ||
| 631 | exit (1); | ||
| 632 | } | ||
| 633 | |||
| 634 | /* Get the interesting section info, like start and size of .bss... */ | ||
| 635 | get_section_info (&in_file); | ||
| 636 | |||
| 637 | /* The size of the dumped executable is the size of the original | ||
| 638 | executable plus the size of the heap and the size of the .bss section. */ | ||
| 639 | size = in_file.size + | ||
| 640 | extra_bss_size + | ||
| 641 | extra_bss_size_static; | ||
| 642 | if (!open_output_file (&out_file, out_filename, size)) | ||
| 643 | { | ||
| 644 | printf ("Failed to open %s (%lu)...bailing.\n", | ||
| 645 | out_filename, GetLastError ()); | ||
| 646 | exit (1); | ||
| 647 | } | ||
| 648 | |||
| 649 | copy_executable_and_dump_data (&in_file, &out_file); | ||
| 650 | |||
| 651 | /* Patch up header fields; profiler is picky about this. */ | ||
| 652 | { | ||
| 653 | PIMAGE_DOS_HEADER dos_header; | ||
| 654 | PIMAGE_NT_HEADERS nt_header; | ||
| 655 | HANDLE hImagehelp = LoadLibrary ("imagehlp.dll"); | ||
| 656 | DWORD headersum; | ||
| 657 | DWORD checksum; | ||
| 658 | |||
| 659 | dos_header = (PIMAGE_DOS_HEADER) out_file.file_base; | ||
| 660 | nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew); | ||
| 661 | |||
| 662 | nt_header->OptionalHeader.CheckSum = 0; | ||
| 663 | /* nt_header->FileHeader.TimeDateStamp = time (NULL); */ | ||
| 664 | /* dos_header->e_cp = size / 512; */ | ||
| 665 | /* nt_header->OptionalHeader.SizeOfImage = size; */ | ||
| 666 | |||
| 667 | pfnCheckSumMappedFile = (void *) GetProcAddress (hImagehelp, "CheckSumMappedFile"); | ||
| 668 | if (pfnCheckSumMappedFile) | ||
| 669 | { | ||
| 670 | /* nt_header->FileHeader.TimeDateStamp = time (NULL); */ | ||
| 671 | pfnCheckSumMappedFile (out_file.file_base, | ||
| 672 | out_file.size, | ||
| 673 | &headersum, | ||
| 674 | &checksum); | ||
| 675 | nt_header->OptionalHeader.CheckSum = checksum; | ||
| 676 | } | ||
| 677 | FreeLibrary (hImagehelp); | ||
| 678 | } | ||
| 679 | |||
| 680 | close_file_data (&in_file); | ||
| 681 | close_file_data (&out_file); | ||
| 682 | } | ||
| 683 | |||
| 684 | /* eof */ | ||