diff --git a/build/cmake/setup.cmake b/build/cmake/setup.cmake index 6818972584..297a251f28 100644 --- a/build/cmake/setup.cmake +++ b/build/cmake/setup.cmake @@ -565,6 +565,7 @@ check_symbol_exists(dlopen dlfcn.h HAVE_DLOPEN) cmake_pop_check_state() if(HAVE_DLOPEN) check_symbol_exists(dladdr dlfcn.h HAVE_DLADDR) + check_symbol_exists(dl_iterate_phdr link.h HAVE_DL_ITERATE_PHDR) endif() if(APPLE) diff --git a/configure b/configure index 801ec6a64b..30f90e8582 100755 --- a/configure +++ b/configure @@ -35456,6 +35456,18 @@ fi +fi +done + + + for ac_func in dl_iterate_phdr +do : + ac_fn_c_check_func "$LINENO" "dl_iterate_phdr" "ac_cv_func_dl_iterate_phdr" +if test "x$ac_cv_func_dl_iterate_phdr" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_DL_ITERATE_PHDR 1 +_ACEOF + fi done diff --git a/configure.ac b/configure.ac index ef411b1bf2..d8919d3cc9 100644 --- a/configure.ac +++ b/configure.ac @@ -4839,7 +4839,7 @@ else ]) ]) - dnl check also for dlerror() + dnl check also for some optional functions which we may use if test "$HAVE_DL_FUNCS" = 1; then AC_CHECK_FUNCS(dladdr, AC_DEFINE(HAVE_DLADDR), @@ -4851,6 +4851,8 @@ else ]) ] ) + + AC_CHECK_FUNCS(dl_iterate_phdr) fi fi diff --git a/interface/wx/dynlib.h b/interface/wx/dynlib.h index 43fdf48225..9fc0b12122 100644 --- a/interface/wx/dynlib.h +++ b/interface/wx/dynlib.h @@ -23,6 +23,10 @@ public: /** Retrieves the load address and the size of this module. + Note that under ELF systems (such as Linux) the region defined by the + parameters of this function can be discontinuous and contain multiple + segments belonging to the module with holes between them. + @param addr The pointer to the location to return load address in, may be @NULL. @@ -38,6 +42,8 @@ public: /** Returns the base name of this module, e.g.\ @c "kernel32.dll" or @c "libc-2.3.2.so". + + This name is empty for the main program itself. */ wxString GetName() const; @@ -217,13 +223,20 @@ public: bool IsLoaded() const; /** - This static method returns a wxArray containing the details of all - modules loaded into the address space of the current project. The array - elements are objects of the type: wxDynamicLibraryDetails. The array - will be empty if an error occurred. + This static method returns a vector-like object containing the details + of all modules loaded into the address space of the current project. - This method is currently implemented only under Win32 and Linux and is - useful mostly for diagnostics purposes. + The array elements are objects of the type wxDynamicLibraryDetails. + Under Unix systems they appear in the order in which they libraries + have been loaded, with the module corresponding to the main program + itself coming first. + + The returned array will be empty if an error occurred or if the + function is not implemented for the current platform. + + This method is currently implemented only under Win32 and Unix systems + providing `dl_iterate_phdr()` function (such as Linux) and is useful + mostly for diagnostics purposes. */ static wxDynamicLibraryDetailsArray ListLoaded(); diff --git a/setup.h.in b/setup.h.in index de326beefe..b3bc67dbe9 100644 --- a/setup.h.in +++ b/setup.h.in @@ -935,6 +935,9 @@ /* Define if you have the dladdr function. */ #undef HAVE_DLADDR +/* Define if you have the dl_iterate_phdr function. */ +#undef HAVE_DL_ITERATE_PHDR + /* Define if you have Posix fnctl() function. */ #undef HAVE_FCNTL diff --git a/src/unix/dlunix.cpp b/src/unix/dlunix.cpp index 876738437e..144120504c 100644 --- a/src/unix/dlunix.cpp +++ b/src/unix/dlunix.cpp @@ -33,6 +33,10 @@ #include #endif +#ifdef HAVE_DL_ITERATE_PHDR + #include +#endif + // if some flags are not supported, just ignore them #ifndef RTLD_LAZY #define RTLD_LAZY 0 @@ -125,20 +129,38 @@ void wxDynamicLibrary::ReportError(const wxString& message, // listing loaded modules // ---------------------------------------------------------------------------- +#ifdef HAVE_DL_ITERATE_PHDR + // wxDynamicLibraryDetails declares this class as its friend, so put the code // initializing new details objects here class wxDynamicLibraryDetailsCreator { public: - // create a new wxDynamicLibraryDetails from the given data - static wxDynamicLibraryDetails - New(void *start, void *end, const wxString& path) + static int Callback(dl_phdr_info* info, size_t /* size */, void* data) { + const wxString path = wxString::FromUTF8(info->dlpi_name); + wxDynamicLibraryDetails details; details.m_path = path; details.m_name = path.AfterLast(wxT('/')); - details.m_address = start; - details.m_length = (char *)end - (char *)start; + + // Find the first and last address belonging to this module. + decltype(info->dlpi_addr) start = 0, end = 0; + for ( decltype(info->dlpi_phnum) n = 0; n < info->dlpi_phnum; n++ ) + { + const auto& segment = info->dlpi_phdr[n]; + if ( !segment.p_vaddr || !segment.p_memsz ) + continue; + + if ( !start || segment.p_vaddr < start ) + start = segment.p_vaddr; + + if ( !end || segment.p_vaddr + segment.p_memsz > end ) + end = segment.p_vaddr + segment.p_memsz; + } + + details.m_address = wxUIntToPtr(info->dlpi_addr + start); + details.m_length = end - start; // try to extract the library version from its name const size_t posExt = path.rfind(wxT(".so")); @@ -161,78 +183,24 @@ public: } } - return details; + auto dlls = static_cast(data); + dlls->push_back(std::move(details)); + + // Return 0 to keep iterating. + return 0; } }; +#endif // HAVE_DL_ITERATE_PHDR + /* static */ wxDynamicLibraryDetailsArray wxDynamicLibrary::ListLoaded() { wxDynamicLibraryDetailsArray dlls; -#ifdef __LINUX__ - // examine /proc/self/maps to find out what is loaded in our address space - wxFFile file(wxT("/proc/self/maps")); - if ( file.IsOpened() ) - { - // details of the module currently being parsed - wxString pathCur; - void *startCur = nullptr, - *endCur = nullptr; - - char path[1024]; - char buf[1024]; - while ( fgets(buf, WXSIZEOF(buf), file.fp()) ) - { - // format is: "start-end perm offset maj:min inode path", see proc(5) - void *start, - *end; - switch ( sscanf(buf, "%p-%p %*4s %*p %*02x:%*02x %*d %1023s\n", - &start, &end, path) ) - { - case 2: - // there may be no path column - path[0] = '\0'; - break; - - case 3: - // nothing to do, read everything we wanted - break; - - default: - // chop '\n' - buf[strlen(buf) - 1] = '\0'; - wxLogDebug(wxT("Failed to parse line \"%s\" in /proc/self/maps."), - buf); - continue; - } - - wxASSERT_MSG( start >= endCur, - wxT("overlapping regions in /proc/self/maps?") ); - - wxString pathNew = wxString::FromAscii(path); - if ( pathCur.empty() ) - { - // new module start - pathCur = pathNew; - startCur = start; - endCur = end; - } - else if ( pathCur == pathNew && endCur == end ) - { - // continuation of the same module in the address space - endCur = end; - } - else // end of the current module - { - dlls.Add(wxDynamicLibraryDetailsCreator::New(startCur, - endCur, - pathCur)); - pathCur.clear(); - } - } - } -#endif // __LINUX__ +#ifdef HAVE_DL_ITERATE_PHDR + dl_iterate_phdr(wxDynamicLibraryDetailsCreator::Callback, &dlls); +#endif // HAVE_DL_ITERATE_PHDR return dlls; }