185 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			185 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* dlfcn.cc
 | |
| 
 | |
|    Copyright 1998, 2000, 2001, 2002, 2003, 2004, 2008, 2009, 2010 Red Hat, Inc.
 | |
| 
 | |
| This file is part of Cygwin.
 | |
| 
 | |
| This software is a copyrighted work licensed under the terms of the
 | |
| Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 | |
| details. */
 | |
| 
 | |
| #include "winsup.h"
 | |
| #include <psapi.h>
 | |
| #include <stdlib.h>
 | |
| #include <ctype.h>
 | |
| #include "path.h"
 | |
| #include "perprocess.h"
 | |
| #include "dlfcn.h"
 | |
| #include "cygtls.h"
 | |
| #include "tls_pbuf.h"
 | |
| 
 | |
| static void __stdcall
 | |
| set_dl_error (const char *str)
 | |
| {
 | |
|   strcpy (_my_tls.locals.dl_buffer, strerror (get_errno ()));
 | |
|   _my_tls.locals.dl_error = 1;
 | |
| }
 | |
| 
 | |
| /* Look for an executable file given the name and the environment
 | |
|    variable to use for searching (eg., PATH); returns the full
 | |
|    pathname (static buffer) if found or NULL if not. */
 | |
| inline const char * __stdcall
 | |
| check_path_access (const char *mywinenv, const char *name, path_conv& buf)
 | |
| {
 | |
|   return find_exec (name, buf, mywinenv, FE_NNF | FE_NATIVE | FE_CWD | FE_DLL);
 | |
| }
 | |
| 
 | |
| /* Search LD_LIBRARY_PATH for dll, if it exists.
 | |
|    Return Windows version of given path. */
 | |
| static bool __stdcall
 | |
| get_full_path_of_dll (const char* str, path_conv &real_filename)
 | |
| {
 | |
|   int len = strlen (str);
 | |
| 
 | |
|   /* empty string? */
 | |
|   if (len == 0)
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return false;		/* Yes.  Let caller deal with it. */
 | |
|     }
 | |
| 
 | |
|   tmp_pathbuf tp;
 | |
|   char *name = tp.c_get ();
 | |
| 
 | |
|   strcpy (name, str);	/* Put it somewhere where we can manipulate it. */
 | |
| 
 | |
|   if (isabspath (name) ||
 | |
|       (check_path_access ("LD_LIBRARY_PATH=", name, real_filename)
 | |
|        ?: check_path_access ("/usr/lib", name, real_filename)) == NULL)
 | |
|     real_filename.check (name, PC_SYM_FOLLOW | PC_NOFULL | PC_NULLEMPTY);
 | |
| 
 | |
|   if (!real_filename.error)
 | |
|     return true;
 | |
| 
 | |
|   set_errno (real_filename.error);
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| void *
 | |
| dlopen (const char *name, int)
 | |
| {
 | |
|   void *ret;
 | |
| 
 | |
|   if (name == NULL)
 | |
|     ret = (void *) GetModuleHandle (NULL); /* handle for the current module */
 | |
|   else
 | |
|     {
 | |
|       /* handle for the named library */
 | |
|       path_conv pc;
 | |
|       if (!get_full_path_of_dll (name, pc))
 | |
| 	ret = NULL;
 | |
|       else
 | |
| 	{
 | |
| 	  tmp_pathbuf tp;
 | |
| 	  wchar_t *path = tp.w_get ();
 | |
| 
 | |
| 	  pc.get_wide_win32_path (path);
 | |
| 	  /* Check if the last path component contains a dot.  If so,
 | |
| 	     leave the filename alone.  Otherwise add a traiing dot
 | |
| 	     to override LoadLibrary's automatic adding of a ".dll" suffix. */
 | |
| 	  wchar_t *last_bs = wcsrchr (path, L'\\');
 | |
| 	  if (last_bs && !wcschr (last_bs, L'.'))
 | |
| 	    wcscat (last_bs, L".");
 | |
| 
 | |
| 	  /* Workaround for broken DLLs built against Cygwin versions 1.7.0-49
 | |
| 	     up to 1.7.0-57.  They override the cxx_malloc pointer in their
 | |
| 	     DLL initialization code even if loaded dynamically.  This is a
 | |
| 	     no-no since a later dlclose lets cxx_malloc point into nirvana.
 | |
| 	     The below kludge "fixes" that by reverting the original cxx_malloc
 | |
| 	     pointer after LoadLibrary.  This implies that their overrides
 | |
| 	     won't be applied; that's OK.  All overrides should be present at
 | |
| 	     final link time, as Windows doesn't allow undefined references;
 | |
| 	     it would actually be wrong for a dlopen'd DLL to opportunistically
 | |
| 	     override functions in a way that wasn't known then.  We're not
 | |
| 	     going to try and reproduce the full ELF dynamic loader here!  */
 | |
| 
 | |
| 	  /* Store original cxx_malloc pointer. */
 | |
| 	  struct per_process_cxx_malloc *tmp_malloc;
 | |
| 	  tmp_malloc = __cygwin_user_data.cxx_malloc;
 | |
| 
 | |
| 	  ret = (void *) LoadLibraryW (path);
 | |
| 
 | |
| 	  /* Restore original cxx_malloc pointer. */
 | |
| 	  __cygwin_user_data.cxx_malloc = tmp_malloc;
 | |
| 
 | |
| 	  if (ret == NULL)
 | |
| 	    __seterrno ();
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   if (!ret)
 | |
|     set_dl_error ("dlopen");
 | |
|   debug_printf ("ret %p", ret);
 | |
| 
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| void *
 | |
| dlsym (void *handle, const char *name)
 | |
| {
 | |
|   void *ret = NULL;
 | |
|   if (handle == RTLD_DEFAULT)
 | |
|     { /* search all modules */
 | |
|       HANDLE cur_proc = GetCurrentProcess ();
 | |
|       HMODULE *modules;
 | |
|       DWORD needed, i;
 | |
|       if (!EnumProcessModules (cur_proc, NULL, 0, &needed))
 | |
| 	{
 | |
| 	dlsym_fail:
 | |
| 	  set_dl_error ("dlsym");
 | |
| 	  return NULL;
 | |
| 	}
 | |
|       modules = (HMODULE*) alloca (needed);
 | |
|       if (!EnumProcessModules (cur_proc, modules, needed, &needed))
 | |
| 	goto dlsym_fail;
 | |
|       for (i = 0; i < needed / sizeof (HMODULE); i++)
 | |
| 	if ((ret = (void *) GetProcAddress (modules[i], name)))
 | |
| 	  break;
 | |
|     }
 | |
|   else
 | |
|     ret = (void *) GetProcAddress ((HMODULE)handle, name);
 | |
|   if (!ret)
 | |
|     set_dl_error ("dlsym");
 | |
|   debug_printf ("ret %p", ret);
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| int
 | |
| dlclose (void *handle)
 | |
| {
 | |
|   int ret;
 | |
|   if (handle == GetModuleHandle (NULL))
 | |
|     ret = 0;
 | |
|   else if (FreeLibrary ((HMODULE) handle))
 | |
|     ret = 0;
 | |
|   else
 | |
|     ret = -1;
 | |
|   if (ret)
 | |
|     set_dl_error ("dlclose");
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| char *
 | |
| dlerror ()
 | |
| {
 | |
|   char *res;
 | |
|   if (!_my_tls.locals.dl_error)
 | |
|     res = NULL;
 | |
|   else
 | |
|     {
 | |
|       _my_tls.locals.dl_error = 0;
 | |
|       res = _my_tls.locals.dl_buffer;
 | |
|     }
 | |
|   return res;
 | |
| }
 |