/* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Portions Copyright (c) 1999 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 1.1 (the "License"). You may not use this file * except in compliance with the License. Please obtain a copy of the * License at http://www.apple.com/publicsource 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 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 "register_funcs.h" #import "images.h" #import "allocate.h" #import "lock.h" /* * Since there typically only one function registered from the Objective-C * runtime or one for the zero overhead C++ execption mechanism for each of * these a struct with one function pointer is staticly allocated so normally * malloc will never be called. */ struct add_image_func { void (*func)(struct mach_header *mh, unsigned long vmaddr_slide); struct add_image_func *next; }; static struct add_image_func add_image_func_list = { NULL, NULL }; struct remove_image_func { void (*func)(struct mach_header *mh, unsigned long vmaddr_slide); struct remove_image_func *next; }; static struct remove_image_func remove_image_func_list = { NULL, NULL }; struct link_module_func { void (*func)(module_state *module); struct link_module_func *next; }; static struct link_module_func link_module_func_list = { NULL, NULL }; struct unlink_module_func { void (*func)(module_state *module); struct unlink_module_func *next; }; static struct unlink_module_func unlink_module_func_list = { NULL, NULL }; struct replace_module_func { void (*func)(module_state *oldmodule, module_state *newmodule); struct replace_module_func *next; }; static struct replace_module_func replace_module_func_list = { NULL, NULL }; static enum bool call_registered_funcs_for_add_images_in_objects( void); static void call_funcs_for_add_image( struct mach_header *mh, unsigned long vmaddr_slide); static enum bool call_registered_funcs_for_linked_modules_in_objects( void); static void call_funcs_for_link_module( module_state *module); /* * register_func_for_add_image() adds the specified function pointer to the * list of functions to be called when images are added and then calls it for * each image currently in the program. */ void register_func_for_add_image( void (*func)(struct mach_header *mh, unsigned long vmaddr_slide)) { struct add_image_func *add_image_func; unsigned long i; struct object_images *p; struct library_images *q; enum link_state link_state; /* * Add this func to the list of functions to be called when images are * added to this program. */ /* set lock for dyld data structures */ set_lock(); if(add_image_func_list.func == NULL){ add_image_func_list.func = func; } else{ add_image_func = allocate(sizeof(struct add_image_func)); add_image_func->func = func; add_image_func->next = add_image_func_list.next; add_image_func_list.next = add_image_func; } /* TODO: fix this to be lock safe when objects get unloaded */ /* * Call this func for the images currently part of the program. */ for(p = &object_images ; ; p = p->next_images){ for(i = 0; i < p->nimages; i++){ link_state = GET_LINK_STATE(p->images[i].module); if(link_state == UNUSED) continue; release_lock(); func(p->images[i].image.mh, p->images[i].image.vmaddr_slide); set_lock(); } if(p->next_images == NULL) break; } for(q = &library_images ; ; q = q->next_images){ for(i = 0; i < q->nimages; i++){ release_lock(); func(q->images[i].image.mh, q->images[i].image.vmaddr_slide); set_lock(); } if(q->next_images == NULL) break; } /* release lock for dyld data structures */ release_lock(); } /* * register_func_for_remove_image() adds the specified function pointer to the * list of functions to be called when images are removed. */ void register_func_for_remove_image( void (*func)(struct mach_header *mh, unsigned long vmaddr_slide)) { struct remove_image_func *remove_image_func; /* * Add this func to the list of functions to be called when images are * removed from this program. */ /* set lock for dyld data structures */ set_lock(); if(remove_image_func_list.func == NULL){ remove_image_func_list.func = func; } else{ remove_image_func = allocate(sizeof(struct remove_image_func)); remove_image_func->func = func; remove_image_func->next = remove_image_func_list.next; remove_image_func_list.next = remove_image_func; } /* release lock for dyld data structures */ release_lock(); } /* * call_registered_funcs_for_add_images() calls the functions registered to * be called when images get added for the images that have not had these * called. */ void call_registered_funcs_for_add_images( void) { unsigned long i; struct library_images *q; /* * First for the object_images that have not had the functions * registered to be called call them. Because objects can be unloaded * and we have to release the lock to call the registered functions * we need to repeatedly pass through the data structures for the loaded * objects after each call until we get through cleanly. */ while(call_registered_funcs_for_add_images_in_objects() == TRUE) ; /* * Next for the library modules that have not had the functions * registered to be called call them. Note libraries can't be unloaded. */ q = &library_images; do{ for(i = 0; i < q->nimages; i++){ if(q->images[i].image.registered == FALSE){ q->images[i].image.registered = TRUE; call_funcs_for_add_image(q->images[i].image.mh, q->images[i].image.vmaddr_slide); } } q = q->next_images; }while(q != NULL); } /* * call_registered_funcs_for_add_images_in_objects() goes through the list of * objects and finds an object that has not had the functions registered to * be called for add images and call the functions for at most one object * image. If a functions are call TRUE is returned. If no functions are called * (because all object images have had the registered functions called) then * FALSE is returned. */ static enum bool call_registered_funcs_for_add_images_in_objects( void) { unsigned long i; struct object_images *p; enum link_state link_state; 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; if(p->images[i].image.registered == FALSE){ p->images[i].image.registered = TRUE; call_funcs_for_add_image(p->images[i].image.mh, p->images[i].image.vmaddr_slide); return(TRUE); } } p = p->next_images; }while(p != NULL); return(FALSE); } /* * call_funcs_for_add_image() calls each of the functions that was registered * to be called when images are added for the new image now being added to the * program. */ static void call_funcs_for_add_image( struct mach_header *mh, unsigned long vmaddr_slide) { struct add_image_func *add_image_func; for(add_image_func = &add_image_func_list; add_image_func != NULL && add_image_func->func != NULL; add_image_func = add_image_func->next){ /* * Since functions can only be added and not removed the * add_image_func_list will be valid when we get the lock back and * at worse may contain more functions than we started the loop. * If this happens a function will get called twice on the same * image. */ release_lock(); add_image_func->func(mh, vmaddr_slide); set_lock(); } } /* * call_funcs_for_remove_image() calls each of the functions that was registered * to be called when images are removed for the image now being removed from the * program. */ void call_funcs_for_remove_image( struct mach_header *mh, unsigned long vmaddr_slide) { struct remove_image_func *remove_image_func; for(remove_image_func = &remove_image_func_list; remove_image_func != NULL && remove_image_func->func != NULL; remove_image_func = remove_image_func->next){ /* * Since functions can only be added and not removed the * remove_image_func_list will be valid when we get the lock back * and at worse may contain more functions than we started the loop. * If this happens a function will get called twice on the same * image. */ release_lock(); remove_image_func->func(mh, vmaddr_slide); set_lock(); } } /* * register_func_for_link_module() adds the specified function pointer to the * list of functions to be called when modules are bound and then calls it for * each module currently bound into the program. */ void register_func_for_link_module( void (*func)(module_state *module)) { struct link_module_func *link_module_func; unsigned long i, j; struct object_images *p; struct library_images *q; enum link_state link_state; /* * Add this func to the list of functions to be called when modules are * bound into this program. */ /* set lock for dyld data structures */ set_lock(); if(link_module_func_list.func == NULL){ link_module_func_list.func = func; } else{ link_module_func = allocate(sizeof(struct link_module_func)); link_module_func->func = func; link_module_func->next = link_module_func_list.next; link_module_func_list.next = link_module_func; } /* TODO: fix this to be lock safe when objects get unloaded */ /* * Call this func for the modules currently bound in the program. */ for(p = &object_images ; ; p = p->next_images){ for(i = 0; i < p->nimages; i++){ /* skip modules that that are not linked */ link_state = GET_LINK_STATE(p->images[i].module); if(link_state != LINKED && link_state != FULLY_LINKED) continue; release_lock(); func(&(p->images[i].module)); set_lock(); } if(p->next_images == NULL) break; } for(q = &library_images ; ; q = q->next_images){ for(i = 0; i < q->nimages; i++){ for(j = 0; j < q->images[i].image.dyst->nmodtab; j++){ link_state = GET_LINK_STATE(q->images[i].modules[j]); /* skip modules that that not being linked */ if(link_state != LINKED && link_state != FULLY_LINKED) continue; release_lock(); func(q->images[i].modules + j); set_lock(); } } if(q->next_images == NULL) break; } /* release lock for dyld data structures */ release_lock(); } /* * call_registered_funcs_for_linked_modules() calls the functions registered to * be called when modules get linked for modules that are in the relocatated * state and sets them to the register state. */ void call_registered_funcs_for_linked_modules( void) { unsigned long i, j; enum link_state link_state; struct library_images *q; struct segment_command *linkedit_segment; struct dysymtab_command *dyst; struct dylib_module *dylib_modules; /* * First for the object_images modules that have been relocated call * the functions registered to be called when modules get linked. * Because objects can be unloaded and we have to release the lock to * call the registered functions we need to repeatedly pass through the * data structures for the loaded objects after each call until we get * through cleanly. */ while(call_registered_funcs_for_linked_modules_in_objects() == TRUE) ; /* * Next for the library modules that have been relocated call the * functions registered to be called when modules get linked. */ q = &library_images; do{ for(i = 0; i < q->nimages; i++){ linkedit_segment = q->images[i].image.linkedit_segment; dyst = q->images[i].image.dyst; dylib_modules = (struct dylib_module *) (q->images[i].image.vmaddr_slide + linkedit_segment->vmaddr + dyst->modtaboff - linkedit_segment->fileoff); for(j = 0; j < dyst->nmodtab; j++){ /* skip modules that that not in the relocated state */ link_state = GET_LINK_STATE(q->images[i].modules[j]); /* skip modules that that not being linked */ if(link_state != RELOCATED) continue; SET_LINK_STATE(q->images[i].modules[j], REGISTERING); if(link_module_func_list.func == NULL) continue; call_funcs_for_link_module(q->images[i].modules + j); } } q = q->next_images; }while(q != NULL); } /* * call_registered_funcs_for_linked_modules_in_objects() goes through the list * of objects and finds an object that is in the relocated state and then calls * the functions registered to be called for linked modules for at most one * object image. If a functions are called TRUE is returned. If no functions * are called (because there are no object images left in the relocated state) * then FALSE is returned. */ static enum bool call_registered_funcs_for_linked_modules_in_objects( void) { unsigned long i; enum link_state link_state; struct object_images *p; /* * First for the object_images modules that have been relocated call * the functions registered to be called when modules get linked. */ p = &object_images; do{ for(i = 0; i < p->nimages; i++){ /* skip modules that that not in the relocated state */ link_state = GET_LINK_STATE(p->images[i].module); if(link_state != RELOCATED) continue; SET_LINK_STATE(p->images[i].module, REGISTERING); if(link_module_func_list.func == NULL) continue; call_funcs_for_link_module(&(p->images[i].module)); return(TRUE); } p = p->next_images; }while(p != NULL); return(FALSE); } /* * call_funcs_for_link_module() calls each of the functions that were register * to be called when modules are bound for the new module now being bound into * the program. */ static void call_funcs_for_link_module( module_state *module) { struct link_module_func *link_module_func; for(link_module_func = &link_module_func_list; link_module_func != NULL && link_module_func->func != NULL; link_module_func = link_module_func->next){ /* * Since functions can only be added and not removed the * link_module_func_list will be valid when we get the lock back and * at worse may contain more functions than we started the loop. * If this happens a function will get called twice on the same * module. */ release_lock(); link_module_func->func(module); set_lock(); } } void register_func_for_unlink_module( void (*func)(module_state *module)) { struct unlink_module_func *unlink_module_func; /* set lock for dyld data structures */ set_lock(); /* * Add this func to the list of functions to be called when modules are * unlinked from this program. */ if(unlink_module_func_list.func == NULL){ unlink_module_func_list.func = func; } else{ unlink_module_func = allocate(sizeof(struct unlink_module_func)); unlink_module_func->func = func; unlink_module_func->next = unlink_module_func_list.next; unlink_module_func_list.next = unlink_module_func; } /* TODO: call these unlink_module functions at some point */ /* release lock for dyld data structures */ release_lock(); } void register_func_for_replace_module( void (*func)(module_state *oldmodule, module_state *newmodule)) { struct replace_module_func *replace_module_func; /* set lock for dyld data structures */ set_lock(); /* * Add this func to the list of functions to be called when modules are * replaced in this program. */ if(replace_module_func_list.func == NULL){ replace_module_func_list.func = func; } else{ replace_module_func = allocate(sizeof(struct replace_module_func)); replace_module_func->func = func; replace_module_func->next = replace_module_func_list.next; replace_module_func_list.next = replace_module_func; } /* TODO: call these replace_module functions at some point */ /* release lock for dyld data structures */ release_lock(); }