Imported Upstream version 2.2.0
[deb_libcec.git] / src / lib / platform / windows / dlfcn-win32.cpp
CommitLineData
cbbe90dd
JB
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 "env.h"
21#include <windows.h>
22#include <stdio.h>
23
24#include "dlfcn-win32.h"
25
26/* Note:
27 * MSDN says these functions are not thread-safe. We make no efforts to have
28 * any kind of thread safety.
29 */
30
31/* I have no special reason to have set MAX_GLOBAL_OBJECTS to this value. Any
32 * comments are welcome.
33 */
34#define MAX_OBJECTS 255
35
36static HMODULE global_objects[MAX_OBJECTS];
37
38/* This function adds an object to the list of global objects.
39 * The implementation is very simple and slow.
40 * TODO: should failing this function be enough to fail the call to dlopen( )?
41 */
42static void global_object_add( HMODULE hModule )
43{
44 int i;
45
46 for( i = 0 ; i < MAX_OBJECTS ; i++ )
47 {
48 if( !global_objects[i] )
49 {
50 global_objects[i] = hModule;
51 break;
52 }
53 }
54}
55
56static void global_object_rem( HMODULE hModule )
57{
58 int i;
59
60 for( i = 0 ; i < MAX_OBJECTS ; i++ )
61 {
62 if( global_objects[i] == hModule )
63 {
64 global_objects[i] = 0;
65 break;
66 }
67 }
68}
69
70/* Argument to last function. Used in dlerror( ) */
71static char last_name[MAX_PATH];
72
73static int copy_string( char *dest, int dest_size, const char *src )
74{
75 int i = 0;
76
77 if( src && dest )
78 {
79 for( i = 0 ; i < dest_size-1 ; i++ )
80 {
81 if( !src[i] )
82 break;
83 else
84 dest[i] = src[i];
85 }
86 }
87 dest[i] = '\0';
88
89 return i;
90}
91
92void *dlopen( const char *file, int mode )
93{
94 HMODULE hModule;
95 UINT uMode;
96
97 /* Do not let Windows display the critical-error-handler message box */
98 uMode = SetErrorMode( SEM_FAILCRITICALERRORS );
99
100 if( file == 0 )
101 {
102 /* Save NULL pointer for error message */
103 _snprintf_s( last_name, MAX_PATH, MAX_PATH, "0x%p", file );
104
105 /* POSIX says that if the value of file is 0, a handle on a global
106 * symbol object must be provided. That object must be able to access
107 * all symbols from the original program file, and any objects loaded
108 * with the RTLD_GLOBAL flag.
109 * The return value from GetModuleHandle( ) allows us to retrieve
110 * symbols only from the original program file. For objects loaded with
111 * the RTLD_GLOBAL flag, we create our own list later on.
112 */
113 hModule = GetModuleHandle( NULL );
114 }
115 else
116 {
117 char lpFileName[MAX_PATH];
118 int i;
119
120 /* MSDN says backslashes *must* be used instead of forward slashes. */
121 for( i = 0 ; i < sizeof(lpFileName)-1 ; i++ )
122 {
123 if( !file[i] )
124 break;
125 else if( file[i] == '/' )
126 lpFileName[i] = '\\';
127 else
128 lpFileName[i] = file[i];
129 }
130 lpFileName[i] = '\0';
131
132 /* Save file name for error message */
133 copy_string( last_name, sizeof(last_name), lpFileName );
134
135 /* POSIX says the search path is implementation-defined.
136 * LOAD_WITH_ALTERED_SEARCH_PATH is used to make it behave more closely
137 * to UNIX's search paths (start with system folders instead of current
138 * folder).
139 */
140 hModule = LoadLibraryEx( (LPSTR) lpFileName, NULL,
141 LOAD_WITH_ALTERED_SEARCH_PATH );
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
150 if( hModule && (mode & RTLD_GLOBAL) )
151 global_object_add( hModule );
152 }
153
154 /* Return to previous state of the error-mode bit flags. */
155 SetErrorMode( uMode );
156
157 return (void *) hModule;
158}
159
160int dlclose( void *handle )
161{
162 HMODULE hModule = (HMODULE) handle;
163 BOOL ret;
164
165 /* Save handle for error message */
166 _snprintf_s( last_name, MAX_PATH, MAX_PATH, "0x%p", handle );
167
168 ret = FreeLibrary( hModule );
169
170 /* If the object was loaded with RTLD_GLOBAL, remove it from list of global
171 * objects.
172 */
173 if( ret )
174 global_object_rem( hModule );
175
176 /* dlclose's return value in inverted in relation to FreeLibrary's. */
177 ret = !ret;
178
179 return (int) ret;
180}
181
182void *dlsym( void *handle, const char *name )
183{
184 FARPROC symbol;
185 HMODULE myhandle = (HMODULE) handle;
186
187 /* Save symbol name for error message */
188 copy_string( last_name, sizeof(last_name), name );
189
190 symbol = GetProcAddress( myhandle, name );
191#if 0
192 if( symbol == NULL )
193 {
194 HMODULE hModule;
195
196 /* If the handle for the original program file is passed, also search
197 * in all globally loaded objects.
198 */
199
200 hModule = GetModuleHandle( NULL );
201
202 if( hModule == handle )
203 {
204 int i;
205
206 for( i = 0 ; i < MAX_OBJECTS ; i++ )
207 {
208 if( global_objects[i] != 0 )
209 {
210 symbol = GetProcAddress( global_objects[i], name );
211 if( symbol != NULL )
212 break;
213 }
214 }
215 }
216
217
218 CloseHandle( hModule );
219 }
220#endif
221 return (void*) symbol;
222}
223
224char *dlerror( void )
225{
226 DWORD dwMessageId;
227 /* POSIX says this function doesn't have to be thread-safe, so we use one
228 * static buffer.
229 * MSDN says the buffer cannot be larger than 64K bytes, so we set it to
230 * the limit.
231 */
232 static char lpBuffer[65535];
233 DWORD ret;
234
235 dwMessageId = GetLastError( );
236
237 if( dwMessageId == 0 )
238 return NULL;
239
240 /* Format error message to:
241 * "<argument to function that failed>": <Windows localized error message>
242 */
243 ret = copy_string( lpBuffer, sizeof(lpBuffer), "\"" );
244 ret += copy_string( lpBuffer+ret, sizeof(lpBuffer)-ret, last_name );
245 ret += copy_string( lpBuffer+ret, sizeof(lpBuffer)-ret, "\": " );
246 ret += FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwMessageId,
247 MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
248 lpBuffer+ret, sizeof(lpBuffer)-ret, NULL );
249
250 if( ret > 1 )
251 {
252 /* POSIX says the string must not have trailing <newline> */
253 if( lpBuffer[ret-2] == '\r' && lpBuffer[ret-1] == '\n' )
254 lpBuffer[ret-2] = '\0';
255 }
256
257 /* POSIX says that invoking dlerror( ) a second time, immediately following
258 * a prior invocation, shall result in NULL being returned.
259 */
260 SetLastError(0);
261
262 return lpBuffer;
263}
264