/* * 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@ */ #ifndef __OPENSTEP__ /* Rhapsody is not indr'ed yet */ #define NO_INDR_LIBC #endif #import #import #import #import #import #import "stuff/openstep_mach.h" #define mach_error mach_error_not_used #import #undef mach_error #import #import "images.h" #import "symbols.h" #import "errors.h" #import "debug.h" #import "lock.h" #import "dyld_init.h" /* * This used to print the error codes as symbolic values and is set to TRUE in * pickup_environment_variables() in dyld_init.c if the environment variable * DYLD_ERROR_PRINT is set. */ enum bool dyld_error_print = FALSE; /* * These are the pointers to the user's three error handler functions. */ void (*user_undefined_handler)( const char *symbol_name) = NULL; module_state * (*user_multiple_handler)( struct nlist *symbol, module_state *old_module, module_state *new_module) = NULL; void (*user_linkEdit_handler)( enum link_edit_error_class error_class, int error_number, const char *file_name, const char *error_string) = NULL; /* * Error strings are assembled in the variable error_string using one of the * routines below and passed to the link edit error handler through * link_edit_error(). * * The definition of the variable error_string is allocated in it's own section * in section_order.s so it can go at the end of the data segment. Since * normally errors never happen it is never touched and never dirtied. * The definition of error_string is in section_order.s and needs to be kept in * sync with the 'C' definition. */ static char *last; static unsigned long left; static void print_error_class( enum link_edit_error_class error_class); static void print_error_number( enum link_edit_error_class error_class, int error_number); /* * These variables are used for the NSLINKMODULE_OPTION_RETURN_ON_ERROR used * with the NSLinkModule() API. When this option is used return_on_error gets * set to TRUE and the user error handlers are not called. The error * information is then stored away in these variables along with error_string * (see above) and then can be retrieved with the NSLinkEditError() API. * The code through out dyld tests return_on_error in error cases and backs out * any changes if an error occurs. Like error_string NSLinkEditError_fileName * is declared in section_order.s and needs to be kept in sync with the 'C' * definition here. */ enum bool return_on_error = FALSE; extern char NSLinkEditError_fileName[MAXPATHLEN + 1]; enum link_edit_error_class NSLinkEditError_errorClass = 0; int NSLinkEditError_errorNumber = 0; /* * check_and_report_undefineds() checks for undefined symbols and if there are * any it reports them to the user's error handler if one is set. If one is not * set this writes an error message on stderr and exits. */ enum bool check_and_report_undefineds( void) { struct symbol_list *undefined; struct image *primary_image; if(undefined_list.next == &undefined_list) return(TRUE); /* * If the there is a handler and we are not doing return on error * call the user's handler. */ if(user_multiple_handler != NULL && return_on_error == FALSE){ /* * Call the user's undefined symbol handler while there are * undefined symbols and the handler is set. */ while(undefined_list.next != &undefined_list && user_undefined_handler != NULL){ linkedit_error_enter(); release_lock(); user_undefined_handler((const char *)undefined_list.next->name); set_lock(); } } /* create the error message */ error("Undefined symbols:"); for(undefined = undefined_list.next; undefined != &undefined_list; undefined = undefined->next){ if(undefined->image != NULL && undefined->symbol != NULL && force_flat_namespace == FALSE && (undefined->image->mh->flags & MH_TWOLEVEL) == MH_TWOLEVEL){ if(undefined->image->umbrella_name != NULL) add_error_string("%.*s undefined reference to %s expected " "to be defined in ", (int)undefined->image->name_size, undefined->image->umbrella_name, undefined->name); else add_error_string("%s undefined reference to %s expected " "to be defined in ", undefined->image->name, undefined->name); if(GET_LIBRARY_ORDINAL(undefined->symbol->n_desc) == EXECUTABLE_ORDINAL) add_error_string("the executable\n"); else if(GET_LIBRARY_ORDINAL(undefined->symbol->n_desc) == SELF_LIBRARY_ORDINAL){ if(undefined->image->umbrella_name != NULL) add_error_string("%.*s\n", (int)undefined->image->name_size, undefined->image->umbrella_name); else add_error_string("%s\n", undefined->image->name); } else{ /* could make this check if(GET_LIBRARY_ORDINAL(undefined->symbol->n_desc) - 1 > image.ndependent_images) <<< bad LIBRARY_ORDINAL in object file >>> */ primary_image = undefined->image->dependent_images[ GET_LIBRARY_ORDINAL(undefined->symbol->n_desc) - 1]; if(primary_image->umbrella_name != NULL) add_error_string("%.*s\n", (int)primary_image->name_size, primary_image->umbrella_name); else add_error_string("%s\n", primary_image->name); } } else{ add_error_string("%s\n", undefined->name); } } /* * If we are doing return on error record the error information and * return FALSE to indicate there are undefined symbols and to back out * the operation in progess. */ if(return_on_error == TRUE){ NSLinkEditError_fileName[0] = '\0'; NSLinkEditError_errorClass = DYLD_UNDEFINED; NSLinkEditError_errorNumber = 0; return(FALSE); } /* * The default is to write(2) an error message to stderr (file * descriptor 2) and exit if the user does not have a handler. */ write(2, error_string, ERROR_STRING_SIZE - left); /* TODO: figure out what to do for debugging */ halt(); exit(DYLD_EXIT_FAILURE_BASE + DYLD_UNDEFINED); /* just here to keep the compiler happy */ return(FALSE); } /* * multiply_defined_error() is called when there is multiply defined symbol. * It called the user's error handler if one is set and discards the symbol * the user does not want to use. If no handler is set this write an error * message on stderr and exits. */ void multiply_defined_error( char *symbol_name, struct image *new_image, struct nlist *new_symbol, module_state *new_module, char *new_library_name, char *new_module_name, struct image *prev_image, struct nlist *prev_symbol, module_state *prev_module, char *prev_library_name, char *prev_module_name) { module_state *user_module; unsigned long symbol_value; /* * If the there is a handler and we are not doing return on error * call the user's handler. */ if(user_multiple_handler != NULL && return_on_error == FALSE){ multiply_defined_enter(); user_module = user_multiple_handler(prev_symbol, prev_module, new_module); multiply_defined_exit(); /* * If the user wants the new module mark the prev_symbol as * discarded and set the symbol pointers to the new value. */ if(user_module == new_module){ discard_symbol(prev_image, prev_symbol); /* update the symbol pointers to use the new symbol's value */ symbol_value = new_symbol->n_value; if((new_symbol->n_type & N_TYPE) != N_ABS) symbol_value += new_image->vmaddr_slide; change_symbol_pointers_in_flat_images(symbol_name, symbol_value, FALSE); } return; } /* set up the error message */ error("multiple definitions of symbol %s", symbol_name); if(prev_library_name != NULL) add_error_string("%s(%s) definition of %s\n", prev_library_name, prev_module_name, symbol_name); else add_error_string("%s definition of %s\n", prev_module_name, symbol_name); if(new_library_name != NULL) add_error_string("%s(%s) definition of %s\n", new_library_name, new_module_name, symbol_name); else add_error_string("%s definition of %s\n", new_module_name, symbol_name); /* * If we are doing return on error record the error information and * return to the dyld operation to back out the operation in progess. */ if(return_on_error == TRUE){ NSLinkEditError_fileName[0] = '\0'; NSLinkEditError_errorClass = DYLD_MULTIPLY_DEFINED; NSLinkEditError_errorNumber = 0; return; } /* * The default is to write(2) an error message to stderr (file * descriptor 2) and exit if the user does not have a handler. */ write(2, error_string, ERROR_STRING_SIZE - left); /* TODO: figure out what to do for debugging */ halt(); exit(DYLD_EXIT_FAILURE_BASE + DYLD_MULTIPLY_DEFINED); } /* * unlinked_lazy_pointer_handler() is the value store into a lazy symbol * pointer when the reset_lazy_references option to unlink is used. For now * it generates a link_edit_error(). Later it will walk back the stack and * dissassemble the instruction before the return address and figure out the * lazy pointer and what symbol was trying to be called. */ void unlinked_lazy_pointer_handler( void) { error("Call to undefined routine after a NSUnLinkModule with the " "NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES option"); link_edit_error(DYLD_OTHER_ERROR, DYLD_LAZY_BIND, NULL); halt(); exit(DYLD_EXIT_FAILURE_BASE + DYLD_OTHER_ERROR); } /* * dead_lock_error() is called from set_lock() if the same thread that has the * lock again trys to set the lock. This can happen if the user's multiply * defined error handler or linkedit error handler attempts a dyld operation. */ void dead_lock_error( void) { extern enum bool dyld_dead_lock_hang; error("dead lock (dyld operation attempted in a thread already doing " "a dyld operation)"); write(2, error_string, ERROR_STRING_SIZE - left); if(dyld_dead_lock_hang){ for(;;) ; } halt(); exit(DYLD_EXIT_FAILURE_BASE + DYLD_DEAD_LOCK); } /* * link_edit_error() is called when there is a link edit error. It calls the * user's link edit error handler if set. If the handler returns or none is * set an error message is written to stderr and exit is called. */ void link_edit_error( enum link_edit_error_class error_class, int error_number, char *file_name) { enum bool recoverable; recoverable = FALSE; /* * Translate the recoverable error classes to their external error * class. If we are also doing return on error record that the error is * recoverable so we know to return to the caller for these cases. */ if(error_class == DYLD_MACH_RESOURCE_RECOVERABLE){ error_class = DYLD_MACH_RESOURCE; if(return_on_error == TRUE) recoverable = TRUE; } if(error_class == DYLD_OTHER_ERROR && error_number == DYLD_INVALID_ARGS && return_on_error == TRUE){ recoverable = TRUE; } /* * If the there is a handler and we are not doing return on error * call the user's handler. Note the lock is taken when the handler * is called so it can't do any dyld operation. */ if(user_linkEdit_handler != NULL && return_on_error == FALSE){ linkedit_error_enter(); user_linkEdit_handler(error_class, error_number, (const char *)file_name, (const char *)error_string); /* * If the error is of class FILE_ACCESS, FILE_FORMAT or WARNING then * return to the dyld operation. */ if(error_class == DYLD_FILE_ACCESS || error_class == DYLD_FILE_FORMAT || error_class == DYLD_WARNING) return; } /* * If we are doing return on error and the error is not fatal or * recoverable then record the error information and return to the dyld * operation to back out the operation in progess. */ if(return_on_error == TRUE && (error_class == DYLD_FILE_ACCESS || error_class == DYLD_FILE_FORMAT || error_class == DYLD_WARNING || recoverable == TRUE)){ if(file_name == NULL) NSLinkEditError_fileName[0] = '\0'; else{ strncpy(NSLinkEditError_fileName, file_name, MAXPATHLEN); NSLinkEditError_fileName[MAXPATHLEN] = '\0'; } NSLinkEditError_errorClass = error_class; NSLinkEditError_errorNumber = error_number; return; } /* * The default handling for WARNING is to write(2) the message to stderr * (file descriptor 2) and return to the dyld operation. */ if(error_class == DYLD_WARNING){ write(2, error_string, ERROR_STRING_SIZE - left); reset_error_string(); return; } /* * The default handling for all other errors and the processing for all * fatal errors is to write(2) the message to stderr (file descriptor 2) * and exit. */ write(2, error_string, ERROR_STRING_SIZE - left); if(dyld_error_print){ print_error_class(error_class); print_error_number(error_class, error_number); } /* TODO: figure out what to do for debugging */ halt(); exit(DYLD_EXIT_FAILURE_BASE + error_class); } /* * abort() is used by malloc() which drags in too much stuff out of libc. */ void abort( void) { kill(getpid(), SIGABRT); halt(); exit(DYLD_EXIT_FAILURE_BASE + DYLD_UNIX_RESOURCE); } /* * reset_error_string() logicly clears the error string. */ void reset_error_string( void) { last = error_string; left = ERROR_STRING_SIZE; error_string[0] = '\0'; } /* * error() takes the vprintf() style aguments and writes them into the start * of the error buffer preceded with "dyld: ". This is used for non-kernel * errors. */ void error( const char *format, ...) { va_list ap; va_start(ap, format); set_error_string("dyld: "); add_error_string("%s ", executables_name); vadd_error_string(format, ap); add_error_string("\n"); va_end(ap); } /* * system_error() takes the vprintf() style aguments and writes them into the * start of the error buffer preceded with "dyld: " and appends the errno * information. This is used for UNIX system call errors. */ void system_error( int errnum, const char *format, ...) { va_list ap; va_start(ap, format); set_error_string("dyld: "); add_error_string("%s ", executables_name); vadd_error_string(format, ap); add_error_string(" (%s, errno = %d)\n", strerror(errnum), errnum); va_end(ap); } /* * system_error() takes the vprintf() style aguments and writes them into the * start of the error buffer preceded with "dyld: " and appends the mach error * information. This is used for mach call errors. */ void mach_error( kern_return_t r, char *format, ...) { va_list ap; va_start(ap, format); set_error_string("dyld: "); add_error_string("%s ", executables_name); vadd_error_string(format, ap); #ifdef NO_INDR_LIBC add_error_string(" (kern return = %d)\n", r); #else add_error_string(" (%s, kern return = %d)\n", mach_error_string(r), r); #endif va_end(ap); } void set_error_string( const char *format, ...) { va_list ap; va_start(ap, format); vset_error_string(format, ap); va_end(ap); } void vset_error_string( const char *format, va_list ap) { unsigned long new; last = error_string; left = ERROR_STRING_SIZE; /* for now hope the string does not overflow */ #ifdef __OPENSTEP__ new = vsprintf(error_string, format, ap); #else new = vsnprintf(last, left, format, ap); #endif last += new; left -= new; } void add_error_string( const char *format, ...) { va_list ap; va_start(ap, format); vadd_error_string(format, ap); va_end(ap); } void vadd_error_string( const char *format, va_list ap) { unsigned long new; /* for now hope the string does not overflow */ #ifdef __OPENSTEP__ new = vsprintf(last, format, ap); #else new = vsnprintf(last, left, format, ap); #endif last += new; left -= new; } static const char * const link_edit_error_class_names[] = { "DYLD_FILE_ACCESS", "DYLD_FILE_FORMAT", "DYLD_MACH_RESOURCE", "DYLD_UNIX_RESOURCE", "DYLD_OTHER_ERROR", "DYLD_WARNING" }; static void print_error_class( enum link_edit_error_class error_class) { if(error_class <= DYLD_WARNING) print("error_class = %s\n", link_edit_error_class_names[error_class]); else print("error_class = %u\n", error_class); } #import static void print_error_number( enum link_edit_error_class error_class, int error_number) { if(error_class == DYLD_OTHER_ERROR){ if(error_number == DYLD_RELOCATION) print("error_number = DYLD_RELOCATION\n"); else if(error_number == DYLD_LAZY_BIND) print("error_number = DYLD_LAZY_BIND\n"); else if(error_number == DYLD_INDR_LOOP) print("error_number = DYLD_INDR_LOOP\n"); else print("error_number = %d\n", error_number); } else{ if(error_number == EBADMACHO) print("error_number = EBADMACHO\n"); else if(error_number == EBADEXEC) print("error_number = EBADEXEC\n"); else if(error_number == EBADARCH) print("error_number = EBADARCH\n"); else if(error_number == ESHLIBVERS) print("error_number = ESHLIBVERS\n"); else print("error_number = %d\n", error_number); } } /* * print() just a wrapper around vfprintf(stderr, ) */ void print( const char *format, ...) { va_list ap; va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); }