Back to index

plt-scheme  4.2.1
dlcompat.inc
Go to the documentation of this file.
00001 /*
00002 Copyright (c) 2002 Jorge Acereda <jacereda@users.sourceforge.net>
00003 
00004 
00005 Permission is hereby granted, free of charge, to any person obtaining
00006 a copy of this software and associated documentation files (the
00007 "Software"), to deal in the Software without restriction, including
00008 without limitation the rights to use, copy, modify, merge, publish,
00009 distribute, sublicense, and/or sell copies of the Software, and to
00010 permit persons to whom the Software is furnished to do so, subject to
00011 the following conditions:
00012 
00013 The above copyright notice and this permission notice shall be
00014 included in all copies or substantial portions of the Software.
00015 
00016 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00017 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00018 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00019 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
00020 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
00021 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
00022 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00023 */
00024 
00025 static void * dlopen(const char *path, int mode);
00026 static void * dlsym(void * handle, const char *symbol);
00027 static const char * dlerror(void);
00028 static int dlclose(void * handle);
00029 
00030 #define RTLD_LAZY    0x1
00031 #define RTLD_NOW     0x2
00032 #define RTLD_LOCAL   0x4
00033 #define RTLD_GLOBAL  0x8
00034 #define RTLD_NOLOAD  0x10
00035 #define RTLD_NODELETE       0x80
00036 
00037 #include <stdio.h>
00038 #include <stdlib.h>
00039 #include <string.h>
00040 #include <sys/types.h>
00041 #include <sys/stat.h>
00042 #include <stdarg.h>
00043 #include <limits.h>
00044 #include <mach-o/dyld.h>
00045 
00046 /* Define this to make dlcompat reuse data block. This way in theory we save
00047  * a little bit of overhead. However we then couldn't correctly catch excess
00048  * calls to dlclose(). Hence we don't use this feature
00049  */
00050 #undef REUSE_STATUS
00051 
00052 /* Size of the internal error message buffer (used by dlerror()) */
00053 #define ERR_STR_LEN                256
00054 
00055 /* Maximum number of search paths supported by getSearchPath */
00056 #define MAX_SEARCH_PATHS    32
00057 
00058 
00059 #define MAGIC_DYLIB_OFI ((NSObjectFileImage) 'DYOF')
00060 #define MAGIC_DYLIB_MOD ((NSModule) 'DYMO')
00061 
00062 /* internal flags */
00063 #define DL_IN_LIST 0x01
00064 
00065 /* This is our central data structure. Whenever a module is loaded via
00066  * dlopen(), we create such a struct.
00067  */
00068 struct dlstatus
00069 {
00070        struct dlstatus * next;     /* pointer to next element in the linked list */
00071        NSModule module;
00072        const struct mach_header *lib;
00073        int refs;                          /* reference count */
00074        int mode;                          /* mode in which this module was loaded */
00075        dev_t device;
00076        ino_t inode;
00077        int flags;                         /* Any internal flags we may need */
00078 };
00079 
00080 /* Head node of the dlstatus list */
00081 static struct dlstatus mainStatus =
00082        { 0, MAGIC_DYLIB_MOD,NULL, -1, RTLD_GLOBAL, 0, 0 };
00083 static struct dlstatus * stqueue = &mainStatus;
00084 
00085 
00086 /* Storage for the last error message (used by dlerror()) */
00087 static char err_str[ERR_STR_LEN];
00088 static int err_filled = 0;
00089 
00090 
00091 /* Prototypes to internal functions */
00092 static void debug(const char * fmt, ...);
00093 static void error(const char * str, ...);
00094 static const char * safegetenv(const char * s);
00095 static const char * searchList(void);
00096 static const char * getSearchPath(int i);
00097 static const char * getFullPath(int i, const char * file);
00098 static const struct stat * findFile(const char * file, const char ** fullPath);
00099 static int isValidStatus(struct dlstatus * status);
00100 static int isFlagSet(int mode, int flag);
00101 static struct dlstatus * lookupStatus(const struct stat * sbuf);
00102 static void insertStatus(struct dlstatus * dls, const struct stat * sbuf);
00103 static int promoteLocalToGlobal(struct dlstatus * dls);
00104 static void * reference (struct dlstatus * dls, int mode);
00105 static void * dlsymIntern(struct dlstatus * dls, const char *symbol, int canSetError);
00106 static struct dlstatus * allocStatus(void);
00107 static struct dlstatus * loadModule(const char * path, const struct stat * sbuf, int mode);
00108 
00109 /* Functions */
00110 
00111 #ifdef MZ_PRECISE_GC
00112 START_XFORM_SKIP;
00113 #endif
00114 
00115 static void debug(const char * fmt, ...)
00116 {
00117 #if DEBUG > 1
00118        va_list arg;
00119        va_start(arg, fmt);
00120        fprintf(stderr, "DLDEBUG: ");
00121        vfprintf(stderr, fmt, arg);
00122        fprintf(stderr, "\n");
00123        fflush(stderr);
00124        va_end(arg);
00125 #endif
00126 }
00127 
00128 static void error(const char * str, ...)
00129 {
00130        va_list arg;
00131        va_start(arg, str);
00132        strncpy(err_str, "dlcompat: ", ERR_STR_LEN);
00133        vsnprintf(err_str+10, ERR_STR_LEN-10, str, arg);
00134        va_end(arg);
00135        debug("ERROR: %s\n", err_str);
00136        err_filled = 1;
00137 }
00138 
00139 static void warning(const char * str)
00140 {
00141 #if DEBUG > 0
00142        fprintf(stderr, "WARNING: dlcompat: %s\n", str);
00143 #endif
00144 }
00145 
00146 static const char * safegetenv(const char * s)
00147 {
00148        const char * ss = getenv(s);
00149        return ss? ss : "";
00150 }
00151 
00152 /* Compute and return a list of all directories that we should search when
00153  * trying to locate a module. We first look at the values of LD_LIBRARY_PATH
00154  * and DYLD_LIBRARY_PATH, and then finally fall back to looking into
00155  * /usr/lib and /lib. Since both of the environments variables can contain a
00156  * list of colon seperated paths, we simply concat them and the two other paths
00157  * into one big string, which we then can easily parse.
00158  * Splitting this string into the actual path list is done by getSearchPath()
00159  */
00160 static const char * searchList()
00161 {
00162        char * buf;
00163        const char * ldlp = safegetenv("LD_LIBRARY_PATH");
00164        const char * dyldlp = safegetenv("DYLD_LIBRARY_PATH");
00165        size_t buf_size = strlen(ldlp) + strlen(dyldlp) + 20;
00166        buf = malloc(buf_size);
00167        snprintf(buf, buf_size, "%s:%s:/usr/lib:/lib", dyldlp, ldlp);
00168        return buf;
00169 }
00170 
00171 /* Returns the ith search path from the list as computed by searchList() */
00172 static const char * getSearchPath(int i)
00173 {
00174        static const char * list = 0;
00175        static const char * path[MAX_SEARCH_PATHS] = {0};
00176        static int end = 0;
00177        if (!list && !end) 
00178               list = searchList();
00179        while (!path[i] && !end) 
00180        {
00181               path[i] = strsep((char**)&list, ":");
00182               if (path[i][0] == 0)
00183                      path[i] = 0;
00184               end = list == 0;
00185        }
00186        return path[i];
00187 }
00188 
00189 static const char * getFullPath(int i, const char * file)
00190 {
00191        static char buf[PATH_MAX];
00192        const char * path = getSearchPath(i);
00193        if (path)
00194               snprintf(buf, PATH_MAX, "%s/%s", path, file);
00195        return path? buf : 0;
00196 }
00197 
00198 /* Given a file name, try to determine the full path for that file. Starts
00199  * its search in the current directory, and then tries all paths in the 
00200  * search list in the order they are specified there.
00201  */
00202 static const struct stat * findFile(const char * file, const char ** fullPath)
00203 {
00204        int i = 0;
00205        static struct stat sbuf;
00206        debug("finding file %s",file);
00207        *fullPath = file;
00208        do
00209        {
00210               if (0 == stat(*fullPath, &sbuf))
00211                      return &sbuf;
00212        }
00213        while ((*fullPath = getFullPath(i++, file)));
00214        return 0;
00215 }
00216 
00217 /* Determine whether a given dlstatus is valid or not */
00218 static int isValidStatus(struct dlstatus * status)
00219 {
00220        /* Walk the list to verify status is contained in it */
00221        struct dlstatus * dls = stqueue;
00222        while (dls && status != dls)
00223               dls = dls->next;
00224 
00225        if (dls == 0)
00226               error("invalid handle");
00227        else if (dls->module == 0)
00228               error("handle to closed library");
00229        else 
00230               return TRUE;
00231        return FALSE;
00232 }
00233 
00234 static int isFlagSet(int mode, int flag)
00235 {
00236        return (mode & flag) == flag;
00237 }
00238 
00239 static struct dlstatus * lookupStatus(const struct stat * sbuf)
00240 {
00241        struct dlstatus * dls = stqueue;
00242        debug("looking for status");
00243        while (dls && (       /* isFlagSet(dls->mode, RTLD_UNSHARED) */ 0
00244                      || sbuf->st_dev != dls->device
00245                      || sbuf->st_ino != dls->inode) && dls->refs)
00246               dls = dls->next;
00247        return dls;
00248 }
00249 
00250 static void insertStatus(struct dlstatus * dls, const struct stat * sbuf)
00251 {
00252        debug("inserting status");
00253        dls->inode = sbuf->st_ino;
00254        dls->device = sbuf->st_dev;
00255        dls->refs = 0;
00256        dls->mode = 0;
00257        if ((dls->flags & DL_IN_LIST) == 0) {
00258               dls->next = stqueue;
00259               stqueue = dls;
00260               dls->flags |= DL_IN_LIST;
00261        }      
00262 }
00263 
00264 static struct dlstatus * allocStatus()
00265 {
00266        struct dlstatus * dls;
00267 #ifdef REUSE_STATUS
00268        dls = stqueue;
00269        while (dls && dls->module)
00270               dls = dls->next;
00271        if (!dls)
00272 #endif
00273        dls = malloc(sizeof(*dls));
00274        dls->flags = 0;
00275        return dls;
00276 }
00277 
00278 static int promoteLocalToGlobal(struct dlstatus * dls)
00279 {
00280        static int (*p)(NSModule module) = 0;
00281        debug("promoting");
00282        if(!p)
00283               _dyld_func_lookup("__dyld_NSMakePrivateModulePublic", 
00284                        (unsigned long *)&p);
00285        return (dls->module == MAGIC_DYLIB_MOD) || (p && p(dls->module));
00286 }
00287 
00288 static void * reference (struct dlstatus * dls, int mode)
00289 {
00290        if (dls)
00291        {
00292               if (dls->module == MAGIC_DYLIB_MOD && isFlagSet(mode, RTLD_LOCAL))
00293               {
00294                      warning("trying to open a .dylib with RTLD_LOCAL");
00295                      error("unable to open a .dylib with RTLD_LOCAL");
00296                      return NULL;
00297               }
00298               if (isFlagSet(mode, RTLD_GLOBAL) &&
00299                      !isFlagSet(dls->mode, RTLD_GLOBAL) &&
00300                      !promoteLocalToGlobal(dls))
00301               {
00302                      error("unable to promote local module to global");
00303                      return NULL;
00304               }
00305               dls->mode |= mode;
00306               dls->refs++;
00307        }
00308        else
00309               debug("reference called with NULL argument");
00310 
00311        return dls;
00312 }
00313 
00314 static void * dlsymIntern(struct dlstatus * dls, const char *symbol,int canSetError)
00315 {
00316        NSSymbol * nssym = 0;
00317        /* If it is a module - use NSLookupSymbolInModule */
00318        if (dls->module != MAGIC_DYLIB_MOD) {
00319        
00320               nssym = NSLookupSymbolInModule(dls->module, symbol);
00321               if (!nssym && canSetError)
00322                      error("unable to find symbol \"%s\"", symbol);
00323        }             
00324        else {
00325               if (dls->lib != NULL) {
00326                      /* dylib, use NSIsSymbolNameDefinedInImage */
00327                      if (NSIsSymbolNameDefinedInImage(dls->lib,symbol)) {
00328                             nssym = NSLookupSymbolInImage(dls->lib,
00329                                                           symbol,
00330                                                           NSLOOKUPSYMBOLINIMAGE_OPTION_BIND 
00331                                                           | NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR
00332                                                           );
00333                      }
00334               }
00335               else {
00336                      /* Global context, use NSLookupAndBindSymbol */
00337                      nssym = NSLookupAndBindSymbol(symbol);
00338               }                                           
00339               if (!nssym) {
00340                      NSLinkEditErrors ler;
00341                      int lerno;
00342                      const char* errstr;
00343                      const char* file;           
00344                      NSLinkEditError(&ler,&lerno,&file,&errstr);      
00345                      if (errstr && strlen(errstr)) {    
00346                             if (canSetError)     
00347                                    error(errstr);
00348                             debug("%s",errstr);  
00349                      }      
00350                      
00351               }
00352        }                          
00353        if (!nssym)
00354               return NULL;
00355        return NSAddressOfSymbol(nssym);
00356 }
00357 
00358 static struct dlstatus * loadModule(const char * path, 
00359                                    const struct stat * sbuf, int mode)
00360 {
00361        NSObjectFileImage ofi = 0;
00362        NSObjectFileImageReturnCode ofirc;
00363        struct dlstatus * dls; 
00364        NSLinkEditErrors ler;
00365        int lerno;
00366        const char* errstr;
00367        const char* file;
00368        void (*init)(void);
00369 
00370        ofirc = NSCreateObjectFileImageFromFile(path, &ofi);
00371        switch (ofirc)
00372        {
00373               case NSObjectFileImageSuccess:
00374                      break;
00375               case NSObjectFileImageInappropriateFile:
00376                      if (isFlagSet(mode, RTLD_LOCAL))
00377                      {
00378                             warning("trying to open a .dylib with RTLD_LOCAL");
00379                             error("unable to open this file with RTLD_LOCAL");
00380                             return NULL;
00381                      }                           
00382                      break;
00383               case NSObjectFileImageFailure:
00384                      error("object file setup failure");
00385                      return NULL;
00386               case NSObjectFileImageArch:
00387                      error("no object for this architecture");
00388                      return NULL;
00389               case NSObjectFileImageFormat:
00390                      error("bad object file format");
00391                      return NULL;
00392               case NSObjectFileImageAccess:
00393                      error("can't read object file");
00394                      return NULL;
00395               default:
00396                      error("unknown error from NSCreateObjectFileImageFromFile()");
00397                      return NULL;
00398        }
00399        dls = lookupStatus(sbuf);
00400        if (!dls) {
00401               dls = allocStatus();
00402        }      
00403        if (!dls)
00404        {
00405               error("unable to allocate memory");
00406               return NULL;
00407        }
00408        dls->lib=0;
00409        if (ofirc == NSObjectFileImageInappropriateFile)
00410        {
00411               if ((dls->lib = NSAddImage(path, NSADDIMAGE_OPTION_RETURN_ON_ERROR)))
00412               {
00413                      debug("Dynamic lib loaded at %ld",dls->lib);
00414                      ofi = MAGIC_DYLIB_OFI;
00415                      dls->module = MAGIC_DYLIB_MOD;
00416                      ofirc = NSObjectFileImageSuccess;
00417               }
00418        }
00419        else
00420        {
00421               /* Should change this to take care of RLTD_LAZY etc */
00422               dls->module = NSLinkModule(ofi, path,
00423                                    NSLINKMODULE_OPTION_RETURN_ON_ERROR
00424                                     | NSLINKMODULE_OPTION_PRIVATE
00425                                    | NSLINKMODULE_OPTION_BINDNOW);
00426               NSDestroyObjectFileImage(ofi);
00427        }
00428        if (!dls->module)
00429        {
00430               NSLinkEditError(&ler,&lerno,&file,&errstr);                    
00431               free(dls); 
00432               error(errstr);
00433               return NULL;
00434        }
00435        insertStatus(dls, sbuf);
00436        
00437        if ((init = dlsymIntern(dls, "__init",0)))
00438        {
00439               debug("calling _init()");
00440               init();
00441        }
00442 
00443        return dls;
00444 }
00445 
00446 static void * dlopen(const char * path, int mode)
00447 {
00448        const struct stat * sbuf;
00449        struct dlstatus * dls;
00450        const char * fullPath;
00451        if (!path)
00452        {
00453               return &mainStatus;
00454        }      
00455        if (!(sbuf = findFile(path, &fullPath)))
00456        {
00457               error("file \"%s\" not found", path);
00458               return NULL;
00459        }
00460        /* Now checks that it hasn't been closed already */
00461        if ((dls = lookupStatus(sbuf)) && (dls->refs > 0))
00462        {
00463               /* debug("status found"); */
00464               return reference(dls, mode);       
00465        }
00466        if (isFlagSet(mode, RTLD_NOLOAD))
00467        {
00468               error("no existing handle and RTLD_NOLOAD specified");
00469               return NULL;
00470        }
00471        return reference(loadModule(fullPath, sbuf, mode),mode);
00472 }
00473 
00474 static void * dlsym(void * handle, const char *symbol)
00475 {
00476        struct dlstatus * dls = handle;
00477        void * addr = 0;
00478 
00479        if (!isValidStatus(dls))
00480               return NULL;
00481        addr = dlsymIntern(dls, symbol,1);
00482        return addr;
00483 }
00484 
00485 static int dlclose(void * handle)
00486 {
00487        struct dlstatus * dls = handle;
00488        if (!isValidStatus(dls))
00489               return 1;
00490        if (dls->module == MAGIC_DYLIB_MOD)
00491        {
00492               warning("trying to close a .dylib!");
00493               error("dynamic libraries cannot be closed");
00494               return 1;
00495        }
00496        if (!dls->module)
00497        {
00498               error("module already closed");
00499               return 1;
00500        }
00501        dls->refs--;
00502        if (!dls->refs)
00503        {
00504               unsigned long options = 0;
00505               void (*fini)(void);
00506               if ((fini = dlsymIntern(dls, "__fini",0)))
00507               {
00508                      debug("calling _fini()");
00509                      fini();
00510               }
00511               if (isFlagSet(dls->mode, RTLD_NODELETE))
00512                      options |= NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED;
00513               if (!NSUnLinkModule(dls->module, options))
00514               {
00515                      error("unable to unlink module");
00516                      return 1;
00517               }
00518               dls->module = 0;
00519               /* Note: the dlstatus struct dls is neither removed from the list
00520                * nor is the memory it occupies freed. This shouldn't pose a 
00521                * problem in mostly all cases, though.
00522                */
00523        }
00524        return 0;
00525 }
00526 
00527 static const char * dlerror(void)
00528 {
00529        const char * e = err_filled ? err_str : 0;
00530        err_filled = 0;
00531        return e;
00532 }
00533 
00534 #ifdef MZ_PRECISE_GC
00535 END_XFORM_SKIP;
00536 #endif