/* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * 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 extern void fork_mach_init(void); #import "stuff/openstep_mach.h" #import #import #import #import #import #import "inline_strcmp.h" #import "dyld_debug_defs.h" #import "lock.h" #import "debug.h" #import "images.h" #import "symbols.h" #import "reloc.h" #import "errors.h" #import "allocate.h" #import "register_funcs.h" #import "mod_init_funcs.h" #import "dyld_init.h" /* this header file is created by mig */ #import "_dyld_event.h" /* 4 minuite or 240 second or 240000 milliseconds timeouts */ #ifndef __MACH30__ static const unsigned long send_timeout = 240000; #endif static const unsigned long rcv_timeout = 240000; /* * This used to trace what is going on the the dyld debug interface and is set * to TRUE in pickup_environment_variables() in dyld_init.c if the environment * variable DYLD_DEBUG_TRACE is set. */ enum bool dyld_debug_trace = FALSE; /* * This stuff should be produced by mig(1) but it isn't so it is copied from * from the mig generated files to create these structures. This is only needed * so the correct size of the request and reply messages can be allocated by * the server loop. */ struct inbound_message_request { #ifdef __MACH30__ mach_msg_header_t head; union{ struct{ mach_msg_body_t msgh_body; mach_msg_port_descriptor_t subscriber; NDR_record_t NDR; boolean_t inconsistent_data_ok; } subscribe_to_events_request; struct{ mach_msg_body_t msgh_body; mach_msg_ool_descriptor_t name; NDR_record_t NDR; boolean_t inconsistent_data_ok; mach_msg_type_number_t nameCnt; } defining_module_request; struct{ NDR_record_t NDR; boolean_t inconsistent_data_ok; module_t module; } is_module_bound_request; struct{ NDR_record_t NDR; boolean_t inconsistent_data_ok; module_t module; } module_name_request; struct{ NDR_record_t NDR; boolean_t inconsistent_data_ok; module_t module; } bind_module_request; }u; mach_msg_trailer_t trailer; #else /* !defined(__MACH30__) */ msg_header_t head; msg_type_t inconsistent_data_okType; boolean_t inconsistent_data_ok; union{ struct{ msg_type_t subscriberType; mach_port_t subscriber; } subscribe_to_events_request; struct{ msg_type_long_t nameType; string_t name; } defining_module_request; struct{ msg_type_t moduleType; module_t module; } is_module_bound_request; struct{ msg_type_t moduleType; module_t module; } module_name_request; struct{ msg_type_t moduleType; module_t module; } bind_module_request; }u; #endif /* __MACH30__ */ }; struct inbound_message_reply { #ifdef __MACH30__ mach_msg_header_t head; union{ struct{ NDR_record_t NDR; kern_return_t RetCode; } subscribe_to_events_reply; struct{ NDR_record_t NDR; kern_return_t RetCode; module_t module; dyld_debug_return_t return_value; } defining_module_reply; struct{ NDR_record_t NDR; kern_return_t RetCode; boolean_t bound; dyld_debug_return_t return_value; } is_module_bound_reply; struct{ mach_msg_body_t msgh_body; mach_msg_ool_descriptor_t image_name; mach_msg_ool_descriptor_t module_name; NDR_record_t NDR; mach_msg_type_number_t image_nameCnt; mach_msg_type_number_t module_nameCnt; dyld_debug_return_t return_value; } module_name_reply; struct{ NDR_record_t NDR; kern_return_t RetCode; dyld_debug_return_t return_value; } bind_module_reply; }u; #else /* !defined(__MACH30__) */ msg_header_t head; msg_type_t RetCodeType; kern_return_t RetCode; msg_type_t _dyld_debug_RetCodeType; enum dyld_debug_return _dyld_debug_RetCode; union{ #ifndef __MWERKS__ struct{ /* just dyld_debug return value */ } subscribe_to_events_reply; #endif struct{ msg_type_t moduleType; module_t module; } defining_module_reply; struct{ msg_type_t boundType; boolean_t bound; } is_module_bound_reply; struct{ msg_type_long_t image_nameType; string_t image_name; msg_type_long_t module_nameType; string_t module_name; } module_name_reply; #ifndef __MWERKS__ struct{ /* just dyld_debug return value */ } bind_module_reply; #endif }u; #endif /* __MACH30__ */ }; boolean_t _dyld_debug_server( struct inbound_message_request *request, struct inbound_message_reply *reply); /* * These are uses for inbound messages. After these values are set they are * copied into pointers in the (__DATA,__dyld) section in the images. */ mach_port_t debug_port = MACH_PORT_NULL; mach_port_t debug_thread = MACH_PORT_NULL; /* * This is used for outbound messages. */ struct event_port { mach_port_t port; struct event_port *next; }; static struct event_port event_port_list = { MACH_PORT_NULL, NULL }; /* * These are used by send_past_events() to send a DYLD_IMAGE_ADDED event for * the dynamic linker itself if it is a separate image. Know if it is a * separate image dyld_image is declared as common symbol here (with a value * of 0 or FALSE) and as a data symbol in dyld_reloc.c as TRUE and will be * over ridden from there if the dynamic linker is linked as a separate image. */ boolean_t dyld_image; /* these must be common, uninitialized globals */ struct mach_header *dyld_image_header; unsigned long dyld_image_vmaddr_slide; char *dyld_image_name; /* * This is used by dyld_event_MsgError() and send_event() to know when a * message to an event_port failed and it should be removed from the event_port * list. */ static boolean_t event_MsgError = FALSE; /* * core_image is set by start_debug_thread() from it's parameter start_from_core * so the server routines for inbound messages know if they are running in a * core file. */ static enum bool core_image = FALSE; static enum dyld_debug_return set_dyld_data( unsigned long offset, unsigned long data); static void server_loop( void); static enum dyld_debug_return send_past_events( mach_port_t subscriber); /* * These variables are used for the dyld/gdb interface as described in the * header file . */ unsigned int gdb_dyld_version = 2; unsigned int gdb_nobject_images = NOBJECT_IMAGES; unsigned int gdb_nlibrary_images = NLIBRARY_IMAGES; unsigned int gdb_object_image_size = sizeof(struct object_image); unsigned int gdb_library_image_size = sizeof(struct library_image); /* * gdb_dyld_state_changed() is a dummy routine called by dyld after images get * added or removed/ Gdb is expected to set a break point at * gdb_dyld_state_changed() then re-read dyld internal data as specified in * the header file . */ void gdb_dyld_state_changed( void) { /* do nothing */ } /* * These booleans are used to allow the debugging thread to get the the state * of the dyld data structures to a known state. */ volatile enum bool debug_thread_waiting_for_lock = FALSE; volatile enum bool lock_in_multiply_defined_handler = FALSE; static volatile enum bool lock_in_linkedit_error_handler = FALSE; void multiply_defined_enter(void) { lock_in_multiply_defined_handler = TRUE; /* * If the debug thread gets this error let it run the user's multiply * defined error handler if any or let the error get printed and exit. */ if(debug_thread != mach_thread_self()) return; if(debug_thread_waiting_for_lock == TRUE) thread_suspend(mach_thread_self()); } void multiply_defined_exit(void) { lock_in_multiply_defined_handler = FALSE; } void linkedit_error_enter(void) { /* * If the debug thread gets this error let it run the user's linkedit * error hander if any or let the error get printed and exit. */ if(debug_thread != mach_thread_self()) return; lock_in_linkedit_error_handler = TRUE; if(debug_thread_waiting_for_lock == TRUE) thread_suspend(mach_thread_self()); } /* * start_debug_thread() is called either from an external process that has * created a new thread in the process and jumps here or from a process that * created a task from a core file and then created a thread that jumps here. * They get the address of this routine from the (__DATA,__dyld) section of the * images which gets set in images.c when the image gets loaded. * * This routine allocates the debug_port for inbound messages and allocates a * port set for outbound messages. Then this thread loops and responds to * inbound messges. This must use only mach calls when core_image is TRUE as * it is being run from a core image. */ void start_debug_thread( enum bool start_from_core) { kern_return_t r; /* * The CORE_DEBUG is used to debug recreating a task from a core file. * By writing different values as we proceed through the logic the * task that created us can see where we are by looking at our memory * with vm_read()'s. This is basicly a poor man's printf. */ #ifdef CORE_DEBUG set_dyld_data(24, 1); #endif if(dyld_debug_trace) print("start_debug_thread called\n"); /* * This is needed when we are started from a core file to get the * valid mach task self port so we can do the mach calls needed for * this debug thread. */ if(start_from_core == TRUE) fork_mach_init(); #ifdef CORE_DEBUG set_dyld_data(24, 2); #endif /* * debug_thread is the thread id for this thread which responds to * inbound messages. It's value is set into the (__DATA,__dyld) * section so external processes know which thread to not suppend * in the task when sending it a message. */ debug_thread = mach_thread_self(); if(dyld_debug_trace) print("debug_thread %d\n", debug_thread); #ifdef CORE_DEBUG set_dyld_data(24, 3); #endif /* * Put the value of debug_thread into the executable images' * (__DATA,__dyld) section. This uses a lock to try to protect * setting debug_thread in the (__DATA,__dyld) section so that if more * than one task try to start a thread only one should succeed. But * there is a critical region. Where if the lock is taken and we get * reschuled before debug_thread gets set in the (__DATA,__dyld) * section. If in this region another task trys to start the debugging * thread after seeing debug_thread is not set it will suppended all * the threads in this task not knowing that one of them was the * debugging thread starting up holding the lock. Then the lock will * never be released and debug_thread in the (__DATA,__dyld) section * will never be set as the thread holding the lock is suppended. */ if(lock_is_set(debug_thread_lock)){ thread_terminate(mach_thread_self()); } if(set_dyld_data(16, debug_thread) != DYLD_SUCCESS){ clear_lock(debug_thread_lock); thread_terminate(mach_thread_self()); } #ifdef CORE_DEBUG set_dyld_data(24, 4); #endif core_image = start_from_core; /* * debug_port is the port for inbound messages. It's value is set into * the (__DATA,__dyld) section so external processes know the port to * send messages to. */ #ifdef __MACH30__ if((r = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &debug_port)) != KERN_SUCCESS){ #else if((r = port_allocate(mach_task_self(), (int *)&debug_port)) != KERN_SUCCESS){ #endif clear_lock(debug_thread_lock); (void)set_dyld_data(16, MACH_PORT_NULL); thread_terminate(mach_thread_self()); } if(dyld_debug_trace) print("debug_port %d\n", debug_port); #ifdef CORE_DEBUG set_dyld_data(24, 5); #endif /* * Put the value of debug_port into the executable images' * (__DATA,__dyld) section. Since the debug_thread was set at an * offset (16) greater than where we put the debug port (12) an error * can occur. */ (void)set_dyld_data(12, debug_port); #ifdef CORE_DEBUG set_dyld_data(24, 6); #endif /* * This thread now becomes the debug thread that responds to inbound * messages. */ server_loop(); } /* * set_dyld_data() sets the unsigned long data into the (__DATA,__dyld) * section of the executable at the specified offset. If the executable has * a (__DATA,__dyld) section and it is large enough such that at the offset * an unsigned long can be placed there this routine returns DYLD_SUCCESS. * else it return DYLD_FAILURE. */ static enum dyld_debug_return set_dyld_data( unsigned long offset, unsigned long data) { unsigned long i, j; struct load_command *lc, *load_commands; struct segment_command *sg; struct section *s; /* the first image in the object_images is the executable */ load_commands = (struct load_command *) ((char *)object_images.images[0].image.mh + sizeof(struct mach_header)); lc = load_commands; for(i = 0; i < object_images.images[0].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++){ if(strcmp(s->segname, "__DATA") == 0 && strcmp(s->sectname, "__dyld") == 0){ if(s->size >= offset + sizeof(unsigned long)){ *((long *)(s->addr + offset)) = data; return(DYLD_SUCCESS); } } s++; } } lc = (struct load_command *)((char *)lc + lc->cmdsize); } return(DYLD_FAILURE); } /* * server_loop() is the debug thread's loop that responds to inbound messages. */ static void server_loop(void) { struct inbound_message_request request; struct inbound_message_reply reply; kern_return_t r; boolean_t b; struct thread_basic_info info; unsigned int info_count, suspend_count, resume_count, i; mach_port_t resumed_thread; /* enter server loop */ while(TRUE){ /* receive an inbound message */ #ifdef __MACH30__ r = mach_msg( &request.head, /* msg */ MACH_RCV_MSG, /* option */ 0, /* send_size */ sizeof(struct inbound_message_request), /* receive_limit */ debug_port, /* receive_name */ MACH_MSG_TIMEOUT_NONE, /* timeout */ MACH_PORT_NULL); /* notify */ #else /* * Set the port the message will be received on to the debug_port, * and the maximum size of the message that will be received to the * size of an inbound message. */ request.head.msg_local_port = debug_port; request.head.msg_size = sizeof(struct inbound_message_request); r = msg_receive(&request.head, MACH_MSG_OPTION_NONE, 0); #endif #ifdef CORE_DEBUG set_dyld_data(24, 7); #endif /* * For any message receive error it is just ignored. This happens * in the normal course for example: * * The interface that gets the debug_port in this task from another * task must also translate the debug_port it read out of this * task's (__DATA,__dyld) section we left for it. It does this by * doing a port_extract/port_insert pair. When this is done when * in the msg_receive() above it will terminate between the calls * with RCV_INVALID_PORT. So if this is the case continue back * around the server loop into msg_receive() again. * * Since there maybe more than one debugger sending messages a * message sent by one that has an error is just ignored so that * others can still be processed. That is we don't stop the debug * thread once it has been started. */ #ifdef __MACH30__ if(r != MACH_MSG_SUCCESS) #else if(r != RCV_SUCCESS) #endif continue; /* * Now before we feed the request to the server routines we need * to get the dyld data structures into a known state. All other * threads are suspended at this point. To get things into a known * state we set debug_thread_waiting_for_lock. Then if a thread has * the lock we resume it until the lock is released knowing that * the thread will suppend itself if debug_thread_waiting_for_lock * is set when it releases the lock. This resuming of the thread * that has the lock needs to loop because if another task wants to * send an inbound message it will suspend all threads again and * the thread that has the lock may not have released the lock or * gotten to an error handler. */ /* davidp 1/7/99 * release_lock() sets thread_that_has_dyld_lock to MACH_PORT_NULL * and then resuspends the thread that had the lock. This is fine a * long as that thread's initial suspend count was 1 - the resuspend * logic just before cant_unlock: accounts for the suspend done in * release_lock(). However, if the thread holding the lock has a * suspend count greater than 1 we're hosed since the resuspend * logic operates on thread_that_has_dyld_lock which was set to * MACH_PORT_NULL in release_lock(). To get around this I added a * local that captures thread_that_has_dyld_lock and the rest of * this function only operates on that local. */ if(core_image == TRUE) goto cant_unlock; resume_count = 0; resumed_thread = MACH_PORT_NULL; debug_thread_waiting_for_lock = TRUE; if(dyld_lock == TRUE && thread_that_has_dyld_lock != MACH_PORT_NULL && lock_in_multiply_defined_handler == FALSE && lock_in_linkedit_error_handler == FALSE){ info_count = THREAD_BASIC_INFO_COUNT; r = thread_info(thread_that_has_dyld_lock, THREAD_BASIC_INFO, (thread_info_t)&info, &info_count); /* * If can't get the thread info for the thread that had the * lock it must have been aborted. So the lock won't be released * and the if the core flag is not set no information will be * returned. */ if(r != KERN_SUCCESS) goto cant_unlock; /* davidp 1/7/99 * Only capture this once per lock switch session. If the lock * is held thread_that_has_dyld_lock will be set to * MACH_PORT_NULL when the lock is released and we will lose * resumed_thread and not restore the thread's suspend count * properly */ if(resumed_thread == MACH_PORT_NULL) resumed_thread = thread_that_has_dyld_lock; for(suspend_count = info.suspend_count; suspend_count > 0; suspend_count--){ r = thread_resume(resumed_thread); resume_count++; /* * If the suspend count was already 0, then stop the * thread_resumes as the thread is runable and this is what * we wanted. */ if(r == KERN_FAILURE) break; /* * If the thread is not a thread, the thread must have been * aborted so again the lock won't be released. */ if(r != KERN_SUCCESS) goto cant_unlock; } /* * The thread has the lock is now runable so switch to it so it * can release the lock. The loop will continue to switch to * this thread until the lock is released or a handler is called * by the thread that has the lock. */ r = thread_switch(resumed_thread, SWITCH_OPTION_NONE, 0); /* * Again if the thread is not a thread, the thread must have * been aborted so again the lock won't be released. */ if(r != KERN_SUCCESS) goto cant_unlock; } while((dyld_lock == TRUE || thread_that_has_dyld_lock != MACH_PORT_NULL) && lock_in_multiply_defined_handler == FALSE && lock_in_linkedit_error_handler == FALSE); /* * If we did any thread_resume()'s on the thread_that_has_dyld_lock * we need to get it's suspend count back to the correct value. One * thread_suppend() was done by the thread itself when if it got * to the point to released the lock (that is it did not end up in * an error handler). */ if(resume_count != 0){ if(dyld_lock == FALSE) resume_count--; #if !defined(__GONZO_BUNSEN_BEAKER__) && !defined(__HERA__) && !defined(__OPENSTEP__) usleep(20000); #endif for(i = 0; i < resume_count; i++){ r = thread_suspend(resumed_thread); /* * If the thread is not a thread, the thread must have * been aborted the lock is released so we are done. */ if(r != KERN_SUCCESS) break; } resumed_thread = MACH_PORT_NULL; } cant_unlock: if(core_image == FALSE) debug_thread_waiting_for_lock = FALSE; /* * Feed into server (_dyld_debug_server() is created by mig). */ b = _dyld_debug_server(&request, &reply); if(b == FALSE){ /* * The wrong message ID is this case, this means that it might * be a new message type in the libdyld interface but the * dynamic linker being talked to here is old. Ignore this * message and assume the sender has some timeout waiting for * the reply to know the message will not be replied to. */ continue; } /* send the reply */ #ifdef __MACH30__ r = mach_msg( &reply.head, /* msg */ MACH_SEND_MSG, /* option */ reply.head.msgh_size, /* send_size */ 0, /* receive_limit */ MACH_PORT_NULL, /* receive_name */ MACH_MSG_TIMEOUT_NONE, /* timeout */ MACH_PORT_NULL); /* notify */ #else reply.head.msg_local_port = debug_port; msg_send(&reply.head, MACH_MSG_OPTION_NONE, 0); #endif } } /* * server_dyld_debug_defining_module() is the server size of * _dyld_debug_defining_module() which is passed a symbol name and returns * which module the symbol name is being or would be used from. */ #ifdef __MACH30__ kern_return_t #else enum dyld_debug_return #endif server_dyld_debug_defining_module( mach_port_t debug_port, boolean_t inconsistent_data_ok, char *name, unsigned int nameCnt, struct dyld_debug_module *module #ifdef __MACH30__ , dyld_debug_return_t *retval #endif ) { struct nlist *defined_symbol; module_state *defined_module; struct image *defined_image; struct library_image *defined_library_image; enum dyld_debug_return return_value; /* first set all the fields of the dyld debug module to zero */ memset(module, '\0', sizeof(struct dyld_debug_module)); /* * If the lock is set and the caller does not want inconsistent data * then just return DYLD_INCONSISTENT_DATA and do nothing else. */ if(dyld_lock == TRUE && inconsistent_data_ok == FALSE){ return_value = DYLD_INCONSISTENT_DATA; goto done; } /* look up this symbol name */ lookup_symbol(name, NULL, NULL, FALSE, &defined_symbol, &defined_module, &defined_image, &defined_library_image, NULL); /* * If there is no symbol by this name in the images so return * DYLD_INVALID_ARGUMENTS to indicate this. */ if(defined_symbol == NULL){ return_value = DYLD_INVALID_ARGUMENTS; goto done; } /* set the fields of the dyld debug module */ module->header = defined_image->mh; module->vmaddr_slide = defined_image->vmaddr_slide; if(defined_library_image != NULL) module->module_index = defined_module - defined_library_image->modules; return_value = DYLD_SUCCESS; done: (void)vm_deallocate(mach_task_self(), (vm_address_t)name, (vm_size_t)nameCnt); #ifdef __MACH30__ *retval = return_value; return(KERN_SUCCESS); #else return(return_value); #endif } /* * server_dyld_debug_is_module_bound() the server size of * _dyld_debug_is_module_bound() which is passed a dyld_debug_module struct and * sets bound to TRUE if the module is bound and FALSE otherwise. */ #ifdef __MACH30__ kern_return_t #else enum dyld_debug_return #endif server_dyld_debug_is_module_bound( mach_port_t debug_port, boolean_t inconsistent_data_ok, struct dyld_debug_module module, boolean_t *bound #ifdef __MACH30__ , dyld_debug_return_t *retval #endif ) { unsigned long i, j; struct object_images *p; struct library_images *q; enum link_state link_state; enum dyld_debug_return return_value; /* Assume the module is not linked */ *bound = FALSE; /* * If the lock is set and the caller does not want inconsistent data * then just return DYLD_INCONSISTENT_DATA and do nothing else. */ if(dyld_lock == TRUE && inconsistent_data_ok == FALSE){ return_value = DYLD_INCONSISTENT_DATA; goto done; } /* * Look through the object images and then the library images trying * to find this module. Then if found see if it is linked. */ 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; if(module.header == p->images[i].image.mh){ if(module.module_index != 0) return(DYLD_INVALID_ARGUMENTS); if(link_state == LINKED || link_state == FULLY_LINKED) *bound = TRUE; return_value = DYLD_SUCCESS; goto done; } } if(p->next_images == NULL) break; } for(q = &library_images ; ; q = q->next_images){ for(i = 0; i < q->nimages; i++){ if(module.header == q->images[i].image.mh){ if(module.module_index >= q->images[i].image.dyst->nmodtab) return(DYLD_INVALID_ARGUMENTS); j = module.module_index; link_state = GET_LINK_STATE(q->images[i].modules[j]); if(link_state == LINKED || link_state == FULLY_LINKED) *bound = TRUE; return_value = DYLD_SUCCESS; goto done; } } if(q->next_images == NULL) break; } return_value = DYLD_FAILURE; done: #ifdef __MACH30__ *retval = return_value; return(KERN_SUCCESS); #else return(return_value); #endif } /* * server_dyld_debug_bind_module() is the server side of * _dyld_debug_bind_module() which binds the modules specified by * dyld_debug_module into the program. */ #ifdef __MACH30__ kern_return_t #else enum dyld_debug_return #endif server_dyld_debug_bind_module( mach_port_t debug_port, boolean_t inconsistent_data_ok, struct dyld_debug_module module #ifdef __MACH30__ , dyld_debug_return_t *retval #endif ) { unsigned long i, j; struct object_images *p; struct library_images *q; enum link_state link_state; enum dyld_debug_return return_value; /* * If the lock is set and the caller does not want inconsistent data * then just return DYLD_INCONSISTENT_DATA and do nothing else. */ if(dyld_lock == TRUE && inconsistent_data_ok == FALSE){ return_value = DYLD_INCONSISTENT_DATA; goto done; } /* * Look through the object images and then the library images trying * to find this module. Then if found see if it is linked and if not * force it to be linked. */ 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; if(module.header == p->images[i].image.mh){ if(module.module_index != 0){ return_value = DYLD_INVALID_ARGUMENTS; goto done; } /* * Object images are never in a linkable state, that is they * have been linked or can't be linked (they were removed o * replaced). */ if(link_state == LINKED || link_state == FULLY_LINKED){ return_value = DYLD_SUCCESS; goto done; } else{ return_value = DYLD_INVALID_ARGUMENTS; goto done; } } } if(p->next_images == NULL) break; } for(q = &library_images ; ; q = q->next_images){ for(i = 0; i < q->nimages; i++){ if(module.header == q->images[i].image.mh){ if(module.module_index >= q->images[i].image.dyst->nmodtab){ return_value = DYLD_INVALID_ARGUMENTS; goto done; } j = module.module_index; link_state = GET_LINK_STATE(q->images[i].modules[j]); /* * We must beable to get the lock to bind this module, * if the lock is set or we are in an error handler we * don't try to bind this module. */ if(dyld_lock == TRUE || lock_in_multiply_defined_handler == TRUE || lock_in_linkedit_error_handler == TRUE){ return_value = DYLD_INCONSISTENT_DATA; goto done; } /* * Library modules in a linked state simply return success * as nothing needs to be done. Otherwise if the module is * in any other state except unlinked (it was removed or * replaced) it is an error as it can't be linked again. */ if(link_state == LINKED || link_state == FULLY_LINKED){ return_value = DYLD_SUCCESS; goto done; } if(link_state != UNLINKED && link_state != PREBOUND_UNLINKED){ return_value = DYLD_INVALID_ARGUMENTS; goto done; } /* * Since this is the debug thread responding to a * message all other threads should have been suspened by * task sending the message and with the tests above on the * lock state this we should always beable to get the lock. */ set_lock(); /* * If this module is in the prebound unlinked state first * undo the prebinding and then set it to the unlinked * state. */ if(link_state == PREBOUND_UNLINKED){ undo_prebinding_for_library_module( q->images[i].modules + j, &(q->images[i].image), q->images + i); SET_LINK_STATE(q->images[i].modules[j], UNLINKED); } /* * This module is not linked. So force to be linked, * resolve undefineds, relocate modules that got * linked in and check and report undefined symbols. */ link_library_module(q->images + i, &(q->images[i].image), q->images[i].modules + j, FALSE, FALSE, FALSE); resolve_undefineds(FALSE, FALSE); relocate_modules_being_linked(FALSE); check_and_report_undefineds(FALSE); call_registered_funcs_for_add_images(); call_registered_funcs_for_linked_modules(); call_image_init_routines(FALSE); call_module_initializers(FALSE, FALSE, FALSE); /* release lock for dyld data structures */ release_lock(); return_value = DYLD_SUCCESS; goto done; } } if(q->next_images == NULL) break; } return_value = DYLD_FAILURE; done: #ifdef __MACH30__ *retval = return_value; return(KERN_SUCCESS); #else return(return_value); #endif } /* * server_dyld_debug_module_name() the server size of _dyld_debug_module_name() * which is passed a dyld_debug_module struct and sets image_name and * module_name to the names for the module and the nameCnts to the sizes. */ #ifdef __MACH30__ kern_return_t #else enum dyld_debug_return #endif server_dyld_debug_module_name( mach_port_t debug_port, boolean_t inconsistent_data_ok, struct dyld_debug_module module, char **image_name, unsigned int *image_nameCnt, char **module_name, unsigned int *module_nameCnt #ifdef __MACH30__ , dyld_debug_return_t *retval #endif ) { unsigned long i, j; struct object_images *p; struct library_images *q; struct segment_command *linkedit_segment; struct symtab_command *st; struct dysymtab_command *dyst; char *strings; struct dylib_module *dylib_modules; struct load_command *lc; struct dylinker_command *dyld; enum dyld_debug_return return_value; enum link_state link_state; /* Assume the module is not valid */ *image_name = NULL; *image_nameCnt = 0; *module_name = NULL; *module_nameCnt = 0; /* * If the lock is set and the caller does not want inconsistent data * then just return DYLD_INCONSISTENT_DATA and do nothing else. */ if(dyld_lock == TRUE && inconsistent_data_ok == FALSE){ return_value = DYLD_INCONSISTENT_DATA; goto done; } /* * Look through the object images and then the library images trying * to find this module. Then if found see if it is linked. */ 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; if(module.header == p->images[i].image.mh){ if(module.module_index != 0){ return_value = DYLD_INVALID_ARGUMENTS; goto done; } *image_name = p->images[i].image.name; *image_nameCnt = strlen(*image_name); return_value = DYLD_SUCCESS; goto done; } } if(p->next_images == NULL) break; } for(q = &library_images ; ; q = q->next_images){ for(i = 0; i < q->nimages; i++){ if(module.header == q->images[i].image.mh){ if(module.module_index >= q->images[i].image.dyst->nmodtab){ return_value = DYLD_INVALID_ARGUMENTS; goto done; } j = module.module_index; *image_name = q->images[i].image.name; *image_nameCnt = strlen(*image_name); linkedit_segment = q->images[i].image.linkedit_segment; st = q->images[i].image.st; dyst = q->images[i].image.dyst; 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); *module_name = strings + dylib_modules[j].module_name, *module_nameCnt = strlen(*module_name); return_value = DYLD_SUCCESS; goto done; } } if(q->next_images == NULL) break; } /* * This could be the dyld image. In that case try to return the name * in the LC_ID_DYLINKER command. */ if(module.header == dyld_image_header){ if(dyld_image_name == NULL){ lc = (struct load_command *) ((char *)dyld_image_header + sizeof(struct mach_header)); dyld = NULL; for(i = 0; i < dyld_image_header->ncmds; i++){ switch(lc->cmd){ case LC_ID_DYLINKER: if(dyld == NULL){ dyld = (struct dylinker_command *)lc; dyld_image_name = (char *)dyld + dyld->name.offset; } break; } lc = (struct load_command *)((char *)lc + lc->cmdsize); } } if(dyld_image_name != NULL){ *image_name = dyld_image_name; *image_nameCnt = strlen(*image_name); return_value = DYLD_SUCCESS; goto done; } } return_value = DYLD_FAILURE; done: #ifdef __MACH30__ *retval = return_value; return(KERN_SUCCESS); #else return(return_value); #endif } #ifdef DYLD_PROFILING /* * server_dyld_debug_monoutput() the server size of _dyld_debug_monoutput() * which calls dyld_monoutput. */ #ifdef __MACH30__ kern_return_t #else enum dyld_debug_return #endif server_dyld_debug_monoutput( mach_port_t debug_port #ifdef __MACH30__ , dyld_debug_return_t *retval #endif ) { dyld_monoutput(); #ifdef __MACH30__ *retval = DYLD_SUCCESS; return(KERN_SUCCESS); #else return(DYLD_SUCCESS); #endif } #endif /* DYLD_PROFILING */ /* * server_dyld_debug_add_event_subscriber() is the server size of * _dyld_debug_add_event_subscriber() which adds the subscriber to the list of * event ports that dyld event messages are sent to. Then all past events are * sent to the subscriber. */ #ifdef __MACH30__ kern_return_t #else enum dyld_debug_return #endif server_dyld_debug_add_event_subscriber( mach_port_t debug_port, boolean_t inconsistent_data_ok, mach_port_t subscriber #ifdef __MACH30__ , dyld_debug_return_t *retval #endif ) { struct event_port *event_port; enum dyld_debug_return return_value; /* * For core files server_dyld_debug_subscribe_to_events() is a valid * call but since the program is not running only past events will be * sent. */ /* * If the lock is set and the caller does not want inconsistent data * then just return DYLD_INCONSISTENT_DATA and do nothing else. */ if(dyld_lock == TRUE && inconsistent_data_ok == FALSE){ return_value = DYLD_INCONSISTENT_DATA; goto done; } /* * Send past events to the new subscriber. */ if(send_past_events(subscriber) != DYLD_SUCCESS){ return_value = DYLD_FAILURE; goto done; } /* * Add this port to the list of event ports. */ if(event_port_list.port == MACH_PORT_NULL){ event_port_list.port = subscriber; } else{ event_port = allocate_with_no_error_check( sizeof(struct event_port)); if(event_port == NULL){ return_value = DYLD_FAILURE; goto done; } event_port->port = subscriber; event_port->next = event_port_list.next; event_port_list.next = event_port; } return_value = DYLD_SUCCESS; done: #ifdef __MACH30__ *retval = return_value; return(KERN_SUCCESS); #else return(return_value); #endif } /* * send_past_events() is called when a new subscriber port is added. This * routine then sends the current state of the dynamic linker over as a series * of events. */ static enum dyld_debug_return send_past_events( mach_port_t subscriber) { unsigned long i, j; struct object_images *p; struct library_images *q; struct dyld_event event; enum link_state link_state; /* * First send all the DYLD_IMAGE_ADDED events for object images and * library images. */ memset(&event, '\0', sizeof(struct dyld_event)); event.type = DYLD_IMAGE_ADDED; if(dyld_image == TRUE){ event.arg[0].header = dyld_image_header; event.arg[0].vmaddr_slide = dyld_image_vmaddr_slide; event_MsgError = FALSE; user_dyld_event_server_callback(subscriber, rcv_timeout, #ifndef __MACH30__ send_timeout, #endif event); if(event_MsgError == TRUE) return(DYLD_FAILURE); } 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; event.arg[0].header = p->images[i].image.mh; event.arg[0].vmaddr_slide = p->images[i].image.vmaddr_slide; event_MsgError = FALSE; user_dyld_event_server_callback(subscriber, rcv_timeout, #ifndef __MACH30__ send_timeout, #endif event); if(event_MsgError == TRUE) return(DYLD_FAILURE); } if(p->next_images == NULL) break; } for(q = &library_images ; ; q = q->next_images){ for(i = 0; i < q->nimages; i++){ event.arg[0].header = q->images[i].image.mh; event.arg[0].vmaddr_slide = q->images[i].image.vmaddr_slide; event_MsgError = FALSE; user_dyld_event_server_callback(subscriber, rcv_timeout, #ifndef __MACH30__ send_timeout, #endif event); if(event_MsgError == TRUE) return(DYLD_FAILURE); } if(q->next_images == NULL) break; } /* * Second send all the DYLD_MODULE_BOUND events for object images and * library images. */ memset(&event, '\0', sizeof(struct dyld_event)); event.type = DYLD_MODULE_BOUND; 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; event.arg[0].header = p->images[i].image.mh; event.arg[0].vmaddr_slide = p->images[i].image.vmaddr_slide; event_MsgError = FALSE; user_dyld_event_server_callback(subscriber, rcv_timeout, #ifndef __MACH30__ send_timeout, #endif event); if(event_MsgError == TRUE) return(DYLD_FAILURE); } 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; 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; event_MsgError = FALSE; user_dyld_event_server_callback(subscriber, rcv_timeout, #ifndef __MACH30__ send_timeout, #endif event); if(event_MsgError == TRUE) return(DYLD_FAILURE); } } if(q->next_images == NULL) break; } /* send end marker for past events */ memset(&event, '\0', sizeof(struct dyld_event)); event.type = DYLD_PAST_EVENTS_END; event_MsgError = FALSE; user_dyld_event_server_callback(subscriber, rcv_timeout, #ifndef __MACH30__ send_timeout, #endif event); if(event_MsgError == TRUE) return(DYLD_FAILURE); return(DYLD_SUCCESS); } /* * send_event() sends the specified event to all the event ports. */ void send_event( struct dyld_event *event) { struct event_port *event_port, *prev; /* * This loop expects "standard delayed free" behavior by free() as we * will use event_port->next after event_port has been free()'ed. */ prev = NULL; for(event_port = &event_port_list; event_port->port != MACH_PORT_NULL; event_port = event_port->next){ event_MsgError = FALSE; user_dyld_event_server_callback(event_port->port, rcv_timeout, #ifndef __MACH30__ send_timeout, #endif *event); if(event_MsgError == TRUE){ #ifdef __MACH30__ (void)mach_port_deallocate(mach_task_self(), event_port->port); #else (void)port_deallocate(mach_task_self(), event_port->port); #endif event_port->port = MACH_PORT_NULL; if(prev != NULL){ prev->next = event_port->next; free(event_port); } } else{ prev = event_port; } if(event_port->next == NULL) return; } } #ifndef __MACH30__ /* * dyld_event_MsgError() is called by user_dyld_event_server_callback() when * there is an error is sending the mach message via * user_dyld_event_server_callback() in send_event() above. * This sets the flag event_MsgError to tell send_event() to remove the * event_port from the list. */ void dyld_event_MsgError( msg_return_t msg_result) { event_MsgError = TRUE; } #endif