/* * 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 #ifndef __OPENSTEP__ extern const struct section *getsectbynamefromheader( const struct mach_header *mhp, const char *segname, const char *sectname); #endif #import #import #import #import "stuff/openstep_mach.h" #import #import #ifndef __MACH30__ #import "../profileServer/profileServer.h" #endif /* __MACH30__ */ #import "inline_strcmp.h" #import "dyld_init.h" #import "images.h" #import "symbols.h" #import "errors.h" #import "reloc.h" #import "lock.h" #import "debug.h" /* * This is set in _dyld_init() and used for the cputype and cpusubtype. */ struct host_basic_info host_basic_info = { 0 }; #if (defined(__GONZO_BUNSEN_BEAKER__) || defined(__HERA__)) && defined(__ppc__) /* set if this is a g4 processor */ enum bool processor_has_vec = FALSE; #endif /* * If the executable has the MH_BINDATLOAD flag set then executable_bind_at_load * is set to TRUE and both non-lazy and lazy undefined references are placed on * the undefined list as all modules get bound. This will also be set to TRUE * if the dyld environment variable DYLD_BIND_AT_LAUNCH is set. If the * environment variable is set then dyld_bind_at_launch gets set to TRUE. */ enum bool executable_bind_at_load = FALSE; static enum bool dyld_bind_at_launch = FALSE; /* * If the executable has the MH_FORCE_FLAT flag set then force_flat_namespace i * set to TRUE and the program and all its images are bound with a flat * namespace. This will also be set to TRUE if the dyld environment variable * DYLD_FORCE_FLAT_NAMESPACE is set. If the environment variable is set then * dyld_force_flat_namespace gets set to TRUE. */ enum bool force_flat_namespace = FALSE; static enum bool dyld_force_flat_namespace = FALSE; /* * The string home points at the enviroment variable HOME. It is use in setting * the default fallback search paths if needed. */ char *home = "/"; /* * The string dyld_framework_path points to the framework search path and comes * from the enviroment variable DYLD_FRAMEWORK_PATH. It should look like * "path1:path2:path3". The string dyld_fallback_framework_path points to the * fallback framework search path and comes from the enviroment variable * DYLD_FALLBACK_FRAMEWORK_PATH. The default_fallback_framework_path will get * set in load_library_image() in images.c if it is needed. */ char *dyld_framework_path = NULL; char *dyld_fallback_framework_path = NULL; char *default_fallback_framework_path = NULL; /* * The string dyld_library_path points to the dynamic shared library search * path and comes from the enviroment variable DYLD_LIBRARY_PATH. It should * look like "path1:path2:path3". The string dyld_fallback_library_path points * to the dynamic shared library fallback search path and comes from the * enviroment variable DYLD_FALLBACK_LIBRARY_PATH. The * default_fallback_library_path will get set in load_library_image() in * images.c if it is needed. */ char *dyld_library_path = NULL; char *dyld_fallback_library_path = NULL; char *default_fallback_library_path = NULL; /* * The string dyld_image_suffix points to the dynamic shared library * suffix and comes from the enviroment variable DYLD_IMAGE_SUFFIX. */ char *dyld_image_suffix = NULL; /* * The string dyld_insert_libraries points to a list of dynamic shared libraries * and comes from the enviroment variable DYLD_INSERT_LIBRARIES. It should * look like "lib1:lib2:lib3". */ char *dyld_insert_libraries = NULL; enum bool dyld_print_libraries = FALSE; enum bool dyld_mem_protect = FALSE; enum bool dyld_ebadexec_only = FALSE; enum bool dyld_dead_lock_hang = FALSE; unsigned long dyld_prebind_debug = 0; unsigned long dyld_hints_debug = 0; unsigned long dyld_sample_debug = 0; enum bool dyld_executable_path_debug = FALSE; enum bool dyld_two_level_debug = FALSE; enum bool dyld_abort_multiple_inits = FALSE; enum bool dyld_new_local_shared_regions = FALSE; /* * This indicates if the profile server for shared pcsample buffers exists. */ enum bool profile_server = FALSE; /* * This indicates if the launching of this program is being done using the * prebound state. This is assumed to be the case and is disabled if the * assumptions to do this are not true at launch time. The prebound state of * images is only used when the program is first launched. After the program * is launched the variable launched is set to TRUE and all prebound libraries * loaded then have the prebinding undone. */ enum bool prebinding = TRUE; enum bool launched = FALSE; enum bool executable_prebound = FALSE; static struct segment_command *data_seg; static void get_data_segment( void); /* * The variable executables_path is an absolute path of the executable being * run. This is used to support shared library install paths that are relative * to the executable. These install paths by convention start with * "@executable_path" and that is replaced by the real absolute path of the * executable. To get an absolute path of the executable the exec interface to * the entry point code was changed to pass the first argument to the exec() * system call (this can be but is not always the same as argv[0]). The exec * path is picked up in pickup_environment_variables() as the pointer to it * passed by the kernel which follows the terminating 0 of the envp[] array. * The variable exec_path is set to this. The current working directory is * may also be needed along with the exec_path to construct an absolute path of * the executable into the variable executables_path, see the routine * create_executables_path(). */ char *executables_path = NULL; char *exec_path = NULL; static void pickup_environment_variables( char *envp[]); /* * _dyld_init() is the start off point for the dynamic link editor. It is * called before any part of an executable program runs. This is done either * in the executable runtime startoff or by the kernel as a result of an exec(2) * system call (which goes through __dyld_start to get here). * * This routine causes the dynamic shared libraries an executable uses to be * mapped, sets up the executable and the libraries to call the dynamic link * editor when a lazy reference to a symbol is first used, resolves all non-lazy * symbol references needed to start running the program and then returns to * the executable program to start up the program. */ unsigned long _dyld_init( struct mach_header *mh, unsigned long argc, char **argv, char **envp) { unsigned int count; kern_return_t r; unsigned long entry_point; #ifndef __MACH30__ struct section *s; #endif #ifdef MALLOC_DEBUG extern void cthread_init(void); cthread_init(); #endif /* set lock for dyld data structures */ set_lock(); /* * Get the cputype and cpusubtype of the machine we're running on. */ count = HOST_BASIC_INFO_COUNT; if((r = host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t) (&host_basic_info), &count)) != KERN_SUCCESS){ mach_error(r, "can't get host basic info"); } #if defined(__GONZO_BUNSEN_BEAKER__) && defined(__ppc__) if(host_basic_info.cpu_type == CPU_TYPE_POWERPC && host_basic_info.cpu_subtype == CPU_SUBTYPE_POWERPC_7400) processor_has_vec = TRUE; #endif /* * Pickup the environment variables for the dynamic link editor. */ pickup_environment_variables(envp); /* * Create the executable's path from the exec_path and the current * working directory (if needed). If we did not pick up the exec_path * (we are running with an old kernel) use argv[0] if has a slash in it * as it is a path relative to the current working directory. Of course * argv[0] may not have anything to do with the filename being executed * in all cases but it is likely to be right. */ if(exec_path != NULL) create_executables_path(exec_path); else if(strchr(argv[0], '/') != NULL) create_executables_path(argv[0]); if(dyld_executable_path_debug == TRUE) printf("executables_path = %s\n", executables_path == NULL ? "NULL" : executables_path); #ifdef DYLD_PROFILING s = (struct section *) getsectbynamefromheader( &_mh_dylinker_header, SEG_TEXT, SECT_TEXT); monstartup((char *)(s->addr + dyld_image_vmaddr_slide), (char *)(s->addr + dyld_image_vmaddr_slide + s->size)); #endif #ifndef __MACH30__ /* * See if the profile server for shared pcsample buffers exists. * Then if so try to setup a pcsample buffer for dyld itself. */ profile_server = profile_server_exists(); if(profile_server == TRUE){ s = (struct section *) getsectbynamefromheader( &_mh_dylinker_header, SEG_TEXT, SECT_TEXT); shared_pcsample_buffer("/usr/lib/dyld", s, dyld_image_vmaddr_slide); } #endif /* __MACH30__ */ /* * Start off by loading the executable image as the first object image * that make up the program. This in turn will load the dynamic shared * libraries the executable uses and the libraries those libraries use * to the list of library images that make up the program. */ if((mh->flags & MH_FORCE_FLAT) != 0 || dyld_force_flat_namespace == TRUE) force_flat_namespace = TRUE; executable_prebound = (mh->flags & MH_PREBOUND) == MH_PREBOUND; load_executable_image(argv[0], mh, &entry_point); /* * If the prebinding set is still set then try to setup this program to * use the prebound state in it's images. If any of these fail then * undo any prebinding and bind as usual. */ if((mh->flags & MH_PREBOUND) != MH_PREBOUND){ /* * The executable is not prebound but if the libraries are setup * for prebinding and the executable when built had no undefined * symbols then try to use the prebound libraries. This is for * the flat namespace case (and only some sub cases, see the * comments in try_to_use_prebound_libraries()). If this fails * then the two-level namespace cases are handled by the routine * find_twolevel_prebound_lib_subtrees() which is called below. */ if(prebinding == TRUE){ if((mh->flags & MH_NOUNDEFS) == MH_NOUNDEFS){ try_to_use_prebound_libraries(); } else{ if(dyld_prebind_debug != 0) print("dyld: %s: prebinding disabled because " "executable not marked with MH_NOUNDEFS\n", argv[0]); prebinding = FALSE; } } } else if(prebinding == TRUE){ set_images_to_prebound(); } if(prebinding == FALSE){ /* * The program was not fully prebound but it we are not forcing * flat namespace semantics we can still use any sub trees of * libraries that are all two-level namespace and prebound. */ if(force_flat_namespace == FALSE) find_twolevel_prebound_lib_subtrees(); /* * First undo any images that were prebound. */ undo_prebound_images(); /* * Build the initial list of non-lazy symbol references based on the * executable. */ if((mh->flags & MH_BINDATLOAD) != 0 || dyld_bind_at_launch == TRUE) executable_bind_at_load = TRUE; setup_initial_undefined_list(FALSE); /* * With the undefined list set up link in the needed modules. */ link_in_need_modules(FALSE, FALSE); } else{ if(dyld_prebind_debug != 0){ if((mh->flags & MH_PREBOUND) != MH_PREBOUND) print("dyld: %s: prebinding enabled using only prebound " "libraries\n", argv[0]); else print("dyld: %s: prebinding enabled\n", argv[0]); } } launched = TRUE; /* * If DYLD_EBADEXEC_ONLY is set then print a message as the program * will launch. */ if(dyld_ebadexec_only == TRUE){ error("executable: %s will be launched (DYLD_EBADEXEC_ONLY set, " "program not started)", argv[0]); link_edit_error(DYLD_FILE_ACCESS, EBADEXEC, argv[0]); } /* release lock for dyld data structures */ release_lock(); /* * Return the address of the executable's entry point which is used if * this routine was called from __dyld_start. Otherwise this was called * from the runtime startoff of the executable and this return value is * ignored. */ return(entry_point); } /* * pickup_environment_variables() goes through the pointer to the environment * strings and sets the global variables for dyld based on the environment * strings. */ static void pickup_environment_variables( char *envp[]) { char **p; for(p = envp; *p != NULL; p++){ if(**p == 'D' && strncmp(*p, "DYLD_", sizeof("DYLD_") - 1) == 0){ if(strncmp(*p, "DYLD_FRAMEWORK_PATH=", sizeof("DYLD_FRAMEWORK_PATH=") - 1) == 0){ /* * Framework path searching is not done for setuid programs * which are not run by the real user. Futher the * evironment varaible for the framework path is cleared so * that if this program executes a non-set uid program this * part of the evironment will not be passed along so that * that program also will not have it's frameworks searched * for. */ if(getuid() != 0 && (getuid() != geteuid() || getgid() != getegid())) (*p)[sizeof("DYLD_FRAMEWORK_PATH=") - 1] = '\0'; else if(*(*p + sizeof("DYLD_FRAMEWORK_PATH=") - 1) != '\0') dyld_framework_path = *p + sizeof("DYLD_FRAMEWORK_PATH=") - 1; } else if(strncmp(*p, "DYLD_FALLBACK_FRAMEWORK_PATH=", sizeof("DYLD_FALLBACK_FRAMEWORK_PATH=") - 1) == 0){ if(getuid() != 0 && (getuid() != geteuid() || getgid() != getegid())) (*p)[sizeof("DYLD_FALLBACK_FRAMEWORK_PATH=")- 1] = '\0'; else if(*(*p + sizeof( "DYLD_FALLBACK_FRAMEWORK_PATH=") - 1) != '\0') dyld_fallback_framework_path = *p + sizeof("DYLD_FALLBACK_FRAMEWORK_PATH=") - 1; } else if(strncmp(*p, "DYLD_LIBRARY_PATH=", sizeof("DYLD_LIBRARY_PATH=") - 1) == 0){ /* * Library path searching is not done for setuid programs * which are not run by the real user. Futher the * evironment varaible for the library path is cleared so * that if this program executes a non-set uid program this * part of the evironment will not be passed along so that * that program also will not have it's libraries searched * for. */ if(getuid() != 0 && (getuid() != geteuid() || getgid() != getegid())) (*p)[sizeof("DYLD_LIBRARY_PATH=") - 1] = '\0'; else if(*(*p + sizeof("DYLD_LIBRARY_PATH=") - 1) != '\0') dyld_library_path = *p + sizeof("DYLD_LIBRARY_PATH=") - 1; } else if(strncmp(*p, "DYLD_FALLBACK_LIBRARY_PATH=", sizeof("DYLD_FALLBACK_LIBRARY_PATH=") - 1) == 0){ if(getuid() != 0 && (getuid() != geteuid() || getgid() != getegid())) (*p)[sizeof("DYLD_FALLBACK_LIBRARY_PATH=") - 1] = '\0'; else if(*(*p + sizeof( "DYLD_FALLBACK_LIBRARY_PATH=") - 1) != '\0') dyld_fallback_library_path = *p + sizeof("DYLD_FALLBACK_LIBRARY_PATH=") - 1; } else if(strncmp(*p, "DYLD_IMAGE_SUFFIX=", sizeof("DYLD_IMAGE_SUFFIX=") - 1) == 0){ /* * Image suffix searching is not done for setuid programs * which are not run by the real user. Futher the * evironment varaible for the library path is cleared so * that if this program executes a non-set uid program this * part of the evironment will not be passed along so that * that program also will not have it's libraries searched * for. */ if(getuid() != 0 && (getuid() != geteuid() || getgid() != getegid())) (*p)[sizeof("DYLD_IMAGE_SUFFIX=") - 1] = '\0'; else if(*(*p + sizeof("DYLD_IMAGE_SUFFIX=") - 1) != '\0') dyld_image_suffix = *p + sizeof("DYLD_IMAGE_SUFFIX=") - 1; } else if(strncmp(*p, "DYLD_INSERT_LIBRARIES=", sizeof("DYLD_INSERT_LIBRARIES=") - 1) == 0){ /* * Library insertion is also not done for setuid programs * which are not run by the real user. Futher the * evironment varaible for the library insertion is cleared * so that if this program executes a non-set uid program * this part of the evironment will not be passed along so * that that program also will not have libraries inserted. */ if(getuid() != 0 && (getuid() != geteuid() || getgid() != getegid())) (*p)[sizeof("DYLD_INSERT_LIBRARIES=") - 1] = '\0'; else if(*(*p + sizeof("DYLD_INSERT_LIBRARIES=")- 1) != '\0') dyld_insert_libraries = *p + sizeof("DYLD_INSERT_LIBRARIES=") - 1; } else if(strncmp(*p, "DYLD_DEBUG_TRACE=", sizeof("DYLD_DEBUG_TRACE=") - 1) == 0){ dyld_debug_trace = TRUE; } else if(strncmp(*p, "DYLD_ERROR_PRINT=", sizeof("DYLD_ERROR_PRINT=") - 1) == 0){ dyld_error_print = TRUE; } else if(strncmp(*p, "DYLD_PRINT_LIBRARIES=", sizeof("DYLD_PRINT_LIBRARIES=") - 1) == 0){ dyld_print_libraries = TRUE; } else if(strncmp(*p, "DYLD_MEM_PROTECT=", sizeof("DYLD_MEM_PROTECT=") - 1) == 0){ dyld_mem_protect = TRUE; get_data_segment(); mem_prot_lock = *global_lock; global_lock = &mem_prot_lock; mem_prot_debug_lock = *debug_thread_lock; debug_thread_lock = &mem_prot_debug_lock; } else if(strncmp(*p, "DYLD_EBADEXEC_ONLY=", sizeof("DYLD_EBADEXEC_ONLY=") - 1) == 0){ dyld_ebadexec_only = TRUE; } else if(strncmp(*p, "DYLD_BIND_AT_LAUNCH=", sizeof("DYLD_BIND_AT_LAUNCH=") - 1) == 0){ dyld_bind_at_launch = TRUE; } else if(strncmp(*p, "DYLD_FORCE_FLAT_NAMESPACE=", sizeof("DYLD_FORCE_FLAT_NAMESPACE=") - 1) == 0){ dyld_force_flat_namespace = TRUE; } else if(strncmp(*p, "DYLD_DEAD_LOCK_HANG=", sizeof("DYLD_DEAD_LOCK_HANG=") - 1) == 0){ dyld_dead_lock_hang = TRUE; } else if(strncmp(*p, "DYLD_ABORT_MULTIPLE_INITS=", sizeof("DYLD_ABORT_MULTIPLE_INITS=") - 1) == 0){ dyld_abort_multiple_inits = TRUE; } else if(strncmp(*p, "DYLD_NEW_LOCAL_SHARED_REGIONS=", sizeof("DYLD_NEW_LOCAL_SHARED_REGIONS=") - 1) == 0){ dyld_new_local_shared_regions = TRUE; } else if(strncmp(*p, "DYLD_PREBIND_DEBUG=", sizeof("DYLD_PREBIND_DEBUG") - 1) == 0){ if(strcmp("3", *p + sizeof("DYLD_PREBIND_DEBUG=") - 1) == 0) dyld_prebind_debug = 3; else if(strcmp("2", *p + sizeof("DYLD_PREBIND_DEBUG=") - 1) == 0) dyld_prebind_debug = 2; else dyld_prebind_debug = 1; } else if(strncmp(*p, "DYLD_HINTS_DEBUG=", sizeof("DYLD_HINTS_DEBUG") - 1) == 0){ if(strcmp("2", *p + sizeof("DYLD_HINTS_DEBUG=") - 1) == 0) dyld_hints_debug = 2; else dyld_hints_debug = 1; } else if(strncmp(*p, "DYLD_SAMPLE_DEBUG=", sizeof("DYLD_SAMPLE_DEBUG") - 1) == 0){ if(strcmp("2", *p + sizeof("DYLD_SAMPLE_DEBUG=") - 1) == 0) dyld_sample_debug = 2; else dyld_sample_debug = 1; } else if(strncmp(*p, "DYLD_EXECUTABLE_PATH_DEBUG=", sizeof("DYLD_EXECUTABLE_PATH_DEBUG=") - 1) == 0){ dyld_executable_path_debug = TRUE; } else if(strncmp(*p, "DYLD_TWO_LEVEL_DEBUG=", sizeof("DYLD_TWO_LEVEL_DEBUG=") - 1) == 0){ dyld_two_level_debug = TRUE; } } else if(**p == 'H' && strncmp(*p, "HOME=", sizeof("HOME=")-1) == 0){ home = save_string(*p + sizeof("HOME=") - 1); } } #if defined(__MACH30__) && (defined(__ppc__) || defined(__i386__)) /* * Pickup the pointer to the exec path. This is placed by the kernel * just after the trailing 0 of the envp[] array. If exec_path is left * as NULL other code will use argv[0] as a guess. */ /* * This feature was added to MacOS X PR2. So in case we are running * with a kernel that does not set this we do a few checks on the * address of the pointer and the value of the pointer. These checks * are temporary hacks and only work on PowerPC and i386 knowing the * address of the top of the stack (0xc0000000) and the stack grows * to lower addresses and that the bytes of the exec path should be * on the stack above the pointer. */ if((unsigned long)(p + 1) < 0xc0000000 && (unsigned long)(p[1]) > (unsigned long)(p + 1) && (unsigned long)(p[1]) < 0xc0000000){ exec_path = p[1]; } if(dyld_executable_path_debug == TRUE) printf("exec_path = %s\n", exec_path == NULL ? "NULL" : exec_path); #endif } /* * getenv for the libc code I use. The normal getenv() calls * _dyld_lookup_and_bind() which dyld can't call. This is needed for isspace() * in strtod needed by vsnprintf() for locale stuff. */ char * getenv(const char *name) { return(NULL); } /* * _NSGetEnviron() is needed by the malloc() in libc in MacOS X DP4 beginning * with Gonzo1H6. */ char *** _NSGetEnviron( void) { static char *p = NULL; static char **pp = &p; return(&pp); } #ifdef DYLD_PROFILING /* * profiling_exit is used as the symbol "__exit" in the routines * relocate_symbol_pointers_in_{object_image,library_image} to get the gmon.out * file written out. */ void profiling_exit( int status) { dyld_monoutput(); _exit(status); } /* * dyld_monoutput() causes dyld's gmon.out file to be written an profiling to * be turned off. */ void dyld_monoutput( void) { struct stat stat_buf; char *p, gmon_out[1024]; static enum bool done = FALSE; if(done == TRUE) return; done = TRUE; if(stat("/dyld.gmon.out", &stat_buf) == 0){ p = strrchr(executables_name, '/'); if(p == NULL) p = executables_name; else p++; sprintf(gmon_out, "/dyld.gmon.out/%s.%d", p, getpid()); monoutput(gmon_out); } } #endif /* DYLD_PROFILING */ /* * get_data_segment() sets data_seg to point at the data segment command for the * dynamic linker. */ static void get_data_segment( void) { long i; data_seg = (struct segment_command *) ((char *)&_mh_dylinker_header + sizeof(struct mach_header)); for(i = 0; i < _mh_dylinker_header.ncmds; i++){ if(data_seg->cmd == LC_SEGMENT){ if(strncmp(data_seg->segname, SEG_DATA, sizeof(data_seg->segname)) == 0){ return; } } data_seg = (struct segment_command *) ((char *)data_seg + data_seg->cmdsize); } data_seg = NULL; } void protect_data_segment( void) { kern_return_t r; if((r = vm_protect(mach_task_self(), data_seg->vmaddr + dyld_image_vmaddr_slide, (vm_size_t)data_seg->vmsize, FALSE, VM_PROT_READ)) != KERN_SUCCESS){ mach_error(r, "can't vm_protect data segment of dyld"); link_edit_error(DYLD_MACH_RESOURCE, r, "dyld"); } } void unprotect_data_segment( void) { kern_return_t r; if((r = vm_protect(mach_task_self(), data_seg->vmaddr + dyld_image_vmaddr_slide, (vm_size_t)data_seg->vmsize, FALSE, data_seg->initprot)) != KERN_SUCCESS){ mach_error(r, "can't vm_(un)protect data segment of dyld"); link_edit_error(DYLD_MACH_RESOURCE, r, "dyld"); } }