Revision: miles@gnu.org--gnu-2004/emacs--cvs-trunk--0--patch-483
[bpt/emacs.git] / src / unexnext.c
CommitLineData
5743648e 1/* Dump Emacs in macho format.
f80dc888 2 Copyright (C) 1990, 1993 Free Software Foundation, Inc.
5743648e
RS
3 Written by Bradley Taylor (btaylor@next.com).
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
f80dc888 9the Free Software Foundation; either version 2, or (at your option)
5743648e
RS
10any 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; see the file COPYING. If not, write to
3b7ad313
EN
19the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20Boston, MA 02111-1307, USA. */
5743648e
RS
21
22
23#undef __STRICT_BSD__
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <stdarg.h>
f80dc888
JB
28#include <mach/mach.h>
29#include <mach-o/loader.h>
acacb292 30#include <mach-o/reloc.h>
5743648e
RS
31#include <sys/file.h>
32#include <sys/stat.h>
3e4a1803 33#include <unistd.h>
e6bab6e4
RS
34/* Instead of unistd.h, this used to include libc.h.
35 "Nelson H. F. Beebe" <beebe@math.utah.edu> says that doesn't work
36 in system version 3.3. */
5743648e
RS
37
38
f80dc888 39int malloc_cookie;
5743648e
RS
40
41/*
42 * Kludge: we don't expect any program data beyond VM_HIGHDATA
43 * What is really needed is a way to find out from malloc() which
44 * pages it vm_allocated and write only those out into the data segment.
45 *
46 * This kludge may break when we stop using fixed virtual address
177c0ea7 47 * shared libraries. Actually, emacs will probably continue working, but be
5743648e
RS
48 * much larger on disk than it needs to be (because non-malloced data will
49 * be in the file).
50 */
51static const unsigned VM_HIGHDATA = 0x2000000;
52
53typedef struct region_t {
54 vm_address_t address;
55 vm_size_t size;
56 vm_prot_t protection;
57 vm_prot_t max_protection;
58 vm_inherit_t inheritance;
59 boolean_t shared;
60 port_t object_name;
61 vm_offset_t offset;
62} region_t;
63
64
65static void
66grow(
67 struct load_command ***the_commands,
68 unsigned *the_commands_len
69 )
70{
71 if (*the_commands == NULL) {
72 *the_commands_len = 1;
73 *the_commands = malloc(sizeof(*the_commands));
74 } else {
75 (*the_commands_len)++;
177c0ea7 76 *the_commands = realloc(*the_commands,
5743648e
RS
77 (*the_commands_len *
78 sizeof(**the_commands)));
79 }
80}
81
82
83static void
84save_command(
85 struct load_command *command,
86 struct load_command ***the_commands,
87 unsigned *the_commands_len
88 )
89{
90 struct load_command **tmp;
91
92 grow(the_commands, the_commands_len);
93 tmp = &(*the_commands)[*the_commands_len - 1];
94 *tmp = malloc(command->cmdsize);
95 bcopy(command, *tmp, command->cmdsize);
96}
97
98static void
99fatal_unexec(char *format, ...)
100{
101 va_list ap;
102
103 va_start(ap, format);
104 fprintf(stderr, "unexec: ");
105 vfprintf(stderr, format, ap);
106 fprintf(stderr, "\n");
107 va_end(ap);
108}
109
110static int
111read_macho(
112 int fd,
113 struct mach_header *the_header,
114 struct load_command ***the_commands,
115 unsigned *the_commands_len
116 )
117{
118 struct load_command command;
119 struct load_command *buf;
120 int i;
121 int size;
122
123 if (read(fd, the_header, sizeof(*the_header)) != sizeof(*the_header)) {
124 fatal_unexec("cannot read macho header");
125 return (0);
126 }
127 for (i = 0; i < the_header->ncmds; i++) {
177c0ea7 128 if (read(fd, &command, sizeof(struct load_command)) !=
5743648e
RS
129 sizeof(struct load_command)) {
130 fatal_unexec("cannot read macho load command header");
131 return (0);
132 }
133 size = command.cmdsize - sizeof(struct load_command);
134 if (size < 0) {
135 fatal_unexec("bogus load command size");
136 return (0);
137 }
138 buf = malloc(command.cmdsize);
139 buf->cmd = command.cmd;
140 buf->cmdsize = command.cmdsize;
177c0ea7
JB
141 if (read(fd, ((char *)buf +
142 sizeof(struct load_command)),
5743648e
RS
143 size) != size) {
144 fatal_unexec("cannot read load command data");
145 return (0);
146 }
147 save_command(buf, the_commands, the_commands_len);
148 }
149 return (1);
150}
151
152static int
153filldatagap(
154 vm_address_t start_address,
155 vm_size_t *size,
156 vm_address_t end_address
157 )
158{
159 vm_address_t address;
160 vm_size_t gapsize;
161
162 address = (start_address + *size);
163 gapsize = end_address - address;
164 *size += gapsize;
165 if (vm_allocate(task_self(), &address, gapsize,
166 FALSE) != KERN_SUCCESS) {
167 fatal_unexec("cannot vm_allocate");
168 return (0);
169 }
170 return (1);
171}
172
173static int
174get_data_region(
175 vm_address_t *address,
176 vm_size_t *size
177 )
178{
179 region_t region;
180 kern_return_t ret;
181 struct section *sect;
182
6930925e 183 sect = (struct section *) getsectbyname(SEG_DATA, SECT_DATA);
5743648e
RS
184 region.address = 0;
185 *address = 0;
186 for (;;) {
177c0ea7
JB
187 ret = vm_region(task_self(),
188 &region.address,
189 &region.size,
190 &region.protection,
191 &region.max_protection,
5743648e 192 &region.inheritance,
177c0ea7
JB
193 &region.shared,
194 &region.object_name,
5743648e
RS
195 &region.offset);
196 if (ret != KERN_SUCCESS || region.address >= VM_HIGHDATA) {
197 break;
198 }
199 if (*address != 0) {
200 if (region.address > *address + *size) {
177c0ea7 201 if (!filldatagap(*address, size,
5743648e
RS
202 region.address)) {
203 return (0);
204 }
177c0ea7 205 }
5743648e
RS
206 *size += region.size;
207 } else {
208 if (region.address == sect->addr) {
209 *address = region.address;
210 *size = region.size;
177c0ea7 211 }
5743648e
RS
212 }
213 region.address += region.size;
214 }
215 return (1);
216}
217
218static char *
219my_malloc(
220 vm_size_t size
221 )
222{
223 vm_address_t address;
224
225 if (vm_allocate(task_self(), &address, size, TRUE) != KERN_SUCCESS) {
226 return (NULL);
227 }
228 return ((char *)address);
229}
230
231static void
232my_free(
233 char *buf,
234 vm_size_t size
235 )
236{
237 vm_deallocate(task_self(), (vm_address_t)buf, size);
238}
239
240static int
241unexec_doit(
242 int infd,
243 int outfd
244 )
245{
246 int i;
247 struct load_command **the_commands = NULL;
248 unsigned the_commands_len;
249 struct mach_header the_header;
acacb292 250 int fgrowth = 0;
5743648e
RS
251 int fdatastart;
252 int fdatasize;
253 int size;
254 struct stat st;
255 char *buf;
256 vm_address_t data_address;
257 vm_size_t data_size;
acacb292
RS
258 vm_size_t vmaddr_growth = 0;
259 vm_size_t dataseg_vmaddr, dataseg_vmend;
5743648e
RS
260
261 struct segment_command *segment;
262
acacb292
RS
263#ifdef NS_TARGET
264 unsigned long extreloff = 0;
265 unsigned long nextrel = 0;
266 struct dysymtab_command *dysymtab;
267 struct relocation_info reloc_info;
268#endif
269
5743648e
RS
270 if (!read_macho(infd, &the_header, &the_commands, &the_commands_len)) {
271 return (0);
272 }
273
274
f80dc888 275 malloc_cookie = malloc_freezedry ();
5743648e
RS
276 if (!get_data_region(&data_address, &data_size)) {
277 return (0);
278 }
279
280
281 /*
282 * DO NOT USE MALLOC IN THIS SECTION
283 */
284 {
285 /*
286 * Fix offsets
287 */
288 for (i = 0; i < the_commands_len; i++) {
289 switch (the_commands[i]->cmd) {
290 case LC_SEGMENT:
291 segment = ((struct segment_command *)
292 the_commands[i]);
293 if (strcmp(segment->segname, SEG_DATA) == 0) {
294 fdatastart = segment->fileoff;
295 fdatasize = segment->filesize;
177c0ea7 296 fgrowth = (data_size -
5743648e
RS
297 segment->filesize);
298 segment->vmsize = data_size;
299 segment->filesize = data_size;
acacb292
RS
300 dataseg_vmaddr = segment->vmaddr;
301 dataseg_vmend = segment->vmaddr + segment->vmsize;
302 vmaddr_growth = segment->vmaddr + segment->vmsize;
303 } else {
304 ((struct segment_command *)the_commands[i])->fileoff += fgrowth;
305 }
306
307 if( strcmp( segment->segname, SEG_LINKEDIT ) == 0 ) {
308 segment->vmaddr = vmaddr_growth;
5743648e 309 }
acacb292 310
5743648e
RS
311 break;
312 case LC_SYMTAB:
313 ((struct symtab_command *)
314 the_commands[i])->symoff += fgrowth;
315 ((struct symtab_command *)
316 the_commands[i])->stroff += fgrowth;
317 break;
318 case LC_SYMSEG:
319 ((struct symseg_command *)
320 the_commands[i])->offset += fgrowth;
321 break;
acacb292
RS
322#ifdef NS_TARGET
323 case LC_DYSYMTAB:
324 dysymtab = ((struct dysymtab_command *)the_commands[i]);
325 extreloff = dysymtab->extreloff;
326 nextrel = dysymtab->nextrel;
327 dysymtab->indirectsymoff += fgrowth;
328 dysymtab->extreloff += fgrowth;
329 break;
330#endif
5743648e
RS
331 default:
332 break;
333 }
334 }
177c0ea7 335
5743648e
RS
336 /*
337 * Write header
338 */
177c0ea7 339 if (write(outfd, &the_header,
5743648e
RS
340 sizeof(the_header)) != sizeof(the_header)) {
341 fatal_unexec("cannot write output file");
342 return (0);
343 }
177c0ea7 344
5743648e
RS
345 /*
346 * Write commands
347 */
348 for (i = 0; i < the_commands_len; i++) {
177c0ea7
JB
349 if (write(outfd, the_commands[i],
350 the_commands[i]->cmdsize) !=
5743648e
RS
351 the_commands[i]->cmdsize) {
352 fatal_unexec("cannot write output file");
353 return (0);
354 }
355 }
177c0ea7 356
5743648e
RS
357 /*
358 * Write original text
359 */
177c0ea7 360 if (lseek(infd, the_header.sizeofcmds + sizeof(the_header),
5743648e
RS
361 L_SET) < 0) {
362 fatal_unexec("cannot seek input file");
363 return (0);
364 }
177c0ea7 365 size = fdatastart - (sizeof(the_header) +
5743648e
RS
366 the_header.sizeofcmds);
367 buf = my_malloc(size);
368 if (read(infd, buf, size) != size) {
369 my_free(buf, size);
370 fatal_unexec("cannot read input file");
371 }
372 if (write(outfd, buf, size) != size) {
373 my_free(buf, size);
374 fatal_unexec("cannot write output file");
375 return (0);
376 }
377 my_free(buf, size);
177c0ea7
JB
378
379
5743648e
RS
380 /*
381 * Write new data
382 */
177c0ea7 383 if (write(outfd, (char *)data_address,
5743648e
RS
384 data_size) != data_size) {
385 fatal_unexec("cannot write output file");
386 return (0);
387 }
177c0ea7 388
5743648e
RS
389 }
390
391 /*
392 * OKAY TO USE MALLOC NOW
393 */
394
395 /*
396 * Write rest of file
397 */
398 fstat(infd, &st);
399 if (lseek(infd, fdatasize, L_INCR) < 0) {
400 fatal_unexec("cannot seek input file");
401 return (0);
402 }
403 size = st.st_size - lseek(infd, 0, L_INCR);
404
405 buf = malloc(size);
406 if (read(infd, buf, size) != size) {
407 free(buf);
408 fatal_unexec("cannot read input file");
409 return (0);
410 }
411 if (write(outfd, buf, size) != size) {
412 free(buf);
413 fatal_unexec("cannot write output file");
414 return (0);
415 }
416 free(buf);
acacb292
RS
417
418#ifdef NS_TARGET
419 /*
420 * Fix up relocation entries in the data segment.
421 */
422
423 if (lseek(infd, extreloff, L_SET) < 0) {
424 fatal_unexec("cannot seek input file");
425 return (0);
426 }
177c0ea7 427
acacb292
RS
428 for (i = 0; i < nextrel; i++)
429 {
430 long zeroval = 0;
431
432 if (read(infd, &reloc_info, sizeof (reloc_info)) != sizeof (reloc_info)) {
433 fatal_unexec("cannot read input file");
434 return (0);
435 }
436 if (reloc_info.r_address >= dataseg_vmaddr && reloc_info.r_address < dataseg_vmend)
437 {
438 if (lseek (outfd, fdatastart + reloc_info.r_address - dataseg_vmaddr, L_SET) < 0 ) {
439 fatal_unexec("cannot seek input file");
440 return (0);
441 }
442 switch (reloc_info.r_length) {
443 case 0:
444 if (write(outfd, &zeroval, 1) != 1) {
445 fatal_unexec("cannot write output file");
446 return (0);
447 }
448 break;
449 case 1:
450 if (write(outfd, &zeroval, 2) != 2) {
451 fatal_unexec("cannot write output file");
452 return (0);
453 }
454 break;
455 case 2:
456 if (write(outfd, &zeroval, 4) != 4) {
457 fatal_unexec("cannot write output file");
458 return (0);
459 }
460 break;
461 }
462 }
463 }
464#endif
465
5743648e
RS
466 return (1);
467}
468
469void
470unexec(
471 char *outfile,
472 char *infile
473 )
474{
475 int infd;
476 int outfd;
477 char tmpbuf[L_tmpnam];
478 char *tmpfile;
479
480 infd = open(infile, O_RDONLY, 0);
481 if (infd < 0) {
482 fatal_unexec("cannot open input file `%s'", infile);
483 exit(1);
484 }
177c0ea7 485
5743648e
RS
486 tmpnam(tmpbuf);
487 tmpfile = rindex(tmpbuf, '/');
488 if (tmpfile == NULL) {
489 tmpfile = tmpbuf;
490 } else {
491 tmpfile++;
492 }
493 outfd = open(tmpfile, O_WRONLY|O_TRUNC|O_CREAT, 0755);
494 if (outfd < 0) {
495 close(infd);
496 fatal_unexec("cannot open tmp file `%s'", tmpfile);
497 exit(1);
498 }
499 if (!unexec_doit(infd, outfd)) {
500 close(infd);
501 close(outfd);
502 unlink(tmpfile);
503 exit(1);
504 }
505 close(infd);
506 close(outfd);
507 if (rename(tmpfile, outfile) < 0) {
508 unlink(tmpfile);
509 fatal_unexec("cannot rename `%s' to `%s'", tmpfile, outfile);
510 exit(1);
511 }
512}
ab5796a9
MB
513
514/* arch-tag: 9796bdc3-c050-417a-b2f5-4cfd31032634
515 (do not change this comment) */