/* * Copyright (c) 2008 Hypertriton, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Cross-platform interface to dynamic linking loader. */ #include #include #include #include #include #include #include #include #include #if defined(BEOS) # include # include #elif defined(NETWARE) # include #elif defined(OS390) # include #elif defined(_WIN32) # include /* Conflicts */ # include # include # include #else # include # include # ifdef HAVE_DLFCN_H # include # endif # ifdef HAVE_DL_H # include # endif # ifdef HAVE_MACH_O_DYLD_H # include # endif # ifndef RTLD_NOW # define RTLD_NOW 1 # endif # ifndef RTLD_GLOBAL # define RTLD_GLOBAL 0 # endif #endif #if defined(BEOS) typedef struct ag_dso_beos { struct ag_dso dso; image_id handle; } AG_DSO_BeOS; #elif defined(OS2) typedef struct ag_dso_os2 { struct ag_dso dso; HMODULE handle; } AG_DSO_OS2; #else typedef struct ag_dso_generic { struct ag_dso dso; void *handle; } AG_DSO_Generic; #endif struct ag_dsoq agLoadedDSOs = TAILQ_HEAD_INITIALIZER(agLoadedDSOs); #ifdef AG_THREADS AG_Mutex agDSOLock; #endif /* Load a DSO using the load_add_on() interface on BeOS. */ #ifdef BEOS static AG_DSO * LoadDSO_BEOS(const char *path) { AG_DSO_BeOS *d; image_id handle; if ((handle = load_add_on(path)) < B_NO_ERROR) { AG_SetError("%s: %s", path, strerror(handle)); return (NULL); } d = Malloc(sizeof(AG_DSO_BeOS)); d->handle = handle; return (AG_DSO *)d; } #endif /* BEOS */ /* Load a DSO using the DosLoadModule() interface on OS/2. */ #ifdef OS2 static AG_DSO * LoadDSO_OS2(const char *path) { AG_DSO_OS2 *d; char failedMod[256]; HMODULE handle; int rv; rv = DosLoadModule(failedMod, sizeof(failedMod), path, &handle); if (rv != 0) { AG_SetError("%s: DosLoadModule() failed: %s", path, failedMod); return (NULL); } d = Malloc(sizeof(AG_DSO_OS2)); d->handle = handle; return (AG_DSO *)d; } #endif /* OS2 */ /* Load a DSO using the dllload() interface on OS/390. */ #ifdef OS390 static AG_DSO * LoadDSO_OS390(const char *path) { AG_DSO_Generic *d; d = Malloc(sizeof(AG_DSO_Generic)); if ((d->handle = dllload(path)) == NULL) { AG_SetError("%s: dllload() failed", path); free(d); return (NULL); } return (AG_DSO *)d; } #endif /* OS390 */ /* Load a DSO using the LoadLibraryExW() interface on Windows. */ #ifdef _WIN32 static AG_DSO * LoadDSO_WIN32(const char *path) { AG_DSO_Generic *d; char buf[AG_PATHNAME_MAX], *p; UINT em; d = Malloc(sizeof(AG_DSO_Generic)); Strlcpy(buf, path, sizeof(buf)); for (p = buf; *p != '\0'; p++) { if (*p == '/') { *p = '\\'; } } em = SetErrorMode(SEM_FAILCRITICALERRORS); d->handle = LoadLibraryEx(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); if (d->handle == NULL) { d->handle = LoadLibraryEx(path, NULL, 0); if (d->handle == NULL) { AG_SetError("%s: LoadLibraryEx() failed", path); free(d); return (NULL); } } SetErrorMode(em); return (AG_DSO *)d; } #endif /* _WIN32 */ /* Load a DSO using the HP-UX shl_load() interface. */ #ifdef HAVE_SHL_LOAD static AG_DSO * LoadDSO_SHL(const char *path) { AG_DSO_Generic *d; d = Malloc(sizeof(AG_DSO_Generic)); if ((d->handle = shl_load(path, BIND_IMMEDIATE, 0L)) == NULL) { AG_SetError("%s: shl_load() failed", path); free(d); return (NULL); } return (AG_DSO *)d; } #endif /* HAVE_SHL_LOAD */ /* Load a DSO using the dyld NSLinkModule() interface. */ #ifdef HAVE_DYLD #define DYLD_LIBRARY_HANDLE ((void *)-1) static AG_DSO * LoadDSO_DYLD(const char *path) { AG_DSO_Generic *d; NSObjectFileImage image; NSObjectFileImageReturnCode rv; d = Malloc(sizeof(AG_DSO_Generic)); d->handle = NULL; rv = NSCreateObjectFileImageFromFile(path, &image); if (rv == NSObjectFileImageSuccess) { # if defined(HAVE_DYLD_RETURN_ON_ERROR) d->handle = (void *)NSLinkModule(image, path, NSLINKMODULE_OPTION_RETURN_ON_ERROR| NSLINKMODULE_OPTION_NONE); if (d->handle == NULL) { NSLinkEditErrors errors; int errorNumber; const char *fileName; const char *s = NULL; NSLinkEditError(&errors, &errorNumber, &fileName, &s); AG_SetError("%s", s); goto fail; } # else /* !HAVE_DYLD_RETURN_ON_ERROR */ d->handle = (void *)NSLinkModule(image, path, FALSE); if (d->handle == NULL) { AG_SetError("%s: NSLinkModule() failed", path); goto fail; } # endif /* HAVE_DYLD_RETURN_ON_ERROR */ NSDestroyObjectFileImage(image); } else if ((rv == NSObjectFileImageFormat || rv == NSObjectFileImageInappropriateFile) && NSAddLibrary(path) == TRUE) { d->handle = (void *)( (NSModule)DYLD_LIBRARY_HANDLE ); if (d->handle == NULL) { AG_SetError("%s: NSAddLibrary() failed", path); goto fail; } } return (AG_DSO *)d; fail: if (rv == NSObjectFileImageSuccess) { NSDestroyObjectFileImage(image); } free(d); return (NULL); } #endif /* HAVE_DYLD */ /* Load a DSO using the standard dlopen() interface. */ #ifdef HAVE_DLOPEN static AG_DSO * LoadDSO_DLOPEN(const char *path) { AG_DSO_Generic *d; int flags = RTLD_NOW|RTLD_GLOBAL; d = Malloc(sizeof(AG_DSO_Generic)); # if defined(_AIX) /* Special archive.a(dso.so) syntax requires RTLD_MEMBER flag */ if (strchr(&path[1], '(') && path[strlen(path)-1] == ')') flags |= RTLD_MEMBER; # endif if ((d->handle = dlopen(path, flags)) == NULL) { AG_SetError("%s: %s", path, dlerror()); free(d); return (NULL); } return (AG_DSO *)d; } #endif /* HAVE_DLOPEN */ /* * Load the specified module into the process address space. If it already * exists, return the existing structure incrementing its reference count. */ AG_DSO * AG_LoadDSO(const char *name, Uint flags) { char path[AG_PATHNAME_MAX]; AG_DSO *dso; int i; AG_MutexLock(&agDSOLock); /* See if module has already been loaded. */ TAILQ_FOREACH(dso, &agLoadedDSOs, dsos) { if (strcmp(dso->name, name) == 0) break; } if (dso != NULL) { dso->refCount++; goto out; } /* Scan the module directories for the file. */ path[0] = '\0'; for (i = 0; i < agModuleDirCount; i++) { Strlcpy(path, agModuleDirs[i], sizeof(path)); Strlcat(path, AG_PATHSEP, sizeof(path)); #if defined(__AMIGAOS4__) Strlcat(path, name, sizeof(path)); Strlcat(path, ".ixlibrary", sizeof(path)); #elif defined(HPUX) Strlcat(path, name, sizeof(path)); Strlcat(path, ".sl", sizeof(path)); #elif defined(_WIN32) || defined(OS2) Strlcat(path, name, sizeof(path)); Strlcat(path, ".dll", sizeof(path)); #else Strlcat(path, "lib", sizeof(path)); Strlcat(path, name, sizeof(path)); Strlcat(path, ".so", sizeof(path)); #endif if (AG_FileExists(path)) break; } if (i == agModuleDirCount) { AG_SetError("Cannot find \"%s\" module (last checked in: %s)", name, path); goto fail; } /* Load the module in memory. */ Verbose("Loading: %s (%s)\n", name, path); #if defined(BEOS) dso = LoadDSO_BEOS(path); #elif defined(OS2) dso = LoadDSO_OS2(path); #elif defined(OS390) dso = LoadDSO_OS390(path); #elif defined(_WIN32) dso = LoadDSO_WIN32(path); #elif defined(HAVE_SHL_LOAD) dso = LoadDSO_SHL(path); #elif defined(HAVE_DYLD) dso = LoadDSO_DYLD(path); #elif defined(HAVE_DLOPEN) dso = LoadDSO_DLOPEN(path); #else AG_SetError("Dynamic linking is supported on this platform"); dso = NULL; #endif if (dso == NULL) goto fail; Strlcpy(dso->name, name, sizeof(dso->name)); Strlcpy(dso->path, path, sizeof(dso->path)); dso->flags = 0; dso->refCount = 1; TAILQ_INIT(&dso->syms); TAILQ_INSERT_TAIL(&agLoadedDSOs, dso, dsos); out: AG_MutexUnlock(&agDSOLock); return (dso); fail: AG_MutexUnlock(&agDSOLock); return (NULL); } /* * Decrement the reference count of the specified DSO object. If it reaches * zero, unload the module from the process's address space. */ int AG_UnloadDSO(AG_DSO *dso) { AG_DSOSym *cSym; int rv = -1; AG_MutexLock(&agDSOLock); if (--dso->refCount > 0) { rv = 0; goto out; } #if defined(BEOS) { AG_DSO_BeOS *d = (AG_DSO_BeOS *)dso; if (unload_add_on(d->handle) < B_NO_ERROR) { AG_SetError("%s: unload_add_on() failed", dso->name); goto out; } } #elif defined(OS2) { AG_DSO_OS2 *d = (AG_DSO_OS2 *)dso; if (DosFreeModule(d->handle) != 0) { AG_SetError("%s: DosFreeModule() failed", dso->name); goto out; } } #elif defined(OS390) { AG_DSO_Generic *d = (AG_DSO_Generic *)dso; if (dllfree(d->handle) != 0) { AG_SetError("%s: dllfree() failed", dso->name); goto out; } } #elif defined(_WIN32) { AG_DSO_Generic *d = (AG_DSO_Generic *)dso; if (!FreeLibrary(d->handle)) { AG_SetError("%s: FreeLibrary() failed", dso->name); goto out; } } #elif defined(HAVE_SHL_LOAD) { AG_DSO_Generic *d = (AG_DSO_Generic *)dso; shl_unload((shl_t)d->handle); } #elif defined(HAVE_DYLD) { AG_DSO_Generic *d = (AG_DSO_Generic *)dso; if (d->handle != DYLD_LIBRARY_HANDLE) NSUnLinkModule(d->handle, FALSE); } #elif defined(HAVE_DLOPEN) { AG_DSO_Generic *d = (AG_DSO_Generic *)dso; # ifdef NETWARE void *NLMHandle = getnlmhandle(); TAILQ_FOREACH(cSym, &dso->syms, syms) UnImportPublicObject(NLMHandle, cSym->sym); # endif if (dlclose(d->handle) != 0) { AG_SetError("%s: dlclose: %s", dso->name, strerror(errno)); goto out; } } #else AG_SetError("Dynamic linking is supported on this platform"); goto out; #endif /* DSO_USE_FOO */ while ((cSym = TAILQ_FIRST(&dso->syms)) != NULL) { free(cSym->sym); free(cSym); } TAILQ_REMOVE(&agLoadedDSOs, dso, dsos); free(dso); rv = 0; out: AG_MutexUnlock(&agDSOLock); return (rv); } #ifdef BEOS /* Look up a symbol using the BeOS get_image_symbol() interface. */ static int SymDSO_BeOS(AG_DSO_BeOS *d, const char *sym, void **p) { AG_DSO_BeOS *d = (AG_DSO_BeOS *)dso; int rv; rv = get_image_symbol(d->handle, sym, B_SYMBOL_TYPE_ANY, p); if (rv != B_OK) { AG_SetError("Symbol not found: \"%s\"", sym); return (-1); } return (0); } #endif /* BEOS */ #ifdef OS2 /* Look up a symbol using the OS/2 DosQueryProcAddr() interface. */ static int SymDSO_OS2(AG_DSO_OS2 *d, const char *sym, void **p) { PFN fn; rv = DosQueryProcAddr(d->handle, 0, sym, &fn); if (rv != 0) { AG_SetError("Symbol not found: \"%s\"", sym); return (-1); } *p = (void *)fn; return (0); } #endif /* OS2 */ #ifdef OS390 /* Look up a symbol using the OS/390 dllqueryvar() interface. */ static int SymDSO_OS390(AG_DSO_Generic *d, const char *sym, void **p) { if ((*p = dllqueryfn(d->handle, sym)) != NULL || (*p = dllqueryvar(d->handle, sym)) != NULL) { return (0); } AG_SetError("Symbol not found: \"%s\"", sym); return (-1); } #endif /* OS390 */ #ifdef _WIN32 /* Look up a symbol using the Windows GetProcAddress() interface. */ static int SymDSO_WIN32(AG_DSO_Generic *d, const char *sym, void **p) { *p = (void *)GetProcAddress(d->handle, sym); /* XXX no way to determine error */ return (0); } #endif /* _WIN32 */ #ifdef HAVE_SHL_LOAD /* Look up a symbol using the shl_findsym() interface. */ static int SymDSO_SHL(AG_DSO_Generic *d, const char *sym, void **p) { int rv; *p = NULL; errno = 0; if ((rv = shl_findsym((shl_t *)&d->handle, sym, TYPE_PROCEDURE, p)) == -1) { if (errno != 0) { goto notfound; } if ((rv = shl_findsym((shl_t *)&d->handle, sym, TYPE_DATA, p)) == -1) { goto notfound; } } return (0); notfound: AG_SetError("%s: Symbol not found: \"%s\"", AGDSO(d)->name, sym); return (-1); } #endif /* HAVE_SHL_LOAD */ #ifdef HAVE_DYLD /* Look up a symbol using the dyld interface. */ static int SymDSO_DYLD(AG_DSO_Generic *d, const char *sym, void **p) { int rv; NSSymbol symbol; size_t symLen = strlen(sym); char *symUnder = Malloc(symLen+2); symUnder[0] = '_'; Strlcpy(&symUnder[1], sym, (symLen+2)-1); *p = NULL; # ifdef NSLINKMODULE_OPTION_PRIVATE if (d->handle == DYLD_LIBRARY_HANDLE) { symbol = NSLookupAndBindSymbol(symUnder); } else { symbol = NSLookupSymbolInModule((NSModule)d->handle, symUnder); } # else symbol = NSLookupAndBindSymbol(symUnder); # endif free(symUnder); if (symbol == NULL) { AG_SetError("%s: Undefined symbol: %s", AGDSO(d)->name, sym); return (-1); } *p = NSAddressOfSymbol(symbol); return (0); } #endif /* HAVE_DYLD */ #ifdef HAVE_DLOPEN /* Look up a symbol using the standard dlopen() interface. */ static int SymDSO_DLOPEN(AG_DSO_Generic *d, const char *sym, void **p) { char *error; *p = NULL; # if !defined(__ELF__) && (defined(__NetBSD__) || defined(__OpenBSD__) || \ defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FabBSD__)) { size_t symLen = strlen(sym); char *symUnder = Malloc(symLen+2); symUnder[0] = '_'; Strlcpy(&symUnder[1], sym, (symLen+2)-1); *p = dlsym(d->handle, symUnder); free(symUnder); } # else /* __ELF__ */ *p = dlsym(d->handle, sym); # endif /* !__ELF__ */ if ((error = dlerror()) != NULL) { AG_SetError("%s: %s", AGDSO(d)->name, error); return (-1); } return (0); } #endif /* HAVE_DLOPEN */ /* Resolve a symbol in the specified DSO. */ int AG_SymDSO(AG_DSO *dso, const char *sym, void **p) { AG_DSOSym *cSym; int rv = 0; AG_MutexLock(&agDSOLock); #if defined(BEOS) rv = SymDSO_BeOS((AG_DSO_BeOS *)dso, sym, p); #elif defined(OS2) rv = SymDSO_OS2((AG_DSO_OS2 *)dso, sym, p); #elif defined(OS390) rv = SymDSO_OS390((AG_DSO_Generic *)dso, sym, p); #elif defined(_WIN32) rv = SymDSO_WIN32((AG_DSO_Generic *)dso, sym, p); #elif defined(HAVE_SHL_LOAD) rv = SymDSO_SHL((AG_DSO_Generic *)dso, sym, p); #elif defined(HAVE_DYLD) rv = SymDSO_DYLD((AG_DSO_Generic *)dso, sym, p); #elif defined(HAVE_DLOPEN) rv = SymDSO_DLOPEN((AG_DSO_Generic *)dso, sym, p); #else AG_SetError("Dynamic linking is supported on this platform"); rv = -1; #endif if (rv == 0) { TAILQ_FOREACH(cSym, &dso->syms, syms) { if (strcmp(cSym->sym, sym) == 0) break; } if (cSym == NULL) { cSym = Malloc(sizeof(AG_DSOSym)); cSym->sym = Strdup(sym); cSym->p = p; TAILQ_INSERT_TAIL(&dso->syms, cSym, syms); } } AG_MutexUnlock(&agDSOLock); return (rv); } /* Return the list of available modules in the registered directories. */ char ** AG_GetDSOList(Uint *count) { char **list; AG_Dir *dir; int i, j; list = Malloc(sizeof(char *)); *count = 0; for (i = 0; i < agModuleDirCount; i++) { if ((dir = AG_OpenDir(agModuleDirs[i])) != NULL) { for (j = 0; j < dir->nents; j++) { char *file = dir->ents[j], *s; char *pStart; if (file[0] == '.') { continue; } #if defined(__AMIGAOS4__) if ((s = Strcasestr(file, ".ixlibrary")) == NULL || s[10] != '\0') { continue; } pStart = s; #elif defined(HPUX) if ((s = Strcasestr(file, ".sl")) == NULL || s[3] != '\0') { continue; } pStart = s; #elif defined(_WIN32) || defined(OS2) if ((s = Strcasestr(file, ".dll")) == NULL || s[4] != '\0') { continue; } pStart = s; #else if (strncmp(file, "lib", 3) != 0 || (s = Strcasestr(file, ".so")) == NULL || s[3] != '\0') { continue; } pStart = &file[3]; #endif *s = '\0'; list = Realloc(list, ((*count)+1)* sizeof(char *)); list[(*count)++] = Strdup(pStart); } AG_CloseDir(dir); } } return (list); } void AG_FreeDSOList(char **list, Uint count) { Uint i; for (i = 0; i < count; i++) { free(list[i]); } free(list); }