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