X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/e0f712ba55fa0d073f6ab93606e428f61fc7caf2..ffe8b3f4e8cf60dd97c3e5ec5f12183ad9006c02:/src/unexmacosx.c diff --git a/src/unexmacosx.c b/src/unexmacosx.c index da4b82b6ca..b41c586d2e 100644 --- a/src/unexmacosx.c +++ b/src/unexmacosx.c @@ -95,6 +95,10 @@ Boston, MA 02111-1307, USA. */ #include #include #include +#include +#if defined (__ppc__) +#include +#endif #include #define VERBOSE 1 @@ -158,6 +162,11 @@ int in_dumped_exec = 0; malloc_zone_t *emacs_zone; +/* file offset of input file's data segment */ +off_t data_segment_old_fileoff; + +struct segment_command *data_segment_scp; + /* Read n bytes from infd into memory starting at address dest. Return true if successful, false otherwise. */ static int @@ -183,6 +192,7 @@ static int unexec_copy (off_t dest, off_t src, ssize_t count) { ssize_t bytes_read; + ssize_t bytes_to_read; char buf[UNEXEC_COPY_BUFSZ]; @@ -194,7 +204,8 @@ unexec_copy (off_t dest, off_t src, ssize_t count) while (count > 0) { - bytes_read = read (infd, buf, UNEXEC_COPY_BUFSZ); + bytes_to_read = count > UNEXEC_COPY_BUFSZ ? UNEXEC_COPY_BUFSZ : count; + bytes_read = read (infd, buf, bytes_to_read); if (bytes_read <= 0) return 0; if (write (outfd, buf, bytes_read) != bytes_read) @@ -276,7 +287,7 @@ print_regions () if (object_name != MACH_PORT_NULL) mach_port_deallocate (target_task, object_name); - + address += size; } } @@ -329,15 +340,15 @@ build_region_list () else { r = (struct region_t *) malloc (sizeof (struct region_t)); - + if (!r) unexec_error ("cannot allocate region structure"); - + r->address = address; r->size = size; r->protection = info.protection; r->max_protection = info.max_protection; - + r->next = 0; if (region_list_head == 0) { @@ -349,13 +360,13 @@ build_region_list () region_list_tail->next = r; region_list_tail = r; } - + /* Deallocate (unused) object name returned by vm_region. */ if (object_name != MACH_PORT_NULL) mach_port_deallocate (target_task, object_name); } - + address += size; } @@ -364,7 +375,7 @@ build_region_list () } -#define MAX_UNEXEC_REGIONS 30 +#define MAX_UNEXEC_REGIONS 200 int num_unexec_regions; vm_range_t unexec_regions[MAX_UNEXEC_REGIONS]; @@ -403,6 +414,46 @@ find_emacs_zone_regions () unexec_regions_recorder); } +static int +unexec_regions_sort_compare (const void *a, const void *b) +{ + vm_address_t aa = ((vm_range_t *) a)->address; + vm_address_t bb = ((vm_range_t *) b)->address; + + if (aa < bb) + return -1; + else if (aa > bb) + return 1; + else + return 0; +} + +static void +unexec_regions_merge () +{ + int i, n; + vm_range_t r; + + qsort (unexec_regions, num_unexec_regions, sizeof (unexec_regions[0]), + &unexec_regions_sort_compare); + n = 0; + r = unexec_regions[0]; + for (i = 1; i < num_unexec_regions; i++) + { + if (r.address + r.size == unexec_regions[i].address) + { + r.size += unexec_regions[i].size; + } + else + { + unexec_regions[n++] = r; + r = unexec_regions[i]; + } + } + unexec_regions[n++] = r; + num_unexec_regions = n; +} + /* More informational messages routines. */ @@ -498,7 +549,7 @@ read_load_commands () nlc = mh.ncmds; lca = (struct load_command **) malloc (nlc * sizeof (struct load_command *)); - + for (i = 0; i < nlc; i++) { struct load_command lc; @@ -513,7 +564,7 @@ read_load_commands () if (lc.cmd == LC_SEGMENT) { struct segment_command *scp = (struct segment_command *) lca[i]; - + if (scp->vmaddr + scp->vmsize > infile_lc_highest_addr) infile_lc_highest_addr = scp->vmaddr + scp->vmsize; @@ -638,7 +689,8 @@ copy_data_segment (struct load_command *lc) else if (strncmp (sectp->sectname, "__la_symbol_ptr", 16) == 0 || strncmp (sectp->sectname, "__nl_symbol_ptr", 16) == 0 || strncmp (sectp->sectname, "__dyld", 16) == 0 - || strncmp (sectp->sectname, "__const", 16) == 0) + || strncmp (sectp->sectname, "__const", 16) == 0 + || strncmp (sectp->sectname, "__cfstring", 16) == 0) { if (!unexec_copy (sectp->offset, old_file_offset, sectp->size)) unexec_error ("cannot copy section %s", sectp->sectname); @@ -647,7 +699,7 @@ copy_data_segment (struct load_command *lc) } else unexec_error ("unrecognized section name in __DATA segment"); - + printf (" section %-16.16s at %#8x - %#8x (sz: %#8x)\n", sectp->sectname, sectp->offset, sectp->offset + sectp->size, sectp->size); @@ -675,7 +727,7 @@ copy_data_segment (struct load_command *lc) for (j = 0; j < num_unexec_regions; j++) { struct segment_command sc; - + sc.cmd = LC_SEGMENT; sc.cmdsize = sizeof (struct segment_command); strncpy (sc.segname, SEG_DATA, 16); @@ -687,7 +739,7 @@ copy_data_segment (struct load_command *lc) sc.initprot = VM_PROT_READ | VM_PROT_WRITE; sc.nsects = 0; sc.flags = 0; - + printf ("Writing segment %-16.16s at %#8x - %#8x (sz: %#8x)\n", sc.segname, sc.fileoff, sc.fileoff + sc.filesize, sc.filesize); @@ -696,7 +748,7 @@ copy_data_segment (struct load_command *lc) unexec_error ("cannot write new __DATA segment"); delta += sc.filesize; file_offset += sc.filesize; - + if (!unexec_write (curr_header_offset, &sc, sc.cmdsize)) unexec_error ("cannot write new __DATA segment's header"); curr_header_offset += sc.cmdsize; @@ -722,6 +774,65 @@ copy_symtab (struct load_command *lc) curr_header_offset += lc->cmdsize; } +/* Fix up relocation entries. */ +static void +unrelocate (const char *name, off_t reloff, int nrel) +{ + int i, unreloc_count; + struct relocation_info reloc_info; + struct scattered_relocation_info *sc_reloc_info + = (struct scattered_relocation_info *) &reloc_info; + + for (unreloc_count = 0, i = 0; i < nrel; i++) + { + if (lseek (infd, reloff, L_SET) != reloff) + unexec_error ("unrelocate: %s:%d cannot seek to reloc_info", name, i); + if (!unexec_read (&reloc_info, sizeof (reloc_info))) + unexec_error ("unrelocate: %s:%d cannot read reloc_info", name, i); + reloff += sizeof (reloc_info); + + if (sc_reloc_info->r_scattered == 0) + switch (reloc_info.r_type) + { + case GENERIC_RELOC_VANILLA: + if (reloc_info.r_address >= data_segment_scp->vmaddr + && reloc_info.r_address < (data_segment_scp->vmaddr + + data_segment_scp->vmsize)) + { + off_t src_off = data_segment_old_fileoff + + reloc_info.r_address - data_segment_scp->vmaddr; + off_t dst_off = data_segment_scp->fileoff + + reloc_info.r_address - data_segment_scp->vmaddr; + + if (!unexec_copy (dst_off, src_off, 1 << reloc_info.r_length)) + unexec_error ("unrelocate: %s:%d cannot copy original value", + name, i); + unreloc_count++; + } + break; + default: + unexec_error ("unrelocate: %s:%d cannot handle type = %d", + name, i, reloc_info.r_type); + } + else + switch (sc_reloc_info->r_type) + { +#if defined (__ppc__) + case PPC_RELOC_PB_LA_PTR: + /* nothing to do for prebound lazy pointer */ + break; +#endif + default: + unexec_error ("unrelocate: %s:%d cannot handle scattered type = %d", + name, i, sc_reloc_info->r_type); + } + } + + if (nrel > 0) + printf ("Fixed up %d/%d %s relocation entries in data segment.\n", + unreloc_count, nrel, name); +} + /* Copy a LC_DYSYMTAB load command from the input file to the output file, adjusting the file offset fields. */ static void @@ -729,10 +840,8 @@ copy_dysymtab (struct load_command *lc) { struct dysymtab_command *dstp = (struct dysymtab_command *) lc; - /* If Mach-O executable is not prebound, relocation entries need - fixing up. This is not supported currently. */ - if (!(mh.flags & MH_PREBOUND) && (dstp->nextrel != 0 || dstp->nlocrel != 0)) - unexec_error ("cannot handle LC_DYSYMTAB with relocation entries"); + unrelocate ("local", dstp->locreloff, dstp->nlocrel); + unrelocate ("external", dstp->extreloff, dstp->nextrel); if (dstp->nextrel > 0) { dstp->extreloff += delta; @@ -753,6 +862,25 @@ copy_dysymtab (struct load_command *lc) curr_header_offset += lc->cmdsize; } +/* Copy a LC_TWOLEVEL_HINTS load command from the input file to the output + file, adjusting the file offset fields. */ +static void +copy_twolevelhints (struct load_command *lc) +{ + struct twolevel_hints_command *tlhp = (struct twolevel_hints_command *) lc; + + if (tlhp->nhints > 0) { + tlhp->offset += delta; + } + + printf ("Writing LC_TWOLEVEL_HINTS command\n"); + + if (!unexec_write (curr_header_offset, lc, lc->cmdsize)) + unexec_error ("cannot write two level hint command to header"); + + curr_header_offset += lc->cmdsize; +} + /* Copy other kinds of load commands from the input file to the output file, ones that do not require adjustments of file offsets. */ static void @@ -785,6 +913,11 @@ dump_it () struct segment_command *scp = (struct segment_command *) lca[i]; if (strncmp (scp->segname, SEG_DATA, 16) == 0) { + /* save data segment file offset and segment_command for + unrelocate */ + data_segment_old_fileoff = scp->fileoff; + data_segment_scp = scp; + copy_data_segment (lca[i]); } else @@ -799,6 +932,9 @@ dump_it () case LC_DYSYMTAB: copy_dysymtab (lca[i]); break; + case LC_TWOLEVEL_HINTS: + copy_twolevelhints (lca[i]); + break; default: copy_other (lca[i]); break; @@ -828,7 +964,7 @@ unexec (char *outfile, char *infile, void *start_data, void *start_bss, { unexec_error ("cannot open input file `%s'", infile); } - + outfd = open (outfile, O_WRONLY | O_TRUNC | O_CREAT, 0755); if (outfd < 0) { @@ -840,6 +976,7 @@ unexec (char *outfile, char *infile, void *start_data, void *start_bss, read_load_commands (); find_emacs_zone_regions (); + unexec_regions_merge (); in_dumped_exec = 1; @@ -888,7 +1025,7 @@ unexec_realloc (void *old_ptr, size_t new_size) /* 2002-04-15 T. Ikegami . The original code to get size failed to reallocate read_buffer (lread.c). */ - int old_size = emacs_zone->size (emacs_zone, old_ptr); + int old_size = malloc_default_zone()->size (emacs_zone, old_ptr); int size = new_size > old_size ? old_size : new_size; if (size) @@ -912,3 +1049,6 @@ unexec_free (void *ptr) else malloc_zone_free (emacs_zone, ptr); } + +/* arch-tag: 1a784f7b-a184-4c4f-9544-da8619593d72 + (do not change this comment) */