#include "drake/common/find_loaded_library.h" #include #include "drake/common/drake_throw.h" #ifdef __APPLE__ #include #include #include #else // Not __APPLE__ #include // dirname #include #include #include #include // PATH_MAX #endif using std::string; namespace drake { #ifdef __APPLE__ // This code has been adapted from: // https://stackoverflow.com/questions/4309117/determining-programmatically-what-modules-are-loaded-in-another-process-os-x/23229148#23229148 namespace { // Reads memory from MacOS specific structures into an `unsigned char*`. unsigned char * ReadProcessMemory(mach_vm_address_t addr, mach_msg_type_number_t* size) { vm_offset_t readMem; kern_return_t kr = vm_read(mach_task_self(), addr, *size, &readMem, size); if (kr != KERN_SUCCESS) { return NULL; } return (reinterpret_cast(readMem)); } } // namespace // Gets the list of all the dynamic libraries that have been loaded. Finds // `library_name` in the list, and returns its absolute directory path. // This function is specific to MacOS std::optional LoadedLibraryPath(const string& library_name) { task_dyld_info dyld_info; mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; // Getinformation from current process. if (task_info(mach_task_self(), TASK_DYLD_INFO, reinterpret_cast(&dyld_info), &count) == KERN_SUCCESS) { // Recover list of dynamic libraries. mach_msg_type_number_t size = sizeof(dyld_all_image_infos); unsigned char* data = ReadProcessMemory(dyld_info.all_image_info_addr, &size); if (!data) { return std::nullopt; } dyld_all_image_infos* infos = reinterpret_cast(data); // Recover number of dynamic libraries in list. mach_msg_type_number_t size2 = sizeof(dyld_image_info) * infos->infoArrayCount; unsigned char* info_addr = ReadProcessMemory( reinterpret_cast(infos->infoArray), &size2); if (!info_addr) { return std::nullopt; } dyld_image_info* info = reinterpret_cast(info_addr); // Loop over the dynamic libraries until `library_name` is found. for (uint32_t i=0; i < infos->infoArrayCount; i++) { const char * pos_slash = strrchr(info[i].imageFilePath, '/'); if (!strcmp(pos_slash + 1, library_name.c_str())) { // Path is always absolute on MacOS. return string(info[i].imageFilePath, pos_slash - info[i].imageFilePath); } } } return std::nullopt; } #else // Not __APPLE__ // Gets the list of all the shared objects that have been loaded. Finds // `library_name` in the list, and returns its absolute directory path. // This function is specific to Linux. std::optional LoadedLibraryPath(const std::string& library_name) { void* handle = dlopen(NULL, RTLD_NOW); link_map *map; dlinfo(handle, RTLD_DI_LINKMAP, &map); // Loop over loaded shared objects until `library_name` is found. while (map) { // Avoid using `basename()` and `dirname()` implemented in `libgen.h` // because as stated in `libgen.h` documentation [1], both functions // may modify the content of the input c-string (which happened in the // original implementation and resulted in a bug). // [1] http://man7.org/linux/man-pages/man3/basename.3.html#DESCRIPTION const char* pos_slash = strrchr(map->l_name, '/'); if (pos_slash && !strcmp(pos_slash + 1, library_name.c_str())) { // Check if path is relative. If so, make it absolute. if (map->l_name[0] != '/') { std::string argv0 = std::filesystem::read_symlink({ "/proc/self/exe"}).string(); return string(dirname(&argv0[0])) + "/" + string(map->l_name, pos_slash - map->l_name); } else { return string(map->l_name, pos_slash - map->l_name); } } map = map->l_next; } return std::nullopt; } #endif } // namespace drake