1 |
|
/* Dump Emacs in Mach-O format for use on Mac OS X. |
2 |
|
Copyright (C) 2001, 2002 Free Software Foundation, Inc. |
3 |
|
|
4 |
|
This file is part of GNU Emacs. |
5 |
|
|
6 |
|
GNU Emacs is free software; you can redistribute it and/or modify |
7 |
|
it under the terms of the GNU General Public License as published by |
8 |
|
the Free Software Foundation; either version 2, or (at your option) |
9 |
|
any later version. |
10 |
|
|
11 |
|
GNU Emacs is distributed in the hope that it will be useful, |
12 |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 |
|
GNU General Public License for more details. |
15 |
|
|
16 |
|
You should have received a copy of the GNU General Public License |
17 |
|
along with GNU Emacs; see the file COPYING. If not, write to |
18 |
|
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
19 |
|
Boston, MA 02111-1307, USA. */ |
20 |
|
|
21 |
|
/* Contributed by Andrew Choi (akochoi@mac.com). */ |
22 |
|
|
23 |
|
/* Documentation note. |
24 |
|
|
25 |
|
Consult the following documents/files for a description of the |
26 |
|
Mach-O format: the file loader.h, man pages for Mach-O and ld, old |
27 |
|
NEXTSTEP documents of the Mach-O format. The tool otool dumps the |
28 |
|
mach header (-h option) and the load commands (-l option) in a |
29 |
|
Mach-O file. The tool nm on Mac OS X displays the symbol table in |
30 |
|
a Mach-O file. For examples of unexec for the Mach-O format, see |
31 |
|
the file unexnext.c in the GNU Emacs distribution, the file |
32 |
|
unexdyld.c in the Darwin port of GNU Emacs 20.7, and unexdyld.c in |
33 |
|
the Darwin port of XEmacs 21.1. Also the Darwin Libc source |
34 |
|
contains the source code for malloc_freezedry and malloc_jumpstart. |
35 |
|
Read that to see what they do. This file was written completely |
36 |
|
from scratch, making use of information from the above sources. */ |
37 |
|
|
38 |
|
/* The Mac OS X implementation of unexec makes use of Darwin's `zone' |
39 |
|
memory allocator. All calls to malloc, realloc, and free in Emacs |
40 |
|
are redirected to unexec_malloc, unexec_realloc, and unexec_free in |
41 |
|
this file. When temacs is run, all memory requests are handled in |
42 |
|
the zone EmacsZone. The Darwin memory allocator library calls |
43 |
|
maintain the data structures to manage this zone. Dumping writes |
44 |
|
its contents to data segments of the executable file. When emacs |
45 |
|
is run, the loader recreates the contents of the zone in memory. |
46 |
|
However since the initialization routine of the zone memory |
47 |
|
allocator is run again, this `zone' can no longer be used as a |
48 |
|
heap. That is why emacs uses the ordinary malloc system call to |
49 |
|
allocate memory. Also, when a block of memory needs to be |
50 |
|
reallocated and the new size is larger than the old one, a new |
51 |
|
block must be obtained by malloc and the old contents copied to |
52 |
|
it. */ |
53 |
|
|
54 |
|
/* Peculiarity of the Mach-O files generated by ld in Mac OS X |
55 |
|
(possible causes of future bugs if changed). |
56 |
|
|
57 |
|
The file offset of the start of the __TEXT segment is zero. Since |
58 |
|
the Mach header and load commands are located at the beginning of a |
59 |
|
Mach-O file, copying the contents of the __TEXT segment from the |
60 |
|
input file overwrites them in the output file. Despite this, |
61 |
|
unexec works fine as written below because the segment load command |
62 |
|
for __TEXT appears, and is therefore processed, before all other |
63 |
|
load commands except the segment load command for __PAGEZERO, which |
64 |
|
remains unchanged. |
65 |
|
|
66 |
|
Although the file offset of the start of the __TEXT segment is |
67 |
|
zero, none of the sections it contains actually start there. In |
68 |
|
fact, the earliest one starts a few hundred bytes beyond the end of |
69 |
|
the last load command. The linker option -headerpad controls the |
70 |
|
minimum size of this padding. Its setting can be changed in |
71 |
|
s/darwin.h. A value of 0x300, e.g., leaves room for about 15 |
72 |
|
additional load commands for the newly created __DATA segments (at |
73 |
|
56 bytes each). Unexec fails if there is not enough room for these |
74 |
|
new segments. |
75 |
|
|
76 |
|
The __TEXT segment contains the sections __text, __cstring, |
77 |
|
__picsymbol_stub, and __const and the __DATA segment contains the |
78 |
|
sections __data, __la_symbol_ptr, __nl_symbol_ptr, __dyld, __bss, |
79 |
|
and __common. The other segments do not contain any sections. |
80 |
|
These sections are copied from the input file to the output file, |
81 |
|
except for __data, __bss, and __common, which are dumped from |
82 |
|
memory. The types of the sections __bss and __common are changed |
83 |
|
from S_ZEROFILL to S_REGULAR. Note that the number of sections and |
84 |
|
their relative order in the input and output files remain |
85 |
|
unchanged. Otherwise all n_sect fields in the nlist records in the |
86 |
|
symbol table (specified by the LC_SYMTAB load command) will have to |
87 |
|
be changed accordingly. |
88 |
|
*/ |
89 |
|
|
90 |
|
#include <stdio.h> |
91 |
|
#include <stdlib.h> |
92 |
|
#include <fcntl.h> |
93 |
|
#include <errno.h> |
94 |
|
#include <stdarg.h> |
95 |
|
#include <sys/types.h> |
96 |
|
#include <unistd.h> |
97 |
|
#include <mach/mach.h> |
98 |
|
#include <mach-o/loader.h> |
99 |
|
#include <objc/malloc.h> |
100 |
|
|
101 |
|
#define VERBOSE 1 |
102 |
|
|
103 |
|
/* Size of buffer used to copy data from the input file to the output |
104 |
|
file in function unexec_copy. */ |
105 |
|
#define UNEXEC_COPY_BUFSZ 1024 |
106 |
|
|
107 |
|
/* Regions with memory addresses above this value are assumed to be |
108 |
|
mapped to dynamically loaded libraries and will not be dumped. */ |
109 |
|
#define VM_DATA_TOP (20 * 1024 * 1024) |
110 |
|
|
111 |
|
/* Used by malloc_freezedry and malloc_jumpstart. */ |
112 |
|
int malloc_cookie; |
113 |
|
|
114 |
|
/* Type of an element on the list of regions to be dumped. */ |
115 |
|
struct region_t { |
116 |
|
vm_address_t address; |
117 |
|
vm_size_t size; |
118 |
|
vm_prot_t protection; |
119 |
|
vm_prot_t max_protection; |
120 |
|
const char *zone_name; |
121 |
|
|
122 |
|
struct region_t *next; |
123 |
|
}; |
124 |
|
|
125 |
|
/* Head and tail of the list of regions to be dumped. */ |
126 |
|
struct region_t *region_list_head; |
127 |
|
struct region_t *region_list_tail; |
128 |
|
|
129 |
|
/* Pointer to array of load commands. */ |
130 |
|
struct load_command **lca; |
131 |
|
|
132 |
|
/* Number of load commands. */ |
133 |
|
int nlc; |
134 |
|
|
135 |
|
/* The highest VM address of segments loaded by the input file. |
136 |
|
Regions with addresses beyond this are assumed to be allocated |
137 |
|
dynamically and thus require dumping. */ |
138 |
|
vm_address_t infile_lc_highest_addr; |
139 |
|
|
140 |
|
/* The lowest file offset used by the all sections in the __TEXT |
141 |
|
segments. This leaves room at the beginning of the file to store |
142 |
|
the Mach-O header. Check this value against header size to ensure |
143 |
|
the added load commands for the new __DATA segments did not |
144 |
|
overwrite any of the sections in the __TEXT segment. */ |
145 |
|
unsigned long text_seg_lowest_offset; |
146 |
|
|
147 |
|
/* Mach header. */ |
148 |
|
struct mach_header mh; |
149 |
|
|
150 |
|
/* Offset at which the next load command should be written. */ |
151 |
|
unsigned long curr_header_offset; |
152 |
|
|
153 |
|
/* Current adjustment that needs to be made to offset values because |
154 |
|
of additional data segments. */ |
155 |
|
unsigned long delta; |
156 |
|
|
157 |
|
int infd, outfd; |
158 |
|
|
159 |
|
int in_dumped_exec = 0; |
160 |
|
|
161 |
|
malloc_zone_t *emacs_zone = 0L; |
162 |
|
|
163 |
|
unsigned num_marked_regions; |
164 |
|
|
165 |
|
#include <mach/mach_error.h> |
166 |
|
#include <mach-o/nlist.h> |
167 |
|
|
168 |
|
#ifndef BIG_HEAP_SIZE |
169 |
|
#define BIG_HEAP_SIZE 0x5000000 |
170 |
|
#endif |
171 |
|
|
172 |
|
int big_heap = BIG_HEAP_SIZE; |
173 |
|
|
174 |
|
char *mach_maplimit = 0; |
175 |
|
char *mach_brkpt = 0; |
176 |
|
char *mach_mapstart = 0; |
177 |
|
|
178 |
|
|
179 |
|
/* Read n bytes from infd into memory starting at address dest. |
180 |
|
Return true if successful, false otherwise. */ |
181 |
|
static int |
182 |
|
unexec_read (void *dest, size_t n) |
183 |
|
{ |
184 |
|
return n == read (infd, dest, n); |
185 |
|
} |
186 |
|
|
187 |
|
/* Write n bytes from memory starting at address src to outfd starting |
188 |
|
at offset dest. Return true if successful, false otherwise. */ |
189 |
|
static int |
190 |
|
unexec_write (off_t dest, const void *src, size_t count) |
191 |
|
{ |
192 |
|
if (lseek (outfd, dest, SEEK_SET) != dest) |
193 |
|
return 0; |
194 |
|
|
195 |
|
return write (outfd, src, count) == count; |
196 |
|
} |
197 |
|
|
198 |
|
/* Copy n bytes from starting offset src in infd to starting offset |
199 |
|
dest in outfd. Return true if successful, false otherwise. */ |
200 |
|
static int |
201 |
|
unexec_copy (off_t dest, off_t src, ssize_t count) |
202 |
|
{ |
203 |
|
ssize_t bytes_read; |
204 |
|
|
205 |
|
char buf[UNEXEC_COPY_BUFSZ]; |
206 |
|
|
207 |
|
if (lseek (infd, src, SEEK_SET) != src) |
208 |
|
return 0; |
209 |
|
|
210 |
|
if (lseek (outfd, dest, SEEK_SET) != dest) |
211 |
|
return 0; |
212 |
|
|
213 |
|
while (count > 0) |
214 |
|
{ |
215 |
|
bytes_read = read (infd, buf, UNEXEC_COPY_BUFSZ); |
216 |
|
if (bytes_read <= 0) |
217 |
|
return 0; |
218 |
|
if (write (outfd, buf, bytes_read) != bytes_read) |
219 |
|
return 0; |
220 |
|
count -= bytes_read; |
221 |
|
} |
222 |
|
|
223 |
|
return 1; |
224 |
|
} |
225 |
|
|
226 |
|
/* Debugging and informational messages routines. */ |
227 |
|
|
228 |
|
static void |
229 |
|
unexec_error (char *format, ...) |
230 |
|
{ |
231 |
|
va_list ap; |
232 |
|
|
233 |
|
va_start (ap, format); |
234 |
|
fprintf (stderr, "unexec: "); |
235 |
|
vfprintf (stderr, format, ap); |
236 |
|
fprintf (stderr, "\n"); |
237 |
|
va_end (ap); |
238 |
|
exit (1); |
239 |
|
} |
240 |
|
|
241 |
|
static void |
242 |
|
print_prot (vm_prot_t prot) |
243 |
|
{ |
244 |
|
if (prot == VM_PROT_NONE) |
245 |
|
printf ("none"); |
246 |
|
else |
247 |
|
{ |
248 |
|
putchar (prot & VM_PROT_READ ? 'r' : ' '); |
249 |
|
putchar (prot & VM_PROT_WRITE ? 'w' : ' '); |
250 |
|
putchar (prot & VM_PROT_EXECUTE ? 'x' : ' '); |
251 |
|
putchar (' '); |
252 |
|
} |
253 |
|
} |
254 |
|
|
255 |
|
static void |
256 |
|
print_region (vm_address_t address, vm_size_t size, vm_prot_t prot, |
257 |
|
vm_prot_t max_prot, const char *zone_name) |
258 |
|
{ |
259 |
|
printf ("%#10x %#10x ", address, size); |
260 |
|
print_prot (prot); |
261 |
|
putchar (' '); |
262 |
|
print_prot (max_prot); |
263 |
|
putchar (' '); |
264 |
|
printf (zone_name ? zone_name : "n/a"); |
265 |
|
putchar ('\n'); |
266 |
|
} |
267 |
|
|
268 |
|
static void |
269 |
|
print_region_list () |
270 |
|
{ |
271 |
|
struct region_t *r; |
272 |
|
|
273 |
|
printf (" address size prot maxp zone_name\n"); |
274 |
|
|
275 |
|
for (r = region_list_head; r; r = r->next) |
276 |
|
print_region (r->address, r->size, r->protection, r->max_protection, r->zone_name); |
277 |
|
} |
278 |
|
|
279 |
|
void |
280 |
|
print_regions () |
281 |
|
{ |
282 |
|
task_t target_task = mach_task_self (); |
283 |
|
vm_address_t address = (vm_address_t) 0; |
284 |
|
vm_size_t size; |
285 |
|
struct vm_region_basic_info info; |
286 |
|
mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT; |
287 |
|
mach_port_t object_name; |
288 |
|
malloc_zone_t *zone; |
289 |
|
|
290 |
|
printf (" address size prot maxp zone_name\n"); |
291 |
|
|
292 |
|
while (vm_region (target_task, &address, &size, VM_REGION_BASIC_INFO, |
293 |
|
(vm_region_info_t) &info, &info_count, &object_name) |
294 |
|
== KERN_SUCCESS && info_count == VM_REGION_BASIC_INFO_COUNT) |
295 |
|
{ |
296 |
|
zone = malloc_zone_from_ptr ((void *) address); |
297 |
|
|
298 |
|
print_region (address, size, info.protection, info.max_protection, zone ? zone->zone_name : "n/a"); |
299 |
|
|
300 |
|
if (object_name != MACH_PORT_NULL) |
301 |
|
mach_port_deallocate (target_task, object_name); |
302 |
|
|
303 |
|
address += size; |
304 |
|
} |
305 |
|
} |
306 |
|
|
307 |
|
/* Build the list of regions that need to be dumped. Regions with |
308 |
|
addresses above VM_DATA_TOP are omitted. Adjacent regions with |
309 |
|
identical protection are merged. Note that non-writable regions |
310 |
|
cannot be omitted because they some regions created at run time are |
311 |
|
read-only. */ |
312 |
|
static void |
313 |
|
build_region_list () |
314 |
|
{ |
315 |
|
task_t target_task = mach_task_self (); |
316 |
|
vm_address_t address = (vm_address_t) 0; |
317 |
|
vm_size_t size; |
318 |
|
struct vm_region_basic_info info; |
319 |
|
mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT; |
320 |
|
mach_port_t object_name; |
321 |
|
struct region_t *r; |
322 |
|
malloc_zone_t *zone; |
323 |
|
const char *zone_name; |
324 |
|
|
325 |
|
#if VERBOSE |
326 |
|
printf ("--- List of All Regions ---\n"); |
327 |
|
printf (" address size prot maxp zone_name\n"); |
328 |
|
#endif |
329 |
|
|
330 |
|
while (vm_region (target_task, &address, &size, VM_REGION_BASIC_INFO, |
331 |
|
(vm_region_info_t) &info, &info_count, &object_name) |
332 |
|
== KERN_SUCCESS && info_count == VM_REGION_BASIC_INFO_COUNT) |
333 |
|
{ |
334 |
|
/* Done when we reach addresses of shared libraries, which are |
335 |
|
loaded in high memory. */ |
336 |
|
if (address >= VM_DATA_TOP) |
337 |
|
break; |
338 |
|
|
339 |
|
zone = malloc_zone_from_ptr ((void *) address); |
340 |
|
zone_name = zone ? (zone->zone_name ? zone->zone_name : "n/a") : "n/a"; |
341 |
|
|
342 |
|
#if VERBOSE |
343 |
|
print_region (address, size, info.protection, info.max_protection, zone_name); |
344 |
|
#endif |
345 |
|
|
346 |
|
/* If a region immediately follows the previous one (the one |
347 |
|
most recently added to the list) and has identical |
348 |
|
protection, merge it with the latter. Otherwise create a |
349 |
|
new list element for it. */ |
350 |
|
|
351 |
|
if (region_list_tail |
352 |
|
&& info.protection == region_list_tail->protection |
353 |
|
&& info.max_protection == region_list_tail->max_protection |
354 |
|
&& region_list_tail->address + region_list_tail->size == address |
355 |
|
&& (!zone || zone_name == region_list_tail->zone_name)) |
356 |
|
{ |
357 |
|
region_list_tail->size += size; |
358 |
|
} |
359 |
|
else |
360 |
|
{ |
361 |
|
r = (struct region_t *) malloc (sizeof (struct region_t)); |
362 |
|
|
363 |
|
if (!r) |
364 |
|
unexec_error ("cannot allocate region structure"); |
365 |
|
|
366 |
|
r->address = address; |
367 |
|
r->size = size; |
368 |
|
r->protection = info.protection; |
369 |
|
r->max_protection = info.max_protection; |
370 |
|
r->zone_name = zone_name; |
371 |
|
|
372 |
|
r->next = 0; |
373 |
|
if (region_list_head == 0) |
374 |
|
{ |
375 |
|
region_list_head = r; |
376 |
|
region_list_tail = r; |
377 |
|
} |
378 |
|
else |
379 |
|
{ |
380 |
|
region_list_tail->next = r; |
381 |
|
region_list_tail = r; |
382 |
|
} |
383 |
|
|
384 |
|
/* Deallocate (unused) object name returned by |
385 |
|
vm_region. */ |
386 |
|
if (object_name != MACH_PORT_NULL) |
387 |
|
mach_port_deallocate (target_task, object_name); |
388 |
|
} |
389 |
|
|
390 |
|
address += size; |
391 |
|
} |
392 |
|
|
393 |
|
printf ("--- List of Regions to be Dumped ---\n"); |
394 |
|
print_region_list (); |
395 |
|
} |
396 |
|
|
397 |
|
|
398 |
|
#define MAX_UNEXEC_REGIONS 30 |
399 |
|
|
400 |
|
int num_unexec_regions; |
401 |
|
vm_range_t unexec_regions[MAX_UNEXEC_REGIONS]; |
402 |
|
|
403 |
|
static void |
404 |
|
unexec_regions_recorder (task_t task, void *rr, unsigned type, |
405 |
|
vm_range_t *ranges, unsigned num) |
406 |
|
{ |
407 |
|
while (num && num_unexec_regions < MAX_UNEXEC_REGIONS) |
408 |
|
{ |
409 |
|
unexec_regions[num_unexec_regions++] = *ranges; |
410 |
|
printf ("%#8x (sz: %#8x)\n", ranges->address, ranges->size); |
411 |
|
ranges++; num--; |
412 |
|
} |
413 |
|
if (num_unexec_regions == MAX_UNEXEC_REGIONS) |
414 |
|
fprintf (stderr, "malloc_freezedry_recorder: too many regions\n"); |
415 |
|
} |
416 |
|
|
417 |
|
static kern_return_t |
418 |
|
unexec_reader (task_t task, vm_address_t address, vm_size_t size, void **ptr) |
419 |
|
{ |
420 |
|
*ptr = (void *) address; |
421 |
|
return KERN_SUCCESS; |
422 |
|
} |
423 |
|
|
424 |
|
void |
425 |
|
find_emacs_zone_regions () |
426 |
|
{ |
427 |
|
num_unexec_regions = 0; |
428 |
|
|
429 |
|
emacs_zone->introspect->enumerator (mach_task_self(), 0, |
430 |
|
MALLOC_PTR_REGION_RANGE_TYPE |
431 |
|
| MALLOC_ADMIN_REGION_RANGE_TYPE, |
432 |
|
(vm_address_t) emacs_zone, |
433 |
|
unexec_reader, |
434 |
|
unexec_regions_recorder); |
435 |
|
} |
436 |
|
|
437 |
|
|
438 |
|
/* More informational messages routines. */ |
439 |
|
|
440 |
|
static void |
441 |
|
print_load_command_name (int lc) |
442 |
|
{ |
443 |
|
switch (lc) |
444 |
|
{ |
445 |
|
case LC_SEGMENT: |
446 |
|
printf ("LC_SEGMENT "); |
447 |
|
break; |
448 |
|
case LC_LOAD_DYLINKER: |
449 |
|
printf ("LC_LOAD_DYLINKER "); |
450 |
|
break; |
451 |
|
case LC_LOAD_DYLIB: |
452 |
|
printf ("LC_LOAD_DYLIB "); |
453 |
|
break; |
454 |
|
case LC_SYMTAB: |
455 |
|
printf ("LC_SYMTAB "); |
456 |
|
break; |
457 |
|
case LC_DYSYMTAB: |
458 |
|
printf ("LC_DYSYMTAB "); |
459 |
|
break; |
460 |
|
case LC_UNIXTHREAD: |
461 |
|
printf ("LC_UNIXTHREAD "); |
462 |
|
break; |
463 |
|
case LC_PREBOUND_DYLIB: |
464 |
|
printf ("LC_PREBOUND_DYLIB"); |
465 |
|
break; |
466 |
|
case LC_TWOLEVEL_HINTS: |
467 |
|
printf ("LC_TWOLEVEL_HINTS"); |
468 |
|
break; |
469 |
|
default: |
470 |
|
printf ("unknown "); |
471 |
|
} |
472 |
|
} |
473 |
|
|
474 |
|
static void |
475 |
|
print_load_command (struct load_command *lc) |
476 |
|
{ |
477 |
|
print_load_command_name (lc->cmd); |
478 |
|
printf ("%10lx", lc->cmdsize); |
479 |
|
|
480 |
|
if (lc->cmd == LC_SEGMENT) |
481 |
|
{ |
482 |
|
struct segment_command *scp; |
483 |
|
struct section *sectp; |
484 |
|
int j; |
485 |
|
|
486 |
|
scp = (struct segment_command *) lc; |
487 |
|
printf (" %-16.16s %#10lx %#8lx\n", |
488 |
|
scp->segname, scp->vmaddr, scp->vmsize); |
489 |
|
|
490 |
|
sectp = (struct section *) (scp + 1); |
491 |
|
for (j = 0; j < scp->nsects; j++) |
492 |
|
{ |
493 |
|
printf (" %-16.16s %#10lx %#8lx\n", |
494 |
|
sectp->sectname, sectp->addr, sectp->size); |
495 |
|
sectp++; |
496 |
|
} |
497 |
|
} |
498 |
|
else |
499 |
|
printf ("\n"); |
500 |
|
} |
501 |
|
|
502 |
|
/* Read header and load commands from input file. Store the latter in |
503 |
|
the global array lca. Store the total number of load commands in |
504 |
|
global variable nlc. */ |
505 |
|
static void |
506 |
|
read_load_commands () |
507 |
|
{ |
508 |
|
int i; |
509 |
|
|
510 |
|
if (!unexec_read (&mh, sizeof (struct mach_header))) |
511 |
|
unexec_error ("cannot read mach-o header"); |
512 |
|
|
513 |
|
if (mh.magic != MH_MAGIC) |
514 |
|
unexec_error ("input file not in Mach-O format"); |
515 |
|
|
516 |
|
if (mh.filetype != MH_EXECUTE) |
517 |
|
unexec_error ("input Mach-O file is not an executable object file"); |
518 |
|
|
519 |
|
#if VERBOSE |
520 |
|
printf ("--- Header Information ---\n"); |
521 |
|
printf ("Magic = 0x%08lx\n", mh.magic); |
522 |
|
printf ("CPUType = %d\n", mh.cputype); |
523 |
|
printf ("CPUSubType = %d\n", mh.cpusubtype); |
524 |
|
printf ("FileType = 0x%lx\n", mh.filetype); |
525 |
|
printf ("NCmds = %ld\n", mh.ncmds); |
526 |
|
printf ("SizeOfCmds = %ld\n", mh.sizeofcmds); |
527 |
|
printf ("Flags = 0x%08lx\n", mh.flags); |
528 |
|
#endif |
529 |
|
|
530 |
|
nlc = mh.ncmds; |
531 |
|
lca = (struct load_command **) malloc (nlc * sizeof (struct load_command *)); |
532 |
|
|
533 |
|
for (i = 0; i < nlc; i++) |
534 |
|
{ |
535 |
|
struct load_command lc; |
536 |
|
/* Load commands are variable-size: so read the command type and |
537 |
|
size first and then read the rest. */ |
538 |
|
if (!unexec_read (&lc, sizeof (struct load_command))) |
539 |
|
unexec_error ("cannot read load command"); |
540 |
|
lca[i] = (struct load_command *) malloc (lc.cmdsize); |
541 |
|
memcpy (lca[i], &lc, sizeof (struct load_command)); |
542 |
|
if (!unexec_read (lca[i] + 1, lc.cmdsize - sizeof (struct load_command))) |
543 |
|
unexec_error ("cannot read content of load command"); |
544 |
|
if (lc.cmd == LC_SEGMENT) |
545 |
|
{ |
546 |
|
struct segment_command *scp = (struct segment_command *) lca[i]; |
547 |
|
|
548 |
|
if (scp->vmaddr + scp->vmsize > infile_lc_highest_addr) |
549 |
|
infile_lc_highest_addr = scp->vmaddr + scp->vmsize; |
550 |
|
|
551 |
|
if (strncmp (scp->segname, SEG_TEXT, 16) == 0) |
552 |
|
{ |
553 |
|
struct section *sectp = (struct section *) (scp + 1); |
554 |
|
int j; |
555 |
|
|
556 |
|
for (j = 0; j < scp->nsects; j++) |
557 |
|
if (sectp->offset < text_seg_lowest_offset) |
558 |
|
text_seg_lowest_offset = sectp->offset; |
559 |
|
} |
560 |
|
} |
561 |
|
} |
562 |
|
|
563 |
|
printf ("Highest address of load commands in input file: %#8x\n", |
564 |
|
infile_lc_highest_addr); |
565 |
|
|
566 |
|
printf ("Lowest offset of all sections in __TEXT segment: %#8lx\n", |
567 |
|
text_seg_lowest_offset); |
568 |
|
|
569 |
|
printf ("--- List of Load Commands in Input File ---\n"); |
570 |
|
printf ("# cmd cmdsize name address size\n"); |
571 |
|
|
572 |
|
for (i = 0; i < nlc; i++) |
573 |
|
{ |
574 |
|
printf ("%1d ", i); |
575 |
|
print_load_command (lca[i]); |
576 |
|
} |
577 |
|
} |
578 |
|
|
579 |
|
/* Copy a LC_SEGMENT load command other than the __DATA segment from |
580 |
|
the input file to the output file, adjusting the file offset of the |
581 |
|
segment and the file offsets of sections contained in it. */ |
582 |
|
static void |
583 |
|
copy_segment (struct load_command *lc) |
584 |
|
{ |
585 |
|
struct segment_command *scp = (struct segment_command *) lc; |
586 |
|
unsigned long old_fileoff = scp->fileoff; |
587 |
|
struct section *sectp; |
588 |
|
int j; |
589 |
|
|
590 |
|
scp->fileoff += delta; |
591 |
|
|
592 |
|
sectp = (struct section *) (scp + 1); |
593 |
|
for (j = 0; j < scp->nsects; j++) |
594 |
|
{ |
595 |
|
sectp->offset += delta; |
596 |
|
sectp++; |
597 |
|
} |
598 |
|
|
599 |
|
printf ("Writing segment %-16.16s at %#8lx - %#8lx (sz: %#8lx)\n", |
600 |
|
scp->segname, scp->fileoff, scp->fileoff + scp->filesize, |
601 |
|
scp->filesize); |
602 |
|
|
603 |
|
if (!unexec_copy (scp->fileoff, old_fileoff, scp->filesize)) |
604 |
|
unexec_error ("cannot copy segment from input to output file"); |
605 |
|
if (!unexec_write (curr_header_offset, lc, lc->cmdsize)) |
606 |
|
unexec_error ("cannot write load command to header"); |
607 |
|
|
608 |
|
curr_header_offset += lc->cmdsize; |
609 |
|
} |
610 |
|
|
611 |
|
/* Copy a LC_SEGMENT load command for the __DATA segment in the input |
612 |
|
file to the output file. We assume that only one such segment load |
613 |
|
command exists in the input file and it contains the sections |
614 |
|
__data, __bss, __common, __la_symbol_ptr, __nl_symbol_ptr, and |
615 |
|
__dyld. The first three of these should be dumped from memory and |
616 |
|
the rest should be copied from the input file. Note that the |
617 |
|
sections __bss and __common contain no data in the input file |
618 |
|
because their flag fields have the value S_ZEROFILL. Dumping these |
619 |
|
from memory makes it necessary to adjust file offset fields in |
620 |
|
subsequently dumped load commands. Then, create new __DATA segment |
621 |
|
load commands for regions on the region list other than the one |
622 |
|
corresponding to the __DATA segment in the input file. */ |
623 |
|
static void |
624 |
|
copy_data_segment (struct load_command *lc) |
625 |
|
{ |
626 |
|
struct segment_command *scp = (struct segment_command *) lc; |
627 |
|
struct section *sectp; |
628 |
|
int j; |
629 |
|
unsigned long header_offset, file_offset, old_file_offset; |
630 |
|
/*struct region_t *r;*/ |
631 |
|
|
632 |
|
if (delta != 0) { |
633 |
|
mh.ncmds--; |
634 |
|
return; |
635 |
|
} |
636 |
|
|
637 |
|
printf ("Writing segment %-16.16s at %#8lx - %#8lx (sz: %#8lx)\n", |
638 |
|
scp->segname, scp->fileoff, scp->fileoff + scp->filesize, |
639 |
|
scp->filesize); |
640 |
|
|
641 |
|
/* Offsets in the output file for writing the next section structure |
642 |
|
and segment data block, respectively. */ |
643 |
|
header_offset = curr_header_offset + sizeof (struct segment_command); |
644 |
|
|
645 |
|
sectp = (struct section *) (scp + 1); |
646 |
|
for (j = 0; j < scp->nsects; j++) |
647 |
|
{ |
648 |
|
old_file_offset = sectp->offset; |
649 |
|
sectp->offset = sectp->addr - scp->vmaddr + scp->fileoff; |
650 |
|
/* The __data section is dumped from memory. The __bss and |
651 |
|
__common sections are also dumped from memory but their flag |
652 |
|
fields require changing (from S_ZEROFILL to S_REGULAR). The |
653 |
|
other three kinds of sections are just copied from the input |
654 |
|
file. */ |
655 |
|
if (strncmp (sectp->sectname, SECT_DATA, 16) == 0) |
656 |
|
{ |
657 |
|
if (!unexec_write (sectp->offset, (void *) sectp->addr, sectp->size)) |
658 |
|
unexec_error ("cannot write section %s", SECT_DATA); |
659 |
|
if (!unexec_write (header_offset, sectp, sizeof (struct section))) |
660 |
|
unexec_error ("cannot write section %s's header", SECT_DATA); |
661 |
|
} |
662 |
|
else if (strncmp (sectp->sectname, SECT_BSS, 16) == 0 |
663 |
|
|| strncmp (sectp->sectname, SECT_COMMON, 16) == 0) |
664 |
|
{ |
665 |
|
sectp->flags = S_REGULAR; |
666 |
|
if (!unexec_write (sectp->offset, (void *) sectp->addr, sectp->size)) |
667 |
|
unexec_error ("cannot write section %s", SECT_DATA); |
668 |
|
if (!unexec_write (header_offset, sectp, sizeof (struct section))) |
669 |
|
unexec_error ("cannot write section %s's header", SECT_DATA); |
670 |
|
} |
671 |
|
else if (strncmp (sectp->sectname, "__la_symbol_ptr", 16) == 0 |
672 |
|
|| strncmp (sectp->sectname, "__nl_symbol_ptr", 16) == 0 |
673 |
|
|| strncmp (sectp->sectname, "__dyld", 16) == 0 |
674 |
|
|| strncmp (sectp->sectname, "__const", 16) == 0 |
675 |
|
|| strncmp (sectp->sectname, "__cfstring", 16) == 0) |
676 |
|
{ |
677 |
|
if (!unexec_copy (sectp->offset, old_file_offset, sectp->size)) |
678 |
|
unexec_error ("cannot copy section %s", sectp->sectname); |
679 |
|
if (!unexec_write (header_offset, sectp, sizeof (struct section))) |
680 |
|
unexec_error ("cannot write section %s's header", sectp->sectname); |
681 |
|
} |
682 |
|
else |
683 |
|
unexec_error ("unrecognized section name in __DATA segment"); |
684 |
|
|
685 |
|
printf (" section %-16.16s at %#8lx - %#8lx (sz: %#8lx)\n", |
686 |
|
sectp->sectname, sectp->offset, sectp->offset + sectp->size, |
687 |
|
sectp->size); |
688 |
|
|
689 |
|
header_offset += sizeof (struct section); |
690 |
|
sectp++; |
691 |
|
} |
692 |
|
|
693 |
|
/* The new filesize of the segment is set to its vmsize because data |
694 |
|
blocks for segments must start at region boundaries. Note that |
695 |
|
this may leave unused locations at the end of the segment data |
696 |
|
block because the total of the sizes of all sections in the |
697 |
|
segment is generally smaller than vmsize. */ |
698 |
|
delta = scp->vmsize - scp->filesize; |
699 |
|
scp->filesize = scp->vmsize; |
700 |
|
if (!unexec_write (curr_header_offset, scp, sizeof (struct segment_command))) |
701 |
|
unexec_error ("cannot write header of __DATA segment"); |
702 |
|
curr_header_offset += lc->cmdsize; |
703 |
|
|
704 |
|
/* Create new __DATA segment load commands for regions on the region |
705 |
|
list that do not corresponding to any segment load commands in |
706 |
|
the input file. |
707 |
|
*/ |
708 |
|
file_offset = scp->fileoff + scp->filesize; |
709 |
|
for (j = 0; j < num_unexec_regions; j++) |
710 |
|
{ |
711 |
|
struct segment_command sc; |
712 |
|
struct section section; |
713 |
|
|
714 |
|
extern char *mach_mapstart; |
715 |
|
extern char *mach_brkpt; |
716 |
|
extern char *mach_maplimit; |
717 |
|
|
718 |
|
sc.cmd = LC_SEGMENT; |
719 |
|
sc.cmdsize = sizeof (struct segment_command) + sizeof(struct section); |
720 |
|
strncpy (sc.segname, SEG_DATA, 16); |
721 |
|
sc.vmaddr = unexec_regions[j].address; |
722 |
|
sc.vmsize = unexec_regions[j].size; |
723 |
|
sc.fileoff = file_offset; |
724 |
|
sc.filesize = unexec_regions[j].size; |
725 |
|
sc.maxprot = VM_PROT_READ | VM_PROT_WRITE; |
726 |
|
sc.initprot = VM_PROT_READ | VM_PROT_WRITE; |
727 |
|
sc.nsects = 1; |
728 |
|
sc.flags = 0; |
729 |
|
|
730 |
|
if (sc.vmaddr == (unsigned long) mach_mapstart && sc.vmsize == (mach_maplimit - mach_mapstart)) { |
731 |
|
#ifdef VERBOSE |
732 |
|
printf ("old sc.filesize = %lx (heap size)\n",sc.filesize); |
733 |
|
#endif |
734 |
|
sc.filesize -= (mach_maplimit - mach_brkpt); |
735 |
|
#ifdef VERBOSE |
736 |
|
printf ("new sc.filesize = %lx (actual heap size)\n",sc.filesize); |
737 |
|
#endif |
738 |
|
} |
739 |
|
|
740 |
|
strncpy (section.sectname,SECT_DATA,sizeof(section.sectname)); |
741 |
|
strncpy (section.segname,SEG_DATA,sizeof(section.segname)); |
742 |
|
section.addr = unexec_regions[j].address; |
743 |
|
section.size = sc.filesize /* unexec_regions[j].size */; |
744 |
|
section.offset = file_offset; |
745 |
|
section.align = 4; |
746 |
|
section.reloff = 0; |
747 |
|
section.nreloc = 0; |
748 |
|
section.flags = S_ATTR_PURE_INSTRUCTIONS | S_REGULAR; |
749 |
|
section.reserved1 = 0; |
750 |
|
section.reserved2 = 0; |
751 |
|
|
752 |
|
printf ("Writing segment %-16.16s at %#8lx - %#8lx (sz: %#8lx)\n", |
753 |
|
sc.segname, sc.fileoff, sc.fileoff + sc.filesize, sc.filesize); |
754 |
|
|
755 |
|
if (!unexec_write (sc.fileoff, (void *) sc.vmaddr, sc.filesize)) |
756 |
|
unexec_error ("cannot write new __DATA segment"); |
757 |
|
delta += sc.filesize; |
758 |
|
file_offset += sc.filesize; |
759 |
|
|
760 |
|
if (!unexec_write (curr_header_offset, &sc, sizeof(sc))) |
761 |
|
unexec_error ("cannot write new __DATA segment's header"); |
762 |
|
curr_header_offset += sizeof(sc); |
763 |
|
|
764 |
|
if (!unexec_write (curr_header_offset, §ion, sizeof(section))) |
765 |
|
unexec_error ("cannot write new __data section's header"); |
766 |
|
curr_header_offset += sizeof(section); |
767 |
|
|
768 |
|
mh.ncmds++; |
769 |
|
} |
770 |
|
} |
771 |
|
|
772 |
|
/* Copy a LC_SYMTAB load command from the input file to the output |
773 |
|
file, adjusting the file offset fields. */ |
774 |
|
static void |
775 |
|
copy_symtab (struct load_command *lc) |
776 |
|
{ |
777 |
|
struct symtab_command *stp = (struct symtab_command *) lc; |
778 |
|
|
779 |
|
stp->symoff += delta; |
780 |
|
stp->stroff += delta; |
781 |
|
|
782 |
|
printf ("Writing LC_SYMTAB command\n"); |
783 |
|
|
784 |
|
if (!unexec_write (curr_header_offset, lc, lc->cmdsize)) |
785 |
|
unexec_error ("cannot write symtab command to header"); |
786 |
|
|
787 |
|
curr_header_offset += lc->cmdsize; |
788 |
|
} |
789 |
|
|
790 |
|
/* Copy a LC_DYSYMTAB load command from the input file to the output |
791 |
|
file, adjusting the file offset fields. */ |
792 |
|
static void |
793 |
|
copy_dysymtab (struct load_command *lc) |
794 |
|
{ |
795 |
|
struct dysymtab_command *dstp = (struct dysymtab_command *) lc; |
796 |
|
|
797 |
|
/* If Mach-O executable is not prebound, relocation entries need |
798 |
|
fixing up. This is not supported currently. */ |
799 |
|
if (!(mh.flags & MH_PREBOUND) && (dstp->nextrel != 0 || dstp->nlocrel != 0)) |
800 |
|
unexec_error ("cannot handle LC_DYSYMTAB with relocation entries"); |
801 |
|
|
802 |
|
if (dstp->nextrel > 0) { |
803 |
|
dstp->extreloff += delta; |
804 |
|
} |
805 |
|
|
806 |
|
if (dstp->nlocrel > 0) { |
807 |
|
dstp->locreloff += delta; |
808 |
|
} |
809 |
|
|
810 |
|
if (dstp->nindirectsyms > 0) |
811 |
|
dstp->indirectsymoff += delta; |
812 |
|
|
813 |
|
printf ("Writing LC_DYSYMTAB command\n"); |
814 |
|
|
815 |
|
if (!unexec_write (curr_header_offset, lc, lc->cmdsize)) |
816 |
|
unexec_error ("cannot write symtab command to header"); |
817 |
|
|
818 |
|
curr_header_offset += lc->cmdsize; |
819 |
|
} |
820 |
|
|
821 |
|
/* Copy a LC_TWOLEVEL_HINTS load command from the input file to the output |
822 |
|
file, adjusting the file offset fields. */ |
823 |
|
static void |
824 |
|
copy_twolevelhints (struct load_command *lc) |
825 |
|
{ |
826 |
|
struct twolevel_hints_command *tlhp = (struct twolevel_hints_command *) lc; |
827 |
|
|
828 |
|
if (tlhp->nhints > 0) { |
829 |
|
tlhp->offset += delta; |
830 |
|
} |
831 |
|
|
832 |
|
printf ("Writing LC_TWOLEVEL_HINTS command\n"); |
833 |
|
|
834 |
|
if (!unexec_write (curr_header_offset, lc, lc->cmdsize)) |
835 |
|
unexec_error ("cannot write two level hint command to header"); |
836 |
|
|
837 |
|
curr_header_offset += lc->cmdsize; |
838 |
|
} |
839 |
|
|
840 |
|
/* Copy other kinds of load commands from the input file to the output |
841 |
|
file, ones that do not require adjustments of file offsets. */ |
842 |
|
static void |
843 |
|
copy_other (struct load_command *lc) |
844 |
|
{ |
845 |
|
printf ("Writing "); |
846 |
|
print_load_command_name (lc->cmd); |
847 |
|
printf (" command\n"); |
848 |
|
|
849 |
|
if (!unexec_write (curr_header_offset, lc, lc->cmdsize)) |
850 |
|
unexec_error ("cannot write symtab command to header"); |
851 |
|
|
852 |
|
curr_header_offset += lc->cmdsize; |
853 |
|
} |
854 |
|
|
855 |
|
/* Loop through all load commands and dump them. Then write the Mach |
856 |
|
header. */ |
857 |
|
static void |
858 |
|
dump_it () |
859 |
|
{ |
860 |
|
int i; |
861 |
|
|
862 |
|
printf ("--- Load Commands written to Output File ---\n"); |
863 |
|
|
864 |
|
curr_header_offset = sizeof (struct mach_header); |
865 |
|
delta = 0; |
866 |
|
|
867 |
|
for (i = 0; i < nlc; i++) |
868 |
|
switch (lca[i]->cmd) |
869 |
|
{ |
870 |
|
case LC_SEGMENT: |
871 |
|
{ |
872 |
|
struct segment_command *scp = (struct segment_command *) lca[i]; |
873 |
|
if (strncmp (scp->segname, SEG_DATA, 16) == 0) |
874 |
|
{ |
875 |
|
copy_data_segment (lca[i]); |
876 |
|
} |
877 |
|
else |
878 |
|
{ |
879 |
|
copy_segment (lca[i]); |
880 |
|
} |
881 |
|
} |
882 |
|
break; |
883 |
|
case LC_SYMTAB: |
884 |
|
copy_symtab (lca[i]); |
885 |
|
break; |
886 |
|
case LC_DYSYMTAB: |
887 |
|
copy_dysymtab (lca[i]); |
888 |
|
break; |
889 |
|
case LC_TWOLEVEL_HINTS: |
890 |
|
copy_twolevelhints (lca[i]); |
891 |
|
break; |
892 |
|
default: |
893 |
|
copy_other (lca[i]); |
894 |
|
break; |
895 |
|
} |
896 |
|
|
897 |
|
if (curr_header_offset > text_seg_lowest_offset) |
898 |
|
unexec_error ("not enough room for load commands for new __DATA segments"); |
899 |
|
|
900 |
|
printf ("%d unused bytes follow Mach-O header\n", |
901 |
|
(int) (text_seg_lowest_offset - curr_header_offset)); |
902 |
|
|
903 |
|
mh.sizeofcmds = curr_header_offset - sizeof (struct mach_header); |
904 |
|
if (!unexec_write (0, &mh, sizeof (struct mach_header))) |
905 |
|
unexec_error ("cannot write final header contents"); |
906 |
|
} |
907 |
|
|
908 |
|
#define MAX_MARKED_REGIONS 1024 |
909 |
|
|
910 |
|
vm_range_t marked_regions[MAX_MARKED_REGIONS]; |
911 |
|
|
912 |
|
void mark_region (unsigned long address, unsigned long size) |
913 |
|
{ |
914 |
|
if (num_marked_regions < MAX_MARKED_REGIONS) |
915 |
|
{ |
916 |
|
marked_regions[num_marked_regions].address = address; |
917 |
|
marked_regions[num_marked_regions].size = size; |
918 |
|
|
919 |
|
num_marked_regions++; |
920 |
|
} |
921 |
|
else { |
922 |
|
printf ("warning: too many marked regions\n"); |
923 |
|
} |
924 |
|
} |
925 |
|
|
926 |
|
void add_marked_regions () |
927 |
|
{ |
928 |
|
unsigned n; |
929 |
|
|
930 |
|
num_unexec_regions = 0; |
931 |
|
|
932 |
|
for (n=0 ; n < num_marked_regions ; n++) { |
933 |
|
printf ("marked regions %#8x (sz: %#8x)\n", marked_regions[n].address, marked_regions[n].size); |
934 |
|
unexec_regions[num_unexec_regions++] = marked_regions[n]; |
935 |
|
} |
936 |
|
} |
937 |
|
|
938 |
|
/* Take a snapshot of Emacs and make a Mach-O format executable file |
939 |
|
from it. The file names of the output and input files are outfile |
940 |
|
and infile, respectively. The three other parameters are |
941 |
|
ignored. */ |
942 |
|
void |
943 |
|
unexec (char *outfile, char *infile, void *start_data, void *start_bss, |
944 |
|
void *entry_address) |
945 |
|
{ |
946 |
|
infd = open (infile, O_RDONLY, 0); |
947 |
|
if (infd < 0) |
948 |
|
{ |
949 |
|
unexec_error ("cannot open input file `%s'", infile); |
950 |
|
} |
951 |
|
|
952 |
|
outfd = open (outfile, O_WRONLY | O_TRUNC | O_CREAT, 0755); |
953 |
|
if (outfd < 0) |
954 |
|
{ |
955 |
|
close (infd); |
956 |
|
unexec_error ("cannot open output file `%s'", outfile); |
957 |
|
} |
958 |
|
|
959 |
|
region_list_head = 0L; |
960 |
|
region_list_tail = 0L; |
961 |
|
|
962 |
|
infile_lc_highest_addr = 0; |
963 |
|
text_seg_lowest_offset = 0x10000000; |
964 |
|
|
965 |
|
curr_header_offset = sizeof (struct mach_header); |
966 |
|
delta = 0; |
967 |
|
|
968 |
|
build_region_list (); |
969 |
|
read_load_commands (); |
970 |
|
|
971 |
|
/*find_emacs_zone_regions ();*/ |
972 |
|
add_marked_regions (); |
973 |
|
|
974 |
|
in_dumped_exec = 1; |
975 |
|
|
976 |
|
dump_it (); |
977 |
|
|
978 |
|
close (outfd); |
979 |
|
} |
980 |
|
|
981 |
|
void |
982 |
|
unexec_init_emacs_zone () |
983 |
|
{ |
984 |
|
if (!emacs_zone) { |
985 |
|
emacs_zone = malloc_create_zone (0, 0); |
986 |
|
malloc_set_zone_name (emacs_zone, "EmacsZone"); |
987 |
|
} |
988 |
|
else { |
989 |
|
if (!malloc_zone_check (emacs_zone)) |
990 |
|
unexec_error ("emacs_zone is smashed\n"); |
991 |
|
malloc_zone_register (emacs_zone); |
992 |
|
} |
993 |
|
} |
994 |
|
|
995 |
|
static size_t stub_size (malloc_zone_t *zone, const void *ptr) { |
996 |
|
extern size_t my_size (const void *); |
997 |
|
return my_size (ptr); |
998 |
|
} |
999 |
|
|
1000 |
|
static void *stub_malloc (malloc_zone_t *zone, size_t size) { |
1001 |
|
extern void *my_malloc (size_t); |
1002 |
|
return my_malloc (size); |
1003 |
|
} |
1004 |
|
|
1005 |
|
static void *stub_calloc (malloc_zone_t *zone, size_t num_items, size_t size) { |
1006 |
|
extern void *my_calloc (size_t, size_t); |
1007 |
|
return my_calloc (num_items, size); |
1008 |
|
} |
1009 |
|
|
1010 |
|
static void *stub_valloc (malloc_zone_t *zone, size_t size) { |
1011 |
|
extern void *my_valloc (size_t); |
1012 |
|
return my_valloc (size); |
1013 |
|
} |
1014 |
|
|
1015 |
|
static void *stub_realloc (malloc_zone_t *zone, void *ptr, size_t size) { |
1016 |
|
extern void *my_realloc (void *, size_t); |
1017 |
|
return my_realloc (ptr, size); |
1018 |
|
} |
1019 |
|
|
1020 |
|
static void stub_free (malloc_zone_t *zone, void *ptr) { |
1021 |
|
extern void my_free (void *ptr); |
1022 |
|
my_free (ptr); |
1023 |
|
} |
1024 |
|
|
1025 |
|
void init_darwin_zone_compat () |
1026 |
|
{ |
1027 |
|
extern unsigned malloc_num_zones; |
1028 |
|
malloc_zone_t *default_zone; |
1029 |
|
|
1030 |
|
default_zone = malloc_default_zone (); |
1031 |
|
|
1032 |
|
emacs_zone = malloc_create_zone (0,0); |
1033 |
|
|
1034 |
|
emacs_zone->size = (void *) stub_size; |
1035 |
|
emacs_zone->malloc = (void *) stub_malloc; |
1036 |
|
emacs_zone->calloc = (void *) stub_calloc; |
1037 |
|
emacs_zone->valloc = (void *) stub_valloc; |
1038 |
|
emacs_zone->realloc = (void *) stub_realloc; |
1039 |
|
emacs_zone->free = (void *) stub_free; |
1040 |
|
/* if the zone introspector is ever called, the program will crash */ |
1041 |
|
|
1042 |
|
/* we could support any number of zones, but I'm being lazy */ |
1043 |
|
assert (malloc_num_zones <= 2); |
1044 |
|
|
1045 |
|
if (default_zone) |
1046 |
|
{ |
1047 |
|
malloc_zone_unregister (default_zone); |
1048 |
|
malloc_zone_unregister (emacs_zone); |
1049 |
|
|
1050 |
|
malloc_zone_register (emacs_zone); |
1051 |
|
malloc_zone_register (default_zone); |
1052 |
|
} |
1053 |
|
} |
1054 |
|
|
1055 |
|
void term_darwin_zone_compat () |
1056 |
|
{ |
1057 |
|
|
1058 |
|
} |
1059 |
|
|
1060 |
|
char *my_sbrk (int incr) |
1061 |
|
{ |
1062 |
|
char *temp, *ptr; |
1063 |
|
kern_return_t rtn; |
1064 |
|
|
1065 |
|
if (mach_brkpt == 0) { |
1066 |
|
if ((rtn = vm_allocate(mach_task_self(), (vm_address_t *) &mach_brkpt, big_heap, 1)) != KERN_SUCCESS) { |
1067 |
|
mach_error ("my_sbrk(): vm_allocate() failed", rtn); |
1068 |
|
return ((char *)-1); |
1069 |
|
} |
1070 |
|
if (!mach_brkpt) { |
1071 |
|
/* FIX-ME: this fprintf call will most probably fail because memory isn't initialized */ |
1072 |
|
fprintf (stderr, "my_sbrk(): cannot allocate heap\n"); |
1073 |
|
return ((char *)-1); |
1074 |
|
} |
1075 |
|
mark_region ((unsigned long) mach_brkpt, (unsigned long) big_heap); |
1076 |
|
|
1077 |
|
mach_mapstart = mach_brkpt; |
1078 |
|
mach_maplimit = mach_brkpt + big_heap; |
1079 |
|
} |
1080 |
|
if (incr == 0) { |
1081 |
|
return (mach_brkpt); |
1082 |
|
} else { |
1083 |
|
ptr = mach_brkpt + incr; |
1084 |
|
if (ptr <= mach_maplimit) { |
1085 |
|
temp = mach_brkpt; |
1086 |
|
mach_brkpt = ptr; |
1087 |
|
return (temp); |
1088 |
|
} else { |
1089 |
|
fprintf (stderr, "my_sbrk(): no more memory\n"); |
1090 |
|
fflush (stderr); |
1091 |
|
return ((char *)-1); |
1092 |
|
} |
1093 |
|
} |
1094 |
|
} |
1095 |
|
|
1096 |
|
#ifdef UNIXSAVE |
1097 |
|
#include "save.c" |
1098 |
|
#endif |
1099 |
|
|
1100 |
|
|