376 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			376 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* loader-preopen.c -- emulate dynamic linking using preloaded_symbols
 | |
| 
 | |
|    Copyright (C) 1998, 1999, 2000, 2004, 2006,
 | |
|                  2007, 2008 Free Software Foundation, Inc.
 | |
|    Written by Thomas Tanner, 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	preopen_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_dlvtable *) lt__zalloc (sizeof *vtable);
 | |
|     }
 | |
| 
 | |
|   if (vtable && !vtable->name)
 | |
|     {
 | |
|       vtable->name		= "lt_preopen";
 | |
|       vtable->sym_prefix	= 0;
 | |
|       vtable->module_open	= vm_open;
 | |
|       vtable->module_close	= vm_close;
 | |
|       vtable->find_sym		= vm_sym;
 | |
|       vtable->dlloader_init	= vl_init;
 | |
|       vtable->dlloader_exit	= vl_exit;
 | |
|       vtable->dlloader_data	= loader_data;
 | |
|       vtable->priority		= LT_DLLOADER_PREPEND;
 | |
|     }
 | |
| 
 | |
|   if (vtable && (vtable->dlloader_data != loader_data))
 | |
|     {
 | |
|       LT__SETERROR (INIT_LOADER);
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|   return vtable;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /* --- IMPLEMENTATION --- */
 | |
| 
 | |
| 
 | |
| /* Wrapper type to chain together symbol lists of various origins.  */
 | |
| typedef struct symlist_chain
 | |
| {
 | |
|   struct symlist_chain *next;
 | |
|   const lt_dlsymlist   *symlist;
 | |
| } symlist_chain;
 | |
| 
 | |
| 
 | |
| static int add_symlist   (const lt_dlsymlist *symlist);
 | |
| static int free_symlists (void);
 | |
| 
 | |
| /* The start of the symbol lists chain.  */
 | |
| static symlist_chain	       *preloaded_symlists		= 0;
 | |
| 
 | |
| /* A symbol list preloaded before lt_init() was called.  */
 | |
| static const	lt_dlsymlist   *default_preloaded_symbols	= 0;
 | |
| 
 | |
| 
 | |
| /* A function called through the vtable to initialise this loader.  */
 | |
| static int
 | |
| vl_init (lt_user_data LT__UNUSED loader_data)
 | |
| {
 | |
|   int errors = 0;
 | |
| 
 | |
|   preloaded_symlists = 0;
 | |
|   if (default_preloaded_symbols)
 | |
|     {
 | |
|       errors = lt_dlpreload (default_preloaded_symbols);
 | |
|     }
 | |
| 
 | |
|   return errors;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* 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;
 | |
|   free_symlists ();
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* 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 LT__UNUSED loader_data, const char *filename,
 | |
|          lt_dladvise LT__UNUSED advise)
 | |
| {
 | |
|   symlist_chain *lists;
 | |
|   lt_module	 module = 0;
 | |
| 
 | |
|   if (!preloaded_symlists)
 | |
|     {
 | |
|       LT__SETERROR (NO_SYMBOLS);
 | |
|       goto done;
 | |
|     }
 | |
| 
 | |
|   /* Can't use NULL as the reflective symbol header, as NULL is
 | |
|      used to mark the end of the entire symbol list.  Self-dlpreopened
 | |
|      symbols follow this magic number, chosen to be an unlikely
 | |
|      clash with a real module name.  */
 | |
|   if (!filename)
 | |
|     {
 | |
|       filename = "@PROGRAM@";
 | |
|     }
 | |
| 
 | |
|   for (lists = preloaded_symlists; lists; lists = lists->next)
 | |
|     {
 | |
|       const lt_dlsymlist *symbol;
 | |
|       for (symbol= lists->symlist; symbol->name; ++symbol)
 | |
| 	{
 | |
| 	  if (!symbol->address && streq (symbol->name, filename))
 | |
| 	    {
 | |
| 	      /* If the next symbol's name and address is 0, it means
 | |
| 		 the module just contains the originator and no symbols.
 | |
| 		 In this case we pretend that we never saw the module and
 | |
| 	         hope that some other loader will be able to load the module
 | |
| 	         and have access to its symbols */
 | |
| 	      const lt_dlsymlist *next_symbol = symbol +1;
 | |
| 	      if (next_symbol->address && next_symbol->name)
 | |
| 		{
 | |
| 	          module = (lt_module) lists->symlist;
 | |
| 	          goto done;
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   LT__SETERROR (FILE_NOT_FOUND);
 | |
| 
 | |
|  done:
 | |
|   return module;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* A function called through the vtable when a particular module
 | |
|    should be unloaded.  */
 | |
| static int
 | |
| vm_close (lt_user_data LT__UNUSED loader_data, lt_module LT__UNUSED module)
 | |
| {
 | |
|   /* Just to silence gcc -Wall */
 | |
|   module = 0;
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* 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 LT__UNUSED loader_data, lt_module module, const char *name)
 | |
| {
 | |
|   lt_dlsymlist	       *symbol = (lt_dlsymlist*) module;
 | |
| 
 | |
|   symbol +=2;			/* Skip header (originator then libname). */
 | |
| 
 | |
|   while (symbol->name)
 | |
|     {
 | |
|       if (streq (symbol->name, name))
 | |
| 	{
 | |
| 	  return symbol->address;
 | |
| 	}
 | |
| 
 | |
|     ++symbol;
 | |
|   }
 | |
| 
 | |
|   LT__SETERROR (SYMBOL_NOT_FOUND);
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /* --- HELPER FUNCTIONS --- */
 | |
| 
 | |
| 
 | |
| /* The symbol lists themselves are not allocated from the heap, but
 | |
|    we can unhook them and free up the chain of links between them.  */
 | |
| static int
 | |
| free_symlists (void)
 | |
| {
 | |
|   symlist_chain *lists;
 | |
| 
 | |
|   lists = preloaded_symlists;
 | |
|   while (lists)
 | |
|     {
 | |
|       symlist_chain *next = lists->next;
 | |
|       FREE (lists);
 | |
|       lists = next;
 | |
|     }
 | |
|   preloaded_symlists = 0;
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* Add a new symbol list to the global chain.  */
 | |
| static int
 | |
| add_symlist (const lt_dlsymlist *symlist)
 | |
| {
 | |
|   symlist_chain *lists;
 | |
|   int		 errors   = 0;
 | |
| 
 | |
|   /* Search for duplicate entries:  */
 | |
|   for (lists = preloaded_symlists;
 | |
|        lists && lists->symlist != symlist; lists = lists->next)
 | |
|     /*NOWORK*/;
 | |
| 
 | |
|   /* Don't add the same list twice:  */
 | |
|   if (!lists)
 | |
|     {
 | |
|       symlist_chain *tmp = (symlist_chain *) lt__zalloc (sizeof *tmp);
 | |
| 
 | |
|       if (tmp)
 | |
| 	{
 | |
| 	  tmp->symlist = symlist;
 | |
| 	  tmp->next = preloaded_symlists;
 | |
| 	  preloaded_symlists = tmp;
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  ++errors;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   return errors;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /* --- PRELOADING API CALL IMPLEMENTATIONS --- */
 | |
| 
 | |
| 
 | |
| /* Save a default symbol list for later.  */
 | |
| int
 | |
| lt_dlpreload_default (const lt_dlsymlist *preloaded)
 | |
| {
 | |
|   default_preloaded_symbols = preloaded;
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Add a symbol list to the global chain, or with a NULL argument,
 | |
|    revert to just the default list.  */
 | |
| int
 | |
| lt_dlpreload (const lt_dlsymlist *preloaded)
 | |
| {
 | |
|   int errors = 0;
 | |
| 
 | |
|   if (preloaded)
 | |
|     {
 | |
|       errors = add_symlist (preloaded);
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       free_symlists();
 | |
| 
 | |
|       if (default_preloaded_symbols)
 | |
| 	{
 | |
| 	  errors = lt_dlpreload (default_preloaded_symbols);
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   return errors;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Open all the preloaded modules from the named originator, executing
 | |
|    a callback for each one.  If ORIGINATOR is NULL, then call FUNC for
 | |
|    each preloaded module from the program itself.  */
 | |
| int
 | |
| lt_dlpreload_open (const char *originator, lt_dlpreload_callback_func *func)
 | |
| {
 | |
|   symlist_chain *list;
 | |
|   int		 errors = 0;
 | |
|   int		 found  = 0;
 | |
| 
 | |
|   /* For each symlist in the chain...  */
 | |
|   for (list = preloaded_symlists; list; list = list->next)
 | |
|     {
 | |
|       /* ...that was preloaded by the requesting ORIGINATOR... */
 | |
|       if ((originator && streq (list->symlist->name, originator))
 | |
|           || (!originator && streq (list->symlist->name, "@PROGRAM@")))
 | |
| 	{
 | |
| 	  const lt_dlsymlist *symbol;
 | |
| 	  unsigned int idx = 0;
 | |
| 
 | |
| 	  ++found;
 | |
| 
 | |
| 	  /* ...load the symbols per source compilation unit:
 | |
| 	     (we preincrement the index to skip over the originator entry)  */
 | |
| 	  while ((symbol = &list->symlist[++idx])->name != 0)
 | |
| 	    {
 | |
| 	      if ((symbol->address == 0)
 | |
| 		  && (strneq (symbol->name, "@PROGRAM@")))
 | |
| 		{
 | |
| 		  lt_dlhandle handle = lt_dlopen (symbol->name);
 | |
| 		  if (handle == 0)
 | |
| 		    {
 | |
| 		      ++errors;
 | |
| 		    }
 | |
| 		  else
 | |
| 		    {
 | |
| 		      errors += (*func) (handle);
 | |
| 		    }
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   if (!found)
 | |
|     {
 | |
|       LT__SETERROR(CANNOT_OPEN);
 | |
|       ++errors;
 | |
|     }
 | |
| 
 | |
|   return errors;
 | |
| }
 |