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