cec: bumped interface version to 4 stay in sync with the lib version. added changelog...
[deb_libcec.git] / src / lib / platform / windows / dlfcn-win32.cpp
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-win32.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
35 static 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 */
41 static 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
55 static 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( ) */
70 static char last_name[MAX_PATH];
71
72 static 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
91 void *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 _snprintf_s( last_name, MAX_PATH, MAX_PATH, "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 /* If the object was loaded with RTLD_GLOBAL, add it to list of global
142 * objects, so that its symbols may be retrieved even if the handle for
143 * the original program file is passed. POSIX says that if the same
144 * file is specified in multiple invocations, and any of them are
145 * RTLD_GLOBAL, even if any further invocations use RTLD_LOCAL, the
146 * symbols will remain global.
147 */
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
159 int dlclose( void *handle )
160 {
161 HMODULE hModule = (HMODULE) handle;
162 BOOL ret;
163
164 /* Save handle for error message */
165 _snprintf_s( last_name, MAX_PATH, MAX_PATH, "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
181 void *dlsym( void *handle, const char *name )
182 {
183 FARPROC symbol;
184 HMODULE myhandle = (HMODULE) handle;
185
186 /* Save symbol name for error message */
187 copy_string( last_name, sizeof(last_name), name );
188
189 symbol = GetProcAddress( myhandle, name );
190 #if 0
191 if( symbol == NULL )
192 {
193 HMODULE hModule;
194
195 /* If the handle for the original program file is passed, also search
196 * in all globally loaded objects.
197 */
198
199 hModule = GetModuleHandle( NULL );
200
201 if( hModule == handle )
202 {
203 int i;
204
205 for( i = 0 ; i < MAX_OBJECTS ; i++ )
206 {
207 if( global_objects[i] != 0 )
208 {
209 symbol = GetProcAddress( global_objects[i], name );
210 if( symbol != NULL )
211 break;
212 }
213 }
214 }
215
216
217 CloseHandle( hModule );
218 }
219 #endif
220 return (void*) symbol;
221 }
222
223 char *dlerror( void )
224 {
225 DWORD dwMessageId;
226 /* POSIX says this function doesn't have to be thread-safe, so we use one
227 * static buffer.
228 * MSDN says the buffer cannot be larger than 64K bytes, so we set it to
229 * the limit.
230 */
231 static char lpBuffer[65535];
232 DWORD ret;
233
234 dwMessageId = GetLastError( );
235
236 if( dwMessageId == 0 )
237 return NULL;
238
239 /* Format error message to:
240 * "<argument to function that failed>": <Windows localized error message>
241 */
242 ret = copy_string( lpBuffer, sizeof(lpBuffer), "\"" );
243 ret += copy_string( lpBuffer+ret, sizeof(lpBuffer)-ret, last_name );
244 ret += copy_string( lpBuffer+ret, sizeof(lpBuffer)-ret, "\": " );
245 ret += FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwMessageId,
246 MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
247 lpBuffer+ret, sizeof(lpBuffer)-ret, NULL );
248
249 if( ret > 1 )
250 {
251 /* POSIX says the string must not have trailing <newline> */
252 if( lpBuffer[ret-2] == '\r' && lpBuffer[ret-1] == '\n' )
253 lpBuffer[ret-2] = '\0';
254 }
255
256 /* POSIX says that invoking dlerror( ) a second time, immediately following
257 * a prior invocation, shall result in NULL being returned.
258 */
259 SetLastError(0);
260
261 return lpBuffer;
262 }
263