implemented wxDynamicLibrary::ListLoaded() for Linux; added test for it
[wxWidgets.git] / src / unix / dlunix.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: 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 #if defined(HAVE_DLOPEN) || defined(__DARWIN__)
32 #define USE_POSIX_DL_FUNCS
33 #elif !defined(HAVE_SHL_LOAD)
34 #error "Don't know how to load dynamic libraries on this platform!"
35 #endif
36
37 // ----------------------------------------------------------------------------
38 // constants
39 // ----------------------------------------------------------------------------
40
41 // standard shared libraries extensions for different Unix versions
42 #if defined(__HPUX__)
43 const wxChar *wxDynamicLibrary::ms_dllext = _T(".sl");
44 #elif defined(__DARWIN__)
45 const wxChar *wxDynamicLibrary::ms_dllext = _T(".bundle");
46 #else
47 const wxChar *wxDynamicLibrary::ms_dllext = _T(".so");
48 #endif
49
50 // ============================================================================
51 // wxDynamicLibrary implementation
52 // ============================================================================
53
54 // ----------------------------------------------------------------------------
55 // dlxxx() emulation for Darwin
56 // ----------------------------------------------------------------------------
57
58 #if defined(__DARWIN__)
59 // ---------------------------------------------------------------------------
60 // For Darwin/Mac OS X
61 // supply the sun style dlopen functions in terms of Darwin NS*
62 // ---------------------------------------------------------------------------
63
64 /* Porting notes:
65 * The dlopen port is a port from dl_next.xs by Anno Siegel.
66 * dl_next.xs is itself a port from dl_dlopen.xs by Paul Marquess.
67 * The method used here is just to supply the sun style dlopen etc.
68 * functions in terms of Darwin NS*.
69 */
70
71 #include <stdio.h>
72 #include <mach-o/dyld.h>
73
74 static char dl_last_error[1024];
75
76 static
77 void TranslateError(const char *path, int number)
78 {
79 unsigned int index;
80 static char *OFIErrorStrings[] =
81 {
82 "%s(%d): Object Image Load Failure\n",
83 "%s(%d): Object Image Load Success\n",
84 "%s(%d): Not an recognisable object file\n",
85 "%s(%d): No valid architecture\n",
86 "%s(%d): Object image has an invalid format\n",
87 "%s(%d): Invalid access (permissions?)\n",
88 "%s(%d): Unknown error code from NSCreateObjectFileImageFromFile\n",
89 };
90 #define NUM_OFI_ERRORS (sizeof(OFIErrorStrings) / sizeof(OFIErrorStrings[0]))
91
92 index = number;
93 if (index > NUM_OFI_ERRORS - 1) {
94 index = NUM_OFI_ERRORS - 1;
95 }
96 sprintf(dl_last_error, OFIErrorStrings[index], path, number);
97 }
98
99 const char *dlerror()
100 {
101 return dl_last_error;
102 }
103
104 void *dlopen(const char *path, int WXUNUSED(mode) /* mode is ignored */)
105 {
106 NSObjectFileImage ofile;
107 NSModule handle = NULL;
108
109 int dyld_result = NSCreateObjectFileImageFromFile(path, &ofile);
110 if ( dyld_result != NSObjectFileImageSuccess )
111 {
112 handle = NULL;
113 }
114 else
115 {
116 handle = NSLinkModule
117 (
118 ofile,
119 path,
120 NSLINKMODULE_OPTION_BINDNOW |
121 NSLINKMODULE_OPTION_RETURN_ON_ERROR
122 );
123 }
124
125 if ( !handle )
126 TranslateError(path, dyld_result);
127
128 return handle;
129 }
130
131 int dlclose(void *handle)
132 {
133 NSUnLinkModule( handle, NSUNLINKMODULE_OPTION_NONE);
134 return 0;
135 }
136
137 void *dlsym(void *handle, const char *symbol)
138 {
139 // as on many other systems, C symbols have prepended underscores under
140 // Darwin but unlike the normal dlopen(), NSLookupSymbolInModule() is not
141 // aware of this
142 wxCharBuffer buf(strlen(symbol) + 1);
143 char *p = buf.data();
144 p[0] = '_';
145 strcpy(p + 1, symbol);
146
147 NSSymbol nsSymbol = NSLookupSymbolInModule( handle, p );
148 return nsSymbol ? NSAddressOfSymbol(nsSymbol) : NULL;
149 }
150
151 #endif // defined(__DARWIN__)
152
153 // ----------------------------------------------------------------------------
154 // loading/unloading DLLs
155 // ----------------------------------------------------------------------------
156
157 wxDllType wxDynamicLibrary::GetProgramHandle()
158 {
159 #ifdef USE_POSIX_DL_FUNCS
160 return dlopen(0, RTLD_LAZY);
161 #else
162 return PROG_HANDLE;
163 #endif
164 }
165
166 /* static */
167 wxDllType wxDynamicLibrary::RawLoad(const wxString& libname, int flags)
168 {
169 wxASSERT_MSG( (flags & wxDL_NOW) == 0,
170 _T("wxDL_LAZY and wxDL_NOW are mutually exclusive.") );
171
172 #ifdef USE_POSIX_DL_FUNCS
173 int rtldFlags = 0;
174
175 #ifdef RTLD_LAZY
176 if ( flags & wxDL_LAZY )
177 {
178 rtldFlags |= RTLD_LAZY;
179 }
180 #endif
181 #ifdef RTLD_NOW
182 if ( flags & wxDL_NOW )
183 {
184 rtldFlags |= RTLD_NOW;
185 }
186 #endif
187 #ifdef RTLD_GLOBAL
188 if ( flags & wxDL_GLOBAL )
189 {
190 rtldFlags |= RTLD_GLOBAL;
191 }
192 #endif
193
194 return dlopen(libname.fn_str(), rtldFlags);
195 #else // !USE_POSIX_DL_FUNCS
196 int shlFlags = 0;
197
198 if ( flags & wxDL_LAZY )
199 {
200 shlFlags |= BIND_DEFERRED;
201 }
202 else if ( flags & wxDL_NOW )
203 {
204 shlFlags |= BIND_IMMEDIATE;
205 }
206
207 return shl_load(libname.fn_str(), shlFlags, 0);
208 #endif // USE_POSIX_DL_FUNCS/!USE_POSIX_DL_FUNCS
209 }
210
211 /* static */
212 void wxDynamicLibrary::Unload(wxDllType handle)
213 {
214 #ifdef wxHAVE_DYNLIB_ERROR
215 int rc =
216 #endif
217
218 #ifdef USE_POSIX_DL_FUNCS
219 dlclose(handle);
220 #else // !USE_POSIX_DL_FUNCS
221 shl_unload(handle);
222 #endif // USE_POSIX_DL_FUNCS/!USE_POSIX_DL_FUNCS
223
224 #ifdef USE_POSIX_DL_FUNCS
225 if ( rc != 0 )
226 Error();
227 #endif
228 }
229
230 /* static */
231 void *wxDynamicLibrary::RawGetSymbol(wxDllType handle, const wxString& name)
232 {
233 void *symbol;
234
235 #ifdef USE_POSIX_DL_FUNCS
236 symbol = dlsym(handle, name.fn_str());
237 #else // !USE_POSIX_DL_FUNCS
238 // note that shl_findsym modifies the handle argument to indicate where the
239 // symbol was found, but it's ok to modify the local handle copy here
240 if ( shl_findsym(&handle, name.fn_str(), TYPE_UNDEFINED, &symbol) != 0 )
241 symbol = 0;
242 #endif // USE_POSIX_DL_FUNCS/!USE_POSIX_DL_FUNCS
243
244 return symbol;
245 }
246
247 // ----------------------------------------------------------------------------
248 // error handling
249 // ----------------------------------------------------------------------------
250
251 #ifdef wxHAVE_DYNLIB_ERROR
252
253 /* static */
254 void wxDynamicLibrary::Error()
255 {
256 #if wxUSE_UNICODE
257 wxWCharBuffer buffer = wxConvLocal.cMB2WC( dlerror() );
258 const wxChar *err = buffer;
259 #else
260 const wxChar *err = dlerror();
261 #endif
262
263 wxLogError(wxT("%s"), err ? err : _("Unknown dynamic library error"));
264 }
265
266 #endif // wxHAVE_DYNLIB_ERROR
267
268 // ----------------------------------------------------------------------------
269 // listing loaded modules
270 // ----------------------------------------------------------------------------
271
272 // wxDynamicLibraryDetails declares this class as its friend, so put the code
273 // initializing new details objects here
274 class wxDynamicLibraryDetailsCreator
275 {
276 public:
277 // create a new wxDynamicLibraryDetails from the given data
278 static wxDynamicLibraryDetails *
279 New(unsigned long start, unsigned long end, const wxString& path)
280 {
281 wxDynamicLibraryDetails *details = new wxDynamicLibraryDetails;
282 details->m_path = path;
283 details->m_name = path.AfterLast(_T('/'));
284 details->m_address = wx_reinterpret_cast(void *, start);
285 details->m_length = end - start;
286
287 // try to extract the library version from its name
288 const size_t posExt = path.rfind(_T(".so"));
289 if ( posExt != wxString::npos )
290 {
291 if ( path.c_str()[posExt + 3] == _T('.') )
292 {
293 // assume "libfoo.so.x.y.z" case
294 details->m_version.assign(path, posExt + 4, wxString::npos);
295 }
296 else
297 {
298 size_t posDash = path.find_last_of(_T('-'), posExt);
299 if ( posDash != wxString::npos )
300 {
301 // assume "libbar-x.y.z.so" case
302 posDash++;
303 details->m_version.assign(path, posDash, posExt - posDash);
304 }
305 }
306 }
307
308 return details;
309 }
310 };
311
312 /* static */
313 wxDynamicLibraryDetailsArray wxDynamicLibrary::ListLoaded()
314 {
315 wxDynamicLibraryDetailsArray dlls;
316
317 #ifdef __LINUX__
318 // examine /proc/self/maps to find out what is loaded in our address space
319 wxFFile file("/proc/self/maps");
320 if ( file.IsOpened() )
321 {
322 // details of the module currently being parsed
323 wxString pathCur;
324 unsigned long startCur,
325 endCur;
326
327 char path[1024];
328 char buf[1024];
329 while ( fgets(buf, WXSIZEOF(buf), file.fp()) )
330 {
331 // format is: start-end perm something? maj:min inode path
332 unsigned long start, end;
333 switch ( sscanf(buf, "%08lx-%08lx %*4s %*08x %*02d:%*02d %*d %1024s\n",
334 &start, &end, path) )
335 {
336 case 2:
337 // there may be no path column
338 path[0] = '\0';
339 break;
340
341 case 3:
342 // nothing to do, read everything we wanted
343 break;
344
345 default:
346 // chop '\n'
347 buf[strlen(buf) - 1] = '\0';
348 wxLogDebug(_T("Failed to parse line \"%s\" in /proc/self/maps."),
349 buf);
350 continue;
351 }
352
353 wxString pathNew = wxString::FromAscii(path);
354 if ( pathCur.empty() )
355 {
356 // new module start
357 pathCur = pathNew;
358 startCur = start;
359 endCur = end;
360 }
361 else if ( pathCur == pathNew )
362 {
363 // continuation of the same module
364 wxASSERT_MSG( start == endCur, _T("hole in /proc/self/maps?") );
365 endCur = end;
366 }
367 else // end of the current module
368 {
369 dlls.Add(wxDynamicLibraryDetailsCreator::New(startCur,
370 endCur,
371 pathCur));
372 pathCur.clear();
373 }
374 }
375 }
376 #endif // __LINUX__
377
378 return dlls;
379 }
380
381 #endif // wxUSE_DYNLIB_CLASS
382