(Vafter_change_functions, Vbefore_change_functions): Declared.
[bpt/emacs.git] / src / unexnext.c
1 /* Dump Emacs in macho format.
2 Copyright (C) 1990, 1993 Free Software Foundation, Inc.
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
9 the Free Software Foundation; either version 2, or (at your option)
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
19 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
20
21
22 #undef __STRICT_BSD__
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <mach/mach.h>
28 #include <mach-o/loader.h>
29 #include <sys/file.h>
30 #include <sys/stat.h>
31 #include <libc.h>
32
33
34 int malloc_cookie;
35
36 /*
37 * Kludge: we don't expect any program data beyond VM_HIGHDATA
38 * What is really needed is a way to find out from malloc() which
39 * pages it vm_allocated and write only those out into the data segment.
40 *
41 * This kludge may break when we stop using fixed virtual address
42 * shared libraries. Actually, emacs will probably continue working, but be
43 * much larger on disk than it needs to be (because non-malloced data will
44 * be in the file).
45 */
46 static const unsigned VM_HIGHDATA = 0x2000000;
47
48 typedef struct region_t {
49 vm_address_t address;
50 vm_size_t size;
51 vm_prot_t protection;
52 vm_prot_t max_protection;
53 vm_inherit_t inheritance;
54 boolean_t shared;
55 port_t object_name;
56 vm_offset_t offset;
57 } region_t;
58
59
60 static void
61 grow(
62 struct load_command ***the_commands,
63 unsigned *the_commands_len
64 )
65 {
66 if (*the_commands == NULL) {
67 *the_commands_len = 1;
68 *the_commands = malloc(sizeof(*the_commands));
69 } else {
70 (*the_commands_len)++;
71 *the_commands = realloc(*the_commands,
72 (*the_commands_len *
73 sizeof(**the_commands)));
74 }
75 }
76
77
78 static void
79 save_command(
80 struct load_command *command,
81 struct load_command ***the_commands,
82 unsigned *the_commands_len
83 )
84 {
85 struct load_command **tmp;
86
87 grow(the_commands, the_commands_len);
88 tmp = &(*the_commands)[*the_commands_len - 1];
89 *tmp = malloc(command->cmdsize);
90 bcopy(command, *tmp, command->cmdsize);
91 }
92
93 static void
94 fatal_unexec(char *format, ...)
95 {
96 va_list ap;
97
98 va_start(ap, format);
99 fprintf(stderr, "unexec: ");
100 vfprintf(stderr, format, ap);
101 fprintf(stderr, "\n");
102 va_end(ap);
103 }
104
105 static int
106 read_macho(
107 int fd,
108 struct mach_header *the_header,
109 struct load_command ***the_commands,
110 unsigned *the_commands_len
111 )
112 {
113 struct load_command command;
114 struct load_command *buf;
115 int i;
116 int size;
117
118 if (read(fd, the_header, sizeof(*the_header)) != sizeof(*the_header)) {
119 fatal_unexec("cannot read macho header");
120 return (0);
121 }
122 for (i = 0; i < the_header->ncmds; i++) {
123 if (read(fd, &command, sizeof(struct load_command)) !=
124 sizeof(struct load_command)) {
125 fatal_unexec("cannot read macho load command header");
126 return (0);
127 }
128 size = command.cmdsize - sizeof(struct load_command);
129 if (size < 0) {
130 fatal_unexec("bogus load command size");
131 return (0);
132 }
133 buf = malloc(command.cmdsize);
134 buf->cmd = command.cmd;
135 buf->cmdsize = command.cmdsize;
136 if (read(fd, ((char *)buf +
137 sizeof(struct load_command)),
138 size) != size) {
139 fatal_unexec("cannot read load command data");
140 return (0);
141 }
142 save_command(buf, the_commands, the_commands_len);
143 }
144 return (1);
145 }
146
147 static int
148 filldatagap(
149 vm_address_t start_address,
150 vm_size_t *size,
151 vm_address_t end_address
152 )
153 {
154 vm_address_t address;
155 vm_size_t gapsize;
156
157 address = (start_address + *size);
158 gapsize = end_address - address;
159 *size += gapsize;
160 if (vm_allocate(task_self(), &address, gapsize,
161 FALSE) != KERN_SUCCESS) {
162 fatal_unexec("cannot vm_allocate");
163 return (0);
164 }
165 return (1);
166 }
167
168 static int
169 get_data_region(
170 vm_address_t *address,
171 vm_size_t *size
172 )
173 {
174 region_t region;
175 kern_return_t ret;
176 struct section *sect;
177
178 sect = (struct section *) getsectbyname(SEG_DATA, SECT_DATA);
179 region.address = 0;
180 *address = 0;
181 for (;;) {
182 ret = vm_region(task_self(),
183 &region.address,
184 &region.size,
185 &region.protection,
186 &region.max_protection,
187 &region.inheritance,
188 &region.shared,
189 &region.object_name,
190 &region.offset);
191 if (ret != KERN_SUCCESS || region.address >= VM_HIGHDATA) {
192 break;
193 }
194 if (*address != 0) {
195 if (region.address > *address + *size) {
196 if (!filldatagap(*address, size,
197 region.address)) {
198 return (0);
199 }
200 }
201 *size += region.size;
202 } else {
203 if (region.address == sect->addr) {
204 *address = region.address;
205 *size = region.size;
206 }
207 }
208 region.address += region.size;
209 }
210 return (1);
211 }
212
213 static char *
214 my_malloc(
215 vm_size_t size
216 )
217 {
218 vm_address_t address;
219
220 if (vm_allocate(task_self(), &address, size, TRUE) != KERN_SUCCESS) {
221 return (NULL);
222 }
223 return ((char *)address);
224 }
225
226 static void
227 my_free(
228 char *buf,
229 vm_size_t size
230 )
231 {
232 vm_deallocate(task_self(), (vm_address_t)buf, size);
233 }
234
235 static int
236 unexec_doit(
237 int infd,
238 int outfd
239 )
240 {
241 int i;
242 struct load_command **the_commands = NULL;
243 unsigned the_commands_len;
244 struct mach_header the_header;
245 int fgrowth;
246 int fdatastart;
247 int fdatasize;
248 int size;
249 struct stat st;
250 char *buf;
251 vm_address_t data_address;
252 vm_size_t data_size;
253
254 struct segment_command *segment;
255
256 if (!read_macho(infd, &the_header, &the_commands, &the_commands_len)) {
257 return (0);
258 }
259
260
261 malloc_cookie = malloc_freezedry ();
262 if (!get_data_region(&data_address, &data_size)) {
263 return (0);
264 }
265
266
267 /*
268 * DO NOT USE MALLOC IN THIS SECTION
269 */
270 {
271 /*
272 * Fix offsets
273 */
274 for (i = 0; i < the_commands_len; i++) {
275 switch (the_commands[i]->cmd) {
276 case LC_SEGMENT:
277 segment = ((struct segment_command *)
278 the_commands[i]);
279 if (strcmp(segment->segname, SEG_DATA) == 0) {
280 fdatastart = segment->fileoff;
281 fdatasize = segment->filesize;
282 fgrowth = (data_size -
283 segment->filesize);
284 segment->vmsize = data_size;
285 segment->filesize = data_size;
286 }
287 break;
288 case LC_SYMTAB:
289 ((struct symtab_command *)
290 the_commands[i])->symoff += fgrowth;
291 ((struct symtab_command *)
292 the_commands[i])->stroff += fgrowth;
293 break;
294 case LC_SYMSEG:
295 ((struct symseg_command *)
296 the_commands[i])->offset += fgrowth;
297 break;
298 default:
299 break;
300 }
301 }
302
303 /*
304 * Write header
305 */
306 if (write(outfd, &the_header,
307 sizeof(the_header)) != sizeof(the_header)) {
308 fatal_unexec("cannot write output file");
309 return (0);
310 }
311
312 /*
313 * Write commands
314 */
315 for (i = 0; i < the_commands_len; i++) {
316 if (write(outfd, the_commands[i],
317 the_commands[i]->cmdsize) !=
318 the_commands[i]->cmdsize) {
319 fatal_unexec("cannot write output file");
320 return (0);
321 }
322 }
323
324 /*
325 * Write original text
326 */
327 if (lseek(infd, the_header.sizeofcmds + sizeof(the_header),
328 L_SET) < 0) {
329 fatal_unexec("cannot seek input file");
330 return (0);
331 }
332 size = fdatastart - (sizeof(the_header) +
333 the_header.sizeofcmds);
334 buf = my_malloc(size);
335 if (read(infd, buf, size) != size) {
336 my_free(buf, size);
337 fatal_unexec("cannot read input file");
338 }
339 if (write(outfd, buf, size) != size) {
340 my_free(buf, size);
341 fatal_unexec("cannot write output file");
342 return (0);
343 }
344 my_free(buf, size);
345
346
347 /*
348 * Write new data
349 */
350 if (write(outfd, (char *)data_address,
351 data_size) != data_size) {
352 fatal_unexec("cannot write output file");
353 return (0);
354 }
355
356 }
357
358 /*
359 * OKAY TO USE MALLOC NOW
360 */
361
362 /*
363 * Write rest of file
364 */
365 fstat(infd, &st);
366 if (lseek(infd, fdatasize, L_INCR) < 0) {
367 fatal_unexec("cannot seek input file");
368 return (0);
369 }
370 size = st.st_size - lseek(infd, 0, L_INCR);
371
372 buf = malloc(size);
373 if (read(infd, buf, size) != size) {
374 free(buf);
375 fatal_unexec("cannot read input file");
376 return (0);
377 }
378 if (write(outfd, buf, size) != size) {
379 free(buf);
380 fatal_unexec("cannot write output file");
381 return (0);
382 }
383 free(buf);
384 return (1);
385 }
386
387 void
388 unexec(
389 char *outfile,
390 char *infile
391 )
392 {
393 int infd;
394 int outfd;
395 char tmpbuf[L_tmpnam];
396 char *tmpfile;
397
398 infd = open(infile, O_RDONLY, 0);
399 if (infd < 0) {
400 fatal_unexec("cannot open input file `%s'", infile);
401 exit(1);
402 }
403
404 tmpnam(tmpbuf);
405 tmpfile = rindex(tmpbuf, '/');
406 if (tmpfile == NULL) {
407 tmpfile = tmpbuf;
408 } else {
409 tmpfile++;
410 }
411 outfd = open(tmpfile, O_WRONLY|O_TRUNC|O_CREAT, 0755);
412 if (outfd < 0) {
413 close(infd);
414 fatal_unexec("cannot open tmp file `%s'", tmpfile);
415 exit(1);
416 }
417 if (!unexec_doit(infd, outfd)) {
418 close(infd);
419 close(outfd);
420 unlink(tmpfile);
421 exit(1);
422 }
423 close(infd);
424 close(outfd);
425 if (rename(tmpfile, outfile) < 0) {
426 unlink(tmpfile);
427 fatal_unexec("cannot rename `%s' to `%s'", tmpfile, outfile);
428 exit(1);
429 }
430 }