diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2004-12-27 13:58:15 -0200 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2004-12-27 13:58:15 -0200 |
| commit | a5eb48a3e69871eb3ed7df5fc09ae6bfea75fbd3 (patch) | |
| tree | c4ee4d8a2202964a9ed529fddf7ceff39db77d06 | |
| parent | 49fe79e9fd6db53703037e0c2e51d74fe6d27400 (diff) | |
| download | lua-a5eb48a3e69871eb3ed7df5fc09ae6bfea75fbd3.tar.gz lua-a5eb48a3e69871eb3ed7df5fc09ae6bfea75fbd3.tar.bz2 lua-a5eb48a3e69871eb3ed7df5fc09ae6bfea75fbd3.zip | |
redesigned loadlib (Windows version not tested yet)
| -rw-r--r-- | loadlib.c | 324 |
1 files changed, 182 insertions, 142 deletions
| @@ -1,28 +1,11 @@ | |||
| 1 | /* | 1 | /* |
| 2 | ** $Id: loadlib.c,v 1.12 2004/12/13 12:14:21 roberto Exp roberto $ | 2 | ** $Id: loadlib.c,v 1.13 2004/12/22 17:43:27 roberto Exp roberto $ |
| 3 | ** Dynamic library loader for Lua | 3 | ** Dynamic library loader for Lua |
| 4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
| 5 | * | 5 | * |
| 6 | * This Lua library exports a single function, called loadlib, which | ||
| 7 | * is called from Lua as loadlib(lib,init), where lib is the full | ||
| 8 | * name of the library to be loaded (including the complete path) and | ||
| 9 | * init is the name of a function to be called after the library is | ||
| 10 | * loaded. Typically, this function will register other functions, thus | ||
| 11 | * making the complete library available to Lua. The init function is | ||
| 12 | * *not* automatically called by loadlib. Instead, loadlib returns the | ||
| 13 | * init function as a Lua function that the client can call when it | ||
| 14 | * thinks is appropriate. In the case of errors, loadlib returns nil and | ||
| 15 | * two strings describing the error. The first string is supplied by | ||
| 16 | * the operating system; it should be informative and useful for error | ||
| 17 | * messages. The second string is "open", "init", or "absent" to identify | ||
| 18 | * the error and is meant to be used for making decisions without having | ||
| 19 | * to look into the first string (whose format is system-dependent). | ||
| 20 | * This module contains an implementation of loadlib for Unix systems | 6 | * This module contains an implementation of loadlib for Unix systems |
| 21 | * that have dlfcn, an implementation for Darwin (Mac OS X), an | 7 | * that have dlfcn, an implementation for Darwin (Mac OS X), an |
| 22 | * implementation for Windows, and a stub for other systems. See | 8 | * implementation for Windows, and a stub for other systems. |
| 23 | * the list at the end of this file for some links to available | ||
| 24 | * implementations of dlfcn and interfaces to other native dynamic | ||
| 25 | * loaders on top of which loadlib could be implemented. | ||
| 26 | */ | 9 | */ |
| 27 | 10 | ||
| 28 | 11 | ||
| @@ -38,96 +21,106 @@ | |||
| 38 | #include "lualib.h" | 21 | #include "lualib.h" |
| 39 | 22 | ||
| 40 | 23 | ||
| 41 | #define ERR_OPEN 1 | 24 | #define LIBPREFIX "LOADLIB: " |
| 42 | #define ERR_FUNCTION 2 | ||
| 43 | #define ERR_ABSENT 3 | ||
| 44 | 25 | ||
| 26 | #define POF LUA_POF | ||
| 27 | #define LIB_FAIL "open" | ||
| 28 | |||
| 29 | |||
| 30 | static void ll_unloadlib (void *lib); | ||
| 31 | static void *ll_load (lua_State *L, const char *path); | ||
| 32 | static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym); | ||
| 45 | 33 | ||
| 46 | static void registerlib (lua_State *L, const void *lib); | ||
| 47 | 34 | ||
| 48 | 35 | ||
| 49 | #if defined(USE_DLOPEN) | 36 | #if defined(USE_DLOPEN) |
| 50 | /* | 37 | /* |
| 51 | * This is an implementation of loadlib based on the dlfcn interface. | 38 | ** {======================================================================== |
| 52 | * The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD, | 39 | ** This is an implementation of loadlib based on the dlfcn interface. |
| 53 | * NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least | 40 | ** The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD, |
| 54 | * as an emulation layer on top of native functions. | 41 | ** NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least |
| 42 | ** as an emulation layer on top of native functions. | ||
| 43 | ** ========================================================================= | ||
| 55 | */ | 44 | */ |
| 56 | 45 | ||
| 57 | #include <dlfcn.h> | 46 | #include <dlfcn.h> |
| 58 | 47 | ||
| 59 | #define freelib dlclose | 48 | static void ll_unloadlib (void *lib) { |
| 60 | 49 | dlclose(lib); | |
| 61 | static int loadlib(lua_State *L, const char *path, const char *init) { | 50 | } |
| 62 | void *lib=dlopen(path,RTLD_NOW); | 51 | |
| 63 | if (lib != NULL) { | 52 | |
| 64 | lua_CFunction f=(lua_CFunction) dlsym(lib,init); | 53 | static void *ll_load (lua_State *L, const char *path) { |
| 65 | if (f != NULL) { | 54 | void *lib = dlopen(path, RTLD_NOW); |
| 66 | registerlib(L, lib); | 55 | if (lib == NULL) lua_pushstring(L, dlerror()); |
| 67 | lua_pushcfunction(L,f); | 56 | return lib; |
| 68 | return 0; | ||
| 69 | } | ||
| 70 | } | ||
| 71 | /* else return appropriate error message */ | ||
| 72 | lua_pushstring(L,dlerror()); | ||
| 73 | if (lib != NULL) { | ||
| 74 | dlclose(lib); | ||
| 75 | return ERR_FUNCTION; /* error loading function */ | ||
| 76 | } | ||
| 77 | else return ERR_OPEN; /* error loading library */ | ||
| 78 | } | 57 | } |
| 79 | 58 | ||
| 80 | 59 | ||
| 60 | static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { | ||
| 61 | lua_CFunction f = (lua_CFunction)dlsym(lib, sym); | ||
| 62 | if (f == NULL) lua_pushstring(L, dlerror()); | ||
| 63 | return f; | ||
| 64 | } | ||
| 65 | |||
| 66 | /* }====================================================== */ | ||
| 67 | |||
| 68 | |||
| 81 | 69 | ||
| 82 | #elif defined(USE_DLL) | 70 | #elif defined(USE_DLL) |
| 83 | /* | 71 | /* |
| 84 | * This is an implementation of loadlib for Windows using native functions. | 72 | ** {====================================================================== |
| 73 | ** This is an implementation of loadlib for Windows using native functions. | ||
| 74 | ** ======================================================================= | ||
| 85 | */ | 75 | */ |
| 86 | 76 | ||
| 87 | #include <windows.h> | 77 | #include <windows.h> |
| 88 | 78 | ||
| 89 | #define freelib(lib) FreeLibrary((HINSTANCE)lib) | ||
| 90 | |||
| 91 | 79 | ||
| 92 | static void pusherror(lua_State *L) { | 80 | static void pusherror (lua_State *L) { |
| 93 | int error = GetLastError(); | 81 | int error = GetLastError(); |
| 94 | char buffer[128]; | 82 | char buffer[128]; |
| 95 | if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, | 83 | if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, |
| 96 | 0, error, 0, buffer, sizeof(buffer), 0)) | 84 | NULL, error, 0, buffer, sizeof(buffer), NULL)) |
| 97 | lua_pushstring(L,buffer); | 85 | lua_pushstring(L,buffer); |
| 98 | else | 86 | else |
| 99 | lua_pushfstring(L, "system error %d\n", error); | 87 | lua_pushfstring(L, "system error %d\n", error); |
| 100 | } | 88 | } |
| 101 | 89 | ||
| 90 | static void ll_unloadlib (void *lib) { | ||
| 91 | FreeLibrary((HINSTANCE)lib); | ||
| 92 | } | ||
| 102 | 93 | ||
| 103 | static int loadlib (lua_State *L, const char *path, const char *init) { | 94 | |
| 95 | static void *ll_load (lua_State *L, const char *path) { | ||
| 104 | HINSTANCE lib = LoadLibrary(path); | 96 | HINSTANCE lib = LoadLibrary(path); |
| 105 | if (lib != NULL) { | 97 | if (lib == NULL) pusherror(L); |
| 106 | lua_CFunction f = (lua_CFunction)GetProcAddress(lib, init); | 98 | return lib; |
| 107 | if (f != NULL) { | ||
| 108 | registerlib(L, lib); | ||
| 109 | lua_pushcfunction(L, f); | ||
| 110 | return 0; | ||
| 111 | } | ||
| 112 | } | ||
| 113 | pusherror(L); | ||
| 114 | if (lib != NULL) { | ||
| 115 | FreeLibrary(lib); | ||
| 116 | return ERR_OPEN; | ||
| 117 | } | ||
| 118 | else return ERR_FUNCTION; | ||
| 119 | } | 99 | } |
| 120 | 100 | ||
| 121 | 101 | ||
| 102 | static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { | ||
| 103 | lua_CFunction f = (lua_CFunction)GetProcAddress((HINSTANCE)lib, sym); | ||
| 104 | if (f == NULL) pusherror(L); | ||
| 105 | return f; | ||
| 106 | } | ||
| 107 | |||
| 108 | /* }====================================================== */ | ||
| 109 | |||
| 110 | |||
| 122 | 111 | ||
| 123 | /* Native Mac OS X / Darwin Implementation */ | ||
| 124 | #elif defined(USE_DYLD) | 112 | #elif defined(USE_DYLD) |
| 113 | /* | ||
| 114 | ** {====================================================================== | ||
| 115 | ** Native Mac OS X / Darwin Implementation | ||
| 116 | ** ======================================================================= | ||
| 117 | */ | ||
| 125 | 118 | ||
| 126 | #include <mach-o/dyld.h> | 119 | #include <mach-o/dyld.h> |
| 127 | 120 | ||
| 128 | 121 | ||
| 129 | /* Mach cannot unload images (?) */ | 122 | #undef POF |
| 130 | #define freelib(lib) ((void)lib) | 123 | #define POF "_" LUA_POF |
| 131 | 124 | ||
| 132 | 125 | ||
| 133 | static void pusherror (lua_State *L) { | 126 | static void pusherror (lua_State *L) { |
| @@ -140,86 +133,155 @@ static void pusherror (lua_State *L) { | |||
| 140 | } | 133 | } |
| 141 | 134 | ||
| 142 | 135 | ||
| 143 | static int loadlib (lua_State *L, const char *path, const char *init) { | 136 | static const char *errorfromcode (NSObjectFileImageReturnCode ret) { |
| 144 | const struct mach_header *lib; | 137 | switch (ret) { |
| 138 | case NSObjectFileImageInappropriateFile: | ||
| 139 | return "file is not a bundle"; | ||
| 140 | case NSObjectFileImageArch: | ||
| 141 | return "library is for wrong CPU type"; | ||
| 142 | case NSObjectFileImageFormat: | ||
| 143 | return "bad format"; | ||
| 144 | case NSObjectFileImageAccess: | ||
| 145 | return "cannot access file"; | ||
| 146 | case NSObjectFileImageFailure: | ||
| 147 | default: | ||
| 148 | return "unable to load library"; | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | |||
| 153 | static void ll_unloadlib (void *lib) { | ||
| 154 | NSUnLinkModule((NSModule)lib, NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES); | ||
| 155 | } | ||
| 156 | |||
| 157 | |||
| 158 | static void *ll_load (lua_State *L, const char *path) { | ||
| 159 | NSObjectFileImage img; | ||
| 160 | NSObjectFileImageReturnCode ret; | ||
| 145 | /* this would be a rare case, but prevents crashing if it happens */ | 161 | /* this would be a rare case, but prevents crashing if it happens */ |
| 146 | if(!_dyld_present()) { | 162 | if(!_dyld_present()) { |
| 147 | lua_pushstring(L, "dyld not present."); | 163 | lua_pushliteral(L, "dyld not present"); |
| 148 | return ERR_OPEN; | 164 | return NULL; |
| 149 | } | 165 | } |
| 150 | lib = NSAddImage(path, NSADDIMAGE_OPTION_RETURN_ON_ERROR); | 166 | ret = NSCreateObjectFileImageFromFile(path, &img); |
| 151 | if(lib != NULL) { | 167 | if (ret == NSObjectFileImageSuccess) { |
| 152 | NSSymbol init_sym = NSLookupSymbolInImage(lib, init, | 168 | NSModule mod = NSLinkModule(img, path, NSLINKMODULE_OPTION_PRIVATE | |
| 153 | NSLOOKUPSYMBOLINIMAGE_OPTION_BIND | | 169 | NSLINKMODULE_OPTION_RETURN_ON_ERROR); |
| 154 | NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); | 170 | NSDestroyObjectFileImage(img); |
| 155 | if(init_sym != NULL) { | 171 | if (mod == NULL) pusherror(L); |
| 156 | lua_CFunction f = (lua_CFunction)NSAddressOfSymbol(init_sym); | 172 | return mod; |
| 157 | registerlib(L, lib); | 173 | } |
| 158 | lua_pushcfunction(L, f); | 174 | lua_pushstring(L, errorfromcode(ret)); |
| 159 | return 0; | 175 | return NULL; |
| 160 | } | 176 | } |
| 177 | |||
| 178 | |||
| 179 | static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { | ||
| 180 | NSSymbol nss = NSLookupSymbolInModule((NSModule)lib, sym); | ||
| 181 | if (nss == NULL) { | ||
| 182 | lua_pushfstring(L, "symbol `%s' not found", sym); | ||
| 183 | return NULL; | ||
| 161 | } | 184 | } |
| 162 | /* else an error ocurred */ | 185 | return (lua_CFunction)NSAddressOfSymbol(nss); |
| 163 | pusherror(L); | ||
| 164 | /* Can't unload image */ | ||
| 165 | return (lib != NULL) ? ERR_FUNCTION : ERR_OPEN; | ||
| 166 | } | 186 | } |
| 167 | 187 | ||
| 188 | /* }====================================================== */ | ||
| 189 | |||
| 168 | 190 | ||
| 169 | 191 | ||
| 170 | #else | 192 | #else |
| 171 | /* Fallback for other systems */ | 193 | /* |
| 194 | ** {====================================================== | ||
| 195 | ** Fallback for other systems | ||
| 196 | ** ======================================================= | ||
| 197 | */ | ||
| 172 | 198 | ||
| 199 | #undef LIB_FAIL | ||
| 200 | #define LIB_FAIL "absent" | ||
| 173 | 201 | ||
| 174 | #define freelib(lib) ((void)lib) | ||
| 175 | 202 | ||
| 176 | static int loadlib (lua_State *L, const char *path, const char *init) { | 203 | static void ll_unloadlib (void *lib) { |
| 177 | (void)path; (void)init; (void)®isterlib; /* to avoid warnings */ | 204 | (void)lib; /* to avoid warnings */ |
| 205 | } | ||
| 206 | |||
| 207 | |||
| 208 | static void *ll_load (lua_State *L, const char *path) { | ||
| 209 | (void)path; /* to avoid warnings */ | ||
| 210 | lua_pushliteral(L,"`loadlib' not supported"); | ||
| 211 | return NULL; | ||
| 212 | } | ||
| 213 | |||
| 214 | |||
| 215 | static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { | ||
| 216 | (void)lib; (void)sym; /* to avoid warnings */ | ||
| 178 | lua_pushliteral(L,"`loadlib' not supported"); | 217 | lua_pushliteral(L,"`loadlib' not supported"); |
| 179 | return ERR_ABSENT; | 218 | return NULL; |
| 180 | } | 219 | } |
| 181 | 220 | ||
| 221 | /* }====================================================== */ | ||
| 182 | #endif | 222 | #endif |
| 183 | 223 | ||
| 184 | 224 | ||
| 185 | /* | 225 | |
| 186 | ** common code for all implementations: put a library into the registry | 226 | static void **ll_register (lua_State *L, const char *path) { |
| 187 | ** so that, when Lua closes its state, the library's __gc metamethod | 227 | void **plib; |
| 188 | ** will be called to unload the library. | 228 | lua_pushfstring(L, "%s%s", LIBPREFIX, path); |
| 189 | */ | 229 | lua_gettable(L, LUA_REGISTRYINDEX); /* check library in registry? */ |
| 190 | static void registerlib (lua_State *L, const void *lib) { | 230 | if (!lua_isnil(L, -1)) /* is there an entry? */ |
| 191 | const void **plib = (const void **)lua_newuserdata(L, sizeof(const void *)); | 231 | plib = (void **)lua_touserdata(L, -1); |
| 192 | *plib = lib; | 232 | else { /* no entry yet; create one */ |
| 193 | luaL_getmetatable(L, "_LOADLIB"); | 233 | lua_pop(L, 1); |
| 194 | lua_setmetatable(L, -2); | 234 | plib = (void **)lua_newuserdata(L, sizeof(const void *)); |
| 195 | lua_pushboolean(L, 1); | 235 | *plib = NULL; |
| 196 | lua_settable(L, LUA_REGISTRYINDEX); /* registry[lib] = true */ | 236 | luaL_getmetatable(L, "_LOADLIB"); |
| 237 | lua_setmetatable(L, -2); | ||
| 238 | lua_pushfstring(L, "%s%s", LIBPREFIX, path); | ||
| 239 | lua_pushvalue(L, -2); | ||
| 240 | lua_settable(L, LUA_REGISTRYINDEX); | ||
| 241 | } | ||
| 242 | return plib; | ||
| 197 | } | 243 | } |
| 198 | 244 | ||
| 245 | |||
| 199 | /* | 246 | /* |
| 200 | ** __gc tag method: calls library's `freelib' function with the lib | 247 | ** __gc tag method: calls library's `ll_unloadlib' function with the lib |
| 201 | ** handle | 248 | ** handle |
| 202 | */ | 249 | */ |
| 203 | static int gctm (lua_State *L) { | 250 | static int gctm (lua_State *L) { |
| 204 | void *lib = *(void **)luaL_checkudata(L, 1, "_LOADLIB"); | 251 | void **lib = (void **)luaL_checkudata(L, 1, "_LOADLIB"); |
| 205 | freelib(lib); | 252 | if (lib) { |
| 253 | if (*lib) ll_unloadlib(*lib); | ||
| 254 | *lib = NULL; /* mark library as closed */ | ||
| 255 | } | ||
| 206 | return 0; | 256 | return 0; |
| 207 | } | 257 | } |
| 208 | 258 | ||
| 209 | 259 | ||
| 260 | static int ll_loadfunc (lua_State *L, const char *path, const char *sym) { | ||
| 261 | const char *reason; | ||
| 262 | void **reg = ll_register(L, path); | ||
| 263 | if (*reg == NULL) *reg = ll_load(L, path); | ||
| 264 | if (*reg == NULL) | ||
| 265 | reason = LIB_FAIL; | ||
| 266 | else { | ||
| 267 | lua_CFunction f = ll_sym(L, *reg, sym); | ||
| 268 | if (f) { | ||
| 269 | lua_pushcfunction(L, f); | ||
| 270 | return 1; /* return function */ | ||
| 271 | } | ||
| 272 | reason = "init"; | ||
| 273 | } | ||
| 274 | lua_pushnil(L); | ||
| 275 | lua_insert(L, -2); | ||
| 276 | lua_pushstring(L, reason); | ||
| 277 | return 3; /* return nil, ll_error, reason */ | ||
| 278 | } | ||
| 279 | |||
| 280 | |||
| 210 | static int ll_loadlib (lua_State *L) { | 281 | static int ll_loadlib (lua_State *L) { |
| 211 | static const char *const errcodes[] = {NULL, "open", "init", "absent"}; | ||
| 212 | const char *path = luaL_checkstring(L, 1); | 282 | const char *path = luaL_checkstring(L, 1); |
| 213 | const char *init = luaL_checkstring(L, 2); | 283 | const char *init = luaL_checkstring(L, 2); |
| 214 | int res = loadlib(L,path,init); | 284 | return ll_loadfunc(L, path, init); |
| 215 | if (res == 0) /* no error? */ | ||
| 216 | return 1; /* function is at stack top */ | ||
| 217 | else { /* error */ | ||
| 218 | lua_pushnil(L); | ||
| 219 | lua_insert(L, -2); /* insert nil before error message */ | ||
| 220 | lua_pushstring(L, errcodes[res]); | ||
| 221 | return 3; | ||
| 222 | } | ||
| 223 | } | 285 | } |
| 224 | 286 | ||
| 225 | 287 | ||
| @@ -261,8 +323,8 @@ static const char *loadC (lua_State *L, const char *fname, const char *name) { | |||
| 261 | fname = luaL_searchpath(L, fname, path); | 323 | fname = luaL_searchpath(L, fname, path); |
| 262 | if (fname == NULL) return path; /* library not found in this path */ | 324 | if (fname == NULL) return path; /* library not found in this path */ |
| 263 | funcname = luaL_gsub(L, name, ".", LUA_OFSEP); | 325 | funcname = luaL_gsub(L, name, ".", LUA_OFSEP); |
| 264 | funcname = lua_pushfstring(L, "%s%s", LUA_POF, funcname); | 326 | funcname = lua_pushfstring(L, "%s%s", POF, funcname); |
| 265 | if (loadlib(L, fname, funcname) != 0) | 327 | if (ll_loadfunc(L, fname, funcname) != 1) |
| 266 | luaL_error(L, "error loading package `%s' (%s)", name, lua_tostring(L, -1)); | 328 | luaL_error(L, "error loading package `%s' (%s)", name, lua_tostring(L, -1)); |
| 267 | return NULL; /* library loaded successfully */ | 329 | return NULL; /* library loaded successfully */ |
| 268 | } | 330 | } |
| @@ -400,25 +462,3 @@ LUALIB_API int luaopen_loadlib (lua_State *L) { | |||
| 400 | return 1; | 462 | return 1; |
| 401 | } | 463 | } |
| 402 | 464 | ||
| 403 | /* | ||
| 404 | * Here are some links to available implementations of dlfcn and | ||
| 405 | * interfaces to other native dynamic loaders on top of which loadlib | ||
| 406 | * could be implemented. Please send contributions and corrections to us. | ||
| 407 | * | ||
| 408 | * AIX | ||
| 409 | * Starting with AIX 4.2, dlfcn is included in the base OS. | ||
| 410 | * There is also an emulation package available. | ||
| 411 | * http://www.faqs.org/faqs/aix-faq/part4/section-21.html | ||
| 412 | * | ||
| 413 | * HPUX | ||
| 414 | * HPUX 11 has dlfcn. For HPUX 10 use shl_*. | ||
| 415 | * http://www.geda.seul.org/mailinglist/geda-dev37/msg00094.html | ||
| 416 | * http://www.stat.umn.edu/~luke/xls/projects/dlbasics/dlbasics.html | ||
| 417 | * | ||
| 418 | * Macintosh, Windows | ||
| 419 | * http://www.stat.umn.edu/~luke/xls/projects/dlbasics/dlbasics.html | ||
| 420 | * | ||
| 421 | * GLIB has wrapper code for BeOS, OS2, Unix and Windows | ||
| 422 | * http://cvs.gnome.org/lxr/source/glib/gmodule/ | ||
| 423 | * | ||
| 424 | */ | ||
