LLDB  mainline
LocateSymbolFile.cpp
Go to the documentation of this file.
1 //===-- LocateSymbolFile.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 
10 
11 #include "lldb/Core/ModuleList.h"
12 #include "lldb/Core/ModuleSpec.h"
13 #include "lldb/Host/FileSystem.h"
14 #include "lldb/Symbol/ObjectFile.h"
15 #include "lldb/Utility/ArchSpec.h"
18 #include "lldb/Utility/Log.h"
20 #include "lldb/Utility/Timer.h"
21 #include "lldb/Utility/UUID.h"
22 
23 #include "llvm/Support/FileSystem.h"
24 
25 // From MacOSX system header "mach/machine.h"
26 typedef int cpu_type_t;
27 typedef int cpu_subtype_t;
28 
29 using namespace lldb;
30 using namespace lldb_private;
31 
32 #if defined(__APPLE__)
33 
34 // Forward declaration of method defined in source/Host/macosx/Symbols.cpp
35 int LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec &module_spec,
36  ModuleSpec &return_module_spec);
37 
38 #else
39 
41  ModuleSpec &return_module_spec) {
42  // Cannot find MacOSX files using debug symbols on non MacOSX.
43  return 0;
44 }
45 
46 #endif
47 
48 static bool FileAtPathContainsArchAndUUID(const FileSpec &file_fspec,
49  const ArchSpec *arch,
50  const lldb_private::UUID *uuid) {
51  ModuleSpecList module_specs;
52  if (ObjectFile::GetModuleSpecifications(file_fspec, 0, 0, module_specs)) {
53  ModuleSpec spec;
54  for (size_t i = 0; i < module_specs.GetSize(); ++i) {
55  bool got_spec = module_specs.GetModuleSpecAtIndex(i, spec);
56  UNUSED_IF_ASSERT_DISABLED(got_spec);
57  assert(got_spec);
58  if ((uuid == nullptr || (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) &&
59  (arch == nullptr ||
60  (spec.GetArchitecturePtr() &&
61  spec.GetArchitecture().IsCompatibleMatch(*arch)))) {
62  return true;
63  }
64  }
65  }
66  return false;
67 }
68 
69 // Given a binary exec_fspec, and a ModuleSpec with an architecture/uuid,
70 // return true if there is a matching dSYM bundle next to the exec_fspec,
71 // and return that value in dsym_fspec.
72 // If there is a .dSYM.yaa compressed archive next to the exec_fspec,
73 // call through Symbols::DownloadObjectAndSymbolFile to download the
74 // expanded/uncompressed dSYM and return that filepath in dsym_fspec.
75 
76 static bool LookForDsymNextToExecutablePath(const ModuleSpec &mod_spec,
77  const FileSpec &exec_fspec,
78  FileSpec &dsym_fspec) {
79  ConstString filename = exec_fspec.GetFilename();
80  FileSpec dsym_directory = exec_fspec;
81  dsym_directory.RemoveLastPathComponent();
82 
83  std::string dsym_filename = filename.AsCString();
84  dsym_filename += ".dSYM";
85  dsym_directory.AppendPathComponent(dsym_filename);
86  dsym_directory.AppendPathComponent("Contents");
87  dsym_directory.AppendPathComponent("Resources");
88  dsym_directory.AppendPathComponent("DWARF");
89 
90  if (FileSystem::Instance().Exists(dsym_directory)) {
91 
92  // See if the binary name exists in the dSYM DWARF
93  // subdir.
94  dsym_fspec = dsym_directory;
95  dsym_fspec.AppendPathComponent(filename.AsCString());
96  if (FileSystem::Instance().Exists(dsym_fspec) &&
98  mod_spec.GetUUIDPtr())) {
99  return true;
100  }
101 
102  // See if we have "../CF.framework" - so we'll look for
103  // CF.framework.dSYM/Contents/Resources/DWARF/CF
104  // We need to drop the last suffix after '.' to match
105  // 'CF' in the DWARF subdir.
106  std::string binary_name(filename.AsCString());
107  auto last_dot = binary_name.find_last_of('.');
108  if (last_dot != std::string::npos) {
109  binary_name.erase(last_dot);
110  dsym_fspec = dsym_directory;
111  dsym_fspec.AppendPathComponent(binary_name);
112  if (FileSystem::Instance().Exists(dsym_fspec) &&
114  mod_spec.GetArchitecturePtr(),
115  mod_spec.GetUUIDPtr())) {
116  return true;
117  }
118  }
119  }
120 
121  // See if we have a .dSYM.yaa next to this executable path.
122  FileSpec dsym_yaa_fspec = exec_fspec;
123  dsym_yaa_fspec.RemoveLastPathComponent();
124  std::string dsym_yaa_filename = filename.AsCString();
125  dsym_yaa_filename += ".dSYM.yaa";
126  dsym_yaa_fspec.AppendPathComponent(dsym_yaa_filename);
127 
128  if (FileSystem::Instance().Exists(dsym_yaa_fspec)) {
129  ModuleSpec mutable_mod_spec = mod_spec;
130  if (Symbols::DownloadObjectAndSymbolFile(mutable_mod_spec, true) &&
131  FileSystem::Instance().Exists(mutable_mod_spec.GetSymbolFileSpec())) {
132  dsym_fspec = mutable_mod_spec.GetSymbolFileSpec();
133  return true;
134  }
135  }
136 
137  return false;
138 }
139 
140 // Given a ModuleSpec with a FileSpec and optionally uuid/architecture
141 // filled in, look for a .dSYM bundle next to that binary. Returns true
142 // if a .dSYM bundle is found, and that path is returned in the dsym_fspec
143 // FileSpec.
144 //
145 // This routine looks a few directory layers above the given exec_path -
146 // exec_path might be /System/Library/Frameworks/CF.framework/CF and the
147 // dSYM might be /System/Library/Frameworks/CF.framework.dSYM.
148 //
149 // If there is a .dSYM.yaa compressed archive found next to the binary,
150 // we'll call DownloadObjectAndSymbolFile to expand it into a plain .dSYM
151 
152 static bool LocateDSYMInVincinityOfExecutable(const ModuleSpec &module_spec,
153  FileSpec &dsym_fspec) {
155  const FileSpec &exec_fspec = module_spec.GetFileSpec();
156  if (exec_fspec) {
157  if (::LookForDsymNextToExecutablePath(module_spec, exec_fspec,
158  dsym_fspec)) {
159  if (log) {
160  LLDB_LOGF(log, "dSYM with matching UUID & arch found at %s",
161  dsym_fspec.GetPath().c_str());
162  }
163  return true;
164  } else {
165  FileSpec parent_dirs = exec_fspec;
166 
167  // Remove the binary name from the FileSpec
168  parent_dirs.RemoveLastPathComponent();
169 
170  // Add a ".dSYM" name to each directory component of the path,
171  // stripping off components. e.g. we may have a binary like
172  // /S/L/F/Foundation.framework/Versions/A/Foundation and
173  // /S/L/F/Foundation.framework.dSYM
174  //
175  // so we'll need to start with
176  // /S/L/F/Foundation.framework/Versions/A, add the .dSYM part to the
177  // "A", and if that doesn't exist, strip off the "A" and try it again
178  // with "Versions", etc., until we find a dSYM bundle or we've
179  // stripped off enough path components that there's no need to
180  // continue.
181 
182  for (int i = 0; i < 4; i++) {
183  // Does this part of the path have a "." character - could it be a
184  // bundle's top level directory?
185  const char *fn = parent_dirs.GetFilename().AsCString();
186  if (fn == nullptr)
187  break;
188  if (::strchr(fn, '.') != nullptr) {
189  if (::LookForDsymNextToExecutablePath(module_spec, parent_dirs,
190  dsym_fspec)) {
191  if (log) {
192  LLDB_LOGF(log, "dSYM with matching UUID & arch found at %s",
193  dsym_fspec.GetPath().c_str());
194  }
195  return true;
196  }
197  }
198  parent_dirs.RemoveLastPathComponent();
199  }
200  }
201  }
202  dsym_fspec.Clear();
203  return false;
204 }
205 
207  const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();
208  const ArchSpec *arch = module_spec.GetArchitecturePtr();
209  const UUID *uuid = module_spec.GetUUIDPtr();
210 
211  static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
212  Timer scoped_timer(
213  func_cat,
214  "LocateExecutableSymbolFileDsym (file = %s, arch = %s, uuid = %p)",
215  exec_fspec ? exec_fspec->GetFilename().AsCString("<NULL>") : "<NULL>",
216  arch ? arch->GetArchitectureName() : "<NULL>", (const void *)uuid);
217 
218  FileSpec symbol_fspec;
219  ModuleSpec dsym_module_spec;
220  // First try and find the dSYM in the same directory as the executable or in
221  // an appropriate parent directory
222  if (!LocateDSYMInVincinityOfExecutable(module_spec, symbol_fspec)) {
223  // We failed to easily find the dSYM above, so use DebugSymbols
224  LocateMacOSXFilesUsingDebugSymbols(module_spec, dsym_module_spec);
225  } else {
226  dsym_module_spec.GetSymbolFileSpec() = symbol_fspec;
227  }
228  return dsym_module_spec.GetSymbolFileSpec();
229 }
230 
231 ModuleSpec Symbols::LocateExecutableObjectFile(const ModuleSpec &module_spec) {
232  ModuleSpec result;
233  const FileSpec &exec_fspec = module_spec.GetFileSpec();
234  const ArchSpec *arch = module_spec.GetArchitecturePtr();
235  const UUID *uuid = module_spec.GetUUIDPtr();
236  static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
237  Timer scoped_timer(
238  func_cat, "LocateExecutableObjectFile (file = %s, arch = %s, uuid = %p)",
239  exec_fspec ? exec_fspec.GetFilename().AsCString("<NULL>") : "<NULL>",
240  arch ? arch->GetArchitectureName() : "<NULL>", (const void *)uuid);
241 
242  ModuleSpecList module_specs;
243  ModuleSpec matched_module_spec;
244  if (exec_fspec &&
245  ObjectFile::GetModuleSpecifications(exec_fspec, 0, 0, module_specs) &&
246  module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec)) {
247  result.GetFileSpec() = exec_fspec;
248  } else {
249  LocateMacOSXFilesUsingDebugSymbols(module_spec, result);
250  }
251  return result;
252 }
253 
254 // Keep "symbols.enable-external-lookup" description in sync with this function.
255 
256 FileSpec
257 Symbols::LocateExecutableSymbolFile(const ModuleSpec &module_spec,
258  const FileSpecList &default_search_paths) {
259  FileSpec symbol_file_spec = module_spec.GetSymbolFileSpec();
260  if (symbol_file_spec.IsAbsolute() &&
261  FileSystem::Instance().Exists(symbol_file_spec))
262  return symbol_file_spec;
263 
264  FileSpecList debug_file_search_paths = default_search_paths;
265 
266  // Add module directory.
267  FileSpec module_file_spec = module_spec.GetFileSpec();
268  // We keep the unresolved pathname if it fails.
269  FileSystem::Instance().ResolveSymbolicLink(module_file_spec,
270  module_file_spec);
271 
272  ConstString file_dir = module_file_spec.GetDirectory();
273  {
274  FileSpec file_spec(file_dir.AsCString("."));
275  FileSystem::Instance().Resolve(file_spec);
276  debug_file_search_paths.AppendIfUnique(file_spec);
277  }
278 
279  if (ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup()) {
280 
281  // Add current working directory.
282  {
283  FileSpec file_spec(".");
284  FileSystem::Instance().Resolve(file_spec);
285  debug_file_search_paths.AppendIfUnique(file_spec);
286  }
287 
288 #ifndef _WIN32
289 #if defined(__NetBSD__)
290  // Add /usr/libdata/debug directory.
291  {
292  FileSpec file_spec("/usr/libdata/debug");
293  FileSystem::Instance().Resolve(file_spec);
294  debug_file_search_paths.AppendIfUnique(file_spec);
295  }
296 #else
297  // Add /usr/lib/debug directory.
298  {
299  FileSpec file_spec("/usr/lib/debug");
300  FileSystem::Instance().Resolve(file_spec);
301  debug_file_search_paths.AppendIfUnique(file_spec);
302  }
303 #endif
304 #endif // _WIN32
305  }
306 
307  std::string uuid_str;
308  const UUID &module_uuid = module_spec.GetUUID();
309  if (module_uuid.IsValid()) {
310  // Some debug files are stored in the .build-id directory like this:
311  // /usr/lib/debug/.build-id/ff/e7fe727889ad82bb153de2ad065b2189693315.debug
312  uuid_str = module_uuid.GetAsString("");
313  std::transform(uuid_str.begin(), uuid_str.end(), uuid_str.begin(),
314  ::tolower);
315  uuid_str.insert(2, 1, '/');
316  uuid_str = uuid_str + ".debug";
317  }
318 
319  size_t num_directories = debug_file_search_paths.GetSize();
320  for (size_t idx = 0; idx < num_directories; ++idx) {
321  FileSpec dirspec = debug_file_search_paths.GetFileSpecAtIndex(idx);
322  FileSystem::Instance().Resolve(dirspec);
323  if (!FileSystem::Instance().IsDirectory(dirspec))
324  continue;
325 
326  std::vector<std::string> files;
327  std::string dirname = dirspec.GetPath();
328 
329  if (!uuid_str.empty())
330  files.push_back(dirname + "/.build-id/" + uuid_str);
331  if (symbol_file_spec.GetFilename()) {
332  files.push_back(dirname + "/" +
333  symbol_file_spec.GetFilename().GetCString());
334  files.push_back(dirname + "/.debug/" +
335  symbol_file_spec.GetFilename().GetCString());
336 
337  // Some debug files may stored in the module directory like this:
338  // /usr/lib/debug/usr/lib/library.so.debug
339  if (!file_dir.IsEmpty())
340  files.push_back(dirname + file_dir.AsCString() + "/" +
341  symbol_file_spec.GetFilename().GetCString());
342  }
343 
344  const uint32_t num_files = files.size();
345  for (size_t idx_file = 0; idx_file < num_files; ++idx_file) {
346  const std::string &filename = files[idx_file];
347  FileSpec file_spec(filename);
348  FileSystem::Instance().Resolve(file_spec);
349 
350  if (llvm::sys::fs::equivalent(file_spec.GetPath(),
351  module_file_spec.GetPath()))
352  continue;
353 
354  if (FileSystem::Instance().Exists(file_spec)) {
356  const size_t num_specs =
357  ObjectFile::GetModuleSpecifications(file_spec, 0, 0, specs);
358  assert(num_specs <= 1 &&
359  "Symbol Vendor supports only a single architecture");
360  if (num_specs == 1) {
361  ModuleSpec mspec;
362  if (specs.GetModuleSpecAtIndex(0, mspec)) {
363  // Skip the uuids check if module_uuid is invalid. For example,
364  // this happens for *.dwp files since at the moment llvm-dwp
365  // doesn't output build ids, nor does binutils dwp.
366  if (!module_uuid.IsValid() || module_uuid == mspec.GetUUID())
367  return file_spec;
368  }
369  }
370  }
371  }
372  }
373 
374  return LocateExecutableSymbolFileDsym(module_spec);
375 }
376 
377 #if !defined(__APPLE__)
378 
379 FileSpec Symbols::FindSymbolFileInBundle(const FileSpec &symfile_bundle,
380  const lldb_private::UUID *uuid,
381  const ArchSpec *arch) {
382  // FIXME
383  return FileSpec();
384 }
385 
386 bool Symbols::DownloadObjectAndSymbolFile(ModuleSpec &module_spec,
387  bool force_lookup) {
388  // Fill in the module_spec.GetFileSpec() for the object file and/or the
389  // module_spec.GetSymbolFileSpec() for the debug symbols file.
390  return false;
391 }
392 
393 #endif
ConstString & GetFilename()
Filename string get accessor.
Definition: FileSpec.cpp:341
static bool LocateDSYMInVincinityOfExecutable(const ModuleSpec &module_spec, FileSpec &dsym_fspec)
#define UNUSED_IF_ASSERT_DISABLED(x)
Definition: lldb-defines.h:134
A class that represents a running process on the host machine.
const char * AsCString(const char *value_if_empty=nullptr) const
Get the string value as a C string.
Definition: ConstString.h:223
bool IsValid() const
Definition: UUID.h:62
int cpu_subtype_t
A file utility class.
Definition: FileSpec.h:56
An architecture specification class.
Definition: ArchSpec.h:33
A timer class that simplifies common timing metrics.
Definition: Timer.h:23
static bool LookForDsymNextToExecutablePath(const ModuleSpec &mod_spec, const FileSpec &exec_fspec, FileSpec &dsym_fspec)
void Clear()
Clears the object state.
Definition: FileSpec.cpp:261
int LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec &module_spec, ModuleSpec &return_module_spec)
std::string GetAsString(llvm::StringRef separator="-") const
Definition: UUID.cpp:38
int cpu_type_t
const char * GetArchitectureName() const
Returns a static string representing the current architecture.
Definition: ArchSpec.cpp:585
#define LIBLLDB_LOG_HOST
Definition: Logging.h:28
ArchSpec * GetArchitecturePtr()
Definition: ModuleSpec.h:103
void AppendPathComponent(llvm::StringRef component)
Definition: FileSpec.cpp:435
FileSpec * GetFileSpecPtr()
Definition: ModuleSpec.h:69
Log * GetLogIfAllCategoriesSet(uint32_t mask)
Definition: Logging.cpp:58
FileSpec & GetFileSpec()
Definition: ModuleSpec.h:75
ArchSpec & GetArchitecture()
Definition: ModuleSpec.h:111
#define LLDB_LOGF(log,...)
Definition: Log.h:249
bool IsAbsolute() const
Returns true if the filespec represents an absolute path.
Definition: FileSpec.cpp:479
ConstString & GetDirectory()
Directory string get accessor.
Definition: FileSpec.cpp:335
static bool FileAtPathContainsArchAndUUID(const FileSpec &file_fspec, const ArchSpec *arch, const lldb_private::UUID *uuid)
A uniqued constant string class.
Definition: ConstString.h:40
const char * GetCString() const
Get the string value as a C string.
Definition: ConstString.h:246
FileSpec & GetSymbolFileSpec()
Definition: ModuleSpec.h:99
Definition: SBAddress.h:15
bool RemoveLastPathComponent()
Removes the last path component by replacing the current path with its parent.
Definition: FileSpec.cpp:446
bool GetModuleSpecAtIndex(size_t i, ModuleSpec &module_spec) const
Definition: ModuleSpec.h:342
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition: FileSpec.cpp:348
static FileSpec LocateExecutableSymbolFileDsym(const ModuleSpec &module_spec)
bool IsEmpty() const
Test for empty string.
Definition: ConstString.h:334
bool FindMatchingModuleSpec(const ModuleSpec &module_spec, ModuleSpec &match_module_spec) const
Definition: ModuleSpec.h:352
bool IsCompatibleMatch(const ArchSpec &rhs) const
Compare an ArchSpec to another ArchSpec, requiring a compatible cpu type match between them...
Definition: ArchSpec.cpp:972