/* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #import #import #import #import #import "stuff/openstep_mach.h" #import #import #import #ifdef hppa #import #endif #ifdef sparc #import #endif #ifdef __ppc__ #import #endif #import #import "stuff/vm_flush_cache.h" #import "images.h" #import "symbols.h" #import "errors.h" #import "reloc.h" #import "debug.h" #import "register_funcs.h" static void reset_lazy_symbol_pointers( unsigned long symbol_index, unsigned long *indirect_symtab, struct image *image); /* * relocate_modules_being_linked() preforms the external relocation for the * modules being linked and sets the values of the symbol pointers for the * symbols being linked. When trying to launch with prebound libraries then * launching_with_prebound_libraries is TRUE. Then only the executable gets * relocated and all library modules in the BEING_LINKED state get set to the * FULLY_LINKED state and the others get set to PREBOUND_UNLINKED state. */ void relocate_modules_being_linked( enum bool launching_with_prebound_libraries) { unsigned long i, j; enum link_state link_state; struct object_images *p; struct library_images *q; struct segment_command *linkedit_segment; struct symtab_command *st; struct dysymtab_command *dyst; struct dylib_module *dylib_modules; struct relocation_info *relocs; struct nlist *symbols; char *strings; struct dyld_event event; memset(&event, '\0', sizeof(struct dyld_event)); event.type = DYLD_MODULE_BOUND; /* * First relocate object_images modules that are being linked. */ p = &object_images; do{ for(i = 0; i < p->nimages; i++){ link_state = GET_LINK_STATE(p->images[i].module); if(link_state == UNUSED) continue; relocate_symbol_pointers_in_object_image(&(p->images[i].image)); /* skip modules that that not in the being linked state */ if(link_state != BEING_LINKED) continue; linkedit_segment = p->images[i].image.linkedit_segment; st = p->images[i].image.st; dyst = p->images[i].image.dyst; /* * Object images could be loaded that do not have the proper * link edit information. */ if(linkedit_segment == NULL || st == NULL || dyst == NULL) continue; relocs = (struct relocation_info *) (p->images[i].image.vmaddr_slide + linkedit_segment->vmaddr + dyst->extreloff - linkedit_segment->fileoff); symbols = (struct nlist *) (p->images[i].image.vmaddr_slide + linkedit_segment->vmaddr + st->symoff - linkedit_segment->fileoff); strings = (char *) (p->images[i].image.vmaddr_slide + linkedit_segment->vmaddr + st->stroff - linkedit_segment->fileoff); /* * If the image has relocations in read-only segments and the * protection needs to change change it. */ if(p->images[i].image.change_protect_on_reloc){ make_image_writable(&(p->images[i].image), "object"); } link_state = external_relocation( &(p->images[i].image), relocs, dyst->nextrel, symbols, strings, NULL, /* library_name */ p->images[i].image.name); SET_LINK_STATE(p->images[i].module, link_state); if(launching_with_prebound_libraries == TRUE) SET_LINK_STATE(p->images[i].module, FULLY_LINKED); /* send the event message that this module was bound */ event.arg[0].header = p->images[i].image.mh; event.arg[0].vmaddr_slide = p->images[i].image.vmaddr_slide; event.arg[0].module_index = 0; send_event(&event); /* * If the image has relocations in read-only segments and the * protection was changed change it back. */ if(p->images[i].image.change_protect_on_reloc){ restore_image_vm_protections(&(p->images[i].image), "object"); } } p = p->next_images; }while(p != NULL); /* * Next relocate the library_images. */ q = &library_images; do{ for(i = 0; i < q->nimages; i++){ linkedit_segment = q->images[i].image.linkedit_segment; st = q->images[i].image.st; dyst = q->images[i].image.dyst; relocs = (struct relocation_info *) (q->images[i].image.vmaddr_slide + linkedit_segment->vmaddr + dyst->extreloff - linkedit_segment->fileoff); symbols = (struct nlist *) (q->images[i].image.vmaddr_slide + linkedit_segment->vmaddr + st->symoff - linkedit_segment->fileoff); strings = (char *) (q->images[i].image.vmaddr_slide + linkedit_segment->vmaddr + st->stroff - linkedit_segment->fileoff); dylib_modules = (struct dylib_module *) (q->images[i].image.vmaddr_slide + linkedit_segment->vmaddr + dyst->modtaboff - linkedit_segment->fileoff); /* * If the image has relocations in read-only segments and the * protection needs to change change it. */ if(q->images[i].image.change_protect_on_reloc) make_image_writable(&(q->images[i].image), "library"); for(j = 0; j < dyst->nmodtab; j++){ /* skip modules that that not in the being linked state */ link_state = GET_LINK_STATE(q->images[i].modules[j]); if(link_state != BEING_LINKED){ if(launching_with_prebound_libraries == TRUE) SET_LINK_STATE(q->images[i].modules[j], PREBOUND_UNLINKED); continue; } if(launching_with_prebound_libraries == FALSE){ link_state = external_relocation( &(q->images[i].image), relocs + dylib_modules[j].iextrel, dylib_modules[j].nextrel, symbols, strings, q->images[i].image.name, strings + dylib_modules[j].module_name); SET_LINK_STATE(q->images[i].modules[j], link_state); } else SET_LINK_STATE(q->images[i].modules[j], FULLY_LINKED); /* send the event message that this module was bound */ event.arg[0].header = q->images[i].image.mh; event.arg[0].vmaddr_slide = q->images[i].image.vmaddr_slide; event.arg[0].module_index = j; send_event(&event); } /* * If the image has relocations in read-only segments and the * protection was changed change it back. */ if(q->images[i].image.change_protect_on_reloc) restore_image_vm_protections(&(q->images[i].image), "library"); if(launching_with_prebound_libraries == FALSE) relocate_symbol_pointers_in_library_image( &(q->images[i].image)); } q = q->next_images; }while(q != NULL); clear_being_linked_list(FALSE); } /* * resolve_external_relocations_in_object_image() preforms the external * relocations for just the one specified object image. */ void resolve_external_relocations_in_object_image( struct object_image *object_image) { enum link_state link_state; struct segment_command *linkedit_segment; struct symtab_command *st; struct dysymtab_command *dyst; struct relocation_info *relocs; struct nlist *symbols; char *strings; linkedit_segment = object_image->image.linkedit_segment; st = object_image->image.st; dyst = object_image->image.dyst; relocs = (struct relocation_info *) (object_image->image.vmaddr_slide + linkedit_segment->vmaddr + dyst->extreloff - linkedit_segment->fileoff); symbols = (struct nlist *) (object_image->image.vmaddr_slide + linkedit_segment->vmaddr + st->symoff - linkedit_segment->fileoff); strings = (char *) (object_image->image.vmaddr_slide + linkedit_segment->vmaddr + st->stroff - linkedit_segment->fileoff); link_state = external_relocation( &(object_image->image), relocs, dyst->nextrel, symbols, strings, NULL, /* library_name */ object_image->image.name); SET_LINK_STATE(object_image->module, link_state); } /* * undo_prebound_lazy_pointers() undoes the prebinding for lazy symbol pointers. * This is also done by local_relocation() which is called if the image can't * be loaded at the addresses it wants in the file. This is called when the * prebinding can't be used and the image was loaded at the address it wants. */ void undo_prebound_lazy_pointers( struct image *image, unsigned long PB_LA_PTR_r_type, enum bool all_lazy_pointers, unsigned long lazy_pointer_address) { unsigned long i, r_slide, r_address, r_type, r_value, value; struct relocation_info *relocs; struct scattered_relocation_info *sreloc; unsigned long cache_flush_high_addr, cache_flush_low_addr; relocs = (struct relocation_info *) (image->vmaddr_slide + image->linkedit_segment->vmaddr + image->dyst->locreloff - image->linkedit_segment->fileoff); cache_flush_high_addr = 0; cache_flush_low_addr = ULONG_MAX; if(image->mh->flags & MH_SPLIT_SEGS) r_slide = image->segs_read_write_addr + image->vmaddr_slide; else r_slide = image->seg1addr + image->vmaddr_slide; r_value = 0; for(i = 0; i < image->dyst->nlocrel; i++){ if((relocs[i].r_address & R_SCATTERED) != 0){ sreloc = (struct scattered_relocation_info *)(relocs + i); r_address = sreloc->r_address; r_type = sreloc->r_type; r_value = sreloc->r_value; /* * If the relocation entry is for a prebound lazy pointer * (r_type is the passed in value PB_LA_PTR_r_type) then undo * the prebinding by using the value of the lazy pointer saved * in r_value plus the slide amount. */ if(r_type == PB_LA_PTR_r_type){ if(all_lazy_pointers == TRUE || lazy_pointer_address == (r_address + r_slide)){ value = r_value + image->vmaddr_slide; *((long *)(r_address + r_slide)) = value; } if(image->cache_sync_on_reloc){ if(r_address + r_slide < cache_flush_low_addr) cache_flush_low_addr = r_address + r_slide; if(r_address + r_slide + (1 << 2) > cache_flush_high_addr) cache_flush_high_addr = r_address + r_slide + (1 << 2); } } } #if defined(hppa) || defined(sparc) else{ r_type = relocs[i].r_type; } /* * If the relocation entry had a pair step over it. */ #ifdef hppa if(r_type == HPPA_RELOC_HI21 || r_type == HPPA_RELOC_LO14 || r_type == HPPA_RELOC_BR17) #endif #ifdef sparc if(r_type == SPARC_RELOC_HI22 || r_type == SPARC_RELOC_LO10 ) #endif i++; #endif /* defined(hppa) || defined(sparc) */ } if(image->cache_sync_on_reloc && cache_flush_high_addr > cache_flush_low_addr) vm_flush_cache(mach_task_self(), cache_flush_low_addr, cache_flush_high_addr - cache_flush_low_addr); } /* * undo_prebinding_for_library_module() is called when a library module is in * the PREBOUND_UNLINKED state to get it into the UNLINKED. The library module * is specified with the module, image and library_image parameters. */ void undo_prebinding_for_library_module( module_state *module, struct image *image, struct library_image *library_image) { struct segment_command *linkedit_segment; struct symtab_command *st; struct dysymtab_command *dyst; struct relocation_info *relocs; struct nlist *symbols; char *strings, *symbol_name; struct dylib_module *dylib_modules; unsigned long module_index, i, j; struct dylib_reference *dylib_references; struct nlist *defined_symbol; module_state *defined_module; struct image *defined_image; struct library_image *defined_library_image; enum link_state link_state; unsigned long *indirect_symtab; enum bool undo_lazy_pointer; linkedit_segment = image->linkedit_segment; st = image->st; dyst = image->dyst; relocs = (struct relocation_info *) (image->vmaddr_slide + linkedit_segment->vmaddr + dyst->extreloff - linkedit_segment->fileoff); symbols = (struct nlist *) (image->vmaddr_slide + linkedit_segment->vmaddr + st->symoff - linkedit_segment->fileoff); strings = (char *) (image->vmaddr_slide + linkedit_segment->vmaddr + st->stroff - linkedit_segment->fileoff); dylib_modules = (struct dylib_module *) (image->vmaddr_slide + linkedit_segment->vmaddr + dyst->modtaboff - linkedit_segment->fileoff); module_index = module - library_image->modules; if(image->change_protect_on_reloc) make_image_writable(image, "library"); undo_external_relocation( TRUE, /* undo_prebinding */ image, relocs + dylib_modules[module_index].iextrel, dylib_modules[module_index].nextrel, symbols, strings, image->name, strings + dylib_modules[module_index].module_name); /* * If this PREBOUND_UNLINKED module is in an image that did not have all * its prebound lazy pointers undone we how have the hard task of * undoing just the prebound lazy pointers this module uses. */ if(image->undone_prebound_lazy_pointers == FALSE){ dylib_references = (struct dylib_reference *) (image->vmaddr_slide + linkedit_segment->vmaddr + dyst->extrefsymoff - linkedit_segment->fileoff); indirect_symtab = (unsigned long *) (image->vmaddr_slide + linkedit_segment->vmaddr + dyst->indirectsymoff - linkedit_segment->fileoff); for(i = dylib_modules[module_index].irefsym; i < dylib_modules[module_index].irefsym + dylib_modules[module_index].nrefsym; i++){ undo_lazy_pointer = FALSE; if(dylib_references[i].flags == REFERENCE_FLAG_UNDEFINED_LAZY){ symbol_name = strings + symbols[dylib_references[i].isym].n_un.n_strx; lookup_symbol(symbol_name, get_primary_image(image, symbols + dylib_references[i].isym), get_hint(image, symbols + dylib_references[i].isym), get_weak(symbols + dylib_references[i].isym), &defined_symbol, &defined_module, &defined_image, &defined_library_image, NULL); /* * If this symbol is in a module that is not bound or has * has a different address than the prebound undefined * symbol we have to undo the prebinding of any lazy pointer * to it. */ if(defined_symbol != NULL){ link_state = GET_LINK_STATE(*defined_module); if((link_state == PREBOUND_UNLINKED || link_state == UNLINKED) || defined_symbol->n_value + defined_image->vmaddr_slide != symbols[dylib_references[i].isym].n_value){ undo_lazy_pointer = TRUE; } } else{ undo_lazy_pointer = TRUE; } } if(dylib_references[i].flags == REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY){ for(j = 0; j < dyst->nmodtab; j++){ if(dylib_references[i].isym >= dylib_modules[j].ilocalsym && dylib_references[i].isym < dylib_modules[j].ilocalsym + dylib_modules[j].nlocalsym) break; } if(j < dyst->nmodtab){ link_state = GET_LINK_STATE(library_image->modules[j]); if(link_state == PREBOUND_UNLINKED || link_state == UNLINKED){ undo_lazy_pointer = TRUE; } } } if(undo_lazy_pointer == TRUE){ reset_lazy_symbol_pointers(dylib_references[i].isym, indirect_symtab, image); } } } if(image->change_protect_on_reloc) restore_image_vm_protections(image, "library"); } /* * reset_lazy_symbol_pointers() is passed a symbol index of a lazy bound symbol. * This routine searches the lazy pointer sections of the specified image for * indirect symbol table entries that matches this symbol index. Then it * calculates the address of that lazy pointer and calls * undo_prebound_lazy_pointers() to undo the prebinding for just the lazy symbol * pointer at this address in this image. */ static void reset_lazy_symbol_pointers( unsigned long symbol_index, unsigned long *indirect_symtab, struct image *image) { unsigned long i, j, k, section_type, lazy_pointer_address; struct load_command *lc; struct segment_command *sg; struct section *s; lc = (struct load_command *)((char *)image->mh + sizeof(struct mach_header)); for(i = 0; i < image->mh->ncmds; i++){ switch(lc->cmd){ case LC_SEGMENT: sg = (struct segment_command *)lc; s = (struct section *) ((char *)sg + sizeof(struct segment_command)); for(j = 0 ; j < sg->nsects ; j++){ section_type = s->flags & SECTION_TYPE; if(section_type == S_LAZY_SYMBOL_POINTERS){ for(k = 0; k < s->size / sizeof(unsigned long); k++){ if(indirect_symtab[s->reserved1 + k] == symbol_index){ lazy_pointer_address = (unsigned long) (image->vmaddr_slide + s->addr + (k * sizeof(long))); undo_prebound_lazy_pointers(image, #if defined(m68k) || defined(__i386__) GENERIC_RELOC_PB_LA_PTR, #endif #ifdef hppa HPPA_RELOC_PB_LA_PTR, #endif #ifdef sparc SPARC_RELOC_PB_LA_PTR, #endif #ifdef __ppc__ PPC_RELOC_PB_LA_PTR, #endif FALSE, /* all_lazy_pointers */ lazy_pointer_address); } } } s++; } } lc = (struct load_command *)((char *)lc + lc->cmdsize); } } /* * make_image_writable() changes the protections of the segments of the image * to its maxprot to make it writeable. This is only called when the image has * relocation entries in read-only segments (bad code gen). The image_type is * a string like "library" or "object" and used only for error messages. */ void make_image_writable( struct image *image, char *image_type) { unsigned long i; struct load_command *lc; struct segment_command *sg; kern_return_t r; /* * If the image has relocations in read-only segments and * the protection needs to change change it. */ lc = (struct load_command *)((char *)image->mh + sizeof(struct mach_header)); for(i = 0; i < image->mh->ncmds; i++){ switch(lc->cmd){ case LC_SEGMENT: sg = (struct segment_command *)lc; if((r = vm_protect(mach_task_self(),sg->vmaddr + image->vmaddr_slide, (vm_size_t)sg->vmsize, FALSE, sg->maxprot)) != KERN_SUCCESS){ mach_error(r, "can't set vm_protection on segment: %.16s " "for %s: %s", sg->segname, image_type, image->name); link_edit_error(DYLD_MACH_RESOURCE, r, image->name); } break; } lc = (struct load_command *)((char *)lc + lc->cmdsize); } } /* * restore_image_vm_protections() undoes what make_image_writable() and changes * the protections of the segments of the image back to its initprot. This * is only called when the image has relocation entries in read-only segments * (bad code gen). The image_type is a string like "library" or "object" and * used only for error messages. */ void restore_image_vm_protections( struct image *image, char *image_type) { unsigned long i; struct load_command *lc; struct segment_command *sg; kern_return_t r; /* * If the image has relocations in read-only segments and * the protection was changed change it back. */ lc = (struct load_command *)((char *)image->mh + sizeof(struct mach_header)); for(i = 0; i < image->mh->ncmds; i++){ switch(lc->cmd){ case LC_SEGMENT: sg = (struct segment_command *)lc; if((r = vm_protect(mach_task_self(),sg->vmaddr + image->vmaddr_slide, (vm_size_t)sg->vmsize, FALSE, sg->initprot)) != KERN_SUCCESS){ mach_error(r, "can't set vm_protection on segment: %.16s " "for %s: %s", sg->segname, image_type, image->name); link_edit_error(DYLD_MACH_RESOURCE, r, image->name); } break; } lc = (struct load_command *) ((char *)lc + lc->cmdsize); } }