blob: e2a8f9210394814a1f6209a4858e23f8d5da99d6 [file] [log] [blame]
/* 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;
}