Main Page | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

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

Generated on Sat Nov 25 19:09:35 2006 for Asterisk by  doxygen 1.4.2