/[gcl]/gcl/o/unexmacosx.c
ViewVC logotype

Diff of /gcl/o/unexmacosx.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.1 by rlbk, Mon Sep 1 05:53:03 2003 UTC revision 1.2 by camm, Thu Sep 4 15:13:35 2003 UTC
# Line 0  Line 1 
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, &section, 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    

Legend:
Removed from v.1.1  
changed lines
  Added in v.1.2

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26