| /* loader-dyld.c -- dynamic linking on darwin and OS X |
| |
| Copyright (C) 1998, 1999, 2000, 2004, 2006, |
| 2007, 2008 Free Software Foundation, Inc. |
| Written by Peter O'Gorman, 1998 |
| |
| NOTE: The canonical source of this file is maintained with the |
| GNU Libtool package. Report bugs to bug-libtool@gnu.org. |
| |
| GNU Libltdl is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2 of the License, or (at your option) any later version. |
| |
| As a special exception to the GNU Lesser General Public License, |
| if you distribute this file as part of a program or library that |
| is built using GNU Libtool, you may include this file under the |
| same distribution terms that you use for the rest of that program. |
| |
| GNU Libltdl is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with GNU Libltdl; see the file COPYING.LIB. If not, a |
| copy can be downloaded from http://www.gnu.org/licenses/lgpl.html, |
| or obtained by writing to the Free Software Foundation, Inc., |
| 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "lt__private.h" |
| #include "lt_dlloader.h" |
| |
| /* Use the preprocessor to rename non-static symbols to avoid namespace |
| collisions when the loader code is statically linked into libltdl. |
| Use the "<module_name>_LTX_" prefix so that the symbol addresses can |
| be fetched from the preloaded symbol list by lt_dlsym(): */ |
| #define get_vtable dyld_LTX_get_vtable |
| |
| LT_BEGIN_C_DECLS |
| LT_SCOPE lt_dlvtable *get_vtable (lt_user_data loader_data); |
| LT_END_C_DECLS |
| |
| |
| /* Boilerplate code to set up the vtable for hooking this loader into |
| libltdl's loader list: */ |
| static int vl_init (lt_user_data loader_data); |
| static int vl_exit (lt_user_data loader_data); |
| static lt_module vm_open (lt_user_data loader_data, const char *filename, |
| lt_dladvise advise); |
| static int vm_close (lt_user_data loader_data, lt_module module); |
| static void * vm_sym (lt_user_data loader_data, lt_module module, |
| const char *symbolname); |
| |
| static lt_dlvtable *vtable = 0; |
| |
| /* Return the vtable for this loader, only the name and sym_prefix |
| attributes (plus the virtual function implementations, obviously) |
| change between loaders. */ |
| lt_dlvtable * |
| get_vtable (lt_user_data loader_data) |
| { |
| if (!vtable) |
| { |
| vtable = lt__zalloc (sizeof *vtable); |
| } |
| |
| if (vtable && !vtable->name) |
| { |
| vtable->name = "lt_dyld"; |
| vtable->sym_prefix = "_"; |
| vtable->dlloader_init = vl_init; |
| vtable->module_open = vm_open; |
| vtable->module_close = vm_close; |
| vtable->find_sym = vm_sym; |
| vtable->dlloader_exit = vl_exit; |
| vtable->dlloader_data = loader_data; |
| vtable->priority = LT_DLLOADER_APPEND; |
| } |
| |
| if (vtable && (vtable->dlloader_data != loader_data)) |
| { |
| LT__SETERROR (INIT_LOADER); |
| return 0; |
| } |
| |
| return vtable; |
| } |
| |
| |
| |
| /* --- IMPLEMENTATION --- */ |
| |
| |
| #if defined(HAVE_MACH_O_DYLD_H) |
| # if !defined(__APPLE_CC__) && !defined(__MWERKS__) && !defined(__private_extern__) |
| /* Is this correct? Does it still function properly? */ |
| # define __private_extern__ extern |
| # endif |
| # include <mach-o/dyld.h> |
| #endif |
| |
| #include <mach-o/getsect.h> |
| |
| /* We have to put some stuff here that isn't in older dyld.h files */ |
| #if !defined(ENUM_DYLD_BOOL) |
| # define ENUM_DYLD_BOOL |
| # undef FALSE |
| # undef TRUE |
| enum DYLD_BOOL { |
| FALSE, |
| TRUE |
| }; |
| #endif |
| #if !defined(LC_REQ_DYLD) |
| # define LC_REQ_DYLD 0x80000000 |
| #endif |
| #if !defined(LC_LOAD_WEAK_DYLIB) |
| # define LC_LOAD_WEAK_DYLIB (0x18 | LC_REQ_DYLD) |
| #endif |
| |
| #if !defined(NSADDIMAGE_OPTION_NONE) |
| # define NSADDIMAGE_OPTION_NONE 0x0 |
| #endif |
| #if !defined(NSADDIMAGE_OPTION_RETURN_ON_ERROR) |
| # define NSADDIMAGE_OPTION_RETURN_ON_ERROR 0x1 |
| #endif |
| #if !defined(NSADDIMAGE_OPTION_WITH_SEARCHING) |
| # define NSADDIMAGE_OPTION_WITH_SEARCHING 0x2 |
| #endif |
| #if !defined(NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED) |
| # define NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED 0x4 |
| #endif |
| #if !defined(NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME) |
| # define NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME 0x8 |
| #endif |
| |
| #if !defined(NSLOOKUPSYMBOLINIMAGE_OPTION_BIND) |
| # define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND 0x0 |
| #endif |
| #if !defined(NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW) |
| # define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW 0x1 |
| #endif |
| #if !defined(NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_FULLY) |
| # define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_FULLY 0x2 |
| #endif |
| #if !defined(NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR) |
| # define NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR 0x4 |
| #endif |
| |
| #define LT__SYMLOOKUP_OPTS (NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW \ |
| | NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR) |
| |
| #if defined(__BIG_ENDIAN__) |
| # define LT__MAGIC MH_MAGIC |
| #else |
| # define LT__MAGIC MH_CIGAM |
| #endif |
| |
| #define DYLD__SETMYERROR(errmsg) LT__SETERRORSTR (dylderror (errmsg)) |
| #define DYLD__SETERROR(errcode) DYLD__SETMYERROR (LT__STRERROR (errcode)) |
| |
| typedef struct mach_header mach_header; |
| typedef struct dylib_command dylib_command; |
| |
| static const char *dylderror (const char *errmsg); |
| static const mach_header *lt__nsmodule_get_header (NSModule module); |
| static const char *lt__header_get_instnam (const mach_header *mh); |
| static const mach_header *lt__match_loadedlib (const char *name); |
| static NSSymbol lt__linkedlib_symbol (const char *symname, const mach_header *mh); |
| |
| static const mach_header *(*lt__addimage) (const char *image_name, |
| unsigned long options) = 0; |
| static NSSymbol (*lt__image_symbol) (const mach_header *image, |
| const char *symbolName, |
| unsigned long options) = 0; |
| static enum DYLD_BOOL (*lt__image_symbol_p) (const mach_header *image, |
| const char *symbolName) = 0; |
| static enum DYLD_BOOL (*lt__module_export) (NSModule module) = 0; |
| |
| static int dyld_cannot_close = 0; |
| |
| |
| /* A function called through the vtable when this loader is no |
| longer needed by the application. */ |
| static int |
| vl_exit (lt_user_data LT__UNUSED loader_data) |
| { |
| vtable = NULL; |
| return 0; |
| } |
| |
| /* A function called through the vtable to initialise this loader. */ |
| static int |
| vl_init (lt_user_data loader_data) |
| { |
| int errors = 0; |
| |
| if (! dyld_cannot_close) |
| { |
| if (!_dyld_present ()) |
| { |
| ++errors; |
| } |
| else |
| { |
| (void) _dyld_func_lookup ("__dyld_NSAddImage", |
| (unsigned long*) <__addimage); |
| (void) _dyld_func_lookup ("__dyld_NSLookupSymbolInImage", |
| (unsigned long*)<__image_symbol); |
| (void) _dyld_func_lookup ("__dyld_NSIsSymbolNameDefinedInImage", |
| (unsigned long*) <__image_symbol_p); |
| (void) _dyld_func_lookup ("__dyld_NSMakePrivateModulePublic", |
| (unsigned long*) <__module_export); |
| dyld_cannot_close = lt_dladderror ("can't close a dylib"); |
| } |
| } |
| |
| return errors; |
| } |
| |
| |
| /* A function called through the vtable to open a module with this |
| loader. Returns an opaque representation of the newly opened |
| module for processing with this loader's other vtable functions. */ |
| static lt_module |
| vm_open (lt_user_data loader_data, const char *filename, |
| lt_dladvise LT__UNUSED advise) |
| { |
| lt_module module = 0; |
| NSObjectFileImage ofi = 0; |
| |
| if (!filename) |
| { |
| return (lt_module) -1; |
| } |
| |
| switch (NSCreateObjectFileImageFromFile (filename, &ofi)) |
| { |
| case NSObjectFileImageSuccess: |
| module = NSLinkModule (ofi, filename, NSLINKMODULE_OPTION_RETURN_ON_ERROR |
| | NSLINKMODULE_OPTION_PRIVATE |
| | NSLINKMODULE_OPTION_BINDNOW); |
| NSDestroyObjectFileImage (ofi); |
| |
| if (module) |
| { |
| lt__module_export (module); |
| } |
| break; |
| |
| case NSObjectFileImageInappropriateFile: |
| if (lt__image_symbol_p && lt__image_symbol) |
| { |
| module = (lt_module) lt__addimage(filename, |
| NSADDIMAGE_OPTION_RETURN_ON_ERROR); |
| } |
| break; |
| |
| case NSObjectFileImageFailure: |
| case NSObjectFileImageArch: |
| case NSObjectFileImageFormat: |
| case NSObjectFileImageAccess: |
| /*NOWORK*/ |
| break; |
| } |
| |
| if (!module) |
| { |
| DYLD__SETERROR (CANNOT_OPEN); |
| } |
| |
| return module; |
| } |
| |
| |
| /* A function called through the vtable when a particular module |
| should be unloaded. */ |
| static int |
| vm_close (lt_user_data loader_data, lt_module module) |
| { |
| int errors = 0; |
| |
| if (module != (lt_module) -1) |
| { |
| const mach_header *mh = (const mach_header *) module; |
| int flags = 0; |
| if (mh->magic == LT__MAGIC) |
| { |
| lt_dlseterror (dyld_cannot_close); |
| ++errors; |
| } |
| else |
| { |
| /* Currently, if a module contains c++ static destructors and it |
| is unloaded, we get a segfault in atexit(), due to compiler and |
| dynamic loader differences of opinion, this works around that. */ |
| if ((const struct section *) NULL != |
| getsectbynamefromheader (lt__nsmodule_get_header (module), |
| "__DATA", "__mod_term_func")) |
| { |
| flags |= NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED; |
| } |
| #if defined(__ppc__) |
| flags |= NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES; |
| #endif |
| if (!NSUnLinkModule (module, flags)) |
| { |
| DYLD__SETERROR (CANNOT_CLOSE); |
| ++errors; |
| } |
| } |
| } |
| |
| return errors; |
| } |
| |
| /* A function called through the vtable to get the address of |
| a symbol loaded from a particular module. */ |
| static void * |
| vm_sym (lt_user_data loader_data, lt_module module, const char *name) |
| { |
| NSSymbol *nssym = 0; |
| const mach_header *mh = (const mach_header *) module; |
| char saveError[256] = "Symbol not found"; |
| |
| if (module == (lt_module) -1) |
| { |
| void *address, *unused; |
| _dyld_lookup_and_bind (name, (unsigned long*) &address, &unused); |
| return address; |
| } |
| |
| if (mh->magic == LT__MAGIC) |
| { |
| if (lt__image_symbol_p && lt__image_symbol) |
| { |
| if (lt__image_symbol_p (mh, name)) |
| { |
| nssym = lt__image_symbol (mh, name, LT__SYMLOOKUP_OPTS); |
| } |
| } |
| |
| } |
| else |
| { |
| nssym = NSLookupSymbolInModule (module, name); |
| } |
| |
| if (!nssym) |
| { |
| strncpy (saveError, dylderror (LT__STRERROR (SYMBOL_NOT_FOUND)), 255); |
| saveError[255] = 0; |
| if (!mh) |
| { |
| mh = (mach_header *)lt__nsmodule_get_header (module); |
| } |
| nssym = lt__linkedlib_symbol (name, mh); |
| } |
| |
| if (!nssym) |
| { |
| LT__SETERRORSTR (saveError); |
| } |
| |
| return nssym ? NSAddressOfSymbol (nssym) : 0; |
| } |
| |
| |
| |
| |
| /* --- HELPER FUNCTIONS --- */ |
| |
| |
| /* Return the dyld error string, or the passed in error string if none. */ |
| static const char * |
| dylderror (const char *errmsg) |
| { |
| NSLinkEditErrors ler; |
| int lerno; |
| const char *file; |
| const char *errstr; |
| |
| NSLinkEditError (&ler, &lerno, &file, &errstr); |
| |
| if (! (errstr && *errstr)) |
| { |
| errstr = errmsg; |
| } |
| |
| return errstr; |
| } |
| |
| /* There should probably be an apple dyld api for this. */ |
| static const mach_header * |
| lt__nsmodule_get_header (NSModule module) |
| { |
| int i = _dyld_image_count(); |
| const char *modname = NSNameOfModule (module); |
| const mach_header *mh = 0; |
| |
| if (!modname) |
| return NULL; |
| |
| while (i > 0) |
| { |
| --i; |
| if (strneq (_dyld_get_image_name (i), modname)) |
| { |
| mh = _dyld_get_image_header (i); |
| break; |
| } |
| } |
| |
| return mh; |
| } |
| |
| /* NSAddImage is also used to get the loaded image, but it only works if |
| the lib is installed, for uninstalled libs we need to check the |
| install_names against each other. Note that this is still broken if |
| DYLD_IMAGE_SUFFIX is set and a different lib was loaded as a result. */ |
| static const char * |
| lt__header_get_instnam (const mach_header *mh) |
| { |
| unsigned long offset = sizeof(mach_header); |
| const char* result = 0; |
| int j; |
| |
| for (j = 0; j < mh->ncmds; j++) |
| { |
| struct load_command *lc; |
| |
| lc = (struct load_command*) (((unsigned long) mh) + offset); |
| if (LC_ID_DYLIB == lc->cmd) |
| { |
| result=(char*)(((dylib_command*) lc)->dylib.name.offset + |
| (unsigned long) lc); |
| } |
| offset += lc->cmdsize; |
| } |
| |
| return result; |
| } |
| |
| static const mach_header * |
| lt__match_loadedlib (const char *name) |
| { |
| const mach_header *mh = 0; |
| int i = _dyld_image_count(); |
| |
| while (i > 0) |
| { |
| const char *id; |
| |
| --i; |
| id = lt__header_get_instnam (_dyld_get_image_header (i)); |
| if (id && strneq (id, name)) |
| { |
| mh = _dyld_get_image_header (i); |
| break; |
| } |
| } |
| |
| return mh; |
| } |
| |
| /* Safe to assume our mh is good. */ |
| static NSSymbol |
| lt__linkedlib_symbol (const char *symname, const mach_header *mh) |
| { |
| NSSymbol symbol = 0; |
| |
| if (lt__image_symbol && NSIsSymbolNameDefined (symname)) |
| { |
| unsigned long offset = sizeof(mach_header); |
| struct load_command *lc; |
| int j; |
| |
| for (j = 0; j < mh->ncmds; j++) |
| { |
| lc = (struct load_command*) (((unsigned long) mh) + offset); |
| if ((LC_LOAD_DYLIB == lc->cmd) || (LC_LOAD_WEAK_DYLIB == lc->cmd)) |
| { |
| unsigned long base = ((dylib_command *) lc)->dylib.name.offset; |
| char *name = (char *) (base + (unsigned long) lc); |
| const mach_header *mh1 = lt__match_loadedlib (name); |
| |
| if (!mh1) |
| { |
| /* Maybe NSAddImage can find it */ |
| mh1 = lt__addimage (name, |
| NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED |
| | NSADDIMAGE_OPTION_WITH_SEARCHING |
| | NSADDIMAGE_OPTION_RETURN_ON_ERROR); |
| } |
| |
| if (mh1) |
| { |
| symbol = lt__image_symbol (mh1, symname, LT__SYMLOOKUP_OPTS); |
| if (symbol) |
| break; |
| } |
| } |
| |
| offset += lc->cmdsize; |
| } |
| } |
| |
| return symbol; |
| } |