512 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			512 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* 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;
 | |
| }
 |