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"
21 #include "lldb/Utility/Timer.h"
22 #include "lldb/Utility/UUID.h"
23 
24 #include "llvm/Support/FileSystem.h"
25 
26 // From MacOSX system header "mach/machine.h"
27 typedef int cpu_type_t;
28 typedef int cpu_subtype_t;
29 
30 using namespace lldb;
31 using namespace lldb_private;
32 
33 #if defined(__APPLE__)
34 
35 // Forward declaration of method defined in source/Host/macosx/Symbols.cpp
36 int LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec &module_spec,
37  ModuleSpec &return_module_spec);
38 
39 #else
40 
42  ModuleSpec &return_module_spec) {
43  // Cannot find MacOSX files using debug symbols on non MacOSX.
44  return 0;
45 }
46 
47 #endif
48 
49 static bool FileAtPathContainsArchAndUUID(const FileSpec &file_fspec,
50  const ArchSpec *arch,
51  const lldb_private::UUID *uuid) {
52  ModuleSpecList module_specs;
53  if (ObjectFile::GetModuleSpecifications(file_fspec, 0, 0, module_specs)) {
54  ModuleSpec spec;
55  for (size_t i = 0; i < module_specs.GetSize(); ++i) {
56  bool got_spec = module_specs.GetModuleSpecAtIndex(i, spec);
57  UNUSED_IF_ASSERT_DISABLED(got_spec);
58  assert(got_spec);
59  if ((uuid == nullptr || (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) &&
60  (arch == nullptr ||
61  (spec.GetArchitecturePtr() &&
62  spec.GetArchitecture().IsCompatibleMatch(*arch)))) {
63  return true;
64  }
65  }
66  }
67  return false;
68 }
69 
70 // Given a binary exec_fspec, and a ModuleSpec with an architecture/uuid,
71 // return true if there is a matching dSYM bundle next to the exec_fspec,
72 // and return that value in dsym_fspec.
73 // If there is a .dSYM.yaa compressed archive next to the exec_fspec,
74 // call through Symbols::DownloadObjectAndSymbolFile to download the
75 // expanded/uncompressed dSYM and return that filepath in dsym_fspec.
76 
77 static bool LookForDsymNextToExecutablePath(const ModuleSpec &mod_spec,
78  const FileSpec &exec_fspec,
79  FileSpec &dsym_fspec) {
80  ConstString filename = exec_fspec.GetFilename();
81  FileSpec dsym_directory = exec_fspec;
82  dsym_directory.RemoveLastPathComponent();
83 
84  std::string dsym_filename = filename.AsCString();
85  dsym_filename += ".dSYM";
86  dsym_directory.AppendPathComponent(dsym_filename);
87  dsym_directory.AppendPathComponent("Contents");
88  dsym_directory.AppendPathComponent("Resources");
89  dsym_directory.AppendPathComponent("DWARF");
90 
91  if (FileSystem::Instance().Exists(dsym_directory)) {
92 
93  // See if the binary name exists in the dSYM DWARF
94  // subdir.
95  dsym_fspec = dsym_directory;
96  dsym_fspec.AppendPathComponent(filename.AsCString());
97  if (FileSystem::Instance().Exists(dsym_fspec) &&
99  mod_spec.GetUUIDPtr())) {
100  return true;
101  }
102 
103  // See if we have "../CF.framework" - so we'll look for
104  // CF.framework.dSYM/Contents/Resources/DWARF/CF
105  // We need to drop the last suffix after '.' to match
106  // 'CF' in the DWARF subdir.
107  std::string binary_name(filename.AsCString());
108  auto last_dot = binary_name.find_last_of('.');
109  if (last_dot != std::string::npos) {
110  binary_name.erase(last_dot);
111  dsym_fspec = dsym_directory;
112  dsym_fspec.AppendPathComponent(binary_name);
113  if (FileSystem::Instance().Exists(dsym_fspec) &&
115  mod_spec.GetArchitecturePtr(),
116  mod_spec.GetUUIDPtr())) {
117  return true;
118  }
119  }
120  }
121 
122  // See if we have a .dSYM.yaa next to this executable path.
123  FileSpec dsym_yaa_fspec = exec_fspec;
124  dsym_yaa_fspec.RemoveLastPathComponent();
125  std::string dsym_yaa_filename = filename.AsCString();
126  dsym_yaa_filename += ".dSYM.yaa";
127  dsym_yaa_fspec.AppendPathComponent(dsym_yaa_filename);
128 
129  if (FileSystem::Instance().Exists(dsym_yaa_fspec)) {
130  ModuleSpec mutable_mod_spec = mod_spec;
131  if (Symbols::DownloadObjectAndSymbolFile(mutable_mod_spec, true) &&
132  FileSystem::Instance().Exists(mutable_mod_spec.GetSymbolFileSpec())) {
133  dsym_fspec = mutable_mod_spec.GetSymbolFileSpec();
134  return true;
135  }
136  }
137 
138  return false;
139 }
140 
141 // Given a ModuleSpec with a FileSpec and optionally uuid/architecture
142 // filled in, look for a .dSYM bundle next to that binary. Returns true
143 // if a .dSYM bundle is found, and that path is returned in the dsym_fspec
144 // FileSpec.
145 //
146 // This routine looks a few directory layers above the given exec_path -
147 // exec_path might be /System/Library/Frameworks/CF.framework/CF and the
148 // dSYM might be /System/Library/Frameworks/CF.framework.dSYM.
149 //
150 // If there is a .dSYM.yaa compressed archive found next to the binary,
151 // we'll call DownloadObjectAndSymbolFile to expand it into a plain .dSYM
152 
153 static bool LocateDSYMInVincinityOfExecutable(const ModuleSpec &module_spec,
154  FileSpec &dsym_fspec) {
156  const FileSpec &exec_fspec = module_spec.GetFileSpec();
157  if (exec_fspec) {
158  if (::LookForDsymNextToExecutablePath(module_spec, exec_fspec,
159  dsym_fspec)) {
160  if (log) {
161  LLDB_LOGF(log, "dSYM with matching UUID & arch found at %s",
162  dsym_fspec.GetPath().c_str());
163  }
164  return true;
165  } else {
166  FileSpec parent_dirs = exec_fspec;
167 
168  // Remove the binary name from the FileSpec
169  parent_dirs.RemoveLastPathComponent();
170 
171  // Add a ".dSYM" name to each directory component of the path,
172  // stripping off components. e.g. we may have a binary like
173  // /S/L/F/Foundation.framework/Versions/A/Foundation and
174  // /S/L/F/Foundation.framework.dSYM
175  //
176  // so we'll need to start with
177  // /S/L/F/Foundation.framework/Versions/A, add the .dSYM part to the
178  // "A", and if that doesn't exist, strip off the "A" and try it again
179  // with "Versions", etc., until we find a dSYM bundle or we've
180  // stripped off enough path components that there's no need to
181  // continue.
182 
183  for (int i = 0; i < 4; i++) {
184  // Does this part of the path have a "." character - could it be a
185  // bundle's top level directory?
186  const char *fn = parent_dirs.GetFilename().AsCString();
187  if (fn == nullptr)
188  break;
189  if (::strchr(fn, '.') != nullptr) {
190  if (::LookForDsymNextToExecutablePath(module_spec, parent_dirs,
191  dsym_fspec)) {
192  if (log) {
193  LLDB_LOGF(log, "dSYM with matching UUID & arch found at %s",
194  dsym_fspec.GetPath().c_str());
195  }
196  return true;
197  }
198  }
199  parent_dirs.RemoveLastPathComponent();
200  }
201  }
202  }
203  dsym_fspec.Clear();
204  return false;
205 }
206 
208  const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();
209  const ArchSpec *arch = module_spec.GetArchitecturePtr();
210  const UUID *uuid = module_spec.GetUUIDPtr();
211 
213  "LocateExecutableSymbolFileDsym (file = %s, arch = %s, uuid = %p)",
214  exec_fspec ? exec_fspec->GetFilename().AsCString("<NULL>") : "<NULL>",
215  arch ? arch->GetArchitectureName() : "<NULL>", (const void *)uuid);
216 
217  FileSpec symbol_fspec;
218  ModuleSpec dsym_module_spec;
219  // First try and find the dSYM in the same directory as the executable or in
220  // an appropriate parent directory
221  if (!LocateDSYMInVincinityOfExecutable(module_spec, symbol_fspec)) {
222  // We failed to easily find the dSYM above, so use DebugSymbols
223  LocateMacOSXFilesUsingDebugSymbols(module_spec, dsym_module_spec);
224  } else {
225  dsym_module_spec.GetSymbolFileSpec() = symbol_fspec;
226  }
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();
237  "LocateExecutableObjectFile (file = %s, arch = %s, uuid = %p)",
238  exec_fspec ? exec_fspec.GetFilename().AsCString("<NULL>") : "<NULL>",
239  arch ? arch->GetArchitectureName() : "<NULL>", (const void *)uuid);
240 
241  ModuleSpecList module_specs;
242  ModuleSpec matched_module_spec;
243  if (exec_fspec &&
244  ObjectFile::GetModuleSpecifications(exec_fspec, 0, 0, module_specs) &&
245  module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec)) {
246  result.GetFileSpec() = exec_fspec;
247  } else {
248  LocateMacOSXFilesUsingDebugSymbols(module_spec, result);
249  }
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
lldb_private::UUID
Definition: UUID.h:23
lldb_private::ArchSpec
Definition: ArchSpec.h:33
FileSystem.h
UNUSED_IF_ASSERT_DISABLED
#define UNUSED_IF_ASSERT_DISABLED(x)
Definition: lldb-defines.h:137
lldb_private::ModuleSpec::GetUUIDPtr
UUID * GetUUIDPtr()
Definition: ModuleSpec.h:98
ModuleSpec.h
UUID.h
LocateMacOSXFilesUsingDebugSymbols
int LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec &module_spec, ModuleSpec &return_module_spec)
Definition: LocateSymbolFile.cpp:41
lldb_private::UUID::IsValid
bool IsValid() const
Definition: UUID.h:79
lldb_private::ModuleSpecList
Definition: ModuleSpec.h:280
LLDB_LOGF
#define LLDB_LOGF(log,...)
Definition: Log.h:249
Reproducer.h
lldb_private::ConstString::AsCString
const char * AsCString(const char *value_if_empty=nullptr) const
Get the string value as a C string.
Definition: ConstString.h:193
LocateSymbolFile.h
cpu_subtype_t
int cpu_subtype_t
Definition: LocateSymbolFile.cpp:28
lldb_private::ModuleSpec::GetFileSpecPtr
FileSpec * GetFileSpecPtr()
Definition: ModuleSpec.h:52
lldb_private::UUID::GetAsString
std::string GetAsString(llvm::StringRef separator="-") const
Definition: UUID.cpp:48
lldb_private::ModuleSpec::GetSymbolFileSpec
FileSpec & GetSymbolFileSpec()
Definition: ModuleSpec.h:82
lldb_private::ArchSpec::IsCompatibleMatch
bool IsCompatibleMatch(const ArchSpec &rhs) const
Compare an ArchSpec to another ArchSpec, requiring a compatible cpu type match between them.
Definition: ArchSpec.cpp:934
lldb_private::ModuleSpec::GetUUID
UUID & GetUUID()
Definition: ModuleSpec.h:104
LIBLLDB_LOG_HOST
#define LIBLLDB_LOG_HOST
Definition: Logging.h:28
lldb_private::FileSpec
Definition: FileSpec.h:56
FileAtPathContainsArchAndUUID
static bool FileAtPathContainsArchAndUUID(const FileSpec &file_fspec, const ArchSpec *arch, const lldb_private::UUID *uuid)
Definition: LocateSymbolFile.cpp:49
ModuleList.h
LocateDSYMInVincinityOfExecutable
static bool LocateDSYMInVincinityOfExecutable(const ModuleSpec &module_spec, FileSpec &dsym_fspec)
Definition: LocateSymbolFile.cpp:153
Log.h
lldb_private::ConstString::IsEmpty
bool IsEmpty() const
Test for empty string.
Definition: ConstString.h:304
StreamString.h
lldb_private::ConstString
Definition: ConstString.h:40
Timer.h
lldb_private::GetLogIfAllCategoriesSet
Log * GetLogIfAllCategoriesSet(uint32_t mask)
Definition: Logging.cpp:58
cpu_type_t
int cpu_type_t
Definition: LocateSymbolFile.cpp:27
string
string(SUBSTRING ${p} 10 -1 pStripped) if($
Definition: Plugins/CMakeLists.txt:39
lldb_private::ModuleSpecList::FindMatchingModuleSpec
bool FindMatchingModuleSpec(const ModuleSpec &module_spec, ModuleSpec &match_module_spec) const
Definition: ModuleSpec.h:338
LookForDsymNextToExecutablePath
static bool LookForDsymNextToExecutablePath(const ModuleSpec &mod_spec, const FileSpec &exec_fspec, FileSpec &dsym_fspec)
Definition: LocateSymbolFile.cpp:77
ObjectFile.h
lldb_private::FileSpec::IsAbsolute
bool IsAbsolute() const
Returns true if the filespec represents an absolute path.
Definition: FileSpec.cpp:479
lldb_private::FileSpec::Clear
void Clear()
Clears the object state.
Definition: FileSpec.cpp:261
lldb_private::ModuleSpec::GetFileSpec
FileSpec & GetFileSpec()
Definition: ModuleSpec.h:58
lldb_private::ModuleSpec
Definition: ModuleSpec.h:26
lldb_private::ArchSpec::GetArchitectureName
const char * GetArchitectureName() const
Returns a static string representing the current architecture.
Definition: ArchSpec.cpp:538
uint32_t
lldb_private::FileSpec::RemoveLastPathComponent
bool RemoveLastPathComponent()
Removes the last path component by replacing the current path with its parent.
Definition: FileSpec.cpp:446
lldb_private::ModuleSpec::GetArchitecturePtr
ArchSpec * GetArchitecturePtr()
Definition: ModuleSpec.h:86
LocateExecutableSymbolFileDsym
static FileSpec LocateExecutableSymbolFileDsym(const ModuleSpec &module_spec)
Definition: LocateSymbolFile.cpp:207
ArchSpec.h
lldb_private::ConstString::GetCString
const char * GetCString() const
Get the string value as a C string.
Definition: ConstString.h:216
lldb_private::ModuleSpec::GetArchitecture
ArchSpec & GetArchitecture()
Definition: ModuleSpec.h:94
DataExtractor.h
LLDB_SCOPED_TIMERF
#define LLDB_SCOPED_TIMERF(FMT,...)
Definition: Timer.h:99
lldb_private::FileSpec::AppendPathComponent
void AppendPathComponent(llvm::StringRef component)
Definition: FileSpec.cpp:435
lldb_private
A class that represents a running process on the host machine.
Definition: SBCommandInterpreterRunOptions.h:16
lldb_private::FileSpec::GetDirectory
ConstString & GetDirectory()
Directory string get accessor.
Definition: FileSpec.cpp:335
lldb_private::ModuleSpecList::GetModuleSpecAtIndex
bool GetModuleSpecAtIndex(size_t i, ModuleSpec &module_spec) const
Definition: ModuleSpec.h:328
lldb_private::ModuleSpecList::GetSize
size_t GetSize() const
Definition: ModuleSpec.h:303
lldb_private::Log
Definition: Log.h:49
lldb_private::FileSpec::GetPath
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
DataBuffer.h
lldb
Definition: SBAddress.h:15
lldb_private::FileSpec::GetFilename
ConstString & GetFilename()
Filename string get accessor.
Definition: FileSpec.cpp:341