/* * This file was modified by Christoph Pfisterer * on Tue, Jan 23 2001. See the file "ChangeLog" for details of what * was changed. * * * 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@ */ #include #include #include #include #include #include #include #include "mach-o/dyld.h" #include "dlfcn.h" /* * debugging macros */ #if DEBUG > 0 #define DEBUG_PRINT(format) fprintf(stderr,(format));fflush(stderr) #define DEBUG_PRINT1(format,arg1) fprintf(stderr,(format),(arg1));\ fflush(stderr) #define DEBUG_PRINT2(format,arg1,arg2) fprintf(stderr,(format),\ (arg1),(arg2));fflush(stderr) #define DEBUG_PRINT3(format,arg1,arg2,arg3) fprintf(stderr,(format),\ (arg1),(arg2),(arg3));fflush(stderr) #else #define DEBUG_PRINT(format) /**/ #define DEBUG_PRINT1(format,arg1) /**/ #define DEBUG_PRINT2(format,arg1,arg2) /**/ #define DEBUG_PRINT3(format,arg1,arg2,arg3) /**/ #undef DEBUG #endif /* * The structure of a dlopen() handle. */ struct dlopen_handle { dev_t dev; /* the path's device and inode number from stat(2) */ ino_t ino; int dlopen_mode; /* current dlopen mode for this handle */ int dlopen_count; /* number of times dlopen() called on this handle */ NSModule module; /* the NSModule returned by NSLinkModule() */ struct dlopen_handle *prev; struct dlopen_handle *next; }; static struct dlopen_handle *sasl_dlopen_handles = NULL; static const struct dlopen_handle sasl_main_program_handle = {NULL}; static char *sasl_dlerror_pointer = NULL; /* * NSMakePrivateModulePublic() is not part of the public dyld API so we define * it here. The internal dyld function pointer for * __dyld_NSMakePrivateModulePublic is returned so thats all that maters to get * the functionality need to implement the dlopen() interfaces. */ // Jaguar does not have an enum named bool enum bool { false, true }; static enum bool NSMakePrivateModulePublic( NSModule module) { static enum bool (*p)(NSModule module) = NULL; if(p == NULL) _dyld_func_lookup("__dyld_NSMakePrivateModulePublic", (unsigned long *)&p); if(p == NULL){ #ifdef DEBUG printf("_dyld_func_lookup of __dyld_NSMakePrivateModulePublic " "failed\n"); #endif return(FALSE); } return(p(module)); } /* * helper routine: search for a named module in various locations */ static int _dl_search_paths( const char *filename, char *pathbuf, struct stat *stat_buf) { const char *pathspec; const char *element; const char *p; char *q; char *pathbuf_end; const char *envvars[] = { "$DYLD_LIBRARY_PATH", "$LD_LIBRARY_PATH", "/usr/lib:/lib", NULL }; int envvar_index; pathbuf_end = pathbuf + PATH_MAX - 8; for(envvar_index = 0; envvars[envvar_index]; envvar_index++){ if(envvars[envvar_index][0] == '$'){ pathspec = getenv(envvars[envvar_index]+1); } else { pathspec = envvars[envvar_index]; } if(pathspec != NULL){ element = pathspec; while(*element){ /* extract path list element */ p = element; q = pathbuf; while(*p && *p != ':' && q < pathbuf_end) *q++ = *p++; if(q == pathbuf){ /* empty element */ if(*p){ element = p+1; continue; } break; } if (*p){ element = p+1; } else{ element = p; /* this terminates the loop */ } /* add slash if neccessary */ if(*(q-1) != '/' && q < pathbuf_end){ *q++ = '/'; } /* append module name */ p = filename; while(*p && q < pathbuf_end) *q++ = *p++; *q++ = 0; if(q >= pathbuf_end){ /* maybe add an error message here */ break; } if(stat(pathbuf, stat_buf) == 0){ return 0; } } } } /* we have searched everywhere, now we give up */ return -1; } /* * dlopen() the MacOS X version of the FreeBSD dlopen() interface. */ void * sasl_dlopen( const char *path, int mode) { const char *module_path; void *retval; struct stat stat_buf; NSObjectFileImage objectFileImage; NSObjectFileImageReturnCode ofile_result_code; NSModule module; struct dlopen_handle *p; unsigned long options; NSSymbol NSSymbol; void (*init)(void); char pathbuf[PATH_MAX]; DEBUG_PRINT2("libdl: dlopen(%s,0x%x) -> ", path, (unsigned int)mode); sasl_dlerror_pointer = NULL; /* * A NULL path is to indicate the caller wants a handle for the * main program. */ if(path == NULL){ retval = (void *)&sasl_main_program_handle; DEBUG_PRINT1("main / %p\n", retval); return(retval); } /* see if the path exists and if so get the device and inode number */ if(stat(path, &stat_buf) == -1){ sasl_dlerror_pointer = strerror(errno); if(path[0] == '/'){ DEBUG_PRINT1("ERROR (stat): %s\n", sasl_dlerror_pointer); return(NULL); } /* search for the module in various places */ if(_dl_search_paths(path, pathbuf, &stat_buf)){ /* sasl_dlerror_pointer is unmodified */ DEBUG_PRINT1("ERROR (stat): %s\n", sasl_dlerror_pointer); return(NULL); } DEBUG_PRINT1("found %s -> ", pathbuf); module_path = pathbuf; sasl_dlerror_pointer = NULL; } else{ module_path = path; } /* * If we don't want an unshared handle see if we already have a handle * for this path. */ if((mode & RTLD_UNSHARED) != RTLD_UNSHARED){ p = sasl_dlopen_handles; while(p != NULL){ if(p->dev == stat_buf.st_dev && p->ino == stat_buf.st_ino){ /* skip unshared handles */ if((p->dlopen_mode & RTLD_UNSHARED) == RTLD_UNSHARED) continue; /* * We have already created a handle for this path. The * caller might be trying to promote an RTLD_LOCAL handle * to a RTLD_GLOBAL. Or just looking it up with * RTLD_NOLOAD. */ if((p->dlopen_mode & RTLD_LOCAL) == RTLD_LOCAL && (mode & RTLD_GLOBAL) == RTLD_GLOBAL){ /* promote the handle */ if(NSMakePrivateModulePublic(p->module) == TRUE){ p->dlopen_mode &= ~RTLD_LOCAL; p->dlopen_mode |= RTLD_GLOBAL; p->dlopen_count++; DEBUG_PRINT1("%p\n", p); return(p); } else{ sasl_dlerror_pointer = "can't promote handle from " "RTLD_LOCAL to RTLD_GLOBAL"; DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); return(NULL); } } p->dlopen_count++; DEBUG_PRINT1("%p\n", p); return(p); } p = p->next; } } /* * We do not have a handle for this path if we were just trying to * look it up return NULL to indicate we don't have it. */ if((mode & RTLD_NOLOAD) == RTLD_NOLOAD){ sasl_dlerror_pointer = "no existing handle for path RTLD_NOLOAD test"; DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); return(NULL); } /* try to create an object file image from this path */ ofile_result_code = NSCreateObjectFileImageFromFile(module_path, &objectFileImage); if(ofile_result_code != NSObjectFileImageSuccess){ switch(ofile_result_code){ case NSObjectFileImageFailure: sasl_dlerror_pointer = "object file setup failure"; DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); return(NULL); case NSObjectFileImageInappropriateFile: sasl_dlerror_pointer = "not a Mach-O MH_BUNDLE file type"; DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); return(NULL); case NSObjectFileImageArch: sasl_dlerror_pointer = "no object for this architecture"; DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); return(NULL); case NSObjectFileImageFormat: sasl_dlerror_pointer = "bad object file format"; DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); return(NULL); case NSObjectFileImageAccess: sasl_dlerror_pointer = "can't read object file"; DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); return(NULL); default: sasl_dlerror_pointer = "unknown error from " "NSCreateObjectFileImageFromFile()"; DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); return(NULL); } } /* try to link in this object file image */ options = NSLINKMODULE_OPTION_PRIVATE; if((mode & RTLD_NOW) == RTLD_NOW) options |= NSLINKMODULE_OPTION_BINDNOW; module = NSLinkModule(objectFileImage, module_path, options); NSDestroyObjectFileImage(objectFileImage) ; if(module == NULL){ sasl_dlerror_pointer = "NSLinkModule() failed for dlopen()"; DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); return(NULL); } /* * If the handle is to be global promote the handle. It is done this * way to avoid multiply defined symbols. */ if((mode & RTLD_GLOBAL) == RTLD_GLOBAL){ if(NSMakePrivateModulePublic(module) == FALSE){ sasl_dlerror_pointer = "can't promote handle from RTLD_LOCAL to " "RTLD_GLOBAL"; DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); return(NULL); } } p = malloc(sizeof(struct dlopen_handle)); if(p == NULL){ sasl_dlerror_pointer = "can't allocate memory for the dlopen handle"; DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); return(NULL); } /* fill in the handle */ p->dev = stat_buf.st_dev; p->ino = stat_buf.st_ino; if(mode & RTLD_GLOBAL) p->dlopen_mode = RTLD_GLOBAL; else p->dlopen_mode = RTLD_LOCAL; p->dlopen_mode |= (mode & RTLD_UNSHARED) | (mode & RTLD_NODELETE) | (mode & RTLD_LAZY_UNDEF); p->dlopen_count = 1; p->module = module; p->prev = NULL; p->next = sasl_dlopen_handles; if(sasl_dlopen_handles != NULL) sasl_dlopen_handles->prev = p; sasl_dlopen_handles = p; /* call the init function if one exists */ NSSymbol = NSLookupSymbolInModule(p->module, "__init"); if(NSSymbol != NULL){ init = NSAddressOfSymbol(NSSymbol); init(); } DEBUG_PRINT1("%p\n", p); return(p); } /* * dlsym() the MacOS X version of the FreeBSD dlopen() interface. */ void * sasl_dlsym( void * handle, const char *symbol) { struct dlopen_handle *dlopen_handle, *p; NSSymbol NSSymbol; void *address; DEBUG_PRINT2("libdl: dlsym(%p,%s) -> ", handle, symbol); dlopen_handle = (struct dlopen_handle *)handle; /* * If this is the handle for the main program do a global lookup. */ if(dlopen_handle == (struct dlopen_handle *)&sasl_main_program_handle){ if(NSIsSymbolNameDefined(symbol) == TRUE){ NSSymbol = NSLookupAndBindSymbol(symbol); address = NSAddressOfSymbol(NSSymbol); sasl_dlerror_pointer = NULL; DEBUG_PRINT1("%p\n", address); return(address); } else{ sasl_dlerror_pointer = "symbol not found"; DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); return(NULL); } } /* * Find this handle and do a lookup in just this module. */ p = sasl_dlopen_handles; while(p != NULL){ if(dlopen_handle == p){ NSSymbol = NSLookupSymbolInModule(p->module, symbol); if(NSSymbol != NULL){ address = NSAddressOfSymbol(NSSymbol); sasl_dlerror_pointer = NULL; DEBUG_PRINT1("%p\n", address); return(address); } else{ sasl_dlerror_pointer = "symbol not found"; DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); return(NULL); } } p = p->next; } sasl_dlerror_pointer = "bad handle passed to dlsym()"; DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); return(NULL); } /* * dlerror() the MacOS X version of the FreeBSD dlopen() interface. */ const char * sasl_dlerror( void) { const char *p; p = (const char *)sasl_dlerror_pointer; sasl_dlerror_pointer = NULL; return(p); } /* * dlclose() the MacOS X version of the FreeBSD dlopen() interface. */ int sasl_dlclose( void * handle) { struct dlopen_handle *p, *q; unsigned long options; NSSymbol NSSymbol; void (*fini)(void); DEBUG_PRINT1("libdl: dlclose(%p) -> ", handle); sasl_dlerror_pointer = NULL; q = (struct dlopen_handle *)handle; p = sasl_dlopen_handles; while(p != NULL){ if(p == q){ /* if the dlopen() count is not zero we are done */ p->dlopen_count--; if(p->dlopen_count != 0){ DEBUG_PRINT("OK"); return(0); } /* call the fini function if one exists */ NSSymbol = NSLookupSymbolInModule(p->module, "__fini"); if(NSSymbol != NULL){ fini = NSAddressOfSymbol(NSSymbol); fini(); } /* unlink the module for this handle */ options = 0; if(p->dlopen_mode & RTLD_NODELETE) options |= NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED; if(p->dlopen_mode & RTLD_LAZY_UNDEF) options |= NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES; if(NSUnLinkModule(p->module, options) == FALSE){ sasl_dlerror_pointer = "NSUnLinkModule() failed for dlclose()"; DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); return(-1); } if(p->prev != NULL) p->prev->next = p->next; if(p->next != NULL) p->next->prev = p->prev; if(sasl_dlopen_handles == p) sasl_dlopen_handles = p->next; free(p); DEBUG_PRINT("OK"); return(0); } p = p->next; } sasl_dlerror_pointer = "invalid handle passed to dlclose()"; DEBUG_PRINT1("ERROR: %s\n", sasl_dlerror_pointer); return(-1); }