aboutsummaryrefslogtreecommitdiff
path: root/dlfcn.c
diff options
context:
space:
mode:
Diffstat (limited to 'dlfcn.c')
-rw-r--r--dlfcn.c260
1 files changed, 260 insertions, 0 deletions
diff --git a/dlfcn.c b/dlfcn.c
new file mode 100644
index 0000000..f56a262
--- /dev/null
+++ b/dlfcn.c
@@ -0,0 +1,260 @@
1/*
2 * dlfcn-win32
3 * Copyright (c) 2007 Ramiro Polla
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <windows.h>
21#include <stdio.h>
22
23#include "dlfcn.h"
24
25/* Note:
26 * MSDN says these functions are not thread-safe. We make no efforts to have
27 * any kind of thread safety.
28 */
29
30/* I have no special reason to have set MAX_GLOBAL_OBJECTS to this value. Any
31 * comments are welcome.
32 */
33#define MAX_OBJECTS 255
34
35static HMODULE global_objects[MAX_OBJECTS];
36
37/* This function adds an object to the list of global objects.
38 * The implementation is very simple and slow.
39 * TODO: should failing this function be enough to fail the call to dlopen( )?
40 */
41static void global_object_add( HMODULE hModule )
42{
43 int i;
44
45 for( i = 0 ; i < MAX_OBJECTS ; i++ )
46 {
47 if( !global_objects[i] )
48 {
49 global_objects[i] = hModule;
50 break;
51 }
52 }
53}
54
55static void global_object_rem( HMODULE hModule )
56{
57 int i;
58
59 for( i = 0 ; i < MAX_OBJECTS ; i++ )
60 {
61 if( global_objects[i] == hModule )
62 {
63 global_objects[i] = 0;
64 break;
65 }
66 }
67}
68
69/* Argument to last function. Used in dlerror( ) */
70static char last_name[MAX_PATH];
71
72static int copy_string( char *dest, int dest_size, const char *src )
73{
74 int i = 0;
75
76 if( src && dest )
77 {
78 for( i = 0 ; i < dest_size-1 ; i++ )
79 {
80 if( !src[i] )
81 break;
82 else
83 dest[i] = src[i];
84 }
85 }
86 dest[i] = '\0';
87
88 return i;
89}
90
91void *dlopen( const char *file, int mode )
92{
93 HMODULE hModule;
94 UINT uMode;
95
96 /* Do not let Windows display the critical-error-handler message box */
97 uMode = SetErrorMode( SEM_FAILCRITICALERRORS );
98
99 if( file == 0 )
100 {
101 /* Save NULL pointer for error message */
102 sprintf( last_name, "0x%p", file );
103
104 /* POSIX says that if the value of file is 0, a handle on a global
105 * symbol object must be provided. That object must be able to access
106 * all symbols from the original program file, and any objects loaded
107 * with the RTLD_GLOBAL flag.
108 * The return value from GetModuleHandle( ) allows us to retrieve
109 * symbols only from the original program file. For objects loaded with
110 * the RTLD_GLOBAL flag, we create our own list later on.
111 */
112 hModule = GetModuleHandle( NULL );
113 }
114 else
115 {
116 char lpFileName[MAX_PATH];
117 int i;
118
119 /* MSDN says backslashes *must* be used instead of forward slashes. */
120 for( i = 0 ; i < sizeof(lpFileName)-1 ; i++ )
121 {
122 if( !file[i] )
123 break;
124 else if( file[i] == '/' )
125 lpFileName[i] = '\\';
126 else
127 lpFileName[i] = file[i];
128 }
129 lpFileName[i] = '\0';
130
131 /* Save file name for error message */
132 copy_string( last_name, sizeof(last_name), lpFileName );
133
134 /* POSIX says the search path is implementation-defined.
135 * LOAD_WITH_ALTERED_SEARCH_PATH is used to make it behave more closely
136 * to UNIX's search paths (start with system folders instead of current
137 * folder).
138 */
139 hModule = LoadLibraryEx( (LPSTR) lpFileName, NULL,
140 LOAD_WITH_ALTERED_SEARCH_PATH );
141
142 /* If the object was loaded with RTLD_GLOBAL, add it to list of global
143 * objects, so that its symbols may be retrieved even if the handle for
144 * the original program file is passed. POSIX says that if the same
145 * file is specified in multiple invocations, and any of them are
146 * RTLD_GLOBAL, even if any further invocations use RTLD_LOCAL, the
147 * symbols will remain global.
148 */
149 if( hModule && (mode & RTLD_GLOBAL) )
150 global_object_add( hModule );
151 }
152
153 /* Return to previous state of the error-mode bit flags. */
154 SetErrorMode( uMode );
155
156 return (void *) hModule;
157}
158
159int dlclose( void *handle )
160{
161 HMODULE hModule = (HMODULE) handle;
162 BOOL ret;
163
164 /* Save handle for error message */
165 sprintf( last_name, "0x%p", handle );
166
167 ret = FreeLibrary( hModule );
168
169 /* If the object was loaded with RTLD_GLOBAL, remove it from list of global
170 * objects.
171 */
172 if( ret )
173 global_object_rem( hModule );
174
175 /* dlclose's return value in inverted in relation to FreeLibrary's. */
176 ret = !ret;
177
178 return (int) ret;
179}
180
181void *dlsym( void *handle, const char *name )
182{
183 FARPROC symbol;
184
185 /* Save symbol name for error message */
186 copy_string( last_name, sizeof(last_name), name );
187
188 symbol = GetProcAddress( handle, name );
189
190 if( symbol == NULL )
191 {
192 HMODULE hModule;
193
194 /* If the handle for the original program file is passed, also search
195 * in all globally loaded objects.
196 */
197
198 hModule = GetModuleHandle( NULL );
199
200 if( hModule == handle )
201 {
202 int i;
203
204 for( i = 0 ; i < MAX_OBJECTS ; i++ )
205 {
206 if( global_objects[i] != 0 )
207 {
208 symbol = GetProcAddress( global_objects[i], name );
209 if( symbol != NULL )
210 break;
211 }
212 }
213 }
214
215 CloseHandle( hModule );
216 }
217
218 return (void*) symbol;
219}
220
221char *dlerror( void )
222{
223 DWORD dwMessageId;
224 /* POSIX says this function doesn't have to be thread-safe, so we use one
225 * static buffer.
226 * MSDN says the buffer cannot be larger than 64K bytes, so we set it to
227 * the limit.
228 */
229 static char lpBuffer[65535];
230 DWORD ret;
231
232 dwMessageId = GetLastError( );
233
234 if( dwMessageId == 0 )
235 return NULL;
236
237 /* Format error message to:
238 * "<argument to function that failed>": <Windows localized error message>
239 */
240 ret = copy_string( lpBuffer, sizeof(lpBuffer), "\"" );
241 ret += copy_string( lpBuffer+ret, sizeof(lpBuffer)-ret, last_name );
242 ret += copy_string( lpBuffer+ret, sizeof(lpBuffer)-ret, "\": " );
243 ret += FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwMessageId,
244 MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
245 lpBuffer+ret, sizeof(lpBuffer)-ret, NULL );
246
247 if( ret > 1 )
248 {
249 /* POSIX says the string must not have trailing <newline> */
250 if( lpBuffer[ret-2] == '\r' && lpBuffer[ret-1] == '\n' )
251 lpBuffer[ret-2] = '\0';
252 }
253
254 /* POSIX says that invoking dlerror( ) a second time, immediately following
255 * a prior invocation, shall result in NULL being returned.
256 */
257 SetLastError(0);
258
259 return lpBuffer;
260}