X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/c0466914ba3ad88c402b0301646b4b5db8aeb913..5dcde606e32d1794f8268ea51cd2d1746e45a311:/src/unexmacosx.c diff --git a/src/unexmacosx.c b/src/unexmacosx.c index d38053c315..3949e5f6e0 100644 --- a/src/unexmacosx.c +++ b/src/unexmacosx.c @@ -1,13 +1,13 @@ /* Dump Emacs in Mach-O format for use on Mac OS X. Copyright (C) 2001, 2002, 2003, 2004, 2005, - 2006, 2007 Free Software Foundation, Inc. + 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. This file is part of GNU Emacs. -GNU Emacs is free software; you can redistribute it and/or modify +GNU Emacs is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. GNU Emacs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -15,9 +15,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with GNU Emacs; see the file COPYING. If not, write to -the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -Boston, MA 02110-1301, USA. */ +along with GNU Emacs. If not, see . */ /* Contributed by Andrew Choi (akochoi@mac.com). */ @@ -443,15 +441,13 @@ unexec_regions_recorder (task_t task, void *rr, unsigned type, while (num && num_unexec_regions < MAX_UNEXEC_REGIONS) { - /* Subtract the size of trailing null pages from filesize. It + /* Subtract the size of trailing null bytes from filesize. It can be smaller than vmsize in segment commands. In such a - case, trailing pages are initialized with zeros. */ - for (p = ranges->address + ranges->size; p > ranges->address; - p -= sizeof (int)) - if (*(((int *) p)-1)) - break; - filesize = ROUNDUP_TO_PAGE_BOUNDARY (p - ranges->address); - assert (filesize <= ranges->size); + case, trailing bytes are initialized with zeros. */ + for (p = ranges->address + ranges->size; p > ranges->address; p--) + if (*(((char *) p)-1)) + break; + filesize = p - ranges->address; unexec_regions[num_unexec_regions].filesize = filesize; unexec_regions[num_unexec_regions++].range = *ranges; @@ -503,11 +499,19 @@ unexec_regions_merge () { int i, n; unexec_region_info r; + vm_size_t padsize; qsort (unexec_regions, num_unexec_regions, sizeof (unexec_regions[0]), &unexec_regions_sort_compare); n = 0; r = unexec_regions[0]; + padsize = r.range.address & (pagesize - 1); + if (padsize) + { + r.range.address -= padsize; + r.range.size += padsize; + r.filesize += padsize; + } for (i = 1; i < num_unexec_regions; i++) { if (r.range.address + r.range.size == unexec_regions[i].range.address @@ -520,6 +524,17 @@ unexec_regions_merge () { unexec_regions[n++] = r; r = unexec_regions[i]; + padsize = r.range.address & (pagesize - 1); + if (padsize) + { + if ((unexec_regions[n-1].range.address + + unexec_regions[n-1].range.size) == r.range.address) + unexec_regions[n-1].range.size -= padsize; + + r.range.address -= padsize; + r.range.size += padsize; + r.filesize += padsize; + } } } unexec_regions[n++] = r; @@ -562,6 +577,19 @@ print_load_command_name (int lc) case LC_TWOLEVEL_HINTS: printf ("LC_TWOLEVEL_HINTS"); break; +#ifdef LC_UUID + case LC_UUID: + printf ("LC_UUID "); + break; +#endif +#ifdef LC_DYLD_INFO + case LC_DYLD_INFO: + printf ("LC_DYLD_INFO "); + break; + case LC_DYLD_INFO_ONLY: + printf ("LC_DYLD_INFO_ONLY"); + break; +#endif default: printf ("unknown "); } @@ -797,7 +825,10 @@ copy_data_segment (struct load_command *lc) || strncmp (sectp->sectname, "__la_sym_ptr2", 16) == 0 || strncmp (sectp->sectname, "__dyld", 16) == 0 || strncmp (sectp->sectname, "__const", 16) == 0 - || strncmp (sectp->sectname, "__cfstring", 16) == 0) + || strncmp (sectp->sectname, "__cfstring", 16) == 0 + || strncmp (sectp->sectname, "__gcc_except_tab", 16) == 0 + || strncmp (sectp->sectname, "__program_vars", 16) == 0 + || strncmp (sectp->sectname, "__objc_", 7) == 0) { if (!unexec_copy (sectp->offset, old_file_offset, sectp->size)) unexec_error ("cannot copy section %s", sectp->sectname); @@ -876,12 +907,13 @@ copy_symtab (struct load_command *lc, long delta) /* Fix up relocation entries. */ static void -unrelocate (const char *name, off_t reloff, int nrel) +unrelocate (const char *name, off_t reloff, int nrel, vm_address_t base) { int i, unreloc_count; struct relocation_info reloc_info; struct scattered_relocation_info *sc_reloc_info = (struct scattered_relocation_info *) &reloc_info; + vm_address_t location; for (unreloc_count = 0, i = 0; i < nrel; i++) { @@ -895,14 +927,15 @@ unrelocate (const char *name, off_t reloff, int nrel) 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)) + location = base + reloc_info.r_address; + if (location >= data_segment_scp->vmaddr + && location < (data_segment_scp->vmaddr + + data_segment_scp->vmsize)) { off_t src_off = data_segment_old_fileoff - + reloc_info.r_address - data_segment_scp->vmaddr; + + (location - data_segment_scp->vmaddr); off_t dst_off = data_segment_scp->fileoff - + reloc_info.r_address - data_segment_scp->vmaddr; + + (location - 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", @@ -933,15 +966,73 @@ unrelocate (const char *name, off_t reloff, int nrel) unreloc_count, nrel, name); } +#if __ppc64__ +/* Rebase r_address in the relocation table. */ +static void +rebase_reloc_address (off_t reloff, int nrel, long linkedit_delta, long diff) +{ + int i; + struct relocation_info reloc_info; + struct scattered_relocation_info *sc_reloc_info + = (struct scattered_relocation_info *) &reloc_info; + + for (i = 0; i < nrel; i++, reloff += sizeof (reloc_info)) + { + if (lseek (infd, reloff - linkedit_delta, L_SET) + != reloff - linkedit_delta) + unexec_error ("rebase_reloc_table: cannot seek to reloc_info"); + if (!unexec_read (&reloc_info, sizeof (reloc_info))) + unexec_error ("rebase_reloc_table: cannot read reloc_info"); + + if (sc_reloc_info->r_scattered == 0 + && reloc_info.r_type == GENERIC_RELOC_VANILLA) + { + reloc_info.r_address -= diff; + if (!unexec_write (reloff, &reloc_info, sizeof (reloc_info))) + unexec_error ("rebase_reloc_table: cannot write reloc_info"); + } + } +} +#endif + /* Copy a LC_DYSYMTAB load command from the input file to the output file, adjusting the file offset fields. */ static void copy_dysymtab (struct load_command *lc, long delta) { struct dysymtab_command *dstp = (struct dysymtab_command *) lc; + vm_address_t base; - unrelocate ("local", dstp->locreloff, dstp->nlocrel); - unrelocate ("external", dstp->extreloff, dstp->nextrel); +#ifdef _LP64 +#if __ppc64__ + { + int i; + + base = 0; + for (i = 0; i < nlc; i++) + if (lca[i]->cmd == LC_SEGMENT) + { + struct segment_command *scp = (struct segment_command *) lca[i]; + + if (scp->vmaddr + scp->vmsize > 0x100000000 + && (scp->initprot & VM_PROT_WRITE) != 0) + { + base = data_segment_scp->vmaddr; + break; + } + } + } +#else + /* First writable segment address. */ + base = data_segment_scp->vmaddr; +#endif +#else + /* First segment address in the file (unless MH_SPLIT_SEGS set). */ + base = 0; +#endif + + unrelocate ("local", dstp->locreloff, dstp->nlocrel, base); + unrelocate ("external", dstp->extreloff, dstp->nextrel, base); if (dstp->nextrel > 0) { dstp->extreloff += delta; @@ -960,6 +1051,29 @@ copy_dysymtab (struct load_command *lc, long delta) unexec_error ("cannot write symtab command to header"); curr_header_offset += lc->cmdsize; + +#if __ppc64__ + /* Check if the relocation base needs to be changed. */ + if (base == 0) + { + vm_address_t newbase = 0; + int i; + + for (i = 0; i < num_unexec_regions; i++) + if (unexec_regions[i].range.address + unexec_regions[i].range.size + > 0x100000000) + { + newbase = data_segment_scp->vmaddr; + break; + } + + if (newbase) + { + rebase_reloc_address (dstp->locreloff, dstp->nlocrel, delta, newbase); + rebase_reloc_address (dstp->extreloff, dstp->nextrel, delta, newbase); + } + } +#endif } /* Copy a LC_TWOLEVEL_HINTS load command from the input file to the output @@ -981,6 +1095,36 @@ copy_twolevelhints (struct load_command *lc, long delta) curr_header_offset += lc->cmdsize; } +#ifdef LC_DYLD_INFO +/* Copy a LC_DYLD_INFO(_ONLY) load command from the input file to the output + file, adjusting the file offset fields. */ +static void +copy_dyld_info (struct load_command *lc, long delta) +{ + struct dyld_info_command *dip = (struct dyld_info_command *) lc; + + if (dip->rebase_off > 0) + dip->rebase_off += delta; + if (dip->bind_off > 0) + dip->bind_off += delta; + if (dip->weak_bind_off > 0) + dip->weak_bind_off += delta; + if (dip->lazy_bind_off > 0) + dip->lazy_bind_off += delta; + if (dip->export_off > 0) + dip->export_off += delta; + + printf ("Writing "); + print_load_command_name (lc->cmd); + printf (" command\n"); + + if (!unexec_write (curr_header_offset, lc, lc->cmdsize)) + unexec_error ("cannot write dyld info command to header"); + + curr_header_offset += lc->cmdsize; +} +#endif + /* 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 @@ -1047,6 +1191,12 @@ dump_it () case LC_TWOLEVEL_HINTS: copy_twolevelhints (lca[i], linkedit_delta); break; +#ifdef LC_DYLD_INFO + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + copy_dyld_info (lca[i], linkedit_delta); + break; +#endif default: copy_other (lca[i]); break; @@ -1213,6 +1363,8 @@ unexec_realloc (void *old_ptr, size_t new_size) void unexec_free (void *ptr) { + if (ptr == NULL) + return; if (in_dumped_exec) { if (!ptr_in_unexec_regions (ptr))