From 853b212107db319b289895e8a7017826982d1da9 Mon Sep 17 00:00:00 2001 From: SCG82 Date: Thu, 18 Mar 2021 04:19:41 -0700 Subject: Resolve @loader_path dependencies. Refine recursive search algorithm. --- src/Dependency.cpp | 72 ++++++++++++------------- src/Dependency.h | 6 +-- src/DylibBundler.cpp | 149 ++++++++++++++++++++++++++++++--------------------- src/DylibBundler.h | 3 +- src/Settings.cpp | 14 ++--- src/Settings.h | 14 ++--- src/Utils.cpp | 16 +++--- src/Utils.h | 8 +-- 8 files changed, 154 insertions(+), 128 deletions(-) diff --git a/src/Dependency.cpp b/src/Dependency.cpp index 46edc32..f67d06d 100644 --- a/src/Dependency.cpp +++ b/src/Dependency.cpp @@ -49,24 +49,21 @@ std::string& rtrim(std::string &s) { return s; } -//the paths to search for dylibs, store it globally to parse the environment variables only once -std::vector paths; - //initialize the dylib search paths void initSearchPaths(){ //Check the same paths the system would search for dylibs std::string searchPaths; char *dyldLibPath = std::getenv("DYLD_LIBRARY_PATH"); - if( dyldLibPath!=0 ) + if (dyldLibPath != nullptr) searchPaths = dyldLibPath; dyldLibPath = std::getenv("DYLD_FALLBACK_FRAMEWORK_PATH"); - if (dyldLibPath != 0) + if (dyldLibPath != nullptr) { if (!searchPaths.empty() && searchPaths[ searchPaths.size()-1 ] != ':') searchPaths += ":"; searchPaths += dyldLibPath; } dyldLibPath = std::getenv("DYLD_FALLBACK_LIBRARY_PATH"); - if (dyldLibPath!=0 ) + if (dyldLibPath != nullptr) { if (!searchPaths.empty() && searchPaths[ searchPaths.size()-1 ] != ':') searchPaths += ":"; searchPaths += dyldLibPath; @@ -78,7 +75,7 @@ void initSearchPaths(){ while(std::getline(ss, item, ':')) { if (item[ item.size()-1 ] != '/') item += "/"; - paths.push_back(item); + Settings::addSearchPath(item); } } } @@ -87,52 +84,56 @@ void initSearchPaths(){ // more stuff will then be necessary to do bool missing_prefixes = false; -Dependency::Dependency(std::string path) +Dependency::Dependency(std::string path, const std::string& dependent_file) { char original_file_buffer[PATH_MAX]; std::string original_file; + rtrim(path); if (isRpath(path)) { - original_file = searchFilenameInRpaths(path); + original_file = searchFilenameInRpaths(path, dependent_file); } - else if (not realpath(rtrim(path).c_str(), original_file_buffer)) + else if (realpath(rtrim(path).c_str(), original_file_buffer)) { - std::cerr << "\n/!\\ WARNING : Cannot resolve path '" << path.c_str() << "'" << std::endl; - original_file = path; + original_file = original_file_buffer; } else { - original_file = original_file_buffer; + std::cerr << "\n/!\\ WARNING : Cannot resolve path '" << path.c_str() << "'" << std::endl; + original_file = path; } // check if given path is a symlink - if (original_file != rtrim(path)) - { - filename = stripPrefix(original_file); - prefix = original_file.substr(0, original_file.rfind("/")+1); - addSymlink(path); - } - else - { - filename = stripPrefix(path); - prefix = path.substr(0, path.rfind("/")+1); - } + if (original_file != path) addSymlink(path); + + filename = stripPrefix(original_file); + prefix = original_file.substr(0, original_file.rfind("/")+1); - //check if the lib is in a known location if( !prefix.empty() && prefix[ prefix.size()-1 ] != '/' ) prefix += "/"; + + // check if this dependency is in /usr/lib, /System/Library, or in ignored list + if (!Settings::isPrefixBundled(prefix)) return; + + // check if the lib is in a known location if( prefix.empty() || !fileExists( prefix+filename ) ) { //the paths contains at least /usr/lib so if it is empty we have not initialized it - if( paths.empty() ) initSearchPaths(); + int searchPathAmount = Settings::searchPathAmount(); + if( searchPathAmount == 0 ) + { + initSearchPaths(); + searchPathAmount = Settings::searchPathAmount(); + } //check if file is contained in one of the paths - for( size_t i=0; i #include #include +#include #include #ifdef __linux #include @@ -40,8 +41,8 @@ THE SOFTWARE. std::vector deps; std::map > deps_per_file; std::map deps_collected; -std::set rpaths; std::map > rpaths_per_file; +std::map rpath_to_fullpath; void changeLibPathsOnFile(std::string file_to_fix) { @@ -96,7 +97,6 @@ void collectRpaths(const std::string& filename) } start_pos += 5; std::string rpath = line.substr(start_pos, end_pos - start_pos); - rpaths.insert(rpath); rpaths_per_file[filename].push_back(rpath); read_rpath = false; continue; @@ -110,42 +110,87 @@ void collectRpaths(const std::string& filename) } } -void collectRpathsForFilename(const std::string& filename) -{ - if (rpaths_per_file.find(filename) == rpaths_per_file.end()) - { - collectRpaths(filename); - } -} - -std::string searchFilenameInRpaths(const std::string& rpath_file) +std::string searchFilenameInRpaths(const std::string& rpath_file, const std::string& dependent_file) { char buffer[PATH_MAX]; std::string fullpath; - std::string suffix = rpath_file.substr(rpath_file.rfind("/")+1); + std::string suffix = rpath_file.substr(rpath_file.rfind('/')+1); + + const auto check_path = [&](std::string path) + { + char buffer[PATH_MAX]; + std::string file_prefix = dependent_file.substr(0, dependent_file.rfind('/')+1); + if (dependent_file != rpath_file) + { + std::string path_to_check; + if (path.find("@loader_path") != std::string::npos) + { + path_to_check = std::regex_replace(path, std::regex("@loader_path/"), file_prefix); + } + else if (path.find("@rpath") != std::string::npos) + { + path_to_check = std::regex_replace(path, std::regex("@rpath/"), file_prefix); + } + if (realpath(path_to_check.c_str(), buffer)) + { + fullpath = buffer; + rpath_to_fullpath[rpath_file] = fullpath; + return true; + } + } + return false; + }; - for (std::set::iterator it = rpaths.begin(); it != rpaths.end(); ++it) + // fullpath previously stored + if (rpath_to_fullpath.find(rpath_file) != rpath_to_fullpath.end()) + { + fullpath = rpath_to_fullpath[rpath_file]; + } + else if (!check_path(rpath_file)) { - std::string path = *it + "/" + suffix; - if (realpath(path.c_str(), buffer)) + for (auto rpath : rpaths_per_file[dependent_file]) { - fullpath = buffer; - break; + if (rpath[rpath.size()-1] != '/') rpath += "/"; + if (check_path(rpath+suffix)) break; + } + if (rpath_to_fullpath.find(rpath_file) != rpath_to_fullpath.end()) + { + fullpath = rpath_to_fullpath[rpath_file]; } } if (fullpath.empty()) { - std::cerr << "\n/!\\ WARNING : can't get path for '" << rpath_file << "'\n"; - fullpath = getUserInputDirForFile(suffix) + suffix; - if (realpath(fullpath.c_str(), buffer)) { - fullpath = buffer; + const int searchPathAmount = Settings::searchPathAmount(); + for (int n=0; n rpaths_to_fix; @@ -167,9 +212,9 @@ void fixRpathsOnFile(const std::string& original_file, const std::string& file_t } } -void addDependency(std::string path, std::string filename) +void addDependency(const std::string& path, const std::string& filename) { - Dependency dep(path); + Dependency dep(path, filename); // we need to check if this library was already added to avoid duplicates bool in_deps = false; @@ -197,7 +242,7 @@ void addDependency(std::string path, std::string filename) /* * Fill vector 'lines' with dependencies of given 'filename' */ -void collectDependencies(std::string filename, std::vector& lines) +void collectDependencies(const std::string& filename, std::vector& lines) { // execute "otool -l" on the given file and collect the command's output std::string cmd = "otool -l \"" + filename + "\""; @@ -237,30 +282,30 @@ void collectDependencies(std::string filename, std::vector& lines) } -void collectDependencies(std::string filename) +void collectDependencies(const std::string& filename) { + if (deps_collected.find(filename) != deps_collected.end()) return; + + collectRpaths(filename); + std::vector lines; collectDependencies(filename, lines); std::cout << "."; fflush(stdout); - - const int line_amount = lines.size(); - for(int n=0; n lines; - std::string original_path = deps[n].getOriginalPath(); - if (isRpath(original_path)) - { - original_path = searchFilenameInRpaths(original_path); - } - collectRpathsForFilename(original_path); - collectDependencies(original_path, lines); - - const int line_amount = lines.size(); - for(int n=0; n -void collectDependencies(std::string filename); +void collectDependencies(const std::string& filename); void collectSubDependencies(); void doneWithDeps_go(); bool isRpath(const std::string& path); +std::string searchFilenameInRpaths(const std::string& rpath_file, const std::string& dependent_file); std::string searchFilenameInRpaths(const std::string& rpath_dep); #endif diff --git a/src/Settings.cpp b/src/Settings.cpp index 50f5221..0ace811 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -48,7 +48,7 @@ void bundleLibs(bool on){ bundleLibs_bool = on; } std::string dest_folder_str = "./libs/"; std::string destFolder(){ return dest_folder_str; } -void destFolder(std::string path) +void destFolder(const std::string& path) { dest_folder_str = path; // fix path if needed so it ends with '/' @@ -56,13 +56,13 @@ void destFolder(std::string path) } std::vector files; -void addFileToFix(std::string path){ files.push_back(path); } +void addFileToFix(const std::string& path){ files.push_back(path); } int fileToFixAmount(){ return files.size(); } std::string fileToFix(const int n){ return files[n]; } std::string inside_path_str = "@executable_path/../libs/"; std::string inside_lib_path(){ return inside_path_str; } -void inside_lib_path(std::string p) +void inside_lib_path(const std::string& p) { inside_path_str = p; // fix path if needed so it ends with '/' @@ -76,7 +76,7 @@ void ignore_prefix(std::string prefix) prefixes_to_ignore.push_back(prefix); } -bool isSystemLibrary(std::string prefix) +bool isSystemLibrary(const std::string& prefix) { if(prefix.find("/usr/lib/") == 0) return true; if(prefix.find("/System/Library/") == 0) return true; @@ -84,7 +84,7 @@ bool isSystemLibrary(std::string prefix) return false; } -bool isPrefixIgnored(std::string prefix) +bool isPrefixIgnored(const std::string& prefix) { const int prefix_amount = prefixes_to_ignore.size(); for(int n=0; n searchPaths; -void addSearchPath(std::string path){ searchPaths.push_back(path); } +void addSearchPath(const std::string& path){ searchPaths.push_back(path); } int searchPathAmount(){ return searchPaths.size(); } std::string searchPath(const int n){ return searchPaths[n]; } diff --git a/src/Settings.h b/src/Settings.h index 512a8f6..1b1cb27 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -30,9 +30,9 @@ THE SOFTWARE. namespace Settings { -bool isSystemLibrary(std::string prefix); -bool isPrefixBundled(std::string prefix); -bool isPrefixIgnored(std::string prefix); +bool isSystemLibrary(const std::string& prefix); +bool isPrefixBundled(const std::string& prefix); +bool isPrefixIgnored(const std::string& prefix); void ignore_prefix(std::string prefix); bool canOverwriteFiles(); @@ -48,16 +48,16 @@ bool bundleLibs(); void bundleLibs(bool on); std::string destFolder(); -void destFolder(std::string path); +void destFolder(const std::string& path); -void addFileToFix(std::string path); +void addFileToFix(const std::string& path); int fileToFixAmount(); std::string fileToFix(const int n); std::string inside_lib_path(); -void inside_lib_path(std::string p); +void inside_lib_path(const std::string& p); -void addSearchPath(std::string path); +void addSearchPath(const std::string& path); int searchPathAmount(); std::string searchPath(const int n); diff --git a/src/Utils.cpp b/src/Utils.cpp index 8651c5f..9903f20 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -69,7 +69,7 @@ void tokenize(const string& str, const char* delim, vector* vectorarg) -bool fileExists( std::string filename ) +bool fileExists(const std::string& filename) { if (access( filename.c_str(), F_OK ) != -1) { @@ -93,10 +93,10 @@ bool fileExists( std::string filename ) } } -void copyFile(string from, string to) +void copyFile(const string& from, const string& to) { bool override = Settings::canOverwriteFiles(); - if(!override) + if( from != to && !override ) { if(fileExists( to )) { @@ -124,7 +124,7 @@ void copyFile(string from, string to) } } -std::string system_get_output(std::string cmd) +std::string system_get_output(const std::string& cmd) { FILE * command_output; char output[128]; @@ -161,7 +161,7 @@ std::string system_get_output(std::string cmd) return full_output; } -int systemp(std::string& cmd) +int systemp(const std::string& cmd) { std::cout << " " << cmd.c_str() << std::endl; return system(cmd.c_str()); @@ -185,9 +185,8 @@ std::string getUserInputDirForFile(const std::string& filename) auto searchPath = Settings::searchPath(n); if( !searchPath.empty() && searchPath[ searchPath.size()-1 ] != '/' ) searchPath += "/"; - if( !fileExists( searchPath+filename ) ) { - continue; - } else { + if( fileExists( searchPath+filename ) ) + { std::cerr << (searchPath+filename) << " was found. /!\\ DYLIBBUNDLER MAY NOT CORRECTLY HANDLE THIS DEPENDENCY: Manually check the executable with 'otool -L'" << std::endl; return searchPath; } @@ -213,6 +212,7 @@ std::string getUserInputDirForFile(const std::string& filename) else { std::cerr << (prefix+filename) << " was found. /!\\ DYLIBBUNDLER MAY NOT CORRECTLY HANDLE THIS DEPENDENCY: Manually check the executable with 'otool -L'" << std::endl; + Settings::addSearchPath(prefix); return prefix; } } diff --git a/src/Utils.h b/src/Utils.h index dc76431..fa1016f 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -32,15 +32,15 @@ THE SOFTWARE. class Library; void tokenize(const std::string& str, const char* delimiters, std::vector*); -bool fileExists( std::string filename ); +bool fileExists(const std::string& filename); -void copyFile(std::string from, std::string to); +void copyFile(const std::string& from, const std::string& to); // executes a command in the native shell and returns output in string -std::string system_get_output(std::string cmd); +std::string system_get_output(const std::string& cmd); // like 'system', runs a command on the system shell, but also prints the command to stdout. -int systemp(std::string& cmd); +int systemp(const std::string& cmd); void changeInstallName(const std::string& binary_file, const std::string& old_name, const std::string& new_name); std::string getUserInputDirForFile(const std::string& filename); -- cgit v1.2.3