From f51dc81c8529ae128c9cd55b6fed25e812aebddb Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 11 Mar 2014 16:04:06 +0000 Subject: [PATCH] Add wxDynamicLibrary::GetModuleFromAddress(). Use dladdr() under Unix, if available, to provide the same functionality as we get from GetModuleHandleEx() under MSW and export it in a new public function. Closes #15248. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@76114 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- configure | 57 ++++++++++++++++++++++++ configure.in | 6 +++ docs/changes.txt | 1 + include/wx/dynlib.h | 6 +++ interface/wx/dynlib.h | 15 +++++++ setup.h.in | 3 ++ setup.h_vms | 3 ++ src/msw/dlmsw.cpp | 101 +++++++++++++++++++++++++++++++----------- src/unix/dlunix.cpp | 20 +++++++++ 9 files changed, 186 insertions(+), 26 deletions(-) diff --git a/configure b/configure index 2cfa921283..1e86b67b37 100755 --- a/configure +++ b/configure @@ -33348,6 +33348,63 @@ fi +fi +done + + for ac_func in dladdr +do : + ac_fn_c_check_func "$LINENO" "dladdr" "ac_cv_func_dladdr" +if test "x$ac_cv_func_dladdr" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_DLADDR 1 +_ACEOF + $as_echo "#define HAVE_DLADDR 1" >>confdefs.h + +else + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dladdr in -ldl" >&5 +$as_echo_n "checking for dladdr in -ldl... " >&6; } +if ${ac_cv_lib_dl_dladdr+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dladdr (); +int +main () +{ +return dladdr (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dl_dladdr=yes +else + ac_cv_lib_dl_dladdr=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dladdr" >&5 +$as_echo "$ac_cv_lib_dl_dladdr" >&6; } +if test "x$ac_cv_lib_dl_dladdr" = xyes; then : + $as_echo "#define HAVE_DLADDR 1" >>confdefs.h + +fi + + + fi done diff --git a/configure.in b/configure.in index 1f6adae4fe..c8d2732531 100644 --- a/configure.in +++ b/configure.in @@ -5442,6 +5442,12 @@ if test "$TOOLKIT" != "MSW" -a "$USE_OS2" != 1; then AC_CHECK_LIB(dl, dlerror, AC_DEFINE(HAVE_DLERROR)) ] ) + AC_CHECK_FUNCS(dladdr, + AC_DEFINE(HAVE_DLADDR), + [ + AC_CHECK_LIB(dl, dladdr, AC_DEFINE(HAVE_DLADDR)) + ] + ) fi fi diff --git a/docs/changes.txt b/docs/changes.txt index 3fecbee9ed..fe1da269a2 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -22,6 +22,7 @@ All: - Allow iterating over wxCmdLineParser arguments in order (Armel Asselin). - Add wxScopedArray ctor taking the number of elements to allocate. +- Add wxDynamicLibrary::GetModuleFromAddress() (Luca Bacci). All (GUI): diff --git a/include/wx/dynlib.h b/include/wx/dynlib.h index 5487ccc949..1a91e07193 100644 --- a/include/wx/dynlib.h +++ b/include/wx/dynlib.h @@ -347,6 +347,12 @@ public: // string on others: static wxString GetPluginsDirectory(); + // Return the load address of the module containing the given address or + // NULL if not found. + // + // If path output parameter is non-NULL, fill it with the full path to this + // module disk file on success. + static void* GetModuleFromAddress(const void* addr, wxString* path = NULL); #ifdef __WINDOWS__ // return the handle (HMODULE/HINSTANCE) of the DLL with the given name diff --git a/interface/wx/dynlib.h b/interface/wx/dynlib.h index f1ab382733..dbe3dc97b5 100644 --- a/interface/wx/dynlib.h +++ b/interface/wx/dynlib.h @@ -195,6 +195,21 @@ public: */ static wxDynamicLibraryDetailsArray ListLoaded(); + /** + Returns the load address of the module containing the specified address + or @NULL if not found. + + If the second argument @a path is not @NULL, it is filled with the full + path to the file the module was loaded from upon a successful return. + + This method is implemented under MSW and Unix platforms providing + `dladdr()` call (which include Linux and various BSD systems) and + always returns @NULL elsewhere. + + @since 3.1.0 + */ + static void* GetModuleFromAddress(const void* addr, wxString* path = NULL); + /** Loads DLL with the given @a name into memory. The @a flags argument can be a combination of the styles outlined in the class description. diff --git a/setup.h.in b/setup.h.in index f9268228e2..6b3b5c97b5 100644 --- a/setup.h.in +++ b/setup.h.in @@ -1071,6 +1071,9 @@ /* Define if you have the dlerror function. */ #undef HAVE_DLERROR +/* Define if you have the dladdr function. */ +#undef HAVE_DLADDR + /* Define if you have Posix fnctl() function. */ #undef HAVE_FCNTL diff --git a/setup.h_vms b/setup.h_vms index 9b142a4f4b..c28644194d 100644 --- a/setup.h_vms +++ b/setup.h_vms @@ -1177,6 +1177,9 @@ typedef pid_t GPid; /* Define if you have the dlerror function. */ #define HAVE_DLERROR 1 +/* Define if you have the dladdr function. */ +#undef HAVE_DLADDR + /* Define if you have Posix fnctl() function. */ #define HAVE_FCNTL 1 diff --git a/src/msw/dlmsw.cpp b/src/msw/dlmsw.cpp index a83e47788e..9bdbae9c13 100644 --- a/src/msw/dlmsw.cpp +++ b/src/msw/dlmsw.cpp @@ -297,6 +297,79 @@ wxDynamicLibraryDetailsArray wxDynamicLibrary::ListLoaded() return dlls; } +// ---------------------------------------------------------------------------- +// Getting the module from an address inside it +// ---------------------------------------------------------------------------- + +namespace +{ + +// Tries to dynamically load GetModuleHandleEx() from kernel32.dll and call it +// to get the module handle from the given address. Returns NULL if it fails to +// either resolve the function (which can only happen on pre-Vista systems +// normally) or if the function itself failed. +HMODULE CallGetModuleHandleEx(const void* addr) +{ + typedef BOOL (WINAPI *GetModuleHandleEx_t)(DWORD, LPCTSTR, HMODULE *); + static const GetModuleHandleEx_t INVALID_FUNC_PTR = (GetModuleHandleEx_t)-1; + + static GetModuleHandleEx_t s_pfnGetModuleHandleEx = INVALID_FUNC_PTR; + if ( s_pfnGetModuleHandleEx == INVALID_FUNC_PTR ) + { + wxDynamicLibrary dll(wxT("kernel32.dll"), wxDL_VERBATIM); + + wxDL_INIT_FUNC_AW(s_pfn, GetModuleHandleEx, dll); + + // dll object can be destroyed, kernel32.dll won't be unloaded anyhow + } + + if ( !s_pfnGetModuleHandleEx ) + return NULL; + + // flags are GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | + // GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS + HMODULE hmod; + if ( !s_pfnGetModuleHandleEx(6, (LPCTSTR)addr, &hmod) ) + return NULL; + + return hmod; +} + +} // anonymous namespace + +/* static */ +void* wxDynamicLibrary::GetModuleFromAddress(const void* addr, wxString* path) +{ + HMODULE hmod = CallGetModuleHandleEx(addr); + if ( !hmod ) + { + wxLogLastError(wxT("GetModuleHandleEx")); + return NULL; + } + + if ( path ) + { + TCHAR libname[MAX_PATH]; + if ( !::GetModuleFileName(hmod, libname, MAX_PATH) ) + { + // GetModuleFileName could also return extended-length paths (paths + // prepended with "//?/", maximum length is 32767 charachters) so, + // in principle, MAX_PATH could be unsufficient and we should try + // increasing the buffer size here. + wxLogLastError(wxT("GetModuleFromAddress")); + return NULL; + } + + libname[MAX_PATH-1] = wxT('\0'); + + *path = libname; + } + + // In Windows HMODULE is actually the base address of the module so we + // can just cast it to the address. + return reinterpret_cast(hmod); +} + /* static */ WXHMODULE wxDynamicLibrary::MSWGetModuleHandle(const wxString& name, void *addr) { @@ -304,33 +377,9 @@ WXHMODULE wxDynamicLibrary::MSWGetModuleHandle(const wxString& name, void *addr) // because the former works correctly for comctl32.dll while the latter // returns NULL when comctl32.dll version 6 is used under XP (note that // GetModuleHandleEx() is only available under XP and later, coincidence?) + HMODULE hmod = CallGetModuleHandleEx(addr); - // check if we can use GetModuleHandleEx - typedef BOOL (WINAPI *GetModuleHandleEx_t)(DWORD, LPCTSTR, HMODULE *); - - static const GetModuleHandleEx_t INVALID_FUNC_PTR = (GetModuleHandleEx_t)-1; - - static GetModuleHandleEx_t s_pfnGetModuleHandleEx = INVALID_FUNC_PTR; - if ( s_pfnGetModuleHandleEx == INVALID_FUNC_PTR ) - { - wxDynamicLibrary dll(wxT("kernel32.dll"), wxDL_VERBATIM); - s_pfnGetModuleHandleEx = - (GetModuleHandleEx_t)dll.GetSymbolAorW(wxT("GetModuleHandleEx")); - - // dll object can be destroyed, kernel32.dll won't be unloaded anyhow - } - - // get module handle from its address - if ( s_pfnGetModuleHandleEx ) - { - // flags are GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | - // GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS - HMODULE hmod; - if ( s_pfnGetModuleHandleEx(6, (LPCTSTR)addr, &hmod) && hmod ) - return hmod; - } - - return ::GetModuleHandle(name.t_str()); + return hmod ? hmod : ::GetModuleHandle(name.t_str()); } #endif // wxUSE_DYNLIB_CLASS diff --git a/src/unix/dlunix.cpp b/src/unix/dlunix.cpp index 74c2277321..c51aedf92c 100644 --- a/src/unix/dlunix.cpp +++ b/src/unix/dlunix.cpp @@ -280,5 +280,25 @@ wxDynamicLibraryDetailsArray wxDynamicLibrary::ListLoaded() return dlls; } + +/* static */ +void* wxDynamicLibrary::GetModuleFromAddress(const void* addr, wxString* path) +{ +#ifdef HAVE_DLADDR + Dl_info di = { 0 }; + + if ( dladdr(addr, &di) == 0 ) + return NULL; + + if ( path ) + *path = di.dli_fname; + + return di.dli_fbase; +#endif // HAVE_DLADDR + + return NULL; +} + + #endif // wxUSE_DYNLIB_CLASS