Coverage Report

Created: 2026-10-28 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmBinUtilsLinuxELFLinker.cxx
Line
Count
Source
1
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2
   file LICENSE.rst or https://cmake.org/licensing for details.  */
3
4
#include "cmBinUtilsLinuxELFLinker.h"
5
6
#include <queue>
7
#include <unordered_set>
8
#include <utility>
9
10
#include <cm/memory>
11
#include <cm/string_view>
12
13
#include <cmsys/RegularExpression.hxx>
14
15
#include "cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h"
16
#include "cmELF.h"
17
#include "cmLDConfigLDConfigTool.h"
18
#include "cmMakefile.h"
19
#include "cmMessageType.h"
20
#include "cmRuntimeDependencyArchive.h"
21
#include "cmStringAlgorithms.h"
22
#include "cmSystemTools.h"
23
24
static std::string ReplaceOrigin(std::string const& rpath,
25
                                 std::string const& origin)
26
0
{
27
0
  static cmsys::RegularExpression const originRegex(
28
0
    "(\\$ORIGIN)([^a-zA-Z0-9_]|$)");
29
0
  static cmsys::RegularExpression const originCurlyRegex("\\${ORIGIN}");
30
31
0
  cmsys::RegularExpressionMatch match;
32
0
  if (originRegex.find(rpath.c_str(), match)) {
33
0
    cm::string_view pathv(rpath);
34
0
    auto begin = pathv.substr(0, match.start(1));
35
0
    auto end = pathv.substr(match.end(1));
36
0
    return cmStrCat(begin, origin, end);
37
0
  }
38
0
  if (originCurlyRegex.find(rpath.c_str(), match)) {
39
0
    cm::string_view pathv(rpath);
40
0
    auto begin = pathv.substr(0, match.start());
41
0
    auto end = pathv.substr(match.end());
42
0
    return cmStrCat(begin, origin, end);
43
0
  }
44
0
  return rpath;
45
0
}
46
47
cmBinUtilsLinuxELFLinker::cmBinUtilsLinuxELFLinker(
48
  cmRuntimeDependencyArchive* archive)
49
0
  : cmBinUtilsLinker(archive)
50
0
{
51
0
}
52
53
bool cmBinUtilsLinuxELFLinker::Prepare()
54
0
{
55
0
  std::string tool = this->Archive->GetGetRuntimeDependenciesTool();
56
0
  if (tool.empty()) {
57
0
    tool = "objdump";
58
0
  }
59
0
  if (tool == "objdump") {
60
0
    this->Tool =
61
0
      cm::make_unique<cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool>(
62
0
        this->Archive);
63
0
  } else {
64
0
    this->SetError(cmStrCat(
65
0
      "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL: ", tool));
66
0
    return false;
67
0
  }
68
69
0
  std::string ldConfigTool =
70
0
    this->Archive->GetMakefile()->GetSafeDefinition("CMAKE_LDCONFIG_TOOL");
71
0
  if (ldConfigTool.empty()) {
72
0
    ldConfigTool = "ldconfig";
73
0
  }
74
0
  if (ldConfigTool == "ldconfig") {
75
0
    this->LDConfigTool =
76
0
      cm::make_unique<cmLDConfigLDConfigTool>(this->Archive);
77
0
    if (!this->LDConfigTool->GetLDConfigPaths(this->LDConfigPaths)) {
78
0
      return false;
79
0
    }
80
0
  } else {
81
0
    this->SetError(
82
0
      cmStrCat("Invalid value for CMAKE_LDCONFIG_TOOL: ", ldConfigTool));
83
0
    return false;
84
0
  }
85
86
0
  return true;
87
0
}
88
89
bool cmBinUtilsLinuxELFLinker::ScanDependencies(std::string const& file,
90
                                                cm::TargetType /* unused */)
91
0
{
92
0
  cmELF elf(file.c_str());
93
0
  if (!elf) {
94
0
    return false;
95
0
  }
96
0
  if (elf.GetMachine() != 0) {
97
0
    if (this->Machine != 0) {
98
0
      if (elf.GetMachine() != this->Machine) {
99
0
        this->SetError("All files must have the same architecture.");
100
0
        return false;
101
0
      }
102
0
    } else {
103
0
      this->Machine = elf.GetMachine();
104
0
    }
105
0
  }
106
107
0
  return this->ScanDependencies(file);
108
0
}
109
110
bool cmBinUtilsLinuxELFLinker::ScanDependencies(std::string const& mainFile)
111
0
{
112
0
  std::unordered_set<std::string> resolvedDependencies;
113
0
  std::queue<std::pair<std::string, std::vector<std::string>>> queueToResolve;
114
0
  queueToResolve.push(std::make_pair(mainFile, std::vector<std::string>{}));
115
116
0
  while (!queueToResolve.empty()) {
117
0
    std::string file = std::move(queueToResolve.front().first);
118
0
    std::vector<std::string> parentRpaths =
119
0
      std::move(queueToResolve.front().second);
120
0
    queueToResolve.pop();
121
122
0
    std::string origin = cmSystemTools::GetFilenamePath(file);
123
0
    std::vector<std::string> needed;
124
0
    std::vector<std::string> rpaths;
125
0
    std::vector<std::string> runpaths;
126
0
    if (!this->Tool->GetFileInfo(file, needed, rpaths, runpaths)) {
127
0
      return false;
128
0
    }
129
0
    for (auto& runpath : runpaths) {
130
0
      runpath = ReplaceOrigin(runpath, origin);
131
0
    }
132
0
    for (auto& rpath : rpaths) {
133
0
      rpath = ReplaceOrigin(rpath, origin);
134
0
    }
135
136
0
    std::vector<std::string> searchPaths;
137
0
    if (!runpaths.empty()) {
138
0
      searchPaths = runpaths;
139
0
    } else {
140
0
      searchPaths = rpaths;
141
0
      searchPaths.insert(searchPaths.end(), parentRpaths.begin(),
142
0
                         parentRpaths.end());
143
0
    }
144
145
0
    searchPaths.insert(searchPaths.end(), this->LDConfigPaths.begin(),
146
0
                       this->LDConfigPaths.end());
147
148
0
    for (auto const& dep : needed) {
149
0
      if (resolvedDependencies.count(dep) != 0 ||
150
0
          this->Archive->IsPreExcluded(dep)) {
151
0
        continue;
152
0
      }
153
154
0
      std::string path;
155
0
      bool resolved = false;
156
0
      if (dep.find('/') != std::string::npos) {
157
0
        this->SetError("Paths to dependencies are not supported");
158
0
        return false;
159
0
      }
160
0
      if (!this->ResolveDependency(dep, searchPaths, path, resolved)) {
161
0
        return false;
162
0
      }
163
0
      if (resolved) {
164
0
        resolvedDependencies.emplace(dep);
165
0
        if (!this->Archive->IsPostExcluded(path)) {
166
0
          bool unique;
167
0
          this->Archive->AddResolvedPath(dep, path, unique);
168
0
          if (unique) {
169
0
            std::vector<std::string> combinedParentRpaths = parentRpaths;
170
0
            combinedParentRpaths.insert(combinedParentRpaths.end(),
171
0
                                        rpaths.begin(), rpaths.end());
172
173
0
            queueToResolve.push(std::make_pair(path, combinedParentRpaths));
174
0
          }
175
0
        }
176
0
      } else {
177
0
        this->Archive->AddUnresolvedPath(dep);
178
0
      }
179
0
    }
180
0
  }
181
182
0
  return true;
183
0
}
184
185
namespace {
186
bool FileHasArchitecture(char const* filename, std::uint16_t machine)
187
0
{
188
0
  cmELF elf(filename);
189
0
  if (!elf) {
190
0
    return false;
191
0
  }
192
0
  return machine == 0 || machine == elf.GetMachine();
193
0
}
194
}
195
196
bool cmBinUtilsLinuxELFLinker::ResolveDependency(
197
  std::string const& name, std::vector<std::string> const& searchPaths,
198
  std::string& path, bool& resolved)
199
0
{
200
0
  for (auto const& searchPath : searchPaths) {
201
0
    path = cmStrCat(searchPath, '/', name);
202
0
    if (cmSystemTools::PathExists(path) &&
203
0
        FileHasArchitecture(path.c_str(), this->Machine)) {
204
0
      this->NormalizePath(path);
205
0
      resolved = true;
206
0
      return true;
207
0
    }
208
0
  }
209
210
0
  for (auto const& searchPath : this->Archive->GetSearchDirectories()) {
211
0
    path = cmStrCat(searchPath, '/', name);
212
0
    if (cmSystemTools::PathExists(path) &&
213
0
        FileHasArchitecture(path.c_str(), this->Machine)) {
214
0
      this->Archive->GetMakefile()->IssueMessage(
215
0
        MessageType::WARNING,
216
0
        cmStrCat("Dependency ", name, " found in search directory:\n  ",
217
0
                 searchPath,
218
0
                 "\nSee file(GET_RUNTIME_DEPENDENCIES) documentation for more "
219
0
                 "information."));
220
0
      resolved = true;
221
0
      return true;
222
0
    }
223
0
  }
224
225
0
  resolved = false;
226
0
  return true;
227
0
}