dlfcn.c
Go to the documentation of this file.
00001 /*
00002  * dlfcn-win32
00003  * Copyright (c) 2007 Ramiro Polla
00004  *
00005  * This library is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU Lesser General Public
00007  * License as published by the Free Software Foundation; either
00008  * version 2.1 of the License, or (at your option) any later version.
00009  *
00010  * This library is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  * Lesser General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU Lesser General Public
00016  * License along with this library; if not, write to the Free Software
00017  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00018  */
00019 
00020 #include "dlfcn.h"
00021 #include <stdio.h>
00022 #include <stdlib.h>
00023 
00024 /* Note:
00025  * MSDN says these functions are not thread-safe. We make no efforts to have
00026  * any kind of thread safety.
00027  */
00028 
00029 typedef struct global_object {
00030     HMODULE hModule;
00031     struct global_object *previous;
00032     struct global_object *next;
00033 } global_object;
00034 
00035 static global_object first_object;
00036 
00037 /* These functions implement a double linked list for the global objects. */
00038 static global_object *global_search( HMODULE hModule )
00039 {
00040     global_object *pobject;
00041 
00042     if( hModule == NULL )
00043         return NULL;
00044 
00045     for( pobject = &first_object; pobject ; pobject = pobject->next )
00046         if( pobject->hModule == hModule )
00047             return pobject;
00048 
00049     return NULL;
00050 }
00051 
00052 static void global_add( HMODULE hModule )
00053 {
00054     global_object *pobject;
00055     global_object *nobject;
00056 
00057     if( hModule == NULL )
00058         return;
00059 
00060     pobject = global_search( hModule );
00061 
00062     /* Do not add object again if it's already on the list */
00063     if( pobject )
00064         return;
00065 
00066     for( pobject = &first_object; pobject->next ; pobject = pobject->next );
00067 
00068     nobject = malloc( sizeof(global_object) );
00069 
00070     /* Should this be enough to fail global_add, and therefore also fail
00071      * dlopen?
00072      */
00073     if( !nobject )
00074         return;
00075 
00076     pobject->next = nobject;
00077     nobject->next = NULL;
00078     nobject->previous = pobject;
00079     nobject->hModule = hModule;
00080 }
00081 
00082 static void global_rem( HMODULE hModule )
00083 {
00084     global_object *pobject;
00085 
00086     if( hModule == NULL )
00087         return;
00088 
00089     pobject = global_search( hModule );
00090 
00091     if( !pobject )
00092         return;
00093 
00094     if( pobject->next )
00095         pobject->next->previous = pobject->previous;
00096     if( pobject->previous )
00097         pobject->previous->next = pobject->next;
00098 
00099     free( pobject );
00100 }
00101 
00102 /* POSIX says dlerror( ) doesn't have to be thread-safe, so we use one
00103  * static buffer.
00104  * MSDN says the buffer cannot be larger than 64K bytes, so we set it to
00105  * the limit.
00106  */
00107 static char error_buffer[65535];
00108 static char *current_error;
00109 
00110 static int copy_string( char *dest, int dest_size, const char *src )
00111 {
00112     int i = 0;
00113 
00114     /* gcc should optimize this out */
00115     if( !src && !dest )
00116         return 0;
00117 
00118     for( i = 0 ; i < dest_size-1 ; i++ )
00119     {
00120         if( !src[i] )
00121             break;
00122         else
00123             dest[i] = src[i];
00124     }
00125     dest[i] = '\0';
00126 
00127     return i;
00128 }
00129 
00130 static void save_err_str( const char *str )
00131 {
00132     DWORD dwMessageId;
00133     DWORD pos;
00134 
00135     dwMessageId = GetLastError( );
00136 
00137     if( dwMessageId == 0 )
00138         return;
00139 
00140     /* Format error message to:
00141      * "<argument to function that failed>": <Windows localized error message>
00142      */
00143     pos  = copy_string( error_buffer,     sizeof(error_buffer),     "\"" );
00144     pos += copy_string( error_buffer+pos, sizeof(error_buffer)-pos, str );
00145     pos += copy_string( error_buffer+pos, sizeof(error_buffer)-pos, "\": " );
00146     pos += FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwMessageId,
00147                           MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
00148                           error_buffer+pos, sizeof(error_buffer)-pos, NULL );
00149 
00150     if( pos > 1 )
00151     {
00152         /* POSIX says the string must not have trailing <newline> */
00153         if( error_buffer[pos-2] == '\r' && error_buffer[pos-1] == '\n' )
00154             error_buffer[pos-2] = '\0';
00155     }
00156 
00157     current_error = error_buffer;
00158 }
00159 
00160 static void save_err_ptr_str( const void *ptr )
00161 {
00162     char ptr_buf[19]; /* 0x<pointer> up to 64 bits. */
00163 
00164     sprintf( ptr_buf, "0x%p", ptr );
00165 
00166     save_err_str( ptr_buf );
00167 }
00168 
00169 void *dlopen( const char *file, int mode )
00170 {
00171     HMODULE hModule;
00172     UINT uMode;
00173 
00174     current_error = NULL;
00175 
00176     /* Do not let Windows display the critical-error-handler message box */
00177     uMode = SetErrorMode( SEM_FAILCRITICALERRORS );
00178 
00179     if( file == 0 )
00180     {
00181         /* POSIX says that if the value of file is 0, a handle on a global
00182          * symbol object must be provided. That object must be able to access
00183          * all symbols from the original program file, and any objects loaded
00184          * with the RTLD_GLOBAL flag.
00185          * The return value from GetModuleHandle( ) allows us to retrieve
00186          * symbols only from the original program file. For objects loaded with
00187          * the RTLD_GLOBAL flag, we create our own list later on.
00188          */
00189         hModule = GetModuleHandle( NULL );
00190 
00191         if( !hModule )
00192             save_err_ptr_str( file );
00193     }
00194     else
00195     {
00196         char lpFileName[MAX_PATH];
00197         int i;
00198 
00199         /* MSDN says backslashes *must* be used instead of forward slashes. */
00200         for( i = 0 ; i < sizeof(lpFileName)-1 ; i++ )
00201         {
00202             if( !file[i] )
00203                 break;
00204             else if( file[i] == '/' )
00205                 lpFileName[i] = '\\';
00206             else
00207                 lpFileName[i] = file[i];
00208         }
00209         lpFileName[i] = '\0';
00210 
00211         /* POSIX says the search path is implementation-defined.
00212          * LOAD_WITH_ALTERED_SEARCH_PATH is used to make it behave more closely
00213          * to UNIX's search paths (start with system folders instead of current
00214          * folder).
00215          */
00216         hModule = LoadLibraryEx( (LPSTR) lpFileName, NULL, 
00217                                  LOAD_WITH_ALTERED_SEARCH_PATH );
00218 
00219         /* If the object was loaded with RTLD_GLOBAL, add it to list of global
00220          * objects, so that its symbols may be retrieved even if the handle for
00221          * the original program file is passed. POSIX says that if the same
00222          * file is specified in multiple invocations, and any of them are
00223          * RTLD_GLOBAL, even if any further invocations use RTLD_LOCAL, the
00224          * symbols will remain global.
00225          */
00226         if( !hModule )
00227             save_err_str( lpFileName );
00228         else if( (mode & RTLD_GLOBAL) )
00229             global_add( hModule );
00230     }
00231 
00232     /* Return to previous state of the error-mode bit flags. */
00233     SetErrorMode( uMode );
00234 
00235     return (void *) hModule;
00236 }
00237 
00238 int dlclose( void *handle )
00239 {
00240     HMODULE hModule = (HMODULE) handle;
00241     BOOL ret;
00242 
00243     current_error = NULL;
00244 
00245     ret = FreeLibrary( hModule );
00246 
00247     /* If the object was loaded with RTLD_GLOBAL, remove it from list of global
00248      * objects.
00249      */
00250     if( ret )
00251         global_rem( hModule );
00252     else
00253         save_err_ptr_str( handle );
00254 
00255     /* dlclose's return value in inverted in relation to FreeLibrary's. */
00256     ret = !ret;
00257 
00258     return (int) ret;
00259 }
00260 
00261 void *dlsym( void *handle, const char *name )
00262 {
00263     FARPROC symbol;
00264 
00265     current_error = NULL;
00266 
00267     symbol = GetProcAddress( handle, name );
00268 
00269     if( symbol == NULL )
00270     {
00271         HMODULE hModule;
00272 
00273         /* If the handle for the original program file is passed, also search
00274          * in all globally loaded objects.
00275          */
00276 
00277         hModule = GetModuleHandle( NULL );
00278 
00279         if( hModule == handle )
00280         {
00281             global_object *pobject;
00282 
00283             for( pobject = &first_object; pobject ; pobject = pobject->next )
00284             {
00285                 if( pobject->hModule )
00286                 {
00287                     symbol = GetProcAddress( pobject->hModule, name );
00288                     if( symbol != NULL )
00289                         break;
00290                 }
00291             }
00292         }
00293         /* We don't need to close hModule since GetModuleHandle()
00294          * Does not increment the refcount.
00295          */
00296     }
00297 
00298     if( symbol == NULL )
00299         save_err_str( name );
00300 
00301     return (void*) symbol;
00302 }
00303 
00304 char *dlerror( void )
00305 {
00306     char *error_pointer = current_error;
00307 
00308     /* POSIX says that invoking dlerror( ) a second time, immediately following
00309      * a prior invocation, shall result in NULL being returned.
00310      */
00311     current_error = NULL;
00312 
00313     return error_pointer;
00314 }


rtt
Author(s): RTT Developers
autogenerated on Thu Jan 2 2014 11:35:20