1 //===-- SymbolLocatorDefault.cpp ------------------------------------------===//
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 "SymbolLocatorDefault.h"
10 
11 #include <cstring>
12 #include <optional>
13 
14 #include "Plugins/ObjectFile/wasm/ObjectFileWasm.h"
15 #include "lldb/Core/Debugger.h"
16 #include "lldb/Core/Module.h"
17 #include "lldb/Core/ModuleList.h"
18 #include "lldb/Core/ModuleSpec.h"
19 #include "lldb/Core/PluginManager.h"
20 #include "lldb/Core/Progress.h"
21 #include "lldb/Core/Section.h"
22 #include "lldb/Host/FileSystem.h"
23 #include "lldb/Host/Host.h"
24 #include "lldb/Symbol/ObjectFile.h"
25 #include "lldb/Target/Target.h"
26 #include "lldb/Utility/ArchSpec.h"
27 #include "lldb/Utility/DataBuffer.h"
28 #include "lldb/Utility/DataExtractor.h"
29 #include "lldb/Utility/LLDBLog.h"
30 #include "lldb/Utility/Log.h"
31 #include "lldb/Utility/StreamString.h"
32 #include "lldb/Utility/Timer.h"
33 #include "lldb/Utility/UUID.h"
34 
35 #include "llvm/ADT/SmallSet.h"
36 #include "llvm/Support/FileSystem.h"
37 #include "llvm/Support/ThreadPool.h"
38 
39 // From MacOSX system header "mach/machine.h"
40 typedef int cpu_type_t;
41 typedef int cpu_subtype_t;
42 
43 using namespace lldb;
44 using namespace lldb_private;
45 
LLDB_PLUGIN_DEFINE(SymbolLocatorDefault)46 LLDB_PLUGIN_DEFINE(SymbolLocatorDefault)
47 
48 SymbolLocatorDefault::SymbolLocatorDefault() : SymbolLocator() {}
49 
Initialize()50 void SymbolLocatorDefault::Initialize() {
51   PluginManager::RegisterPlugin(
52       GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance,
53       LocateExecutableObjectFile, LocateExecutableSymbolFile,
54       DownloadObjectAndSymbolFile);
55 }
56 
Terminate()57 void SymbolLocatorDefault::Terminate() {
58   PluginManager::UnregisterPlugin(CreateInstance);
59 }
60 
GetPluginDescriptionStatic()61 llvm::StringRef SymbolLocatorDefault::GetPluginDescriptionStatic() {
62   return "Default symbol locator.";
63 }
64 
CreateInstance()65 SymbolLocator *SymbolLocatorDefault::CreateInstance() {
66   return new SymbolLocatorDefault();
67 }
68 
LocateExecutableObjectFile(const ModuleSpec & module_spec)69 std::optional<ModuleSpec> SymbolLocatorDefault::LocateExecutableObjectFile(
70     const ModuleSpec &module_spec) {
71   const FileSpec &exec_fspec = module_spec.GetFileSpec();
72   const ArchSpec *arch = module_spec.GetArchitecturePtr();
73   const UUID *uuid = module_spec.GetUUIDPtr();
74   LLDB_SCOPED_TIMERF(
75       "LocateExecutableObjectFile (file = %s, arch = %s, uuid = %p)",
76       exec_fspec ? exec_fspec.GetFilename().AsCString("<NULL>") : "<NULL>",
77       arch ? arch->GetArchitectureName() : "<NULL>", (const void *)uuid);
78 
79   ModuleSpecList module_specs;
80   ModuleSpec matched_module_spec;
81   if (exec_fspec &&
82       ObjectFile::GetModuleSpecifications(exec_fspec, 0, 0, module_specs) &&
83       module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec)) {
84     ModuleSpec result;
85     result.GetFileSpec() = exec_fspec;
86     return result;
87   }
88 
89   return {};
90 }
91 
92 // Keep "symbols.enable-external-lookup" description in sync with this function.
LocateExecutableSymbolFile(const ModuleSpec & module_spec,const FileSpecList & default_search_paths)93 std::optional<FileSpec> SymbolLocatorDefault::LocateExecutableSymbolFile(
94     const ModuleSpec &module_spec, const FileSpecList &default_search_paths) {
95 
96   FileSpec symbol_file_spec = module_spec.GetSymbolFileSpec();
97   if (symbol_file_spec.IsAbsolute() &&
98       FileSystem::Instance().Exists(symbol_file_spec))
99     return symbol_file_spec;
100 
101   Progress progress(
102       "Locating external symbol file",
103       module_spec.GetFileSpec().GetFilename().AsCString("<Unknown>"));
104 
105   FileSpecList debug_file_search_paths = default_search_paths;
106 
107   // Add module directory.
108   FileSpec module_file_spec = module_spec.GetFileSpec();
109   // We keep the unresolved pathname if it fails.
110   FileSystem::Instance().ResolveSymbolicLink(module_file_spec,
111                                              module_file_spec);
112 
113   ConstString file_dir = module_file_spec.GetDirectory();
114   {
115     FileSpec file_spec(file_dir.AsCString("."));
116     FileSystem::Instance().Resolve(file_spec);
117     debug_file_search_paths.AppendIfUnique(file_spec);
118   }
119 
120   if (ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup()) {
121 
122     // Add current working directory.
123     {
124       FileSpec file_spec(".");
125       FileSystem::Instance().Resolve(file_spec);
126       debug_file_search_paths.AppendIfUnique(file_spec);
127     }
128 
129 #ifndef _WIN32
130 #if defined(__NetBSD__)
131     // Add /usr/libdata/debug directory.
132     {
133       FileSpec file_spec("/usr/libdata/debug");
134       FileSystem::Instance().Resolve(file_spec);
135       debug_file_search_paths.AppendIfUnique(file_spec);
136     }
137 #else
138     // Add /usr/lib/debug directory.
139     {
140       FileSpec file_spec("/usr/lib/debug");
141       FileSystem::Instance().Resolve(file_spec);
142       debug_file_search_paths.AppendIfUnique(file_spec);
143     }
144 #endif
145 #endif // _WIN32
146   }
147 
148   std::string uuid_str;
149   const UUID &module_uuid = module_spec.GetUUID();
150   if (module_uuid.IsValid()) {
151     // Some debug files are stored in the .build-id directory like this:
152     //   /usr/lib/debug/.build-id/ff/e7fe727889ad82bb153de2ad065b2189693315.debug
153     uuid_str = module_uuid.GetAsString("");
154     std::transform(uuid_str.begin(), uuid_str.end(), uuid_str.begin(),
155                    ::tolower);
156     uuid_str.insert(2, 1, '/');
157     uuid_str = uuid_str + ".debug";
158   }
159 
160   size_t num_directories = debug_file_search_paths.GetSize();
161   for (size_t idx = 0; idx < num_directories; ++idx) {
162     FileSpec dirspec = debug_file_search_paths.GetFileSpecAtIndex(idx);
163     FileSystem::Instance().Resolve(dirspec);
164     if (!FileSystem::Instance().IsDirectory(dirspec))
165       continue;
166 
167     std::vector<std::string> files;
168     std::string dirname = dirspec.GetPath();
169 
170     if (!uuid_str.empty())
171       files.push_back(dirname + "/.build-id/" + uuid_str);
172     if (symbol_file_spec.GetFilename()) {
173       files.push_back(dirname + "/" +
174                       symbol_file_spec.GetFilename().GetCString());
175       files.push_back(dirname + "/.debug/" +
176                       symbol_file_spec.GetFilename().GetCString());
177 
178       // Some debug files may stored in the module directory like this:
179       //   /usr/lib/debug/usr/lib/library.so.debug
180       if (!file_dir.IsEmpty())
181         files.push_back(dirname + file_dir.AsCString() + "/" +
182                         symbol_file_spec.GetFilename().GetCString());
183     }
184 
185     const uint32_t num_files = files.size();
186     for (size_t idx_file = 0; idx_file < num_files; ++idx_file) {
187       const std::string &filename = files[idx_file];
188       FileSpec file_spec(filename);
189       FileSystem::Instance().Resolve(file_spec);
190 
191       if (llvm::sys::fs::equivalent(file_spec.GetPath(),
192                                     module_file_spec.GetPath()))
193         continue;
194 
195       if (FileSystem::Instance().Exists(file_spec)) {
196         lldb_private::ModuleSpecList specs;
197         const size_t num_specs =
198             ObjectFile::GetModuleSpecifications(file_spec, 0, 0, specs);
199         ModuleSpec mspec;
200         bool valid_mspec = false;
201         if (num_specs == 2) {
202           // Special case to handle both i386 and i686 from ObjectFilePECOFF
203           ModuleSpec mspec2;
204           if (specs.GetModuleSpecAtIndex(0, mspec) &&
205               specs.GetModuleSpecAtIndex(1, mspec2) &&
206               mspec.GetArchitecture().GetTriple().isCompatibleWith(
207                   mspec2.GetArchitecture().GetTriple())) {
208             valid_mspec = true;
209           }
210         }
211         if (!valid_mspec) {
212           assert(num_specs <= 1 &&
213                  "Symbol Vendor supports only a single architecture");
214           if (num_specs == 1) {
215             if (specs.GetModuleSpecAtIndex(0, mspec)) {
216               valid_mspec = true;
217             }
218           }
219         }
220         if (valid_mspec) {
221           // Skip the uuids check if module_uuid is invalid. For example,
222           // this happens for *.dwp files since at the moment llvm-dwp
223           // doesn't output build ids, nor does binutils dwp.
224           if (!module_uuid.IsValid() || module_uuid == mspec.GetUUID())
225             return file_spec;
226         }
227       }
228     }
229   }
230 
231   return {};
232 }
233 
DownloadObjectAndSymbolFile(ModuleSpec & module_spec,Status & error,bool force_lookup,bool copy_executable)234 bool SymbolLocatorDefault::DownloadObjectAndSymbolFile(ModuleSpec &module_spec,
235                                                        Status &error,
236                                                        bool force_lookup,
237                                                        bool copy_executable) {
238   return false;
239 }
240