| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmBinUtilsLinuxELFLinker.h" | |
| #include <queue> | |
| #include <unordered_set> | |
| #include <utility> | |
| #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(std::string const& rpath, | |
| std::string const& origin) | |
| { | |
| static cmsys::RegularExpression const originRegex( | |
| "(\\$ORIGIN)([^a-zA-Z0-9_]|$)"); | |
| static cmsys::RegularExpression const 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 { | |
| this->SetError(cmStrCat( | |
| "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL: ", tool)); | |
| 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 { | |
| this->SetError( | |
| cmStrCat("Invalid value for CMAKE_LDCONFIG_TOOL: ", ldConfigTool)); | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool cmBinUtilsLinuxELFLinker::ScanDependencies(std::string const& file, | |
| cm::TargetType /* unused */) | |
| { | |
| 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); | |
| } | |
| bool cmBinUtilsLinuxELFLinker::ScanDependencies(std::string const& mainFile) | |
| { | |
| std::unordered_set<std::string> resolvedDependencies; | |
| std::queue<std::pair<std::string, std::vector<std::string>>> queueToResolve; | |
| queueToResolve.push(std::make_pair(mainFile, std::vector<std::string>{})); | |
| while (!queueToResolve.empty()) { | |
| std::string file = std::move(queueToResolve.front().first); | |
| std::vector<std::string> parentRpaths = | |
| std::move(queueToResolve.front().second); | |
| queueToResolve.pop(); | |
| 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 (resolvedDependencies.count(dep) != 0 || | |
| this->Archive->IsPreExcluded(dep)) { | |
| continue; | |
| } | |
| 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) { | |
| resolvedDependencies.emplace(dep); | |
| 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()); | |
| queueToResolve.push(std::make_pair(path, combinedParentRpaths)); | |
| } | |
| } | |
| } else { | |
| this->Archive->AddUnresolvedPath(dep); | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| namespace { | |
| bool FileHasArchitecture(char const* 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)) { | |
| this->NormalizePath(path); | |
| 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)) { | |
| this->Archive->GetMakefile()->IssueMessage( | |
| MessageType::WARNING, | |
| cmStrCat("Dependency ", name, " found in search directory:\n ", | |
| searchPath, | |
| "\nSee file(GET_RUNTIME_DEPENDENCIES) documentation for more " | |
| "information.")); | |
| resolved = true; | |
| return true; | |
| } | |
| } | |
| resolved = false; | |
| return true; | |
| } |