1 //===-- HostInfoBase.cpp ----------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/Host/Config.h"
10 
11 #include "lldb/Host/FileSystem.h"
12 #include "lldb/Host/Host.h"
13 #include "lldb/Host/HostInfo.h"
14 #include "lldb/Host/HostInfoBase.h"
15 #include "lldb/Utility/ArchSpec.h"
16 #include "lldb/Utility/Log.h"
17 #include "lldb/Utility/StreamString.h"
18 
19 #include "llvm/ADT/StringExtras.h"
20 #include "llvm/ADT/Triple.h"
21 #include "llvm/Support/Host.h"
22 #include "llvm/Support/Path.h"
23 #include "llvm/Support/ScopedPrinter.h"
24 #include "llvm/Support/Threading.h"
25 #include "llvm/Support/raw_ostream.h"
26 
27 #include <mutex>
28 #include <thread>
29 
30 using namespace lldb;
31 using namespace lldb_private;
32 
33 namespace {
34 //----------------------------------------------------------------------
35 // The HostInfoBaseFields is a work around for windows not supporting static
36 // variables correctly in a thread safe way. Really each of the variables in
37 // HostInfoBaseFields should live in the functions in which they are used and
38 // each one should be static, but the work around is in place to avoid this
39 // restriction. Ick.
40 //----------------------------------------------------------------------
41 
42 struct HostInfoBaseFields {
43   ~HostInfoBaseFields() {
44     if (FileSystem::Instance().Exists(m_lldb_process_tmp_dir)) {
45       // Remove the LLDB temporary directory if we have one. Set "recurse" to
46       // true to all files that were created for the LLDB process can be
47       // cleaned up.
48       llvm::sys::fs::remove_directories(m_lldb_process_tmp_dir.GetPath());
49     }
50   }
51 
52   std::string m_host_triple;
53 
54   ArchSpec m_host_arch_32;
55   ArchSpec m_host_arch_64;
56 
57   FileSpec m_lldb_so_dir;
58   FileSpec m_lldb_support_exe_dir;
59   FileSpec m_lldb_headers_dir;
60   FileSpec m_lldb_clang_resource_dir;
61   FileSpec m_lldb_system_plugin_dir;
62   FileSpec m_lldb_user_plugin_dir;
63   FileSpec m_lldb_process_tmp_dir;
64   FileSpec m_lldb_global_tmp_dir;
65 };
66 
67 HostInfoBaseFields *g_fields = nullptr;
68 }
69 
70 void HostInfoBase::Initialize() { g_fields = new HostInfoBaseFields(); }
71 
72 void HostInfoBase::Terminate() {
73   delete g_fields;
74   g_fields = nullptr;
75 }
76 
77 llvm::StringRef HostInfoBase::GetTargetTriple() {
78   static llvm::once_flag g_once_flag;
79   llvm::call_once(g_once_flag, []() {
80     g_fields->m_host_triple =
81         HostInfo::GetArchitecture().GetTriple().getTriple();
82   });
83   return g_fields->m_host_triple;
84 }
85 
86 const ArchSpec &HostInfoBase::GetArchitecture(ArchitectureKind arch_kind) {
87   static llvm::once_flag g_once_flag;
88   llvm::call_once(g_once_flag, []() {
89     HostInfo::ComputeHostArchitectureSupport(g_fields->m_host_arch_32,
90                                              g_fields->m_host_arch_64);
91   });
92 
93   // If an explicit 32 or 64-bit architecture was requested, return that.
94   if (arch_kind == eArchKind32)
95     return g_fields->m_host_arch_32;
96   if (arch_kind == eArchKind64)
97     return g_fields->m_host_arch_64;
98 
99   // Otherwise prefer the 64-bit architecture if it is valid.
100   return (g_fields->m_host_arch_64.IsValid()) ? g_fields->m_host_arch_64
101                                               : g_fields->m_host_arch_32;
102 }
103 
104 llvm::Optional<HostInfoBase::ArchitectureKind> HostInfoBase::ParseArchitectureKind(llvm::StringRef kind) {
105   return llvm::StringSwitch<llvm::Optional<ArchitectureKind>>(kind)
106       .Case(LLDB_ARCH_DEFAULT, eArchKindDefault)
107       .Case(LLDB_ARCH_DEFAULT_32BIT, eArchKind32)
108       .Case(LLDB_ARCH_DEFAULT_64BIT, eArchKind64)
109       .Default(llvm::None);
110 }
111 
112 FileSpec HostInfoBase::GetShlibDir() {
113   static llvm::once_flag g_once_flag;
114   static bool success = false;
115   llvm::call_once(g_once_flag, []() {
116     success = HostInfo::ComputeSharedLibraryDirectory(g_fields->m_lldb_so_dir);
117     Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
118     LLDB_LOG(log, "shlib dir -> `{0}`", g_fields->m_lldb_so_dir);
119   });
120   return success ? g_fields->m_lldb_so_dir : FileSpec();
121 }
122 
123 FileSpec HostInfoBase::GetSupportExeDir() {
124   static llvm::once_flag g_once_flag;
125   static bool success = false;
126   llvm::call_once(g_once_flag, []() {
127     success =
128         HostInfo::ComputeSupportExeDirectory(g_fields->m_lldb_support_exe_dir);
129     Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
130     LLDB_LOG(log, "support exe dir -> `{0}`", g_fields->m_lldb_support_exe_dir);
131   });
132   return success ? g_fields->m_lldb_support_exe_dir : FileSpec();
133 }
134 
135 FileSpec HostInfoBase::GetHeaderDir() {
136   static llvm::once_flag g_once_flag;
137   static bool success = false;
138   llvm::call_once(g_once_flag, []() {
139     success = HostInfo::ComputeHeaderDirectory(g_fields->m_lldb_headers_dir);
140     Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
141     LLDB_LOG(log, "header dir -> `{0}`", g_fields->m_lldb_headers_dir);
142   });
143   return success ? g_fields->m_lldb_headers_dir : FileSpec();
144 }
145 
146 FileSpec HostInfoBase::GetSystemPluginDir() {
147   static llvm::once_flag g_once_flag;
148   static bool success = false;
149   llvm::call_once(g_once_flag, []() {
150     success = HostInfo::ComputeSystemPluginsDirectory(
151         g_fields->m_lldb_system_plugin_dir);
152     Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
153     LLDB_LOG(log, "system plugin dir -> `{0}`",
154              g_fields->m_lldb_system_plugin_dir);
155   });
156   return success ? g_fields->m_lldb_system_plugin_dir : FileSpec();
157 }
158 
159 FileSpec HostInfoBase::GetUserPluginDir() {
160   static llvm::once_flag g_once_flag;
161   static bool success = false;
162   llvm::call_once(g_once_flag, []() {
163     success =
164         HostInfo::ComputeUserPluginsDirectory(g_fields->m_lldb_user_plugin_dir);
165     Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
166     LLDB_LOG(log, "user plugin dir -> `{0}`", g_fields->m_lldb_user_plugin_dir);
167   });
168   return success ? g_fields->m_lldb_user_plugin_dir : FileSpec();
169 }
170 
171 FileSpec HostInfoBase::GetProcessTempDir() {
172   static llvm::once_flag g_once_flag;
173   static bool success = false;
174   llvm::call_once(g_once_flag, []() {
175     success = HostInfo::ComputeProcessTempFileDirectory(
176         g_fields->m_lldb_process_tmp_dir);
177     Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
178     LLDB_LOG(log, "process temp dir -> `{0}`",
179              g_fields->m_lldb_process_tmp_dir);
180   });
181   return success ? g_fields->m_lldb_process_tmp_dir : FileSpec();
182 }
183 
184 FileSpec HostInfoBase::GetGlobalTempDir() {
185   static llvm::once_flag g_once_flag;
186   static bool success = false;
187   llvm::call_once(g_once_flag, []() {
188     success = HostInfo::ComputeGlobalTempFileDirectory(
189         g_fields->m_lldb_global_tmp_dir);
190     Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
191     LLDB_LOG(log, "global temp dir -> `{0}`", g_fields->m_lldb_global_tmp_dir);
192   });
193   return success ? g_fields->m_lldb_global_tmp_dir : FileSpec();
194 }
195 
196 ArchSpec HostInfoBase::GetAugmentedArchSpec(llvm::StringRef triple) {
197   if (triple.empty())
198     return ArchSpec();
199   llvm::Triple normalized_triple(llvm::Triple::normalize(triple));
200   if (!ArchSpec::ContainsOnlyArch(normalized_triple))
201     return ArchSpec(triple);
202 
203   if (auto kind = HostInfo::ParseArchitectureKind(triple))
204     return HostInfo::GetArchitecture(*kind);
205 
206   llvm::Triple host_triple(llvm::sys::getDefaultTargetTriple());
207 
208   if (normalized_triple.getVendorName().empty())
209     normalized_triple.setVendor(host_triple.getVendor());
210   if (normalized_triple.getOSName().empty())
211     normalized_triple.setOS(host_triple.getOS());
212   if (normalized_triple.getEnvironmentName().empty())
213     normalized_triple.setEnvironment(host_triple.getEnvironment());
214   return ArchSpec(normalized_triple);
215 }
216 
217 bool HostInfoBase::ComputePathRelativeToLibrary(FileSpec &file_spec,
218                                                 llvm::StringRef dir) {
219   Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
220 
221   FileSpec lldb_file_spec = GetShlibDir();
222   if (!lldb_file_spec)
223     return false;
224 
225   std::string raw_path = lldb_file_spec.GetPath();
226   if (log)
227     log->Printf("HostInfo::%s() attempting to "
228                 "derive the path %s relative to liblldb install path: %s",
229                 __FUNCTION__, dir.data(), raw_path.c_str());
230 
231   // Drop bin (windows) or lib
232   llvm::StringRef parent_path = llvm::sys::path::parent_path(raw_path);
233   if (parent_path.empty()) {
234     if (log)
235       log->Printf("HostInfo::%s() failed to find liblldb within the shared "
236                   "lib path",
237                   __FUNCTION__);
238     return false;
239   }
240 
241   raw_path = (parent_path + dir).str();
242   if (log)
243     log->Printf("HostInfo::%s() derived the path as: %s", __FUNCTION__,
244                 raw_path.c_str());
245   file_spec.GetDirectory().SetString(raw_path);
246   return (bool)file_spec.GetDirectory();
247 }
248 
249 bool HostInfoBase::ComputeSharedLibraryDirectory(FileSpec &file_spec) {
250   // To get paths related to LLDB we get the path to the executable that
251   // contains this function. On MacOSX this will be "LLDB.framework/.../LLDB".
252   // On other posix systems, we will get .../lib(64|32)?/liblldb.so.
253 
254   FileSpec lldb_file_spec(Host::GetModuleFileSpecForHostAddress(
255       reinterpret_cast<void *>(reinterpret_cast<intptr_t>(
256           HostInfoBase::ComputeSharedLibraryDirectory))));
257 
258   // This is necessary because when running the testsuite the shlib might be a
259   // symbolic link inside the Python resource dir.
260   FileSystem::Instance().ResolveSymbolicLink(lldb_file_spec, lldb_file_spec);
261 
262   // Remove the filename so that this FileSpec only represents the directory.
263   file_spec.GetDirectory() = lldb_file_spec.GetDirectory();
264 
265   return (bool)file_spec.GetDirectory();
266 }
267 
268 bool HostInfoBase::ComputeSupportExeDirectory(FileSpec &file_spec) {
269   file_spec = GetShlibDir();
270   return bool(file_spec);
271 }
272 
273 bool HostInfoBase::ComputeProcessTempFileDirectory(FileSpec &file_spec) {
274   FileSpec temp_file_spec;
275   if (!HostInfo::ComputeGlobalTempFileDirectory(temp_file_spec))
276     return false;
277 
278   std::string pid_str{llvm::to_string(Host::GetCurrentProcessID())};
279   temp_file_spec.AppendPathComponent(pid_str);
280   if (llvm::sys::fs::create_directory(temp_file_spec.GetPath()))
281     return false;
282 
283   file_spec.GetDirectory().SetCString(temp_file_spec.GetCString());
284   return true;
285 }
286 
287 bool HostInfoBase::ComputeTempFileBaseDirectory(FileSpec &file_spec) {
288   llvm::SmallVector<char, 16> tmpdir;
289   llvm::sys::path::system_temp_directory(/*ErasedOnReboot*/ true, tmpdir);
290   file_spec = FileSpec(std::string(tmpdir.data(), tmpdir.size()));
291   FileSystem::Instance().Resolve(file_spec);
292   return true;
293 }
294 
295 bool HostInfoBase::ComputeGlobalTempFileDirectory(FileSpec &file_spec) {
296   file_spec.Clear();
297 
298   FileSpec temp_file_spec;
299   if (!HostInfo::ComputeTempFileBaseDirectory(temp_file_spec))
300     return false;
301 
302   temp_file_spec.AppendPathComponent("lldb");
303   if (llvm::sys::fs::create_directory(temp_file_spec.GetPath()))
304     return false;
305 
306   file_spec.GetDirectory().SetCString(temp_file_spec.GetCString());
307   return true;
308 }
309 
310 bool HostInfoBase::ComputeHeaderDirectory(FileSpec &file_spec) {
311   // TODO(zturner): Figure out how to compute the header directory for all
312   // platforms.
313   return false;
314 }
315 
316 bool HostInfoBase::ComputeSystemPluginsDirectory(FileSpec &file_spec) {
317   // TODO(zturner): Figure out how to compute the system plugins directory for
318   // all platforms.
319   return false;
320 }
321 
322 bool HostInfoBase::ComputeUserPluginsDirectory(FileSpec &file_spec) {
323   // TODO(zturner): Figure out how to compute the user plugins directory for
324   // all platforms.
325   return false;
326 }
327 
328 void HostInfoBase::ComputeHostArchitectureSupport(ArchSpec &arch_32,
329                                                   ArchSpec &arch_64) {
330   llvm::Triple triple(llvm::sys::getProcessTriple());
331 
332   arch_32.Clear();
333   arch_64.Clear();
334 
335   switch (triple.getArch()) {
336   default:
337     arch_32.SetTriple(triple);
338     break;
339 
340   case llvm::Triple::aarch64:
341   case llvm::Triple::ppc64:
342   case llvm::Triple::ppc64le:
343   case llvm::Triple::x86_64:
344     arch_64.SetTriple(triple);
345     arch_32.SetTriple(triple.get32BitArchVariant());
346     break;
347 
348   case llvm::Triple::mips64:
349   case llvm::Triple::mips64el:
350   case llvm::Triple::sparcv9:
351   case llvm::Triple::systemz:
352     arch_64.SetTriple(triple);
353     break;
354   }
355 }
356