Use correct extensions in wxDynamicLibrary::CanonicalizeName() on OS X.
[wxWidgets.git] / src / unix / dlunix.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/unix/dlunix.cpp
3 // Purpose: Unix-specific part of wxDynamicLibrary and related classes
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 2005-01-16 (extracted from common/dynlib.cpp)
7 // RCS-ID: $Id$
8 // Copyright: (c) 2000-2005 Vadim Zeitlin <vadim@wxwindows.org>
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #if wxUSE_DYNLIB_CLASS
27
28 #include "wx/dynlib.h"
29 #include "wx/ffile.h"
30
31 #ifndef WX_PRECOMP
32 #include "wx/intl.h"
33 #include "wx/log.h"
34 #endif
35
36 #ifdef HAVE_DLOPEN
37 #include <dlfcn.h>
38 #endif
39
40 #ifdef __DARWIN__
41 #include <AvailabilityMacros.h>
42 #endif
43
44 // if some flags are not supported, just ignore them
45 #ifndef RTLD_LAZY
46 #define RTLD_LAZY 0
47 #endif
48
49 #ifndef RTLD_NOW
50 #define RTLD_NOW 0
51 #endif
52
53 #ifndef RTLD_GLOBAL
54 #define RTLD_GLOBAL 0
55 #endif
56
57
58 #if defined(HAVE_DLOPEN) || defined(__DARWIN__)
59 #define USE_POSIX_DL_FUNCS
60 #elif !defined(HAVE_SHL_LOAD)
61 #error "Don't know how to load dynamic libraries on this platform!"
62 #endif
63
64 // ----------------------------------------------------------------------------
65 // constants
66 // ----------------------------------------------------------------------------
67
68 // ============================================================================
69 // wxDynamicLibrary implementation
70 // ============================================================================
71
72 // ----------------------------------------------------------------------------
73 // dlxxx() emulation for Darwin
74 // Only useful if the OS X version could be < 10.3 at runtime
75 // ----------------------------------------------------------------------------
76
77 #if defined(__DARWIN__) && (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_3)
78 // ---------------------------------------------------------------------------
79 // For Darwin/Mac OS X
80 // supply the sun style dlopen functions in terms of Darwin NS*
81 // ---------------------------------------------------------------------------
82
83 /* Porting notes:
84 * The dlopen port is a port from dl_next.xs by Anno Siegel.
85 * dl_next.xs is itself a port from dl_dlopen.xs by Paul Marquess.
86 * The method used here is just to supply the sun style dlopen etc.
87 * functions in terms of Darwin NS*.
88 */
89
90 #include <stdio.h>
91 #include <mach-o/dyld.h>
92
93 static char dl_last_error[1024];
94
95 static const char *wx_darwin_dlerror()
96 {
97 return dl_last_error;
98 }
99
100 static void *wx_darwin_dlopen(const char *path, int WXUNUSED(mode) /* mode is ignored */)
101 {
102 NSObjectFileImage ofile;
103 NSModule handle = NULL;
104
105 unsigned dyld_result = NSCreateObjectFileImageFromFile(path, &ofile);
106 if ( dyld_result != NSObjectFileImageSuccess )
107 {
108 handle = NULL;
109
110 static const char *const errorStrings[] =
111 {
112 "%d: Object Image Load Failure",
113 "%d: Object Image Load Success",
114 "%d: Not an recognisable object file",
115 "%d: No valid architecture",
116 "%d: Object image has an invalid format",
117 "%d: Invalid access (permissions?)",
118 "%d: Unknown error code from NSCreateObjectFileImageFromFile"
119 };
120
121 const int index = dyld_result < WXSIZEOF(errorStrings)
122 ? dyld_result
123 : WXSIZEOF(errorStrings) - 1;
124
125 // this call to sprintf() is safe as strings above are fixed at
126 // compile-time and are shorter than WXSIZEOF(dl_last_error)
127 sprintf(dl_last_error, errorStrings[index], dyld_result);
128 }
129 else
130 {
131 handle = NSLinkModule
132 (
133 ofile,
134 path,
135 NSLINKMODULE_OPTION_BINDNOW |
136 NSLINKMODULE_OPTION_RETURN_ON_ERROR
137 );
138
139 if ( !handle )
140 {
141 NSLinkEditErrors err;
142 int code;
143 const char *filename;
144 const char *errmsg;
145
146 NSLinkEditError(&err, &code, &filename, &errmsg);
147 strncpy(dl_last_error, errmsg, WXSIZEOF(dl_last_error)-1);
148 dl_last_error[WXSIZEOF(dl_last_error)-1] = '\0';
149 }
150 }
151
152
153 return handle;
154 }
155
156 static int wx_darwin_dlclose(void *handle)
157 {
158 NSUnLinkModule((NSModule)handle, NSUNLINKMODULE_OPTION_NONE);
159 return 0;
160 }
161
162 static void *wx_darwin_dlsym(void *handle, const char *symbol)
163 {
164 // as on many other systems, C symbols have prepended underscores under
165 // Darwin but unlike the normal dlopen(), NSLookupSymbolInModule() is not
166 // aware of this
167 wxCharBuffer buf(strlen(symbol) + 1);
168 char *p = buf.data();
169 p[0] = '_';
170 strcpy(p + 1, symbol);
171
172 NSSymbol nsSymbol = NSLookupSymbolInModule((NSModule)handle, p );
173 return nsSymbol ? NSAddressOfSymbol(nsSymbol) : NULL;
174 }
175
176 // Add the weak linking attribute to dlopen's declaration
177 extern void * dlopen(const char * __path, int __mode) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER;
178
179 // For all of these methods we test dlopen since all of the dl functions we use were added
180 // to OS X at the same time. This also ensures we don't dlopen with the real function then
181 // dlclose with the internal implementation.
182
183 static inline void *wx_dlopen(const char *__path, int __mode)
184 {
185 #ifdef HAVE_DLOPEN
186 if(&dlopen != NULL)
187 return dlopen(__path, __mode);
188 else
189 #endif
190 return wx_darwin_dlopen(__path, __mode);
191 }
192
193 static inline int wx_dlclose(void *__handle)
194 {
195 #ifdef HAVE_DLOPEN
196 if(&dlopen != NULL)
197 return dlclose(__handle);
198 else
199 #endif
200 return wx_darwin_dlclose(__handle);
201 }
202
203 static inline const char *wx_dlerror()
204 {
205 #ifdef HAVE_DLOPEN
206 if(&dlopen != NULL)
207 return dlerror();
208 else
209 #endif
210 return wx_darwin_dlerror();
211 }
212
213 static inline void *wx_dlsym(void *__handle, const char *__symbol)
214 {
215 #ifdef HAVE_DLOPEN
216 if(&dlopen != NULL)
217 return dlsym(__handle, __symbol);
218 else
219 #endif
220 return wx_darwin_dlsym(__handle, __symbol);
221 }
222
223 #else // __DARWIN__/!__DARWIN__
224
225 // Use preprocessor definitions for non-Darwin or OS X >= 10.3
226 #define wx_dlopen(__path,__mode) dlopen(__path,__mode)
227 #define wx_dlclose(__handle) dlclose(__handle)
228 #define wx_dlerror() dlerror()
229 #define wx_dlsym(__handle,__symbol) dlsym(__handle,__symbol)
230
231 #endif // defined(__DARWIN__)
232
233 // ----------------------------------------------------------------------------
234 // loading/unloading DLLs
235 // ----------------------------------------------------------------------------
236
237 wxDllType wxDynamicLibrary::GetProgramHandle()
238 {
239 #ifdef USE_POSIX_DL_FUNCS
240 return wx_dlopen(0, RTLD_LAZY);
241 #else
242 return PROG_HANDLE;
243 #endif
244 }
245
246 /* static */
247 wxDllType wxDynamicLibrary::RawLoad(const wxString& libname, int flags)
248 {
249 wxASSERT_MSG( !(flags & wxDL_NOW) || !(flags & wxDL_LAZY),
250 wxT("wxDL_LAZY and wxDL_NOW are mutually exclusive.") );
251
252 #ifdef USE_POSIX_DL_FUNCS
253 // we need to use either RTLD_NOW or RTLD_LAZY because if we call dlopen()
254 // with flags == 0 recent versions of glibc just fail the call, so use
255 // RTLD_NOW even if wxDL_NOW was not specified
256 int rtldFlags = flags & wxDL_LAZY ? RTLD_LAZY : RTLD_NOW;
257
258 if ( flags & wxDL_GLOBAL )
259 rtldFlags |= RTLD_GLOBAL;
260
261 return wx_dlopen(libname.fn_str(), rtldFlags);
262 #else // !USE_POSIX_DL_FUNCS
263 int shlFlags = 0;
264
265 if ( flags & wxDL_LAZY )
266 {
267 shlFlags |= BIND_DEFERRED;
268 }
269 else if ( flags & wxDL_NOW )
270 {
271 shlFlags |= BIND_IMMEDIATE;
272 }
273
274 return shl_load(libname.fn_str(), shlFlags, 0);
275 #endif // USE_POSIX_DL_FUNCS/!USE_POSIX_DL_FUNCS
276 }
277
278 /* static */
279 void wxDynamicLibrary::Unload(wxDllType handle)
280 {
281 #ifdef wxHAVE_DYNLIB_ERROR
282 int rc =
283 #endif
284
285 #ifdef USE_POSIX_DL_FUNCS
286 wx_dlclose(handle);
287 #else // !USE_POSIX_DL_FUNCS
288 shl_unload(handle);
289 #endif // USE_POSIX_DL_FUNCS/!USE_POSIX_DL_FUNCS
290
291 #if defined(USE_POSIX_DL_FUNCS) && defined(wxHAVE_DYNLIB_ERROR)
292 if ( rc != 0 )
293 Error();
294 #endif
295 }
296
297 /* static */
298 void *wxDynamicLibrary::RawGetSymbol(wxDllType handle, const wxString& name)
299 {
300 void *symbol;
301
302 #ifdef USE_POSIX_DL_FUNCS
303 symbol = wx_dlsym(handle, name.fn_str());
304 #else // !USE_POSIX_DL_FUNCS
305 // note that shl_findsym modifies the handle argument to indicate where the
306 // symbol was found, but it's ok to modify the local handle copy here
307 if ( shl_findsym(&handle, name.fn_str(), TYPE_UNDEFINED, &symbol) != 0 )
308 symbol = 0;
309 #endif // USE_POSIX_DL_FUNCS/!USE_POSIX_DL_FUNCS
310
311 return symbol;
312 }
313
314 // ----------------------------------------------------------------------------
315 // error handling
316 // ----------------------------------------------------------------------------
317
318 #ifdef wxHAVE_DYNLIB_ERROR
319
320 /* static */
321 void wxDynamicLibrary::Error()
322 {
323 wxString err(wx_dlerror());
324
325 if ( err.empty() )
326 err = _("Unknown dynamic library error");
327
328 wxLogError(wxT("%s"), err);
329 }
330
331 #endif // wxHAVE_DYNLIB_ERROR
332
333 // ----------------------------------------------------------------------------
334 // listing loaded modules
335 // ----------------------------------------------------------------------------
336
337 // wxDynamicLibraryDetails declares this class as its friend, so put the code
338 // initializing new details objects here
339 class wxDynamicLibraryDetailsCreator
340 {
341 public:
342 // create a new wxDynamicLibraryDetails from the given data
343 static wxDynamicLibraryDetails *
344 New(void *start, void *end, const wxString& path)
345 {
346 wxDynamicLibraryDetails *details = new wxDynamicLibraryDetails;
347 details->m_path = path;
348 details->m_name = path.AfterLast(wxT('/'));
349 details->m_address = start;
350 details->m_length = (char *)end - (char *)start;
351
352 // try to extract the library version from its name
353 const size_t posExt = path.rfind(wxT(".so"));
354 if ( posExt != wxString::npos )
355 {
356 if ( path.c_str()[posExt + 3] == wxT('.') )
357 {
358 // assume "libfoo.so.x.y.z" case
359 details->m_version.assign(path, posExt + 4, wxString::npos);
360 }
361 else
362 {
363 size_t posDash = path.find_last_of(wxT('-'), posExt);
364 if ( posDash != wxString::npos )
365 {
366 // assume "libbar-x.y.z.so" case
367 posDash++;
368 details->m_version.assign(path, posDash, posExt - posDash);
369 }
370 }
371 }
372
373 return details;
374 }
375 };
376
377 /* static */
378 wxDynamicLibraryDetailsArray wxDynamicLibrary::ListLoaded()
379 {
380 wxDynamicLibraryDetailsArray dlls;
381
382 #ifdef __LINUX__
383 // examine /proc/self/maps to find out what is loaded in our address space
384 wxFFile file(wxT("/proc/self/maps"));
385 if ( file.IsOpened() )
386 {
387 // details of the module currently being parsed
388 wxString pathCur;
389 void *startCur = NULL,
390 *endCur = NULL;
391
392 char path[1024];
393 char buf[1024];
394 while ( fgets(buf, WXSIZEOF(buf), file.fp()) )
395 {
396 // format is: "start-end perm offset maj:min inode path", see proc(5)
397 void *start,
398 *end;
399 switch ( sscanf(buf, "%p-%p %*4s %*p %*02x:%*02x %*d %1024s\n",
400 &start, &end, path) )
401 {
402 case 2:
403 // there may be no path column
404 path[0] = '\0';
405 break;
406
407 case 3:
408 // nothing to do, read everything we wanted
409 break;
410
411 default:
412 // chop '\n'
413 buf[strlen(buf) - 1] = '\0';
414 wxLogDebug(wxT("Failed to parse line \"%s\" in /proc/self/maps."),
415 buf);
416 continue;
417 }
418
419 wxASSERT_MSG( start >= endCur,
420 wxT("overlapping regions in /proc/self/maps?") );
421
422 wxString pathNew = wxString::FromAscii(path);
423 if ( pathCur.empty() )
424 {
425 // new module start
426 pathCur = pathNew;
427 startCur = start;
428 endCur = end;
429 }
430 else if ( pathCur == pathNew && endCur == end )
431 {
432 // continuation of the same module in the address space
433 endCur = end;
434 }
435 else // end of the current module
436 {
437 dlls.Add(wxDynamicLibraryDetailsCreator::New(startCur,
438 endCur,
439 pathCur));
440 pathCur.clear();
441 }
442 }
443 }
444 #endif // __LINUX__
445
446 return dlls;
447 }
448
449 #endif // wxUSE_DYNLIB_CLASS
450