![]() |
Home page |
Mailing list |
Docs
Asterisk developer's documentation :: Codename Pineapple
dlfcn.c
Go to the documentation of this file.
00001 /* 00002 Copyright (c) 2002 Jorge Acereda <jacereda@users.sourceforge.net> & 00003 Peter O'Gorman <ogorman@users.sourceforge.net> 00004 00005 Portions may be copyright others, see the AUTHORS file included with this 00006 distribution. 00007 00008 Maintained by Peter O'Gorman <ogorman@users.sourceforge.net> 00009 00010 Bug Reports and other queries should go to <ogorman@users.sourceforge.net> 00011 00012 Permission is hereby granted, free of charge, to any person obtaining 00013 a copy of this software and associated documentation files (the 00014 "Software"), to deal in the Software without restriction, including 00015 without limitation the rights to use, copy, modify, merge, publish, 00016 distribute, sublicense, and/or sell copies of the Software, and to 00017 permit persons to whom the Software is furnished to do so, subject to 00018 the following conditions: 00019 00020 The above copyright notice and this permission notice shall be 00021 included in all copies or substantial portions of the Software. 00022 00023 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 00024 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 00025 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 00026 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 00027 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 00028 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 00029 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 00030 */ 00031 00032 #include <pthread.h> 00033 #include <stdio.h> 00034 #include <stdlib.h> 00035 #include <string.h> 00036 #include <sys/types.h> 00037 #include <sys/stat.h> 00038 #include <stdarg.h> 00039 #include <limits.h> 00040 #include <mach-o/dyld.h> 00041 #include <mach-o/nlist.h> 00042 #include <mach-o/getsect.h> 00043 /* Just playing to see if it would compile with the freebsd headers, it does, 00044 * but because of the different values for RTLD_LOCAL etc, it would break binary 00045 * compat... oh well 00046 */ 00047 #ifndef __BSD_VISIBLE 00048 #define __BSD_VISIBLE 1 00049 #endif 00050 00051 #include "asterisk/dlfcn-compat.h" 00052 00053 #ifndef dl_restrict 00054 #define dl_restrict __restrict 00055 #endif 00056 /* This is not available on 10.1 */ 00057 #ifndef LC_LOAD_WEAK_DYLIB 00058 #define LC_LOAD_WEAK_DYLIB (0x18 | LC_REQ_DYLD) 00059 #endif 00060 00061 /* With this stuff here, this thing may actually compile/run on 10.0 systems 00062 * Not that I have a 10.0 system to test it on anylonger 00063 */ 00064 #ifndef LC_REQ_DYLD 00065 #define LC_REQ_DYLD 0x80000000 00066 #endif 00067 #ifndef NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED 00068 #define NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED 0x4 00069 #endif 00070 #ifndef NSADDIMAGE_OPTION_RETURN_ON_ERROR 00071 #define NSADDIMAGE_OPTION_RETURN_ON_ERROR 0x1 00072 #endif 00073 #ifndef NSLOOKUPSYMBOLINIMAGE_OPTION_BIND 00074 #define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND 0x0 00075 #endif 00076 #ifndef NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR 00077 #define NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR 0x4 00078 #endif 00079 /* These symbols will be looked for in dyld */ 00080 static const struct mach_header *(*dyld_NSAddImage) (const char *, unsigned long) = 0; 00081 static int (*dyld_NSIsSymbolNameDefinedInImage) (const struct mach_header *, const char *) = 0; 00082 static NSSymbol(*dyld_NSLookupSymbolInImage) 00083 (const struct mach_header *, const char *, unsigned long) = 0; 00084 00085 /* Define this to make dlcompat reuse data block. This way in theory we save 00086 * a little bit of overhead. However we then couldn't correctly catch excess 00087 * calls to dlclose(). Hence we don't use this feature 00088 */ 00089 #undef REUSE_STATUS 00090 00091 /* Size of the internal error message buffer (used by dlerror()) */ 00092 #define ERR_STR_LEN 251 00093 00094 /* Maximum number of search paths supported by getSearchPath */ 00095 #define MAX_SEARCH_PATHS 32 00096 00097 00098 #define MAGIC_DYLIB_OFI ((NSObjectFileImage) 'DYOF') 00099 #define MAGIC_DYLIB_MOD ((NSModule) 'DYMO') 00100 00101 /* internal flags */ 00102 #define DL_IN_LIST 0x01 00103 00104 /* our mutex */ 00105 static pthread_mutex_t dlcompat_mutex; 00106 /* Our thread specific storage 00107 */ 00108 static pthread_key_t dlerror_key; 00109 00110 struct dlthread 00111 { 00112 int lockcnt; 00113 unsigned char errset; 00114 char errstr[ERR_STR_LEN]; 00115 }; 00116 00117 /* This is our central data structure. Whenever a module is loaded via 00118 * dlopen(), we create such a struct. 00119 */ 00120 struct dlstatus 00121 { 00122 struct dlstatus *next; /* pointer to next element in the linked list */ 00123 NSModule module; 00124 const struct mach_header *lib; 00125 int refs; /* reference count */ 00126 int mode; /* mode in which this module was loaded */ 00127 dev_t device; 00128 ino_t inode; 00129 int flags; /* Any internal flags we may need */ 00130 }; 00131 00132 /* Head node of the dlstatus list */ 00133 static struct dlstatus mainStatus = { 0, MAGIC_DYLIB_MOD, NULL, -1, RTLD_GLOBAL, 0, 0, 0 }; 00134 static struct dlstatus *stqueue = &mainStatus; 00135 00136 00137 /* Storage for the last error message (used by dlerror()) */ 00138 /* static char err_str[ERR_STR_LEN]; */ 00139 /* static int err_filled = 0; */ 00140 00141 /* Prototypes to internal functions */ 00142 static void debug(const char *fmt, ...); 00143 static void error(const char *str, ...); 00144 static const char *safegetenv(const char *s); 00145 static const char *searchList(void); 00146 static const char *getSearchPath(int i); 00147 static const char *getFullPath(int i, const char *file); 00148 static const struct stat *findFile(const char *file, const char **fullPath); 00149 static int isValidStatus(struct dlstatus *status); 00150 static inline int isFlagSet(int mode, int flag); 00151 static struct dlstatus *lookupStatus(const struct stat *sbuf); 00152 static void insertStatus(struct dlstatus *dls, const struct stat *sbuf); 00153 static int promoteLocalToGlobal(struct dlstatus *dls); 00154 static void *reference(struct dlstatus *dls, int mode); 00155 static void *dlsymIntern(struct dlstatus *dls, const char *symbol, int canSetError); 00156 static struct dlstatus *allocStatus(void); 00157 static struct dlstatus *loadModule(const char *path, const struct stat *sbuf, int mode); 00158 static NSSymbol *search_linked_libs(const struct mach_header *mh, const char *symbol); 00159 static const char *get_lib_name(const struct mach_header *mh); 00160 static const struct mach_header *get_mach_header_from_NSModule(NSModule * mod); 00161 static void dlcompat_init_func(void); 00162 static inline void dolock(void); 00163 static inline void dounlock(void); 00164 static void dlerrorfree(void *data); 00165 static void resetdlerror(void); 00166 static const struct mach_header *my_find_image(const char *name); 00167 static const struct mach_header *image_for_address(const void *address); 00168 static void dlcompat_cleanup(void); 00169 static inline const char *dyld_error_str(void); 00170 00171 #if FINK_BUILD 00172 /* Two Global Functions */ 00173 void *dlsym_prepend_underscore(void *handle, const char *symbol); 00174 void *dlsym_auto_underscore(void *handle, const char *symbol); 00175 00176 /* And their _intern counterparts */ 00177 static void *dlsym_prepend_underscore_intern(void *handle, const char *symbol); 00178 static void *dlsym_auto_underscore_intern(void *handle, const char *symbol); 00179 #endif 00180 00181 /* Functions */ 00182 00183 static void debug(const char *fmt, ...) 00184 { 00185 #if DEBUG > 1 00186 va_list arg; 00187 va_start(arg, fmt); 00188 fprintf(stderr, "DLDEBUG: "); 00189 vfprintf(stderr, fmt, arg); 00190 fprintf(stderr, "\n"); 00191 fflush(stderr); 00192 va_end(arg); 00193 #endif 00194 } 00195 00196 static void error(const char *str, ...) 00197 { 00198 va_list arg; 00199 struct dlthread *tss; 00200 char * err_str; 00201 va_start(arg, str); 00202 tss = pthread_getspecific(dlerror_key); 00203 err_str = tss->errstr; 00204 strncpy(err_str, "dlcompat: ", ERR_STR_LEN); 00205 vsnprintf(err_str + 10, ERR_STR_LEN - 10, str, arg); 00206 va_end(arg); 00207 debug("ERROR: %s\n", err_str); 00208 tss->errset = 1; 00209 } 00210 00211 static void warning(const char *str) 00212 { 00213 #if DEBUG > 0 00214 fprintf(stderr, "WARNING: dlcompat: %s\n", str); 00215 #endif 00216 } 00217 00218 static const char *safegetenv(const char *s) 00219 { 00220 const char *ss = getenv(s); 00221 return ss ? ss : ""; 00222 } 00223 00224 /* because this is only used for debugging and error reporting functions, we 00225 * don't really care about how elegant it is... it could use the load 00226 * commands to find the install name of the library, but... 00227 */ 00228 static const char *get_lib_name(const struct mach_header *mh) 00229 { 00230 unsigned long count = _dyld_image_count(); 00231 unsigned long i; 00232 const char *val = NULL; 00233 if (mh) 00234 { 00235 for (i = 0; i < count; i++) 00236 { 00237 if (mh == _dyld_get_image_header(i)) 00238 { 00239 val = _dyld_get_image_name(i); 00240 break; 00241 } 00242 } 00243 } 00244 return val; 00245 } 00246 00247 /* Returns the mach_header for the module bu going through all the loaded images 00248 * and finding the one with the same name as the module. There really ought to be 00249 * an api for doing this, would be faster, but there isn't one right now 00250 */ 00251 static const struct mach_header *get_mach_header_from_NSModule(NSModule * mod) 00252 { 00253 const char *mod_name = NSNameOfModule(mod); 00254 struct mach_header *mh = NULL; 00255 unsigned long count = _dyld_image_count(); 00256 unsigned long i; 00257 debug("Module name: %s", mod_name); 00258 for (i = 0; i < count; i++) 00259 { 00260 if (!strcmp(mod_name, _dyld_get_image_name(i))) 00261 { 00262 mh = _dyld_get_image_header(i); 00263 break; 00264 } 00265 } 00266 return mh; 00267 } 00268 00269 00270 /* Compute and return a list of all directories that we should search when 00271 * trying to locate a module. We first look at the values of LD_LIBRARY_PATH 00272 * and DYLD_LIBRARY_PATH, and then finally fall back to looking into 00273 * /usr/lib and /lib. Since both of the environments variables can contain a 00274 * list of colon separated paths, we simply concat them and the two other paths 00275 * into one big string, which we then can easily parse. 00276 * Splitting this string into the actual path list is done by getSearchPath() 00277 */ 00278 static const char *searchList() 00279 { 00280 size_t buf_size; 00281 static char *buf=NULL; 00282 const char *ldlp = safegetenv("LD_LIBRARY_PATH"); 00283 const char *dyldlp = safegetenv("DYLD_LIBRARY_PATH"); 00284 const char *stdpath = getenv("DYLD_FALLBACK_LIBRARY_PATH"); 00285 if (!stdpath) 00286 stdpath = "/usr/local/lib:/lib:/usr/lib"; 00287 if (!buf) 00288 { 00289 buf_size = strlen(ldlp) + strlen(dyldlp) + strlen(stdpath) + 4; 00290 buf = malloc(buf_size); 00291 snprintf(buf, buf_size, "%s%s%s%s%s%c", dyldlp, (dyldlp[0] ? ":" : ""), ldlp, (ldlp[0] ? ":" : ""), 00292 stdpath, '\0'); 00293 } 00294 return buf; 00295 } 00296 00297 /* Returns the ith search path from the list as computed by searchList() */ 00298 static const char *getSearchPath(int i) 00299 { 00300 static const char *list = 0; 00301 static char **path = (char **)0; 00302 static int end = 0; 00303 static int numsize = MAX_SEARCH_PATHS; 00304 static char **tmp; 00305 /* So we can call free() in the "destructor" we use i=-1 to return the alloc'd array */ 00306 if (i == -1) 00307 { 00308 return (const char*)path; 00309 } 00310 if (!path) 00311 { 00312 path = (char **)calloc(MAX_SEARCH_PATHS, sizeof(char **)); 00313 } 00314 if (!list && !end) 00315 list = searchList(); 00316 if (i >= (numsize)) 00317 { 00318 debug("Increasing size for long PATH"); 00319 tmp = (char **)calloc((MAX_SEARCH_PATHS + numsize), sizeof(char **)); 00320 if (tmp) 00321 { 00322 memcpy(tmp, path, sizeof(char **) * numsize); 00323 free(path); 00324 path = tmp; 00325 numsize += MAX_SEARCH_PATHS; 00326 } 00327 else 00328 { 00329 return 0; 00330 } 00331 } 00332 00333 while (!path[i] && !end) 00334 { 00335 path[i] = strsep((char **)&list, ":"); 00336 00337 if (path[i][0] == 0) 00338 path[i] = 0; 00339 end = (list == 0); 00340 } 00341 return path[i]; 00342 } 00343 00344 static const char *getFullPath(int i, const char *file) 00345 { 00346 static char buf[PATH_MAX]; 00347 const char *path = getSearchPath(i); 00348 if (path) 00349 { 00350 snprintf(buf, PATH_MAX, "%s/%s", path, file); 00351 } 00352 return path ? buf : 0; 00353 } 00354 00355 /* Given a file name, try to determine the full path for that file. Starts 00356 * its search in the current directory, and then tries all paths in the 00357 * search list in the order they are specified there. 00358 */ 00359 static const struct stat *findFile(const char *file, const char **fullPath) 00360 { 00361 int i = 0; 00362 static struct stat sbuf; 00363 char *fileName; 00364 debug("finding file %s", file); 00365 *fullPath = file; 00366 if (0 == stat(file, &sbuf)) 00367 return &sbuf; 00368 if (strchr(file, '/')) 00369 return 0; /* If the path had a / we don't look in env var places */ 00370 fileName = NULL; 00371 if (!fileName) 00372 fileName = (char *)file; 00373 while ((*fullPath = getFullPath(i++, fileName))) 00374 { 00375 if (0 == stat(*fullPath, &sbuf)) 00376 return &sbuf; 00377 } 00378 ; 00379 return 0; 00380 } 00381 00382 /* Determine whether a given dlstatus is valid or not */ 00383 static int isValidStatus(struct dlstatus *status) 00384 { 00385 /* Walk the list to verify status is contained in it */ 00386 struct dlstatus *dls = stqueue; 00387 while (dls && status != dls) 00388 dls = dls->next; 00389 if (dls == 0) 00390 error("invalid handle"); 00391 else if ((dls->module == 0) || (dls->refs == 0)) 00392 error("handle to closed library"); 00393 else 00394 return TRUE; 00395 return FALSE; 00396 } 00397 00398 static inline int isFlagSet(int mode, int flag) 00399 { 00400 return (mode & flag) == flag; 00401 } 00402 00403 static struct dlstatus *lookupStatus(const struct stat *sbuf) 00404 { 00405 struct dlstatus *dls = stqueue; 00406 debug("looking for status"); 00407 while (dls && ( /* isFlagSet(dls->mode, RTLD_UNSHARED) */ 0 00408 || sbuf->st_dev != dls->device || sbuf->st_ino != dls->inode)) 00409 dls = dls->next; 00410 return dls; 00411 } 00412 00413 static void insertStatus(struct dlstatus *dls, const struct stat *sbuf) 00414 { 00415 debug("inserting status"); 00416 dls->inode = sbuf->st_ino; 00417 dls->device = sbuf->st_dev; 00418 dls->refs = 0; 00419 dls->mode = 0; 00420 if ((dls->flags & DL_IN_LIST) == 0) 00421 { 00422 dls->next = stqueue; 00423 stqueue = dls; 00424 dls->flags |= DL_IN_LIST; 00425 } 00426 } 00427 00428 static struct dlstatus *allocStatus() 00429 { 00430 struct dlstatus *dls; 00431 #ifdef REUSE_STATUS 00432 dls = stqueue; 00433 while (dls && dls->module) 00434 dls = dls->next; 00435 if (!dls) 00436 #endif 00437 dls = malloc(sizeof(*dls)); 00438 dls->flags = 0; 00439 return dls; 00440 } 00441 00442 static int promoteLocalToGlobal(struct dlstatus *dls) 00443 { 00444 static int (*p) (NSModule module) = 0; 00445 debug("promoting"); 00446 if (!p) 00447 _dyld_func_lookup("__dyld_NSMakePrivateModulePublic", (unsigned long *)&p); 00448 return (dls->module == MAGIC_DYLIB_MOD) || (p && p(dls->module)); 00449 } 00450 00451 static void *reference(struct dlstatus *dls, int mode) 00452 { 00453 if (dls) 00454 { 00455 if (dls->module == MAGIC_DYLIB_MOD && !isFlagSet(mode, RTLD_GLOBAL)) 00456 { 00457 warning("trying to open a .dylib with RTLD_LOCAL"); 00458 error("unable to open a .dylib with RTLD_LOCAL"); 00459 return NULL; 00460 } 00461 if (isFlagSet(mode, RTLD_GLOBAL) && 00462 !isFlagSet(dls->mode, RTLD_GLOBAL) && !promoteLocalToGlobal(dls)) 00463 { 00464 error("unable to promote local module to global"); 00465 return NULL; 00466 } 00467 dls->mode |= mode; 00468 dls->refs++; 00469 } 00470 else 00471 debug("reference called with NULL argument"); 00472 00473 return dls; 00474 } 00475 00476 static const struct mach_header *my_find_image(const char *name) 00477 { 00478 const struct mach_header *mh = 0; 00479 const char *id = NULL; 00480 int i = _dyld_image_count(); 00481 int j; 00482 mh = (struct mach_header *) 00483 dyld_NSAddImage(name, NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED | 00484 NSADDIMAGE_OPTION_RETURN_ON_ERROR); 00485 if (!mh) 00486 { 00487 for (j = 0; j < i; j++) 00488 { 00489 id = _dyld_get_image_name(j); 00490 if (!strcmp(id, name)) 00491 { 00492 mh = _dyld_get_image_header(j); 00493 break; 00494 } 00495 } 00496 } 00497 return mh; 00498 } 00499 00500 /* 00501 * dyld adds libraries by first adding the directly dependant libraries in link order, and 00502 * then adding the dependencies for those libraries, so we should do the same... but we don't 00503 * bother adding the extra dependencies, if the symbols are neither in the loaded image nor 00504 * any of it's direct dependencies, then it probably isn't there. 00505 */ 00506 NSSymbol *search_linked_libs(const struct mach_header * mh, const char *symbol) 00507 { 00508 int n; 00509 struct load_command *lc = 0; 00510 struct mach_header *wh; 00511 NSSymbol *nssym = 0; 00512 if (dyld_NSAddImage && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage) 00513 { 00514 lc = (struct load_command *)((char *)mh + sizeof(struct mach_header)); 00515 for (n = 0; n < mh->ncmds; n++, lc = (struct load_command *)((char *)lc + lc->cmdsize)) 00516 { 00517 if ((LC_LOAD_DYLIB == lc->cmd) || (LC_LOAD_WEAK_DYLIB == lc->cmd)) 00518 { 00519 if ((wh = (struct mach_header *) 00520 my_find_image((char *)(((struct dylib_command *)lc)->dylib.name.offset + 00521 (char *)lc)))) 00522 { 00523 if (dyld_NSIsSymbolNameDefinedInImage(wh, symbol)) 00524 { 00525 nssym = dyld_NSLookupSymbolInImage(wh, 00526 symbol, 00527 NSLOOKUPSYMBOLINIMAGE_OPTION_BIND | 00528 NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); 00529 break; 00530 } 00531 } 00532 } 00533 } 00534 if ((!nssym) && NSIsSymbolNameDefined(symbol)) 00535 { 00536 /* I've never seen this debug message...*/ 00537 debug("Symbol \"%s\" is defined but was not found", symbol); 00538 } 00539 } 00540 return nssym; 00541 } 00542 00543 /* Up to the caller to free() returned string */ 00544 static inline const char *dyld_error_str() 00545 { 00546 NSLinkEditErrors dylder; 00547 int dylderno; 00548 const char *dylderrstr; 00549 const char *dyldfile; 00550 const char* retStr = NULL; 00551 NSLinkEditError(&dylder, &dylderno, &dyldfile, &dylderrstr); 00552 if (dylderrstr && strlen(dylderrstr)) 00553 { 00554 retStr = malloc(strlen(dylderrstr) +1); 00555 strcpy((char*)retStr,dylderrstr); 00556 } 00557 return retStr; 00558 } 00559 00560 static void *dlsymIntern(struct dlstatus *dls, const char *symbol, int canSetError) 00561 { 00562 NSSymbol *nssym = 0; 00563 void *caller = __builtin_return_address(1); /* Be *very* careful about inlining */ 00564 const struct mach_header *caller_mh = 0; 00565 const char* savedErrorStr = NULL; 00566 resetdlerror(); 00567 #ifndef RTLD_SELF 00568 #define RTLD_SELF ((void *) -3) 00569 #endif 00570 if (NULL == dls) 00571 dls = RTLD_SELF; 00572 if ((RTLD_NEXT == dls) || (RTLD_SELF == dls)) 00573 { 00574 if (dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage) 00575 { 00576 caller_mh = image_for_address(caller); 00577 if (RTLD_SELF == dls) 00578 { 00579 /* FIXME: We should be using the NSModule api, if SELF is an MH_BUNDLE 00580 * But it appears to work anyway, and looking at the code in dyld_libfuncs.c 00581 * this is acceptable. 00582 */ 00583 if (dyld_NSIsSymbolNameDefinedInImage(caller_mh, symbol)) 00584 { 00585 nssym = dyld_NSLookupSymbolInImage(caller_mh, 00586 symbol, 00587 NSLOOKUPSYMBOLINIMAGE_OPTION_BIND | 00588 NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); 00589 } 00590 } 00591 if (!nssym) 00592 { 00593 if (RTLD_SELF == dls) 00594 savedErrorStr = dyld_error_str(); 00595 nssym = search_linked_libs(caller_mh, symbol); 00596 } 00597 } 00598 else 00599 { 00600 if (canSetError) 00601 error("RTLD_SELF and RTLD_NEXT are not supported"); 00602 return NULL; 00603 } 00604 } 00605 if (!nssym) 00606 { 00607 00608 if (RTLD_DEFAULT == dls) 00609 { 00610 dls = &mainStatus; 00611 } 00612 if (!isValidStatus(dls)) 00613 return NULL; 00614 00615 if (dls->module != MAGIC_DYLIB_MOD) 00616 { 00617 nssym = NSLookupSymbolInModule(dls->module, symbol); 00618 if (!nssym && NSIsSymbolNameDefined(symbol)) 00619 { 00620 debug("Searching dependencies"); 00621 savedErrorStr = dyld_error_str(); 00622 nssym = search_linked_libs(get_mach_header_from_NSModule(dls->module), symbol); 00623 } 00624 } 00625 else if (dls->lib && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage) 00626 { 00627 if (dyld_NSIsSymbolNameDefinedInImage(dls->lib, symbol)) 00628 { 00629 nssym = dyld_NSLookupSymbolInImage(dls->lib, 00630 symbol, 00631 NSLOOKUPSYMBOLINIMAGE_OPTION_BIND | 00632 NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); 00633 } 00634 else if (NSIsSymbolNameDefined(symbol)) 00635 { 00636 debug("Searching dependencies"); 00637 savedErrorStr = dyld_error_str(); 00638 nssym = search_linked_libs(dls->lib, symbol); 00639 } 00640 } 00641 else if (dls->module == MAGIC_DYLIB_MOD) 00642 { 00643 /* Global context, use NSLookupAndBindSymbol */ 00644 if (NSIsSymbolNameDefined(symbol)) 00645 { 00646 /* There doesn't seem to be a return on error option for this call??? 00647 this is potentially broken, if binding fails, it will improperly 00648 exit the application. */ 00649 nssym = NSLookupAndBindSymbol(symbol); 00650 } 00651 else 00652 { 00653 if (savedErrorStr) 00654 free((char*)savedErrorStr); 00655 savedErrorStr = malloc(256); 00656 snprintf((char*)savedErrorStr, 256, "Symbol \"%s\" not in global context",symbol); 00657 } 00658 } 00659 } 00660 /* Error reporting */ 00661 if (!nssym) 00662 { 00663 if (!savedErrorStr || !strlen(savedErrorStr)) 00664 { 00665 if (savedErrorStr) 00666 free((char*)savedErrorStr); 00667 savedErrorStr = malloc(256); 00668 snprintf((char*)savedErrorStr, 256,"Symbol \"%s\" not found",symbol); 00669 } 00670 if (canSetError) 00671 { 00672 error(savedErrorStr); 00673 } 00674 else 00675 { 00676 debug(savedErrorStr); 00677 } 00678 if (savedErrorStr) 00679 free((char*)savedErrorStr); 00680 return NULL; 00681 } 00682 return NSAddressOfSymbol(nssym); 00683 } 00684 00685 static struct dlstatus *loadModule(const char *path, const struct stat *sbuf, int mode) 00686 { 00687 NSObjectFileImage ofi = 0; 00688 NSObjectFileImageReturnCode ofirc; 00689 struct dlstatus *dls; 00690 NSLinkEditErrors ler; 00691 int lerno; 00692 const char *errstr; 00693 const char *file; 00694 void (*init) (void); 00695 ofirc = NSCreateObjectFileImageFromFile(path, &ofi); 00696 switch (ofirc) 00697 { 00698 case NSObjectFileImageSuccess: 00699 break; 00700 case NSObjectFileImageInappropriateFile: 00701 if (dyld_NSAddImage && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage) 00702 { 00703 if (!isFlagSet(mode, RTLD_GLOBAL)) 00704 { 00705 warning("trying to open a .dylib with RTLD_LOCAL"); 00706 error("unable to open this file with RTLD_LOCAL"); 00707 return NULL; 00708 } 00709 } 00710 else 00711 { 00712 error("opening this file is unsupported on this system"); 00713 return NULL; 00714 } 00715 break; 00716 case NSObjectFileImageFailure: 00717 error("object file setup failure"); 00718 return NULL; 00719 case NSObjectFileImageArch: 00720 error("no object for this architecture"); 00721 return NULL; 00722 case NSObjectFileImageFormat: 00723 error("bad object file format"); 00724 return NULL; 00725 case NSObjectFileImageAccess: 00726 error("can't read object file"); 00727 return NULL; 00728 default: 00729 error("unknown error from NSCreateObjectFileImageFromFile()"); 00730 return NULL; 00731 } 00732 dls = lookupStatus(sbuf); 00733 if (!dls) 00734 { 00735 dls = allocStatus(); 00736 } 00737 if (!dls) 00738 { 00739 error("unable to allocate memory"); 00740 return NULL; 00741 } 00742 dls->lib = 0; 00743 if (ofirc == NSObjectFileImageInappropriateFile) 00744 { 00745 if ((dls->lib = dyld_NSAddImage(path, NSADDIMAGE_OPTION_RETURN_ON_ERROR))) 00746 { 00747 debug("Dynamic lib loaded at %ld", dls->lib); 00748 ofi = MAGIC_DYLIB_OFI; 00749 dls->module = MAGIC_DYLIB_MOD; 00750 ofirc = NSObjectFileImageSuccess; 00751 /* Although it is possible with a bit of work to modify this so it works and 00752 functions with RTLD_NOW, I don't deem it necessary at the moment */ 00753 } 00754 if (!(dls->module)) 00755 { 00756 NSLinkEditError(&ler, &lerno, &file, &errstr); 00757 if (!errstr || (!strlen(errstr))) 00758 error("Can't open this file type"); 00759 else 00760 error(errstr); 00761 if ((dls->flags & DL_IN_LIST) == 0) 00762 { 00763 free(dls); 00764 } 00765 return NULL; 00766 } 00767 } 00768 else 00769 { 00770 dls->module = NSLinkModule(ofi, path, 00771 NSLINKMODULE_OPTION_RETURN_ON_ERROR | 00772 NSLINKMODULE_OPTION_PRIVATE | 00773 (isFlagSet(mode, RTLD_NOW) ? NSLINKMODULE_OPTION_BINDNOW : 0)); 00774 NSDestroyObjectFileImage(ofi); 00775 if (dls->module) 00776 { 00777 dls->lib = get_mach_header_from_NSModule(dls->module); 00778 } 00779 } 00780 if (!dls->module) 00781 { 00782 NSLinkEditError(&ler, &lerno, &file, &errstr); 00783 if ((dls->flags & DL_IN_LIST) == 0) 00784 { 00785 free(dls); 00786 } 00787 error(errstr); 00788 return NULL; 00789 } 00790 00791 insertStatus(dls, sbuf); 00792 dls = reference(dls, mode); 00793 if ((init = dlsymIntern(dls, "__init", 0))) 00794 { 00795 debug("calling _init()"); 00796 init(); 00797 } 00798 return dls; 00799 } 00800 00801 static void dlcompat_init_func(void) 00802 { 00803 static int inited = 0; 00804 if (!inited) 00805 { 00806 inited = 1; 00807 _dyld_func_lookup("__dyld_NSAddImage", (unsigned long *)&dyld_NSAddImage); 00808 _dyld_func_lookup("__dyld_NSIsSymbolNameDefinedInImage", 00809 (unsigned long *)&dyld_NSIsSymbolNameDefinedInImage); 00810 _dyld_func_lookup("__dyld_NSLookupSymbolInImage", (unsigned long *)&dyld_NSLookupSymbolInImage); 00811 if (pthread_mutex_init(&dlcompat_mutex, NULL)) 00812 exit(1); 00813 if (pthread_key_create(&dlerror_key, &dlerrorfree)) 00814 exit(1); 00815 /* And be neat and tidy and clean up after ourselves */ 00816 atexit(dlcompat_cleanup); 00817 } 00818 } 00819 00820 #if 0 00821 #pragma CALL_ON_LOAD dlcompat_init_func 00822 #endif 00823 00824 static void dlcompat_cleanup(void) 00825 { 00826 struct dlstatus *dls; 00827 struct dlstatus *next; 00828 char *data; 00829 data = (char *)searchList(); 00830 if ( data ) 00831 free( data ); 00832 data = (char *)getSearchPath(-1); 00833 if ( data ) 00834 free( data ); 00835 pthread_mutex_destroy(&dlcompat_mutex); 00836 pthread_key_delete(dlerror_key); 00837 next = stqueue; 00838 while (next && (next != &mainStatus)) 00839 { 00840 dls = next; 00841 next = dls->next; 00842 free(dls); 00843 } 00844 } 00845 00846 static void resetdlerror() 00847 { 00848 struct dlthread *tss; 00849 tss = pthread_getspecific(dlerror_key); 00850 tss->errset = 0; 00851 } 00852 00853 static void dlerrorfree(void *data) 00854 { 00855 free(data); 00856 } 00857 00858 /* We kind of want a recursive lock here, but meet a little trouble 00859 * because they are not available pre OS X 10.2, so we fake it 00860 * using thread specific storage to keep a lock count 00861 */ 00862 static inline void dolock(void) 00863 { 00864 int err = 0; 00865 struct dlthread *tss; 00866 tss = pthread_getspecific(dlerror_key); 00867 if (!tss) 00868 { 00869 tss = malloc(sizeof(struct dlthread)); 00870 tss->lockcnt = 0; 00871 tss->errset = 0; 00872 if (pthread_setspecific(dlerror_key, tss)) 00873 { 00874 fprintf(stderr,"dlcompat: pthread_setspecific failed\n"); 00875 exit(1); 00876 } 00877 } 00878 if (!tss->lockcnt) 00879 err = pthread_mutex_lock(&dlcompat_mutex); 00880 tss->lockcnt = tss->lockcnt +1; 00881 if (err) 00882 exit(err); 00883 } 00884 00885 static inline void dounlock(void) 00886 { 00887 int err = 0; 00888 struct dlthread *tss; 00889 tss = pthread_getspecific(dlerror_key); 00890 tss->lockcnt = tss->lockcnt -1; 00891 if (!tss->lockcnt) 00892 err = pthread_mutex_unlock(&dlcompat_mutex); 00893 if (err) 00894 exit(err); 00895 } 00896 00897 void *dlopen(const char *path, int mode) 00898 { 00899 const struct stat *sbuf; 00900 struct dlstatus *dls; 00901 const char *fullPath; 00902 dlcompat_init_func(); /* Just in case */ 00903 dolock(); 00904 resetdlerror(); 00905 if (!path) 00906 { 00907 dls = &mainStatus; 00908 goto dlopenok; 00909 } 00910 if (!(sbuf = findFile(path, &fullPath))) 00911 { 00912 error("file \"%s\" not found", path); 00913 goto dlopenerror; 00914 } 00915 /* Now checks that it hasn't been closed already */ 00916 if ((dls = lookupStatus(sbuf)) && (dls->refs > 0)) 00917 { 00918 /* debug("status found"); */ 00919 dls = reference(dls, mode); 00920 goto dlopenok; 00921 } 00922 #ifdef RTLD_NOLOAD 00923 if (isFlagSet(mode, RTLD_NOLOAD)) 00924 { 00925 error("no existing handle and RTLD_NOLOAD specified"); 00926 goto dlopenerror; 00927 } 00928 #endif 00929 if (isFlagSet(mode, RTLD_LAZY) && isFlagSet(mode, RTLD_NOW)) 00930 { 00931 error("how can I load something both RTLD_LAZY and RTLD_NOW?"); 00932 goto dlopenerror; 00933 } 00934 dls = loadModule(fullPath, sbuf, mode); 00935 00936 dlopenok: 00937 dounlock(); 00938 return (void *)dls; 00939 dlopenerror: 00940 dounlock(); 00941 return NULL; 00942 } 00943 00944 #if !FINK_BUILD 00945 void *dlsym(void * dl_restrict handle, const char * dl_restrict symbol) 00946 { 00947 int sym_len = strlen(symbol); 00948 void *value = NULL; 00949 char *malloc_sym = NULL; 00950 dolock(); 00951 malloc_sym = malloc(sym_len + 2); 00952 if (malloc_sym) 00953 { 00954 sprintf(malloc_sym, "_%s", symbol); 00955 value = dlsymIntern(handle, malloc_sym, 1); 00956 free(malloc_sym); 00957 } 00958 else 00959 { 00960 error("Unable to allocate memory"); 00961 goto dlsymerror; 00962 } 00963 dounlock(); 00964 return value; 00965 dlsymerror: 00966 dounlock(); 00967 return NULL; 00968 } 00969 #endif 00970 00971 #if FINK_BUILD 00972 00973 void *dlsym_prepend_underscore(void *handle, const char *symbol) 00974 { 00975 void *answer; 00976 dolock(); 00977 answer = dlsym_prepend_underscore_intern(handle, symbol); 00978 dounlock(); 00979 return answer; 00980 } 00981 00982 static void *dlsym_prepend_underscore_intern(void *handle, const char *symbol) 00983 { 00984 /* 00985 * A quick and easy way for porting packages which call dlsym(handle,"sym") 00986 * If the porter adds -Ddlsym=dlsym_prepend_underscore to the CFLAGS then 00987 * this function will be called, and will add the required underscore. 00988 * 00989 * Note that I haven't figured out yet which should be "standard", prepend 00990 * the underscore always, or not at all. These global functions need to go away 00991 * for opendarwin. 00992 */ 00993 int sym_len = strlen(symbol); 00994 void *value = NULL; 00995 char *malloc_sym = NULL; 00996 malloc_sym = malloc(sym_len + 2); 00997 if (malloc_sym) 00998 { 00999 sprintf(malloc_sym, "_%s", symbol); 01000 value = dlsymIntern(handle, malloc_sym, 1); 01001 free(malloc_sym); 01002 } 01003 else 01004 { 01005 error("Unable to allocate memory"); 01006 } 01007 return value; 01008 } 01009 01010 void *dlsym_auto_underscore(void *handle, const char *symbol) 01011 { 01012 void *answer; 01013 dolock(); 01014 answer = dlsym_auto_underscore_intern(handle, symbol); 01015 dounlock(); 01016 return answer; 01017 01018 } 01019 static void *dlsym_auto_underscore_intern(void *handle, const char *symbol) 01020 { 01021 struct dlstatus *dls = handle; 01022 void *addr = 0; 01023 addr = dlsymIntern(dls, symbol, 0); 01024 if (!addr) 01025 addr = dlsym_prepend_underscore_intern(handle, symbol); 01026 return addr; 01027 } 01028 01029 01030 void *dlsym(void * dl_restrict handle, const char * dl_restrict symbol) 01031 { 01032 struct dlstatus *dls = handle; 01033 void *addr = 0; 01034 dolock(); 01035 addr = dlsymIntern(dls, symbol, 1); 01036 dounlock(); 01037 return addr; 01038 } 01039 #endif 01040 01041 int dlclose(void *handle) 01042 { 01043 struct dlstatus *dls = handle; 01044 dolock(); 01045 resetdlerror(); 01046 if (!isValidStatus(dls)) 01047 { 01048 goto dlcloseerror; 01049 } 01050 if (dls->module == MAGIC_DYLIB_MOD) 01051 { 01052 const char *name; 01053 if (!dls->lib) 01054 { 01055 name = "global context"; 01056 } 01057 else 01058 { 01059 name = get_lib_name(dls->lib); 01060 } 01061 warning("trying to close a .dylib!"); 01062 error("Not closing \"%s\" - dynamic libraries cannot be closed", name); 01063 goto dlcloseerror; 01064 } 01065 if (!dls->module) 01066 { 01067 error("module already closed"); 01068 goto dlcloseerror; 01069 } 01070 01071 if (dls->refs == 1) 01072 { 01073 unsigned long options = 0; 01074 void (*fini) (void); 01075 if ((fini = dlsymIntern(dls, "__fini", 0))) 01076 { 01077 debug("calling _fini()"); 01078 fini(); 01079 } 01080 #ifdef __ppc__ 01081 options |= NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES; 01082 #endif 01083 #if 1 01084 /* Currently, if a module contains c++ static destructors and it is unloaded, we 01085 * get a segfault in atexit(), due to compiler and dynamic loader differences of 01086 * opinion, this works around that. 01087 * I really need a way to figure out from code if this is still necessary. 01088 */ 01089 if ((const struct section *)NULL != 01090 getsectbynamefromheader(get_mach_header_from_NSModule(dls->module), 01091 "__DATA", "__mod_term_func")) 01092 { 01093 options |= NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED; 01094 } 01095 #endif 01096 #ifdef RTLD_NODELETE 01097 if (isFlagSet(dls->mode, RTLD_NODELETE)) 01098 options |= NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED; 01099 #endif 01100 if (!NSUnLinkModule(dls->module, options)) 01101 { 01102 error("unable to unlink module"); 01103 goto dlcloseerror; 01104 } 01105 dls->refs--; 01106 dls->module = 0; 01107 /* Note: the dlstatus struct dls is neither removed from the list 01108 * nor is the memory it occupies freed. This shouldn't pose a 01109 * problem in mostly all cases, though. 01110 */ 01111 } 01112 dounlock(); 01113 return 0; 01114 dlcloseerror: 01115 dounlock(); 01116 return 1; 01117 } 01118 01119 const char *dlerror(void) 01120 { 01121 struct dlthread *tss; 01122 char * err_str; 01123 tss = pthread_getspecific(dlerror_key); 01124 err_str = tss->errstr; 01125 tss = pthread_getspecific(dlerror_key); 01126 if (tss->errset == 0) 01127 return 0; 01128 tss->errset = 0; 01129 return (err_str ); 01130 } 01131 01132 /* Given an address, return the mach_header for the image containing it 01133 * or zero if the given address is not contained in any loaded images. 01134 */ 01135 const struct mach_header *image_for_address(const void *address) 01136 { 01137 unsigned long i; 01138 unsigned long j; 01139 unsigned long count = _dyld_image_count(); 01140 struct mach_header *mh = 0; 01141 struct load_command *lc = 0; 01142 unsigned long addr = NULL; 01143 for (i = 0; i < count; i++) 01144 { 01145 addr = (unsigned long)address - _dyld_get_image_vmaddr_slide(i); 01146 mh = _dyld_get_image_header(i); 01147 if (mh) 01148 { 01149 lc = (struct load_command *)((char *)mh + sizeof(struct mach_header)); 01150 for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize)) 01151 { 01152 if (LC_SEGMENT == lc->cmd && 01153 addr >= ((struct segment_command *)lc)->vmaddr && 01154 addr < 01155 ((struct segment_command *)lc)->vmaddr + ((struct segment_command *)lc)->vmsize) 01156 { 01157 goto image_found; 01158 } 01159 } 01160 } 01161 mh = 0; 01162 } 01163 image_found: 01164 return mh; 01165 } 01166 01167 int dladdr(const void * dl_restrict p, Dl_info * dl_restrict info) 01168 { 01169 /* 01170 FIXME: USe the routine image_for_address. 01171 */ 01172 unsigned long i; 01173 unsigned long j; 01174 unsigned long count = _dyld_image_count(); 01175 struct mach_header *mh = 0; 01176 struct load_command *lc = 0; 01177 unsigned long addr = NULL; 01178 unsigned long table_off = (unsigned long)0; 01179 int found = 0; 01180 if (!info) 01181 return 0; 01182 dolock(); 01183 resetdlerror(); 01184 info->dli_fname = 0; 01185 info->dli_fbase = 0; 01186 info->dli_sname = 0; 01187 info->dli_saddr = 0; 01188 /* Some of this was swiped from code posted by Douglas Davidson <ddavidso AT apple DOT com> 01189 * to darwin-development AT lists DOT apple DOT com and slightly modified 01190 */ 01191 for (i = 0; i < count; i++) 01192 { 01193 addr = (unsigned long)p - _dyld_get_image_vmaddr_slide(i); 01194 mh = _dyld_get_image_header(i); 01195 if (mh) 01196 { 01197 lc = (struct load_command *)((char *)mh + sizeof(struct mach_header)); 01198 for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize)) 01199 { 01200 if (LC_SEGMENT == lc->cmd && 01201 addr >= ((struct segment_command *)lc)->vmaddr && 01202 addr < 01203 ((struct segment_command *)lc)->vmaddr + ((struct segment_command *)lc)->vmsize) 01204 { 01205 info->dli_fname = _dyld_get_image_name(i); 01206 info->dli_fbase = (void *)mh; 01207 found = 1; 01208 break; 01209 } 01210 } 01211 if (found) 01212 break; 01213 } 01214 } 01215 if (!found) 01216 { 01217 dounlock(); 01218 return 0; 01219 } 01220 lc = (struct load_command *)((char *)mh + sizeof(struct mach_header)); 01221 for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize)) 01222 { 01223 if (LC_SEGMENT == lc->cmd) 01224 { 01225 if (!strcmp(((struct segment_command *)lc)->segname, "__LINKEDIT")) 01226 break; 01227 } 01228 } 01229 table_off = 01230 ((unsigned long)((struct segment_command *)lc)->vmaddr) - 01231 ((unsigned long)((struct segment_command *)lc)->fileoff) + _dyld_get_image_vmaddr_slide(i); 01232 debug("table off %x", table_off); 01233 01234 lc = (struct load_command *)((char *)mh + sizeof(struct mach_header)); 01235 for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize)) 01236 { 01237 if (LC_SYMTAB == lc->cmd) 01238 { 01239 01240 struct nlist *symtable = (struct nlist *)(((struct symtab_command *)lc)->symoff + table_off); 01241 unsigned long numsyms = ((struct symtab_command *)lc)->nsyms; 01242 struct nlist *nearest = NULL; 01243 unsigned long diff = 0xffffffff; 01244 unsigned long strtable = (unsigned long)(((struct symtab_command *)lc)->stroff + table_off); 01245 debug("symtable %x", symtable); 01246 for (i = 0; i < numsyms; i++) 01247 { 01248 /* Ignore the following kinds of Symbols */ 01249 if ((!symtable->n_value) /* Undefined */ 01250 || (symtable->n_type >= N_PEXT) /* Debug symbol */ 01251 || (!(symtable->n_type & N_EXT)) /* Local Symbol */ 01252 ) 01253 { 01254 symtable++; 01255 continue; 01256 } 01257 if ((addr >= symtable->n_value) && (diff >= (symtable->n_value - addr))) 01258 { 01259 diff = (unsigned long)symtable->n_value - addr; 01260 nearest = symtable; 01261 } 01262 symtable++; 01263 } 01264 if (nearest) 01265 { 01266 info->dli_saddr = nearest->n_value + ((void *)p - addr); 01267 info->dli_sname = (char *)(strtable + nearest->n_un.n_strx); 01268 } 01269 } 01270 } 01271 dounlock(); 01272 return 1; 01273 } 01274 01275 01276 /* 01277 * Implement the dlfunc() interface, which behaves exactly the same as 01278 * dlsym() except that it returns a function pointer instead of a data 01279 * pointer. This can be used by applications to avoid compiler warnings 01280 * about undefined behavior, and is intended as prior art for future 01281 * POSIX standardization. This function requires that all pointer types 01282 * have the same representation, which is true on all platforms FreeBSD 01283 * runs on, but is not guaranteed by the C standard. 01284 */ 01285 #if 0 01286 dlfunc_t dlfunc(void * dl_restrict handle, const char * dl_restrict symbol) 01287 { 01288 union 01289 { 01290 void *d; 01291 dlfunc_t f; 01292 } rv; 01293 int sym_len = strlen(symbol); 01294 char *malloc_sym = NULL; 01295 dolock(); 01296 malloc_sym = malloc(sym_len + 2); 01297 if (malloc_sym) 01298 { 01299 sprintf(malloc_sym, "_%s", symbol); 01300 rv.d = dlsymIntern(handle, malloc_sym, 1); 01301 free(malloc_sym); 01302 } 01303 else 01304 { 01305 error("Unable to allocate memory"); 01306 goto dlfuncerror; 01307 } 01308 dounlock(); 01309 return rv.f; 01310 dlfuncerror: 01311 dounlock(); 01312 return NULL; 01313 } 01314 #endif