/* 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 #include #include #include #include "cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h" #include "cmLDConfigLDConfigTool.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmRuntimeDependencyArchive.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #ifdef CMake_USE_ELF_PARSER # include "cmELF.h" #endif 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( 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(this->Archive); } 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 parentRpaths; #ifdef CMake_USE_ELF_PARSER 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(); } } #endif return this->ScanDependencies(file, parentRpaths); } bool cmBinUtilsLinuxELFLinker::ScanDependencies( std::string const& file, std::vector const& parentRpaths) { std::string origin = cmSystemTools::GetFilenamePath(file); std::vector needed; std::vector rpaths; std::vector 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 searchPaths; if (!runpaths.empty()) { searchPaths = runpaths; } else { searchPaths = rpaths; searchPaths.insert(searchPaths.end(), parentRpaths.begin(), parentRpaths.end()); } std::vector ldConfigPaths; if (!this->LDConfigTool->GetLDConfigPaths(ldConfigPaths)) { return false; } searchPaths.insert(searchPaths.end(), ldConfigPaths.begin(), 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 && !this->ScanDependencies(path, rpaths)) { return false; } } } else { this->Archive->AddUnresolvedPath(dep); } } } return true; } namespace { bool FileHasArchitecture(const char* filename, std::uint16_t machine) { #ifdef CMake_USE_ELF_PARSER cmELF elf(filename); if (!elf) { return false; } return machine == 0 || machine == elf.GetMachine(); #else return true; #endif } } bool cmBinUtilsLinuxELFLinker::ResolveDependency( std::string const& name, std::vector 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; }