aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPip Cet2024-08-20 18:29:53 +0000
committerStefan Kangas2024-12-12 22:48:10 +0100
commit7ce34a3bcf5ed277ef37aa75e1ccbd858543b6cf (patch)
tree4654efb303767c4f67d68d94ac4bb23d8f3e5f2d /src
parent9ccd459e8452cc9e6e81e53f26bbeef20d2d5bb7 (diff)
downloademacs-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.c79
-rw-r--r--src/sheap.h30
-rw-r--r--src/unexaix.c611
-rw-r--r--src/unexcoff.c540
-rw-r--r--src/unexcw.c302
-rw-r--r--src/unexec.h4
-rw-r--r--src/unexelf.c658
-rw-r--r--src/unexhp9k800.c324
-rw-r--r--src/unexmacosx.c1406
-rw-r--r--src/unexsol.c28
-rw-r--r--src/unexw32.c684
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
6This file is part of GNU Emacs.
7
8GNU Emacs is free software: you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation, either version 3 of the License, or (at
11your option) any later version.
12
13GNU Emacs is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along 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
30static int debug_sheap;
31
32char bss_sbrk_buffer[STATIC_HEAP_SIZE];
33char *max_bss_sbrk_ptr;
34
35void *
36bss_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
3Copyright 2016-2024 Free Software Foundation, Inc.
4
5This file is part of GNU Emacs.
6
7GNU Emacs is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 3 of the License, or
10(at your option) any later version.
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along 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. */
26enum { STATIC_HEAP_SIZE = sizeof (Lisp_Object) << 24 };
27
28extern char bss_sbrk_buffer[STATIC_HEAP_SIZE];
29extern char *max_bss_sbrk_ptr;
30extern 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
5This file is part of GNU Emacs.
6
7GNU Emacs is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 3 of the License, or (at
10your option) any later version.
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
19
20/*
21In other words, you are welcome to use, share and improve this program.
22You are forbidden to forbid anyone else to use, share and improve
23what 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
62extern char _data[];
63extern char _text[];
64
65#include <filehdr.h>
66#include <aouthdr.h>
67#include <scnhdr.h>
68#include <syms.h>
69
70static struct filehdr f_hdr; /* File header */
71static struct aouthdr f_ohdr; /* Optional file header (a.out) */
72static off_t bias; /* Bias to add for growth */
73static off_t lnnoptr; /* Pointer to line-number info within file */
74
75static off_t text_scnptr;
76static off_t data_scnptr;
77#define ALIGN(val, pwr) (((val) + ((1L<<(pwr))-1)) & ~((1L<<(pwr))-1))
78static off_t load_scnptr;
79static off_t orig_load_scnptr;
80static off_t orig_data_scnptr;
81static int unrelocate_symbols (int, int, const char *, const char *);
82
83#ifndef MAX_SECTIONS
84#define MAX_SECTIONS 10
85#endif
86
87static int adjust_lnnoptrs (int, int, const char *);
88
89static int pagemask;
90
91#include "lisp.h"
92
93static _Noreturn void
94report_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
106static _Noreturn void ATTRIBUTE_FORMAT_PRINTF (2, 3)
107report_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
116static int make_hdr (int, int, const char *, const char *);
117static void mark_x (const char *);
118static int copy_text_and_data (int);
119static int copy_sym (int, int, const char *, const char *);
120static void write_segment (int, char *, char *);
121
122/* ****************************************************************
123 * unexec
124 *
125 * driving logic.
126 */
127void
128unexec (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 */
162static int
163make_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 = &section[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 = &section[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 = &section[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 = &section[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 */
373static int
374copy_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 */
393static void
394write_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 */
436static int
437copy_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
467static int
468adjust_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
514static int
515unrelocate_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
4This file is part of GNU Emacs.
5
6GNU Emacs is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 3 of the License, or (at
9your option) any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along 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>
67static int save_djgpp_startup_flags;
68#include <libc/atexit.h>
69static 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
76struct 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
103extern int etext;
104
105static long block_copy_start; /* Old executable start point */
106static struct filehdr f_hdr; /* File header */
107static struct aouthdr f_ohdr; /* Optional file header (a.out) */
108long bias; /* Bias to add for growth */
109long lnnoptr; /* Pointer to line-number info within file */
110#define SYMS_START block_copy_start
111
112static long text_scnptr;
113static long data_scnptr;
114
115static long coff_offset;
116
117static 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
127static void
128report_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
140static void
141report_error_1 (int fd, const char *msg, int a1, int a2)
142{
143 emacs_close (fd);
144 error (msg, a1, a2);
145}
146
147static int make_hdr (int, int, const char *, const char *);
148static int copy_text_and_data (int, int);
149static int copy_sym (int, int, const char *, const char *);
150static 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 */
158static int
159make_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
315void
316write_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 */
356static int
357copy_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 */
409static int
410copy_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
463int
464adjust_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 */
511void
512unexec (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
6This file is part of GNU Emacs.
7
8GNU Emacs is free software: you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation, either version 3 of the License, or (at
11your option) any later version.
12
13GNU Emacs is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along 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*/
35typedef struct
36{
37 FILHDR file_header;
38 PEAOUTHDR file_optional_header;
39 SCNHDR section_header[32];
40} exe_header_t;
41
42int debug_unexcw = 0;
43
44/*
45** Read the header from the executable into memory so we can more easily access it.
46*/
47static exe_header_t *
48read_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*/
108static void
109fixup_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*/
245static char *
246add_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
264void
265unexec (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
3void 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
4This file is part of GNU Emacs.
5
6GNU Emacs is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 3 of the License, or (at
9your option) any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
18
19/*
20In other words, you are welcome to use, share and improve this program.
21You are forbidden to forbid anyone else to use, share and improve
22what 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. */
88typedef 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>
184static_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
197static void *
198entry_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
211typedef 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 */
222void
223unexec (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. */
64static long brk_on_dump = 0;
65
66/* Called from main, if we use shared libraries. */
67int
68run_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. */
77static _Noreturn void
78unexec_error (char const *msg)
79{
80 perror (msg);
81 exit (1);
82}
83
84/* Do an lseek and check the result. */
85static void
86check_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
94static void
95save_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
109static void
110update_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
158static void
159read_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
181static void
182write_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
198static int
199calculate_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
213static void
214copy_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
231static void
232copy_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
246static void
247display_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 */
267void
268unexec (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
4This file is part of GNU Emacs.
5
6GNU Emacs is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 3 of the License, or (at
9your option) any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along 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. */
153struct 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. */
163static struct region_t *region_list_head = 0;
164static struct region_t *region_list_tail = 0;
165
166/* Pointer to array of load commands. */
167static struct load_command **lca;
168
169/* Number of load commands. */
170static 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. */
175static 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. */
182static unsigned long text_seg_lowest_offset = 0x10000000;
183
184/* Mach header. */
185static struct mach_header mh;
186
187/* Offset at which the next load command should be written. */
188static unsigned long curr_header_offset = sizeof (struct mach_header);
189
190/* Offset at which the next segment should be written. */
191static unsigned long curr_file_offset = 0;
192
193static unsigned long pagesize;
194#define ROUNDUP_TO_PAGE_BOUNDARY(x) (((x) + pagesize - 1) & ~(pagesize - 1))
195
196static int infd, outfd;
197
198static int in_dumped_exec = 0;
199
200static malloc_zone_t *emacs_zone;
201
202/* file offset of input file's data segment */
203static off_t data_segment_old_fileoff = 0;
204
205static 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. */
209static int
210unexec_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. */
218static int
219unexec_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. */
246static int
247unexec_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. */
270static int
271unexec_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
300static _Noreturn void
301unexec_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
313static void
314print_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
327static void
328print_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
338static void
339print_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. */
354static void
355build_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
434static int num_unexec_regions;
435typedef struct {
436 vm_range_t range;
437 vm_size_t filesize;
438} unexec_region_info;
439static unexec_region_info unexec_regions[MAX_UNEXEC_REGIONS];
440
441static void
442unexec_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
466static kern_return_t
467unexec_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
473static void
474find_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
489static int
490unexec_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
503static void
504unexec_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
554static void
555print_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
635static void
636print_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. */
666static void
667read_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. */
743static void
744copy_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. */
786static void
787copy_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. */
969static void
970copy_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. */
986static void
987unrelocate (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. */
1039static void
1040copy_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. */
1077static void
1078copy_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. */
1097static void
1098copy_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. */
1128static void
1129copy_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. */
1149static void
1150copy_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. */
1164static void
1165dump_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. */
1252void
1253unexec (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
1286void
1287unexec_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
1297typedef 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
1310int
1311ptr_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
1325void *
1326unexec_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
1353void *
1354unexec_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
1394void
1395unexec_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
12void
13unexec (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
4This file is part of GNU Emacs.
5
6GNU Emacs is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 3 of the License, or (at
9your option) any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along 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
37PIMAGE_NT_HEADERS (__stdcall * pfnCheckSumMappedFile) (LPVOID BaseAddress,
38 DWORD FileLength,
39 LPDWORD HeaderSum,
40 LPDWORD CheckSum);
41
42extern char my_begdata[];
43extern char my_begbss[];
44extern char *my_begbss_static;
45
46#include "w32heap.h"
47
48void get_section_info (file_data *p_file);
49void copy_executable_and_dump_data (file_data *, file_data *);
50void dump_bss_and_heap (file_data *p_infile, file_data *p_outfile);
51
52/* Cached info about the .data section in the executable. */
53PIMAGE_SECTION_HEADER data_section;
54PCHAR data_start = 0;
55DWORD_PTR data_size = 0;
56
57/* Cached info about the .bss section in the executable. */
58PIMAGE_SECTION_HEADER bss_section;
59PCHAR bss_start = 0;
60DWORD_PTR bss_size = 0;
61DWORD_PTR extra_bss_size = 0;
62/* bss data that is static might be discontiguous from non-static. */
63PIMAGE_SECTION_HEADER bss_section_static;
64PCHAR bss_start_static = 0;
65DWORD_PTR bss_size_static = 0;
66DWORD_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
73int
74open_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. */
115IMAGE_SECTION_HEADER *
116find_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. */
135static IMAGE_SECTION_HEADER *
136offset_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). */
157static DWORD_PTR
158relocate_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. */
216void
217get_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
363void
364copy_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. */
582void
583unexec (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 */