*** empty log message ***
[emacs.git] / src / unexnext.c
blob937e5d2b80b36b006efbd88c894ae0a727273eb7
1 /* Dump Emacs in macho format.
2 Copyright (C) 1990, 1993, 2001, 2002, 2003, 2004,
3 2005, 2006, 2007 Free Software Foundation, Inc.
4 Written by Bradley Taylor (btaylor@next.com).
6 This file is part of GNU Emacs.
8 GNU Emacs is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2, or (at your option)
11 any later version.
13 GNU Emacs is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GNU Emacs; see the file COPYING. If not, write to
20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 Boston, MA 02110-1301, USA. */
24 #undef __STRICT_BSD__
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <mach/mach.h>
30 #include <mach-o/loader.h>
31 #include <mach-o/reloc.h>
32 #include <sys/file.h>
33 #include <sys/stat.h>
34 #include <unistd.h>
35 /* Instead of unistd.h, this used to include libc.h.
36 "Nelson H. F. Beebe" <beebe@math.utah.edu> says that doesn't work
37 in system version 3.3. */
40 int malloc_cookie;
43 * Kludge: we don't expect any program data beyond VM_HIGHDATA
44 * What is really needed is a way to find out from malloc() which
45 * pages it vm_allocated and write only those out into the data segment.
47 * This kludge may break when we stop using fixed virtual address
48 * shared libraries. Actually, emacs will probably continue working, but be
49 * much larger on disk than it needs to be (because non-malloced data will
50 * be in the file).
52 static const unsigned VM_HIGHDATA = 0x2000000;
54 typedef struct region_t {
55 vm_address_t address;
56 vm_size_t size;
57 vm_prot_t protection;
58 vm_prot_t max_protection;
59 vm_inherit_t inheritance;
60 boolean_t shared;
61 port_t object_name;
62 vm_offset_t offset;
63 } region_t;
66 static void
67 grow(
68 struct load_command ***the_commands,
69 unsigned *the_commands_len
72 if (*the_commands == NULL) {
73 *the_commands_len = 1;
74 *the_commands = malloc(sizeof(*the_commands));
75 } else {
76 (*the_commands_len)++;
77 *the_commands = realloc(*the_commands,
78 (*the_commands_len *
79 sizeof(**the_commands)));
84 static void
85 save_command(
86 struct load_command *command,
87 struct load_command ***the_commands,
88 unsigned *the_commands_len
91 struct load_command **tmp;
93 grow(the_commands, the_commands_len);
94 tmp = &(*the_commands)[*the_commands_len - 1];
95 *tmp = malloc(command->cmdsize);
96 bcopy(command, *tmp, command->cmdsize);
99 static void
100 fatal_unexec(char *format, ...)
102 va_list ap;
104 va_start(ap, format);
105 fprintf(stderr, "unexec: ");
106 vfprintf(stderr, format, ap);
107 fprintf(stderr, "\n");
108 va_end(ap);
111 static int
112 read_macho(
113 int fd,
114 struct mach_header *the_header,
115 struct load_command ***the_commands,
116 unsigned *the_commands_len
119 struct load_command command;
120 struct load_command *buf;
121 int i;
122 int size;
124 if (read(fd, the_header, sizeof(*the_header)) != sizeof(*the_header)) {
125 fatal_unexec("cannot read macho header");
126 return (0);
128 for (i = 0; i < the_header->ncmds; i++) {
129 if (read(fd, &command, sizeof(struct load_command)) !=
130 sizeof(struct load_command)) {
131 fatal_unexec("cannot read macho load command header");
132 return (0);
134 size = command.cmdsize - sizeof(struct load_command);
135 if (size < 0) {
136 fatal_unexec("bogus load command size");
137 return (0);
139 buf = malloc(command.cmdsize);
140 buf->cmd = command.cmd;
141 buf->cmdsize = command.cmdsize;
142 if (read(fd, ((char *)buf +
143 sizeof(struct load_command)),
144 size) != size) {
145 fatal_unexec("cannot read load command data");
146 return (0);
148 save_command(buf, the_commands, the_commands_len);
150 return (1);
153 static int
154 filldatagap(
155 vm_address_t start_address,
156 vm_size_t *size,
157 vm_address_t end_address
160 vm_address_t address;
161 vm_size_t gapsize;
163 address = (start_address + *size);
164 gapsize = end_address - address;
165 *size += gapsize;
166 if (vm_allocate(task_self(), &address, gapsize,
167 FALSE) != KERN_SUCCESS) {
168 fatal_unexec("cannot vm_allocate");
169 return (0);
171 return (1);
174 static int
175 get_data_region(
176 vm_address_t *address,
177 vm_size_t *size
180 region_t region;
181 kern_return_t ret;
182 struct section *sect;
184 sect = (struct section *) getsectbyname(SEG_DATA, SECT_DATA);
185 region.address = 0;
186 *address = 0;
187 for (;;) {
188 ret = vm_region(task_self(),
189 &region.address,
190 &region.size,
191 &region.protection,
192 &region.max_protection,
193 &region.inheritance,
194 &region.shared,
195 &region.object_name,
196 &region.offset);
197 if (ret != KERN_SUCCESS || region.address >= VM_HIGHDATA) {
198 break;
200 if (*address != 0) {
201 if (region.address > *address + *size) {
202 if (!filldatagap(*address, size,
203 region.address)) {
204 return (0);
207 *size += region.size;
208 } else {
209 if (region.address == sect->addr) {
210 *address = region.address;
211 *size = region.size;
214 region.address += region.size;
216 return (1);
219 static char *
220 my_malloc(
221 vm_size_t size
224 vm_address_t address;
226 if (vm_allocate(task_self(), &address, size, TRUE) != KERN_SUCCESS) {
227 return (NULL);
229 return ((char *)address);
232 static void
233 my_free(
234 char *buf,
235 vm_size_t size
238 vm_deallocate(task_self(), (vm_address_t)buf, size);
241 static int
242 unexec_doit(
243 int infd,
244 int outfd
247 int i;
248 struct load_command **the_commands = NULL;
249 unsigned the_commands_len;
250 struct mach_header the_header;
251 int fgrowth = 0;
252 int fdatastart;
253 int fdatasize;
254 int size;
255 struct stat st;
256 char *buf;
257 vm_address_t data_address;
258 vm_size_t data_size;
259 vm_size_t vmaddr_growth = 0;
260 vm_size_t dataseg_vmaddr, dataseg_vmend;
262 struct segment_command *segment;
264 #ifdef NS_TARGET
265 unsigned long extreloff = 0;
266 unsigned long nextrel = 0;
267 struct dysymtab_command *dysymtab;
268 struct relocation_info reloc_info;
269 #endif
271 if (!read_macho(infd, &the_header, &the_commands, &the_commands_len)) {
272 return (0);
276 malloc_cookie = malloc_freezedry ();
277 if (!get_data_region(&data_address, &data_size)) {
278 return (0);
283 * DO NOT USE MALLOC IN THIS SECTION
287 * Fix offsets
289 for (i = 0; i < the_commands_len; i++) {
290 switch (the_commands[i]->cmd) {
291 case LC_SEGMENT:
292 segment = ((struct segment_command *)
293 the_commands[i]);
294 if (strcmp(segment->segname, SEG_DATA) == 0) {
295 fdatastart = segment->fileoff;
296 fdatasize = segment->filesize;
297 fgrowth = (data_size -
298 segment->filesize);
299 segment->vmsize = data_size;
300 segment->filesize = data_size;
301 dataseg_vmaddr = segment->vmaddr;
302 dataseg_vmend = segment->vmaddr + segment->vmsize;
303 vmaddr_growth = segment->vmaddr + segment->vmsize;
304 } else {
305 ((struct segment_command *)the_commands[i])->fileoff += fgrowth;
308 if( strcmp( segment->segname, SEG_LINKEDIT ) == 0 ) {
309 segment->vmaddr = vmaddr_growth;
312 break;
313 case LC_SYMTAB:
314 ((struct symtab_command *)
315 the_commands[i])->symoff += fgrowth;
316 ((struct symtab_command *)
317 the_commands[i])->stroff += fgrowth;
318 break;
319 case LC_SYMSEG:
320 ((struct symseg_command *)
321 the_commands[i])->offset += fgrowth;
322 break;
323 #ifdef NS_TARGET
324 case LC_DYSYMTAB:
325 dysymtab = ((struct dysymtab_command *)the_commands[i]);
326 extreloff = dysymtab->extreloff;
327 nextrel = dysymtab->nextrel;
328 dysymtab->indirectsymoff += fgrowth;
329 dysymtab->extreloff += fgrowth;
330 break;
331 #endif
332 default:
333 break;
338 * Write header
340 if (write(outfd, &the_header,
341 sizeof(the_header)) != sizeof(the_header)) {
342 fatal_unexec("cannot write output file");
343 return (0);
347 * Write commands
349 for (i = 0; i < the_commands_len; i++) {
350 if (write(outfd, the_commands[i],
351 the_commands[i]->cmdsize) !=
352 the_commands[i]->cmdsize) {
353 fatal_unexec("cannot write output file");
354 return (0);
359 * Write original text
361 if (lseek(infd, the_header.sizeofcmds + sizeof(the_header),
362 L_SET) < 0) {
363 fatal_unexec("cannot seek input file");
364 return (0);
366 size = fdatastart - (sizeof(the_header) +
367 the_header.sizeofcmds);
368 buf = my_malloc(size);
369 if (read(infd, buf, size) != size) {
370 my_free(buf, size);
371 fatal_unexec("cannot read input file");
373 if (write(outfd, buf, size) != size) {
374 my_free(buf, size);
375 fatal_unexec("cannot write output file");
376 return (0);
378 my_free(buf, size);
382 * Write new data
384 if (write(outfd, (char *)data_address,
385 data_size) != data_size) {
386 fatal_unexec("cannot write output file");
387 return (0);
393 * OKAY TO USE MALLOC NOW
397 * Write rest of file
399 fstat(infd, &st);
400 if (lseek(infd, fdatasize, L_INCR) < 0) {
401 fatal_unexec("cannot seek input file");
402 return (0);
404 size = st.st_size - lseek(infd, 0, L_INCR);
406 buf = malloc(size);
407 if (read(infd, buf, size) != size) {
408 free(buf);
409 fatal_unexec("cannot read input file");
410 return (0);
412 if (write(outfd, buf, size) != size) {
413 free(buf);
414 fatal_unexec("cannot write output file");
415 return (0);
417 free(buf);
419 #ifdef NS_TARGET
421 * Fix up relocation entries in the data segment.
424 if (lseek(infd, extreloff, L_SET) < 0) {
425 fatal_unexec("cannot seek input file");
426 return (0);
429 for (i = 0; i < nextrel; i++)
431 long zeroval = 0;
433 if (read(infd, &reloc_info, sizeof (reloc_info)) != sizeof (reloc_info)) {
434 fatal_unexec("cannot read input file");
435 return (0);
437 if (reloc_info.r_address >= dataseg_vmaddr && reloc_info.r_address < dataseg_vmend)
439 if (lseek (outfd, fdatastart + reloc_info.r_address - dataseg_vmaddr, L_SET) < 0 ) {
440 fatal_unexec("cannot seek input file");
441 return (0);
443 switch (reloc_info.r_length) {
444 case 0:
445 if (write(outfd, &zeroval, 1) != 1) {
446 fatal_unexec("cannot write output file");
447 return (0);
449 break;
450 case 1:
451 if (write(outfd, &zeroval, 2) != 2) {
452 fatal_unexec("cannot write output file");
453 return (0);
455 break;
456 case 2:
457 if (write(outfd, &zeroval, 4) != 4) {
458 fatal_unexec("cannot write output file");
459 return (0);
461 break;
465 #endif
467 return (1);
470 void
471 unexec(
472 char *outfile,
473 char *infile
476 int infd;
477 int outfd;
478 char tmpbuf[L_tmpnam];
479 char *tmpfile;
481 infd = open(infile, O_RDONLY, 0);
482 if (infd < 0) {
483 fatal_unexec("cannot open input file `%s'", infile);
484 exit(1);
487 tmpnam(tmpbuf);
488 tmpfile = rindex(tmpbuf, '/');
489 if (tmpfile == NULL) {
490 tmpfile = tmpbuf;
491 } else {
492 tmpfile++;
494 outfd = open(tmpfile, O_WRONLY|O_TRUNC|O_CREAT, 0755);
495 if (outfd < 0) {
496 close(infd);
497 fatal_unexec("cannot open tmp file `%s'", tmpfile);
498 exit(1);
500 if (!unexec_doit(infd, outfd)) {
501 close(infd);
502 close(outfd);
503 unlink(tmpfile);
504 exit(1);
506 close(infd);
507 close(outfd);
508 if (rename(tmpfile, outfile) < 0) {
509 unlink(tmpfile);
510 fatal_unexec("cannot rename `%s' to `%s'", tmpfile, outfile);
511 exit(1);
515 /* arch-tag: 9796bdc3-c050-417a-b2f5-4cfd31032634
516 (do not change this comment) */