1349
|
1 /* Dump Emacs in macho format.
|
2660
|
2 Copyright (C) 1990, 1993 Free Software Foundation, Inc.
|
1349
|
3 Written by Bradley Taylor (btaylor@next.com).
|
|
4
|
|
5 This file is part of GNU Emacs.
|
|
6
|
|
7 GNU Emacs is free software; you can redistribute it and/or modify
|
|
8 it under the terms of the GNU General Public License as published by
|
2660
|
9 the Free Software Foundation; either version 2, or (at your option)
|
1349
|
10 any later version.
|
|
11
|
|
12 GNU Emacs is distributed in the hope that it will be useful,
|
|
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15 GNU General Public License for more details.
|
|
16
|
|
17 You should have received a copy of the GNU General Public License
|
|
18 along with GNU Emacs; see the file COPYING. If not, write to
|
14186
|
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
20 Boston, MA 02111-1307, USA. */
|
1349
|
21
|
|
22
|
|
23 #undef __STRICT_BSD__
|
|
24
|
|
25 #include <stdio.h>
|
|
26 #include <stdlib.h>
|
|
27 #include <stdarg.h>
|
2660
|
28 #include <mach/mach.h>
|
|
29 #include <mach-o/loader.h>
|
16853
|
30 #include <mach-o/reloc.h>
|
1349
|
31 #include <sys/file.h>
|
|
32 #include <sys/stat.h>
|
19669
|
33 #include <unistd.h>
|
19670
|
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. */
|
1349
|
37
|
|
38
|
2660
|
39 int malloc_cookie;
|
1349
|
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
|
|
47 * shared libraries. Actually, emacs will probably continue working, but be
|
|
48 * much larger on disk than it needs to be (because non-malloced data will
|
|
49 * be in the file).
|
|
50 */
|
|
51 static const unsigned VM_HIGHDATA = 0x2000000;
|
|
52
|
|
53 typedef 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
|
|
65 static void
|
|
66 grow(
|
|
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)++;
|
|
76 *the_commands = realloc(*the_commands,
|
|
77 (*the_commands_len *
|
|
78 sizeof(**the_commands)));
|
|
79 }
|
|
80 }
|
|
81
|
|
82
|
|
83 static void
|
|
84 save_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
|
|
98 static void
|
|
99 fatal_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
|
|
110 static int
|
|
111 read_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++) {
|
|
128 if (read(fd, &command, sizeof(struct load_command)) !=
|
|
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;
|
|
141 if (read(fd, ((char *)buf +
|
|
142 sizeof(struct load_command)),
|
|
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
|
|
152 static int
|
|
153 filldatagap(
|
|
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
|
|
173 static int
|
|
174 get_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
|
4598
|
183 sect = (struct section *) getsectbyname(SEG_DATA, SECT_DATA);
|
1349
|
184 region.address = 0;
|
|
185 *address = 0;
|
|
186 for (;;) {
|
|
187 ret = vm_region(task_self(),
|
|
188 ®ion.address,
|
|
189 ®ion.size,
|
|
190 ®ion.protection,
|
|
191 ®ion.max_protection,
|
|
192 ®ion.inheritance,
|
|
193 ®ion.shared,
|
|
194 ®ion.object_name,
|
|
195 ®ion.offset);
|
|
196 if (ret != KERN_SUCCESS || region.address >= VM_HIGHDATA) {
|
|
197 break;
|
|
198 }
|
|
199 if (*address != 0) {
|
|
200 if (region.address > *address + *size) {
|
|
201 if (!filldatagap(*address, size,
|
|
202 region.address)) {
|
|
203 return (0);
|
|
204 }
|
|
205 }
|
|
206 *size += region.size;
|
|
207 } else {
|
|
208 if (region.address == sect->addr) {
|
|
209 *address = region.address;
|
|
210 *size = region.size;
|
|
211 }
|
|
212 }
|
|
213 region.address += region.size;
|
|
214 }
|
|
215 return (1);
|
|
216 }
|
|
217
|
|
218 static char *
|
|
219 my_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
|
|
231 static void
|
|
232 my_free(
|
|
233 char *buf,
|
|
234 vm_size_t size
|
|
235 )
|
|
236 {
|
|
237 vm_deallocate(task_self(), (vm_address_t)buf, size);
|
|
238 }
|
|
239
|
|
240 static int
|
|
241 unexec_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;
|
16853
|
250 int fgrowth = 0;
|
1349
|
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;
|
16853
|
258 vm_size_t vmaddr_growth = 0;
|
|
259 vm_size_t dataseg_vmaddr, dataseg_vmend;
|
1349
|
260
|
|
261 struct segment_command *segment;
|
|
262
|
16853
|
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
|
1349
|
270 if (!read_macho(infd, &the_header, &the_commands, &the_commands_len)) {
|
|
271 return (0);
|
|
272 }
|
|
273
|
|
274
|
2660
|
275 malloc_cookie = malloc_freezedry ();
|
1349
|
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;
|
|
296 fgrowth = (data_size -
|
|
297 segment->filesize);
|
|
298 segment->vmsize = data_size;
|
|
299 segment->filesize = data_size;
|
16853
|
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;
|
1349
|
305 }
|
16853
|
306
|
|
307 if( strcmp( segment->segname, SEG_LINKEDIT ) == 0 ) {
|
|
308 segment->vmaddr = vmaddr_growth;
|
|
309 }
|
|
310
|
1349
|
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;
|
16853
|
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
|
1349
|
331 default:
|
|
332 break;
|
|
333 }
|
|
334 }
|
|
335
|
|
336 /*
|
|
337 * Write header
|
|
338 */
|
|
339 if (write(outfd, &the_header,
|
|
340 sizeof(the_header)) != sizeof(the_header)) {
|
|
341 fatal_unexec("cannot write output file");
|
|
342 return (0);
|
|
343 }
|
|
344
|
|
345 /*
|
|
346 * Write commands
|
|
347 */
|
|
348 for (i = 0; i < the_commands_len; i++) {
|
|
349 if (write(outfd, the_commands[i],
|
|
350 the_commands[i]->cmdsize) !=
|
|
351 the_commands[i]->cmdsize) {
|
|
352 fatal_unexec("cannot write output file");
|
|
353 return (0);
|
|
354 }
|
|
355 }
|
|
356
|
|
357 /*
|
|
358 * Write original text
|
|
359 */
|
|
360 if (lseek(infd, the_header.sizeofcmds + sizeof(the_header),
|
|
361 L_SET) < 0) {
|
|
362 fatal_unexec("cannot seek input file");
|
|
363 return (0);
|
|
364 }
|
|
365 size = fdatastart - (sizeof(the_header) +
|
|
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);
|
|
378
|
|
379
|
|
380 /*
|
|
381 * Write new data
|
|
382 */
|
|
383 if (write(outfd, (char *)data_address,
|
|
384 data_size) != data_size) {
|
|
385 fatal_unexec("cannot write output file");
|
|
386 return (0);
|
|
387 }
|
|
388
|
|
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);
|
16853
|
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 }
|
|
427
|
|
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
|
1349
|
466 return (1);
|
|
467 }
|
|
468
|
|
469 void
|
|
470 unexec(
|
|
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 }
|
|
485
|
|
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 }
|