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