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).
5
6This file is part of GNU Emacs.
7
8GNU Emacs is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation; either version 2, or (at your option)
11any later version.
12
13GNU Emacs is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with GNU Emacs; see the file COPYING.  If not, write to
20the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21Boston, MA 02110-1301, USA.  */
22
23
24#undef __STRICT_BSD__
25
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.  */
38
39
40int malloc_cookie;
41
42/*
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.
46 *
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).
51 */
52static const unsigned VM_HIGHDATA = 0x2000000;
53
54typedef 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;
64
65
66static void
67grow(
68     struct load_command ***the_commands,
69     unsigned *the_commands_len
70     )
71{
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)));
80	}
81}
82
83
84static void
85save_command(
86	     struct load_command *command,
87	     struct load_command ***the_commands,
88	     unsigned *the_commands_len
89	     )
90{
91	struct load_command **tmp;
92
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);
97}
98
99static void
100fatal_unexec(char *format, ...)
101{
102	va_list ap;
103
104	va_start(ap, format);
105	fprintf(stderr, "unexec: ");
106	vfprintf(stderr, format, ap);
107	fprintf(stderr, "\n");
108	va_end(ap);
109}
110
111static int
112read_macho(
113	   int fd,
114	   struct mach_header *the_header,
115	   struct load_command ***the_commands,
116	   unsigned *the_commands_len
117	   )
118{
119	struct load_command command;
120	struct load_command *buf;
121	int i;
122	int size;
123
124	if (read(fd, the_header, sizeof(*the_header)) != sizeof(*the_header)) {
125		fatal_unexec("cannot read macho header");
126		return (0);
127	}
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);
133		}
134		size = command.cmdsize - sizeof(struct load_command);
135		if (size < 0) {
136		  	fatal_unexec("bogus load command size");
137			return (0);
138		}
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);
147		}
148		save_command(buf, the_commands, the_commands_len);
149	}
150	return (1);
151}
152
153static int
154filldatagap(
155	    vm_address_t start_address,
156	    vm_size_t *size,
157	    vm_address_t end_address
158	    )
159{
160	vm_address_t address;
161	vm_size_t gapsize;
162
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);
170	}
171	return (1);
172}
173
174static int
175get_data_region(
176		vm_address_t *address,
177		vm_size_t *size
178		)
179{
180	region_t region;
181	kern_return_t ret;
182	struct section *sect;
183
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;
199		}
200		if (*address != 0) {
201			if (region.address > *address + *size) {
202				if (!filldatagap(*address, size,
203						 region.address)) {
204					return (0);
205				}
206			}
207			*size += region.size;
208		} else {
209			if (region.address == sect->addr) {
210				*address = region.address;
211				*size = region.size;
212			}
213		}
214		region.address += region.size;
215	}
216	return (1);
217}
218
219static char *
220my_malloc(
221	  vm_size_t size
222	  )
223{
224	vm_address_t address;
225
226	if (vm_allocate(task_self(), &address, size, TRUE) != KERN_SUCCESS) {
227		return (NULL);
228	}
229	return ((char *)address);
230}
231
232static void
233my_free(
234	char *buf,
235	vm_size_t size
236	)
237{
238	vm_deallocate(task_self(), (vm_address_t)buf, size);
239}
240
241static int
242unexec_doit(
243	    int infd,
244	    int outfd
245	    )
246{
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;
261
262	struct segment_command *segment;
263
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
270
271	if (!read_macho(infd, &the_header, &the_commands, &the_commands_len)) {
272		return (0);
273	}
274
275
276	malloc_cookie = malloc_freezedry ();
277	if (!get_data_region(&data_address, &data_size)) {
278		return (0);
279	}
280
281
282	/*
283	 * DO NOT USE MALLOC IN THIS SECTION
284	 */
285	{
286		/*
287		 * Fix offsets
288		 */
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;
306				}
307
308				if( strcmp( segment->segname, SEG_LINKEDIT ) == 0 ) {
309					segment->vmaddr = vmaddr_growth;
310				}
311
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;
334			}
335		}
336
337		/*
338		 * Write header
339		 */
340		if (write(outfd, &the_header,
341			  sizeof(the_header)) != sizeof(the_header)) {
342			fatal_unexec("cannot write output file");
343			return (0);
344		}
345
346		/*
347		 * Write commands
348		 */
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);
355			}
356		}
357
358		/*
359		 * Write original text
360		 */
361		if (lseek(infd, the_header.sizeofcmds + sizeof(the_header),
362			  L_SET) < 0) {
363		  	fatal_unexec("cannot seek input file");
364			return (0);
365		}
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");
372		}
373		if (write(outfd, buf, size) != size) {
374			my_free(buf, size);
375			fatal_unexec("cannot write output file");
376			return (0);
377		}
378		my_free(buf, size);
379
380
381		/*
382		 * Write new data
383		 */
384		if (write(outfd, (char *)data_address,
385			  data_size) != data_size) {
386			fatal_unexec("cannot write output file");
387			return (0);
388		}
389
390	}
391
392	/*
393	 * OKAY TO USE MALLOC NOW
394	 */
395
396	/*
397	 * Write rest of file
398	 */
399	fstat(infd, &st);
400	if (lseek(infd, fdatasize, L_INCR) < 0) {
401		fatal_unexec("cannot seek input file");
402		return (0);
403	}
404	size = st.st_size - lseek(infd, 0, L_INCR);
405
406	buf = malloc(size);
407	if (read(infd, buf, size) != size) {
408		free(buf);
409		fatal_unexec("cannot read input file");
410		return (0);
411	}
412	if (write(outfd, buf, size) != size) {
413		free(buf);
414		fatal_unexec("cannot write output file");
415		return (0);
416	}
417	free(buf);
418
419#ifdef NS_TARGET
420        /*
421         * Fix up relocation entries in the data segment.
422         */
423
424	if (lseek(infd, extreloff, L_SET) < 0) {
425		fatal_unexec("cannot seek input file");
426		return (0);
427	}
428
429        for (i = 0; i < nextrel; i++)
430        {
431          long zeroval = 0;
432
433          if (read(infd, &reloc_info, sizeof (reloc_info)) != sizeof (reloc_info)) {
434            fatal_unexec("cannot read input file");
435            return (0);
436          }
437          if (reloc_info.r_address >= dataseg_vmaddr && reloc_info.r_address < dataseg_vmend)
438          {
439            if (lseek (outfd, fdatastart + reloc_info.r_address - dataseg_vmaddr, L_SET) < 0 ) {
440              fatal_unexec("cannot seek input file");
441              return (0);
442            }
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);
448		}
449                break;
450              case 1:
451		if (write(outfd, &zeroval, 2) != 2) {
452			fatal_unexec("cannot write output file");
453			return (0);
454		}
455                break;
456              case 2:
457		if (write(outfd, &zeroval, 4) != 4) {
458			fatal_unexec("cannot write output file");
459			return (0);
460		}
461                break;
462            }
463          }
464        }
465#endif
466
467	return (1);
468}
469
470void
471unexec(
472       char *outfile,
473       char *infile
474       )
475{
476	int infd;
477	int outfd;
478	char tmpbuf[L_tmpnam];
479	char *tmpfile;
480
481	infd = open(infile, O_RDONLY, 0);
482	if (infd < 0) {
483	  	fatal_unexec("cannot open input file `%s'", infile);
484		exit(1);
485	}
486
487	tmpnam(tmpbuf);
488	tmpfile = rindex(tmpbuf, '/');
489	if (tmpfile == NULL) {
490		tmpfile = tmpbuf;
491	} else {
492		tmpfile++;
493	}
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);
499	}
500	if (!unexec_doit(infd, outfd)) {
501		close(infd);
502		close(outfd);
503		unlink(tmpfile);
504		exit(1);
505	}
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);
512	}
513}
514
515/* arch-tag: 9796bdc3-c050-417a-b2f5-4cfd31032634
516   (do not change this comment) */
517