| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file Copyright.txt or https://cmake.org/licensing for details. */ |
| |
| #include "cmBinUtilsLinuxELFLinker.h" |
| |
| #include <sstream> |
| |
| #include <cm/memory> |
| #include <cm/string_view> |
| |
| #include <cmsys/RegularExpression.hxx> |
| |
| #include "cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h" |
| #include "cmELF.h" |
| #include "cmLDConfigLDConfigTool.h" |
| #include "cmMakefile.h" |
| #include "cmMessageType.h" |
| #include "cmRuntimeDependencyArchive.h" |
| #include "cmStringAlgorithms.h" |
| #include "cmSystemTools.h" |
| |
| static std::string ReplaceOrigin(const std::string& rpath, |
| const std::string& origin) |
| { |
| static const cmsys::RegularExpression originRegex( |
| "(\\$ORIGIN)([^a-zA-Z0-9_]|$)"); |
| static const cmsys::RegularExpression originCurlyRegex("\\${ORIGIN}"); |
| |
| cmsys::RegularExpressionMatch match; |
| if (originRegex.find(rpath.c_str(), match)) { |
| cm::string_view pathv(rpath); |
| auto begin = pathv.substr(0, match.start(1)); |
| auto end = pathv.substr(match.end(1)); |
| return cmStrCat(begin, origin, end); |
| } |
| if (originCurlyRegex.find(rpath.c_str(), match)) { |
| cm::string_view pathv(rpath); |
| auto begin = pathv.substr(0, match.start()); |
| auto end = pathv.substr(match.end()); |
| return cmStrCat(begin, origin, end); |
| } |
| return rpath; |
| } |
| |
| cmBinUtilsLinuxELFLinker::cmBinUtilsLinuxELFLinker( |
| cmRuntimeDependencyArchive* archive) |
| : cmBinUtilsLinker(archive) |
| { |
| } |
| |
| bool cmBinUtilsLinuxELFLinker::Prepare() |
| { |
| std::string tool = this->Archive->GetGetRuntimeDependenciesTool(); |
| if (tool.empty()) { |
| tool = "objdump"; |
| } |
| if (tool == "objdump") { |
| this->Tool = |
| cm::make_unique<cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool>( |
| this->Archive); |
| } else { |
| std::ostringstream e; |
| e << "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL: " << tool; |
| this->SetError(e.str()); |
| return false; |
| } |
| |
| std::string ldConfigTool = |
| this->Archive->GetMakefile()->GetSafeDefinition("CMAKE_LDCONFIG_TOOL"); |
| if (ldConfigTool.empty()) { |
| ldConfigTool = "ldconfig"; |
| } |
| if (ldConfigTool == "ldconfig") { |
| this->LDConfigTool = |
| cm::make_unique<cmLDConfigLDConfigTool>(this->Archive); |
| if (!this->LDConfigTool->GetLDConfigPaths(this->LDConfigPaths)) { |
| return false; |
| } |
| } else { |
| std::ostringstream e; |
| e << "Invalid value for CMAKE_LDCONFIG_TOOL: " << ldConfigTool; |
| this->SetError(e.str()); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool cmBinUtilsLinuxELFLinker::ScanDependencies( |
| std::string const& file, cmStateEnums::TargetType /* unused */) |
| { |
| std::vector<std::string> parentRpaths; |
| |
| cmELF elf(file.c_str()); |
| if (!elf) { |
| return false; |
| } |
| if (elf.GetMachine() != 0) { |
| if (this->Machine != 0) { |
| if (elf.GetMachine() != this->Machine) { |
| this->SetError("All files must have the same architecture."); |
| return false; |
| } |
| } else { |
| this->Machine = elf.GetMachine(); |
| } |
| } |
| |
| return this->ScanDependencies(file, parentRpaths); |
| } |
| |
| bool cmBinUtilsLinuxELFLinker::ScanDependencies( |
| std::string const& file, std::vector<std::string> const& parentRpaths) |
| { |
| std::string origin = cmSystemTools::GetFilenamePath(file); |
| std::vector<std::string> needed; |
| std::vector<std::string> rpaths; |
| std::vector<std::string> runpaths; |
| if (!this->Tool->GetFileInfo(file, needed, rpaths, runpaths)) { |
| return false; |
| } |
| for (auto& runpath : runpaths) { |
| runpath = ReplaceOrigin(runpath, origin); |
| } |
| for (auto& rpath : rpaths) { |
| rpath = ReplaceOrigin(rpath, origin); |
| } |
| |
| std::vector<std::string> searchPaths; |
| if (!runpaths.empty()) { |
| searchPaths = runpaths; |
| } else { |
| searchPaths = rpaths; |
| searchPaths.insert(searchPaths.end(), parentRpaths.begin(), |
| parentRpaths.end()); |
| } |
| |
| searchPaths.insert(searchPaths.end(), this->LDConfigPaths.begin(), |
| this->LDConfigPaths.end()); |
| |
| for (auto const& dep : needed) { |
| if (!this->Archive->IsPreExcluded(dep)) { |
| std::string path; |
| bool resolved = false; |
| if (dep.find('/') != std::string::npos) { |
| this->SetError("Paths to dependencies are not supported"); |
| return false; |
| } |
| if (!this->ResolveDependency(dep, searchPaths, path, resolved)) { |
| return false; |
| } |
| if (resolved) { |
| if (!this->Archive->IsPostExcluded(path)) { |
| bool unique; |
| this->Archive->AddResolvedPath(dep, path, unique); |
| if (unique) { |
| std::vector<std::string> combinedParentRpaths = parentRpaths; |
| combinedParentRpaths.insert(combinedParentRpaths.end(), |
| rpaths.begin(), rpaths.end()); |
| if (!this->ScanDependencies(path, combinedParentRpaths)) { |
| return false; |
| } |
| } |
| } |
| } else { |
| this->Archive->AddUnresolvedPath(dep); |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| namespace { |
| bool FileHasArchitecture(const char* filename, std::uint16_t machine) |
| { |
| cmELF elf(filename); |
| if (!elf) { |
| return false; |
| } |
| return machine == 0 || machine == elf.GetMachine(); |
| } |
| } |
| |
| bool cmBinUtilsLinuxELFLinker::ResolveDependency( |
| std::string const& name, std::vector<std::string> const& searchPaths, |
| std::string& path, bool& resolved) |
| { |
| for (auto const& searchPath : searchPaths) { |
| path = cmStrCat(searchPath, '/', name); |
| if (cmSystemTools::PathExists(path) && |
| FileHasArchitecture(path.c_str(), this->Machine)) { |
| resolved = true; |
| return true; |
| } |
| } |
| |
| for (auto const& searchPath : this->Archive->GetSearchDirectories()) { |
| path = cmStrCat(searchPath, '/', name); |
| if (cmSystemTools::PathExists(path) && |
| FileHasArchitecture(path.c_str(), this->Machine)) { |
| std::ostringstream warning; |
| warning << "Dependency " << name << " found in search directory:\n " |
| << searchPath |
| << "\nSee file(GET_RUNTIME_DEPENDENCIES) documentation for " |
| << "more information."; |
| this->Archive->GetMakefile()->IssueMessage(MessageType::WARNING, |
| warning.str()); |
| resolved = true; |
| return true; |
| } |
| } |
| |
| resolved = false; |
| return true; |
| } |