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