[LoongArch][Clang] Make the parameters and return value of {x,}vorn.v builti ns ...
[llvm-project.git] / lldb / source / Plugins / SymbolLocator / DebugSymbols / SymbolLocatorDebugSymbols.cpp
blobe5ba02952f2a887e63e96fcf323988af3aabf9d6
1 //===-- SymbolLocatorDebugSymbols.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 //===----------------------------------------------------------------------===//
9 #include "SymbolLocatorDebugSymbols.h"
11 #include "Plugins/ObjectFile/wasm/ObjectFileWasm.h"
12 #include "lldb/Core/Debugger.h"
13 #include "lldb/Core/Module.h"
14 #include "lldb/Core/ModuleList.h"
15 #include "lldb/Core/ModuleSpec.h"
16 #include "lldb/Core/PluginManager.h"
17 #include "lldb/Core/Progress.h"
18 #include "lldb/Core/Section.h"
19 #include "lldb/Host/FileSystem.h"
20 #include "lldb/Host/Host.h"
21 #include "lldb/Host/HostInfo.h"
22 #include "lldb/Symbol/ObjectFile.h"
23 #include "lldb/Target/Target.h"
24 #include "lldb/Utility/ArchSpec.h"
25 #include "lldb/Utility/DataBuffer.h"
26 #include "lldb/Utility/DataExtractor.h"
27 #include "lldb/Utility/LLDBLog.h"
28 #include "lldb/Utility/Log.h"
29 #include "lldb/Utility/StreamString.h"
30 #include "lldb/Utility/Timer.h"
31 #include "lldb/Utility/UUID.h"
33 #include "llvm/ADT/SmallSet.h"
34 #include "llvm/Support/FileSystem.h"
35 #include "llvm/Support/ThreadPool.h"
37 #include "Host/macosx/cfcpp/CFCBundle.h"
38 #include "Host/macosx/cfcpp/CFCData.h"
39 #include "Host/macosx/cfcpp/CFCReleaser.h"
40 #include "Host/macosx/cfcpp/CFCString.h"
42 #include "mach/machine.h"
44 #include <CoreFoundation/CoreFoundation.h>
46 #include <cstring>
47 #include <dirent.h>
48 #include <dlfcn.h>
49 #include <optional>
50 #include <pwd.h>
52 using namespace lldb;
53 using namespace lldb_private;
55 static CFURLRef (*g_dlsym_DBGCopyFullDSYMURLForUUID)(
56 CFUUIDRef uuid, CFURLRef exec_url) = nullptr;
57 static CFDictionaryRef (*g_dlsym_DBGCopyDSYMPropertyLists)(CFURLRef dsym_url) =
58 nullptr;
60 LLDB_PLUGIN_DEFINE(SymbolLocatorDebugSymbols)
62 SymbolLocatorDebugSymbols::SymbolLocatorDebugSymbols() : SymbolLocator() {}
64 void SymbolLocatorDebugSymbols::Initialize() {
65 PluginManager::RegisterPlugin(
66 GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance,
67 LocateExecutableObjectFile, LocateExecutableSymbolFile,
68 DownloadObjectAndSymbolFile, FindSymbolFileInBundle);
71 void SymbolLocatorDebugSymbols::Terminate() {
72 PluginManager::UnregisterPlugin(CreateInstance);
75 llvm::StringRef SymbolLocatorDebugSymbols::GetPluginDescriptionStatic() {
76 return "DebugSymbols symbol locator.";
79 SymbolLocator *SymbolLocatorDebugSymbols::CreateInstance() {
80 return new SymbolLocatorDebugSymbols();
83 std::optional<ModuleSpec> SymbolLocatorDebugSymbols::LocateExecutableObjectFile(
84 const ModuleSpec &module_spec) {
85 Log *log = GetLog(LLDBLog::Host);
86 if (!ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup()) {
87 LLDB_LOGF(log, "Spotlight lookup for .dSYM bundles is disabled.");
88 return {};
90 ModuleSpec return_module_spec;
91 return_module_spec = module_spec;
92 return_module_spec.GetFileSpec().Clear();
93 return_module_spec.GetSymbolFileSpec().Clear();
95 const UUID *uuid = module_spec.GetUUIDPtr();
96 const ArchSpec *arch = module_spec.GetArchitecturePtr();
98 int items_found = 0;
100 if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr ||
101 g_dlsym_DBGCopyDSYMPropertyLists == nullptr) {
102 void *handle = dlopen(
103 "/System/Library/PrivateFrameworks/DebugSymbols.framework/DebugSymbols",
104 RTLD_LAZY | RTLD_LOCAL);
105 if (handle) {
106 g_dlsym_DBGCopyFullDSYMURLForUUID =
107 (CFURLRef(*)(CFUUIDRef, CFURLRef))dlsym(handle,
108 "DBGCopyFullDSYMURLForUUID");
109 g_dlsym_DBGCopyDSYMPropertyLists = (CFDictionaryRef(*)(CFURLRef))dlsym(
110 handle, "DBGCopyDSYMPropertyLists");
114 if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr ||
115 g_dlsym_DBGCopyDSYMPropertyLists == nullptr) {
116 return {};
119 if (uuid && uuid->IsValid()) {
120 // Try and locate the dSYM file using DebugSymbols first
121 llvm::ArrayRef<uint8_t> module_uuid = uuid->GetBytes();
122 if (module_uuid.size() == 16) {
123 CFCReleaser<CFUUIDRef> module_uuid_ref(::CFUUIDCreateWithBytes(
124 NULL, module_uuid[0], module_uuid[1], module_uuid[2], module_uuid[3],
125 module_uuid[4], module_uuid[5], module_uuid[6], module_uuid[7],
126 module_uuid[8], module_uuid[9], module_uuid[10], module_uuid[11],
127 module_uuid[12], module_uuid[13], module_uuid[14], module_uuid[15]));
129 if (module_uuid_ref.get()) {
130 CFCReleaser<CFURLRef> exec_url;
131 const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();
132 if (exec_fspec) {
133 char exec_cf_path[PATH_MAX];
134 if (exec_fspec->GetPath(exec_cf_path, sizeof(exec_cf_path)))
135 exec_url.reset(::CFURLCreateFromFileSystemRepresentation(
136 NULL, (const UInt8 *)exec_cf_path, strlen(exec_cf_path),
137 FALSE));
140 CFCReleaser<CFURLRef> dsym_url(g_dlsym_DBGCopyFullDSYMURLForUUID(
141 module_uuid_ref.get(), exec_url.get()));
142 char path[PATH_MAX];
144 if (dsym_url.get()) {
145 if (::CFURLGetFileSystemRepresentation(
146 dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {
147 LLDB_LOGF(log,
148 "DebugSymbols framework returned dSYM path of %s for "
149 "UUID %s -- looking for the dSYM",
150 path, uuid->GetAsString().c_str());
151 FileSpec dsym_filespec(path);
152 if (path[0] == '~')
153 FileSystem::Instance().Resolve(dsym_filespec);
155 if (FileSystem::Instance().IsDirectory(dsym_filespec)) {
156 dsym_filespec = PluginManager::FindSymbolFileInBundle(
157 dsym_filespec, uuid, arch);
158 ++items_found;
159 } else {
160 ++items_found;
162 return_module_spec.GetSymbolFileSpec() = dsym_filespec;
165 bool success = false;
166 if (log) {
167 if (::CFURLGetFileSystemRepresentation(
168 dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {
169 LLDB_LOGF(log,
170 "DebugSymbols framework returned dSYM path of %s for "
171 "UUID %s -- looking for an exec file",
172 path, uuid->GetAsString().c_str());
176 CFCReleaser<CFDictionaryRef> dict(
177 g_dlsym_DBGCopyDSYMPropertyLists(dsym_url.get()));
178 CFDictionaryRef uuid_dict = NULL;
179 if (dict.get()) {
180 CFCString uuid_cfstr(uuid->GetAsString().c_str());
181 uuid_dict = static_cast<CFDictionaryRef>(
182 ::CFDictionaryGetValue(dict.get(), uuid_cfstr.get()));
185 // Check to see if we have the file on the local filesystem.
186 if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) {
187 ModuleSpec exe_spec;
188 exe_spec.GetFileSpec() = module_spec.GetFileSpec();
189 exe_spec.GetUUID() = module_spec.GetUUID();
190 ModuleSP module_sp;
191 module_sp.reset(new Module(exe_spec));
192 if (module_sp && module_sp->GetObjectFile() &&
193 module_sp->MatchesModuleSpec(exe_spec)) {
194 success = true;
195 return_module_spec.GetFileSpec() = module_spec.GetFileSpec();
196 LLDB_LOGF(log, "using original binary filepath %s for UUID %s",
197 module_spec.GetFileSpec().GetPath().c_str(),
198 uuid->GetAsString().c_str());
199 ++items_found;
203 // Check if the requested image is in our shared cache.
204 if (!success) {
205 SharedCacheImageInfo image_info = HostInfo::GetSharedCacheImageInfo(
206 module_spec.GetFileSpec().GetPath());
208 // If we found it and it has the correct UUID, let's proceed with
209 // creating a module from the memory contents.
210 if (image_info.uuid && (!module_spec.GetUUID() ||
211 module_spec.GetUUID() == image_info.uuid)) {
212 success = true;
213 return_module_spec.GetFileSpec() = module_spec.GetFileSpec();
214 LLDB_LOGF(log,
215 "using binary from shared cache for filepath %s for "
216 "UUID %s",
217 module_spec.GetFileSpec().GetPath().c_str(),
218 uuid->GetAsString().c_str());
219 ++items_found;
223 // Use the DBGSymbolRichExecutable filepath if present
224 if (!success && uuid_dict) {
225 CFStringRef exec_cf_path =
226 static_cast<CFStringRef>(::CFDictionaryGetValue(
227 uuid_dict, CFSTR("DBGSymbolRichExecutable")));
228 if (exec_cf_path && ::CFStringGetFileSystemRepresentation(
229 exec_cf_path, path, sizeof(path))) {
230 LLDB_LOGF(log, "plist bundle has exec path of %s for UUID %s",
231 path, uuid->GetAsString().c_str());
232 ++items_found;
233 FileSpec exec_filespec(path);
234 if (path[0] == '~')
235 FileSystem::Instance().Resolve(exec_filespec);
236 if (FileSystem::Instance().Exists(exec_filespec)) {
237 success = true;
238 return_module_spec.GetFileSpec() = exec_filespec;
243 // Look next to the dSYM for the binary file.
244 if (!success) {
245 if (::CFURLGetFileSystemRepresentation(
246 dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {
247 char *dsym_extension_pos = ::strstr(path, ".dSYM");
248 if (dsym_extension_pos) {
249 *dsym_extension_pos = '\0';
250 LLDB_LOGF(log,
251 "Looking for executable binary next to dSYM "
252 "bundle with name with name %s",
253 path);
254 FileSpec file_spec(path);
255 FileSystem::Instance().Resolve(file_spec);
256 ModuleSpecList module_specs;
257 ModuleSpec matched_module_spec;
258 using namespace llvm::sys::fs;
259 switch (get_file_type(file_spec.GetPath())) {
261 case file_type::directory_file: // Bundle directory?
263 CFCBundle bundle(path);
264 CFCReleaser<CFURLRef> bundle_exe_url(
265 bundle.CopyExecutableURL());
266 if (bundle_exe_url.get()) {
267 if (::CFURLGetFileSystemRepresentation(bundle_exe_url.get(),
268 true, (UInt8 *)path,
269 sizeof(path) - 1)) {
270 FileSpec bundle_exe_file_spec(path);
271 FileSystem::Instance().Resolve(bundle_exe_file_spec);
272 if (ObjectFile::GetModuleSpecifications(
273 bundle_exe_file_spec, 0, 0, module_specs) &&
274 module_specs.FindMatchingModuleSpec(
275 module_spec, matched_module_spec))
278 ++items_found;
279 return_module_spec.GetFileSpec() = bundle_exe_file_spec;
280 LLDB_LOGF(log,
281 "Executable binary %s next to dSYM is "
282 "compatible; using",
283 path);
287 } break;
289 case file_type::fifo_file: // Forget pipes
290 case file_type::socket_file: // We can't process socket files
291 case file_type::file_not_found: // File doesn't exist...
292 case file_type::status_error:
293 break;
295 case file_type::type_unknown:
296 case file_type::regular_file:
297 case file_type::symlink_file:
298 case file_type::block_file:
299 case file_type::character_file:
300 if (ObjectFile::GetModuleSpecifications(file_spec, 0, 0,
301 module_specs) &&
302 module_specs.FindMatchingModuleSpec(module_spec,
303 matched_module_spec))
306 ++items_found;
307 return_module_spec.GetFileSpec() = file_spec;
308 LLDB_LOGF(log,
309 "Executable binary %s next to dSYM is "
310 "compatible; using",
311 path);
313 break;
323 if (items_found)
324 return return_module_spec;
326 return {};
329 std::optional<FileSpec> SymbolLocatorDebugSymbols::FindSymbolFileInBundle(
330 const FileSpec &dsym_bundle_fspec, const UUID *uuid, const ArchSpec *arch) {
331 std::string dsym_bundle_path = dsym_bundle_fspec.GetPath();
332 llvm::SmallString<128> buffer(dsym_bundle_path);
333 llvm::sys::path::append(buffer, "Contents", "Resources", "DWARF");
335 std::error_code EC;
336 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs =
337 FileSystem::Instance().GetVirtualFileSystem();
338 llvm::vfs::recursive_directory_iterator Iter(*vfs, buffer.str(), EC);
339 llvm::vfs::recursive_directory_iterator End;
340 for (; Iter != End && !EC; Iter.increment(EC)) {
341 llvm::ErrorOr<llvm::vfs::Status> Status = vfs->status(Iter->path());
342 if (Status->isDirectory())
343 continue;
345 FileSpec dsym_fspec(Iter->path());
346 ModuleSpecList module_specs;
347 if (ObjectFile::GetModuleSpecifications(dsym_fspec, 0, 0, module_specs)) {
348 ModuleSpec spec;
349 for (size_t i = 0; i < module_specs.GetSize(); ++i) {
350 bool got_spec = module_specs.GetModuleSpecAtIndex(i, spec);
351 assert(got_spec); // The call has side-effects so can't be inlined.
352 UNUSED_IF_ASSERT_DISABLED(got_spec);
353 if ((uuid == nullptr ||
354 (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) &&
355 (arch == nullptr ||
356 (spec.GetArchitecturePtr() &&
357 spec.GetArchitecture().IsCompatibleMatch(*arch)))) {
358 return dsym_fspec;
364 return {};
367 static bool FileAtPathContainsArchAndUUID(const FileSpec &file_fspec,
368 const ArchSpec *arch,
369 const lldb_private::UUID *uuid) {
370 ModuleSpecList module_specs;
371 if (ObjectFile::GetModuleSpecifications(file_fspec, 0, 0, module_specs)) {
372 ModuleSpec spec;
373 for (size_t i = 0; i < module_specs.GetSize(); ++i) {
374 bool got_spec = module_specs.GetModuleSpecAtIndex(i, spec);
375 UNUSED_IF_ASSERT_DISABLED(got_spec);
376 assert(got_spec);
377 if ((uuid == nullptr || (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) &&
378 (arch == nullptr ||
379 (spec.GetArchitecturePtr() &&
380 spec.GetArchitecture().IsCompatibleMatch(*arch)))) {
381 return true;
385 return false;
388 // Given a binary exec_fspec, and a ModuleSpec with an architecture/uuid,
389 // return true if there is a matching dSYM bundle next to the exec_fspec,
390 // and return that value in dsym_fspec.
391 // If there is a .dSYM.yaa compressed archive next to the exec_fspec,
392 // call through PluginManager::DownloadObjectAndSymbolFile to download the
393 // expanded/uncompressed dSYM and return that filepath in dsym_fspec.
394 static bool LookForDsymNextToExecutablePath(const ModuleSpec &mod_spec,
395 const FileSpec &exec_fspec,
396 FileSpec &dsym_fspec) {
397 ConstString filename = exec_fspec.GetFilename();
398 FileSpec dsym_directory = exec_fspec;
399 dsym_directory.RemoveLastPathComponent();
401 std::string dsym_filename = filename.AsCString();
402 dsym_filename += ".dSYM";
403 dsym_directory.AppendPathComponent(dsym_filename);
404 dsym_directory.AppendPathComponent("Contents");
405 dsym_directory.AppendPathComponent("Resources");
406 dsym_directory.AppendPathComponent("DWARF");
408 if (FileSystem::Instance().Exists(dsym_directory)) {
410 // See if the binary name exists in the dSYM DWARF
411 // subdir.
412 dsym_fspec = dsym_directory;
413 dsym_fspec.AppendPathComponent(filename.AsCString());
414 if (FileSystem::Instance().Exists(dsym_fspec) &&
415 FileAtPathContainsArchAndUUID(dsym_fspec, mod_spec.GetArchitecturePtr(),
416 mod_spec.GetUUIDPtr())) {
417 return true;
420 // See if we have "../CF.framework" - so we'll look for
421 // CF.framework.dSYM/Contents/Resources/DWARF/CF
422 // We need to drop the last suffix after '.' to match
423 // 'CF' in the DWARF subdir.
424 std::string binary_name(filename.AsCString());
425 auto last_dot = binary_name.find_last_of('.');
426 if (last_dot != std::string::npos) {
427 binary_name.erase(last_dot);
428 dsym_fspec = dsym_directory;
429 dsym_fspec.AppendPathComponent(binary_name);
430 if (FileSystem::Instance().Exists(dsym_fspec) &&
431 FileAtPathContainsArchAndUUID(dsym_fspec,
432 mod_spec.GetArchitecturePtr(),
433 mod_spec.GetUUIDPtr())) {
434 return true;
439 // See if we have a .dSYM.yaa next to this executable path.
440 FileSpec dsym_yaa_fspec = exec_fspec;
441 dsym_yaa_fspec.RemoveLastPathComponent();
442 std::string dsym_yaa_filename = filename.AsCString();
443 dsym_yaa_filename += ".dSYM.yaa";
444 dsym_yaa_fspec.AppendPathComponent(dsym_yaa_filename);
446 if (FileSystem::Instance().Exists(dsym_yaa_fspec)) {
447 ModuleSpec mutable_mod_spec = mod_spec;
448 Status error;
449 if (PluginManager::DownloadObjectAndSymbolFile(mutable_mod_spec, error,
450 true) &&
451 FileSystem::Instance().Exists(mutable_mod_spec.GetSymbolFileSpec())) {
452 dsym_fspec = mutable_mod_spec.GetSymbolFileSpec();
453 return true;
457 return false;
460 // Given a ModuleSpec with a FileSpec and optionally uuid/architecture
461 // filled in, look for a .dSYM bundle next to that binary. Returns true
462 // if a .dSYM bundle is found, and that path is returned in the dsym_fspec
463 // FileSpec.
465 // This routine looks a few directory layers above the given exec_path -
466 // exec_path might be /System/Library/Frameworks/CF.framework/CF and the
467 // dSYM might be /System/Library/Frameworks/CF.framework.dSYM.
469 // If there is a .dSYM.yaa compressed archive found next to the binary,
470 // we'll call DownloadObjectAndSymbolFile to expand it into a plain .dSYM
471 static bool LocateDSYMInVincinityOfExecutable(const ModuleSpec &module_spec,
472 FileSpec &dsym_fspec) {
473 Log *log = GetLog(LLDBLog::Host);
474 const FileSpec &exec_fspec = module_spec.GetFileSpec();
475 if (exec_fspec) {
476 if (::LookForDsymNextToExecutablePath(module_spec, exec_fspec,
477 dsym_fspec)) {
478 if (log) {
479 LLDB_LOGF(log, "dSYM with matching UUID & arch found at %s",
480 dsym_fspec.GetPath().c_str());
482 return true;
483 } else {
484 FileSpec parent_dirs = exec_fspec;
486 // Remove the binary name from the FileSpec
487 parent_dirs.RemoveLastPathComponent();
489 // Add a ".dSYM" name to each directory component of the path,
490 // stripping off components. e.g. we may have a binary like
491 // /S/L/F/Foundation.framework/Versions/A/Foundation and
492 // /S/L/F/Foundation.framework.dSYM
494 // so we'll need to start with
495 // /S/L/F/Foundation.framework/Versions/A, add the .dSYM part to the
496 // "A", and if that doesn't exist, strip off the "A" and try it again
497 // with "Versions", etc., until we find a dSYM bundle or we've
498 // stripped off enough path components that there's no need to
499 // continue.
501 for (int i = 0; i < 4; i++) {
502 // Does this part of the path have a "." character - could it be a
503 // bundle's top level directory?
504 const char *fn = parent_dirs.GetFilename().AsCString();
505 if (fn == nullptr)
506 break;
507 if (::strchr(fn, '.') != nullptr) {
508 if (::LookForDsymNextToExecutablePath(module_spec, parent_dirs,
509 dsym_fspec)) {
510 if (log) {
511 LLDB_LOGF(log, "dSYM with matching UUID & arch found at %s",
512 dsym_fspec.GetPath().c_str());
514 return true;
517 parent_dirs.RemoveLastPathComponent();
521 dsym_fspec.Clear();
522 return false;
525 static int LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec &module_spec,
526 ModuleSpec &return_module_spec) {
527 Log *log = GetLog(LLDBLog::Host);
528 if (!ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup()) {
529 LLDB_LOGF(log, "Spotlight lookup for .dSYM bundles is disabled.");
530 return 0;
533 return_module_spec = module_spec;
534 return_module_spec.GetFileSpec().Clear();
535 return_module_spec.GetSymbolFileSpec().Clear();
537 const UUID *uuid = module_spec.GetUUIDPtr();
538 const ArchSpec *arch = module_spec.GetArchitecturePtr();
540 int items_found = 0;
542 if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr ||
543 g_dlsym_DBGCopyDSYMPropertyLists == nullptr) {
544 void *handle = dlopen(
545 "/System/Library/PrivateFrameworks/DebugSymbols.framework/DebugSymbols",
546 RTLD_LAZY | RTLD_LOCAL);
547 if (handle) {
548 g_dlsym_DBGCopyFullDSYMURLForUUID =
549 (CFURLRef(*)(CFUUIDRef, CFURLRef))dlsym(handle,
550 "DBGCopyFullDSYMURLForUUID");
551 g_dlsym_DBGCopyDSYMPropertyLists = (CFDictionaryRef(*)(CFURLRef))dlsym(
552 handle, "DBGCopyDSYMPropertyLists");
556 if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr ||
557 g_dlsym_DBGCopyDSYMPropertyLists == nullptr) {
558 return items_found;
561 if (uuid && uuid->IsValid()) {
562 // Try and locate the dSYM file using DebugSymbols first
563 llvm::ArrayRef<uint8_t> module_uuid = uuid->GetBytes();
564 if (module_uuid.size() == 16) {
565 CFCReleaser<CFUUIDRef> module_uuid_ref(::CFUUIDCreateWithBytes(
566 NULL, module_uuid[0], module_uuid[1], module_uuid[2], module_uuid[3],
567 module_uuid[4], module_uuid[5], module_uuid[6], module_uuid[7],
568 module_uuid[8], module_uuid[9], module_uuid[10], module_uuid[11],
569 module_uuid[12], module_uuid[13], module_uuid[14], module_uuid[15]));
571 if (module_uuid_ref.get()) {
572 CFCReleaser<CFURLRef> exec_url;
573 const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();
574 if (exec_fspec) {
575 char exec_cf_path[PATH_MAX];
576 if (exec_fspec->GetPath(exec_cf_path, sizeof(exec_cf_path)))
577 exec_url.reset(::CFURLCreateFromFileSystemRepresentation(
578 NULL, (const UInt8 *)exec_cf_path, strlen(exec_cf_path),
579 FALSE));
582 CFCReleaser<CFURLRef> dsym_url(g_dlsym_DBGCopyFullDSYMURLForUUID(
583 module_uuid_ref.get(), exec_url.get()));
584 char path[PATH_MAX];
586 if (dsym_url.get()) {
587 if (::CFURLGetFileSystemRepresentation(
588 dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {
589 LLDB_LOGF(log,
590 "DebugSymbols framework returned dSYM path of %s for "
591 "UUID %s -- looking for the dSYM",
592 path, uuid->GetAsString().c_str());
593 FileSpec dsym_filespec(path);
594 if (path[0] == '~')
595 FileSystem::Instance().Resolve(dsym_filespec);
597 if (FileSystem::Instance().IsDirectory(dsym_filespec)) {
598 dsym_filespec = PluginManager::FindSymbolFileInBundle(
599 dsym_filespec, uuid, arch);
600 ++items_found;
601 } else {
602 ++items_found;
604 return_module_spec.GetSymbolFileSpec() = dsym_filespec;
607 bool success = false;
608 if (log) {
609 if (::CFURLGetFileSystemRepresentation(
610 dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {
611 LLDB_LOGF(log,
612 "DebugSymbols framework returned dSYM path of %s for "
613 "UUID %s -- looking for an exec file",
614 path, uuid->GetAsString().c_str());
618 CFCReleaser<CFDictionaryRef> dict(
619 g_dlsym_DBGCopyDSYMPropertyLists(dsym_url.get()));
620 CFDictionaryRef uuid_dict = NULL;
621 if (dict.get()) {
622 CFCString uuid_cfstr(uuid->GetAsString().c_str());
623 uuid_dict = static_cast<CFDictionaryRef>(
624 ::CFDictionaryGetValue(dict.get(), uuid_cfstr.get()));
627 // Check to see if we have the file on the local filesystem.
628 if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) {
629 ModuleSpec exe_spec;
630 exe_spec.GetFileSpec() = module_spec.GetFileSpec();
631 exe_spec.GetUUID() = module_spec.GetUUID();
632 ModuleSP module_sp;
633 module_sp.reset(new Module(exe_spec));
634 if (module_sp && module_sp->GetObjectFile() &&
635 module_sp->MatchesModuleSpec(exe_spec)) {
636 success = true;
637 return_module_spec.GetFileSpec() = module_spec.GetFileSpec();
638 LLDB_LOGF(log, "using original binary filepath %s for UUID %s",
639 module_spec.GetFileSpec().GetPath().c_str(),
640 uuid->GetAsString().c_str());
641 ++items_found;
645 // Check if the requested image is in our shared cache.
646 if (!success) {
647 SharedCacheImageInfo image_info = HostInfo::GetSharedCacheImageInfo(
648 module_spec.GetFileSpec().GetPath());
650 // If we found it and it has the correct UUID, let's proceed with
651 // creating a module from the memory contents.
652 if (image_info.uuid && (!module_spec.GetUUID() ||
653 module_spec.GetUUID() == image_info.uuid)) {
654 success = true;
655 return_module_spec.GetFileSpec() = module_spec.GetFileSpec();
656 LLDB_LOGF(log,
657 "using binary from shared cache for filepath %s for "
658 "UUID %s",
659 module_spec.GetFileSpec().GetPath().c_str(),
660 uuid->GetAsString().c_str());
661 ++items_found;
665 // Use the DBGSymbolRichExecutable filepath if present
666 if (!success && uuid_dict) {
667 CFStringRef exec_cf_path =
668 static_cast<CFStringRef>(::CFDictionaryGetValue(
669 uuid_dict, CFSTR("DBGSymbolRichExecutable")));
670 if (exec_cf_path && ::CFStringGetFileSystemRepresentation(
671 exec_cf_path, path, sizeof(path))) {
672 LLDB_LOGF(log, "plist bundle has exec path of %s for UUID %s",
673 path, uuid->GetAsString().c_str());
674 ++items_found;
675 FileSpec exec_filespec(path);
676 if (path[0] == '~')
677 FileSystem::Instance().Resolve(exec_filespec);
678 if (FileSystem::Instance().Exists(exec_filespec)) {
679 success = true;
680 return_module_spec.GetFileSpec() = exec_filespec;
685 // Look next to the dSYM for the binary file.
686 if (!success) {
687 if (::CFURLGetFileSystemRepresentation(
688 dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {
689 char *dsym_extension_pos = ::strstr(path, ".dSYM");
690 if (dsym_extension_pos) {
691 *dsym_extension_pos = '\0';
692 LLDB_LOGF(log,
693 "Looking for executable binary next to dSYM "
694 "bundle with name with name %s",
695 path);
696 FileSpec file_spec(path);
697 FileSystem::Instance().Resolve(file_spec);
698 ModuleSpecList module_specs;
699 ModuleSpec matched_module_spec;
700 using namespace llvm::sys::fs;
701 switch (get_file_type(file_spec.GetPath())) {
703 case file_type::directory_file: // Bundle directory?
705 CFCBundle bundle(path);
706 CFCReleaser<CFURLRef> bundle_exe_url(
707 bundle.CopyExecutableURL());
708 if (bundle_exe_url.get()) {
709 if (::CFURLGetFileSystemRepresentation(bundle_exe_url.get(),
710 true, (UInt8 *)path,
711 sizeof(path) - 1)) {
712 FileSpec bundle_exe_file_spec(path);
713 FileSystem::Instance().Resolve(bundle_exe_file_spec);
714 if (ObjectFile::GetModuleSpecifications(
715 bundle_exe_file_spec, 0, 0, module_specs) &&
716 module_specs.FindMatchingModuleSpec(
717 module_spec, matched_module_spec))
720 ++items_found;
721 return_module_spec.GetFileSpec() = bundle_exe_file_spec;
722 LLDB_LOGF(log,
723 "Executable binary %s next to dSYM is "
724 "compatible; using",
725 path);
729 } break;
731 case file_type::fifo_file: // Forget pipes
732 case file_type::socket_file: // We can't process socket files
733 case file_type::file_not_found: // File doesn't exist...
734 case file_type::status_error:
735 break;
737 case file_type::type_unknown:
738 case file_type::regular_file:
739 case file_type::symlink_file:
740 case file_type::block_file:
741 case file_type::character_file:
742 if (ObjectFile::GetModuleSpecifications(file_spec, 0, 0,
743 module_specs) &&
744 module_specs.FindMatchingModuleSpec(module_spec,
745 matched_module_spec))
748 ++items_found;
749 return_module_spec.GetFileSpec() = file_spec;
750 LLDB_LOGF(log,
751 "Executable binary %s next to dSYM is "
752 "compatible; using",
753 path);
755 break;
765 return items_found;
768 std::optional<FileSpec> SymbolLocatorDebugSymbols::LocateExecutableSymbolFile(
769 const ModuleSpec &module_spec, const FileSpecList &default_search_paths) {
770 const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();
771 const ArchSpec *arch = module_spec.GetArchitecturePtr();
772 const UUID *uuid = module_spec.GetUUIDPtr();
774 LLDB_SCOPED_TIMERF(
775 "LocateExecutableSymbolFileDsym (file = %s, arch = %s, uuid = %p)",
776 exec_fspec ? exec_fspec->GetFilename().AsCString("<NULL>") : "<NULL>",
777 arch ? arch->GetArchitectureName() : "<NULL>", (const void *)uuid);
779 Progress progress(
780 "Locating external symbol file",
781 module_spec.GetFileSpec().GetFilename().AsCString("<Unknown>"));
783 FileSpec symbol_fspec;
784 ModuleSpec dsym_module_spec;
785 // First try and find the dSYM in the same directory as the executable or in
786 // an appropriate parent directory
787 if (!LocateDSYMInVincinityOfExecutable(module_spec, symbol_fspec)) {
788 // We failed to easily find the dSYM above, so use DebugSymbols
789 LocateMacOSXFilesUsingDebugSymbols(module_spec, dsym_module_spec);
790 } else {
791 dsym_module_spec.GetSymbolFileSpec() = symbol_fspec;
794 return dsym_module_spec.GetSymbolFileSpec();
797 static bool GetModuleSpecInfoFromUUIDDictionary(CFDictionaryRef uuid_dict,
798 ModuleSpec &module_spec,
799 Status &error,
800 const std::string &command) {
801 Log *log = GetLog(LLDBLog::Host);
802 bool success = false;
803 if (uuid_dict != NULL && CFGetTypeID(uuid_dict) == CFDictionaryGetTypeID()) {
804 std::string str;
805 CFStringRef cf_str;
806 CFDictionaryRef cf_dict;
808 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
809 CFSTR("DBGError"));
810 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
811 if (CFCString::FileSystemRepresentation(cf_str, str)) {
812 std::string errorstr = command;
813 errorstr += ":\n";
814 errorstr += str;
815 error = Status(errorstr);
819 cf_str = (CFStringRef)CFDictionaryGetValue(
820 (CFDictionaryRef)uuid_dict, CFSTR("DBGSymbolRichExecutable"));
821 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
822 if (CFCString::FileSystemRepresentation(cf_str, str)) {
823 module_spec.GetFileSpec().SetFile(str.c_str(), FileSpec::Style::native);
824 FileSystem::Instance().Resolve(module_spec.GetFileSpec());
825 LLDB_LOGF(log,
826 "From dsymForUUID plist: Symbol rich executable is at '%s'",
827 str.c_str());
831 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
832 CFSTR("DBGDSYMPath"));
833 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
834 if (CFCString::FileSystemRepresentation(cf_str, str)) {
835 module_spec.GetSymbolFileSpec().SetFile(str.c_str(),
836 FileSpec::Style::native);
837 FileSystem::Instance().Resolve(module_spec.GetFileSpec());
838 success = true;
839 LLDB_LOGF(log, "From dsymForUUID plist: dSYM is at '%s'", str.c_str());
843 std::string DBGBuildSourcePath;
844 std::string DBGSourcePath;
846 // If DBGVersion 1 or DBGVersion missing, ignore DBGSourcePathRemapping.
847 // If DBGVersion 2, strip last two components of path remappings from
848 // entries to fix an issue with a specific set of
849 // DBGSourcePathRemapping entries that lldb worked
850 // with.
851 // If DBGVersion 3, trust & use the source path remappings as-is.
853 cf_dict = (CFDictionaryRef)CFDictionaryGetValue(
854 (CFDictionaryRef)uuid_dict, CFSTR("DBGSourcePathRemapping"));
855 if (cf_dict && CFGetTypeID(cf_dict) == CFDictionaryGetTypeID()) {
856 // If we see DBGVersion with a value of 2 or higher, this is a new style
857 // DBGSourcePathRemapping dictionary
858 bool new_style_source_remapping_dictionary = false;
859 bool do_truncate_remapping_names = false;
860 std::string original_DBGSourcePath_value = DBGSourcePath;
861 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
862 CFSTR("DBGVersion"));
863 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
864 std::string version;
865 CFCString::FileSystemRepresentation(cf_str, version);
866 if (!version.empty() && isdigit(version[0])) {
867 int version_number = atoi(version.c_str());
868 if (version_number > 1) {
869 new_style_source_remapping_dictionary = true;
871 if (version_number == 2) {
872 do_truncate_remapping_names = true;
877 CFIndex kv_pair_count = CFDictionaryGetCount((CFDictionaryRef)uuid_dict);
878 if (kv_pair_count > 0) {
879 CFStringRef *keys =
880 (CFStringRef *)malloc(kv_pair_count * sizeof(CFStringRef));
881 CFStringRef *values =
882 (CFStringRef *)malloc(kv_pair_count * sizeof(CFStringRef));
883 if (keys != nullptr && values != nullptr) {
884 CFDictionaryGetKeysAndValues((CFDictionaryRef)uuid_dict,
885 (const void **)keys,
886 (const void **)values);
888 for (CFIndex i = 0; i < kv_pair_count; i++) {
889 DBGBuildSourcePath.clear();
890 DBGSourcePath.clear();
891 if (keys[i] && CFGetTypeID(keys[i]) == CFStringGetTypeID()) {
892 CFCString::FileSystemRepresentation(keys[i], DBGBuildSourcePath);
894 if (values[i] && CFGetTypeID(values[i]) == CFStringGetTypeID()) {
895 CFCString::FileSystemRepresentation(values[i], DBGSourcePath);
897 if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) {
898 // In the "old style" DBGSourcePathRemapping dictionary, the
899 // DBGSourcePath values (the "values" half of key-value path pairs)
900 // were wrong. Ignore them and use the universal DBGSourcePath
901 // string from earlier.
902 if (new_style_source_remapping_dictionary &&
903 !original_DBGSourcePath_value.empty()) {
904 DBGSourcePath = original_DBGSourcePath_value;
906 if (DBGSourcePath[0] == '~') {
907 FileSpec resolved_source_path(DBGSourcePath.c_str());
908 FileSystem::Instance().Resolve(resolved_source_path);
909 DBGSourcePath = resolved_source_path.GetPath();
911 // With version 2 of DBGSourcePathRemapping, we can chop off the
912 // last two filename parts from the source remapping and get a more
913 // general source remapping that still works. Add this as another
914 // option in addition to the full source path remap.
915 module_spec.GetSourceMappingList().Append(DBGBuildSourcePath,
916 DBGSourcePath, true);
917 if (do_truncate_remapping_names) {
918 FileSpec build_path(DBGBuildSourcePath.c_str());
919 FileSpec source_path(DBGSourcePath.c_str());
920 build_path.RemoveLastPathComponent();
921 build_path.RemoveLastPathComponent();
922 source_path.RemoveLastPathComponent();
923 source_path.RemoveLastPathComponent();
924 module_spec.GetSourceMappingList().Append(
925 build_path.GetPath(), source_path.GetPath(), true);
929 if (keys)
930 free(keys);
931 if (values)
932 free(values);
936 // If we have a DBGBuildSourcePath + DBGSourcePath pair, append them to the
937 // source remappings list.
939 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
940 CFSTR("DBGBuildSourcePath"));
941 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
942 CFCString::FileSystemRepresentation(cf_str, DBGBuildSourcePath);
945 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
946 CFSTR("DBGSourcePath"));
947 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
948 CFCString::FileSystemRepresentation(cf_str, DBGSourcePath);
951 if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) {
952 if (DBGSourcePath[0] == '~') {
953 FileSpec resolved_source_path(DBGSourcePath.c_str());
954 FileSystem::Instance().Resolve(resolved_source_path);
955 DBGSourcePath = resolved_source_path.GetPath();
957 module_spec.GetSourceMappingList().Append(DBGBuildSourcePath,
958 DBGSourcePath, true);
961 return success;
964 /// It's expensive to check for the DBGShellCommands defaults setting. Only do
965 /// it once per lldb run and cache the result.
966 static llvm::StringRef GetDbgShellCommand() {
967 static std::once_flag g_once_flag;
968 static std::string g_dbgshell_command;
969 std::call_once(g_once_flag, [&]() {
970 CFTypeRef defaults_setting = CFPreferencesCopyAppValue(
971 CFSTR("DBGShellCommands"), CFSTR("com.apple.DebugSymbols"));
972 if (defaults_setting &&
973 CFGetTypeID(defaults_setting) == CFStringGetTypeID()) {
974 char buffer[PATH_MAX];
975 if (CFStringGetCString((CFStringRef)defaults_setting, buffer,
976 sizeof(buffer), kCFStringEncodingUTF8)) {
977 g_dbgshell_command = buffer;
980 if (defaults_setting) {
981 CFRelease(defaults_setting);
984 return g_dbgshell_command;
987 /// Get the dsymForUUID executable and cache the result so we don't end up
988 /// stat'ing the binary over and over.
989 static FileSpec GetDsymForUUIDExecutable() {
990 // The LLDB_APPLE_DSYMFORUUID_EXECUTABLE environment variable is used by the
991 // test suite to override the dsymForUUID location. Because we must be able
992 // to change the value within a single test, don't bother caching it.
993 if (const char *dsymForUUID_env =
994 getenv("LLDB_APPLE_DSYMFORUUID_EXECUTABLE")) {
995 FileSpec dsymForUUID_executable(dsymForUUID_env);
996 FileSystem::Instance().Resolve(dsymForUUID_executable);
997 if (FileSystem::Instance().Exists(dsymForUUID_executable))
998 return dsymForUUID_executable;
1001 static std::once_flag g_once_flag;
1002 static FileSpec g_dsymForUUID_executable;
1003 std::call_once(g_once_flag, [&]() {
1004 // Try the DBGShellCommand.
1005 llvm::StringRef dbgshell_command = GetDbgShellCommand();
1006 if (!dbgshell_command.empty()) {
1007 g_dsymForUUID_executable = FileSpec(dbgshell_command);
1008 FileSystem::Instance().Resolve(g_dsymForUUID_executable);
1009 if (FileSystem::Instance().Exists(g_dsymForUUID_executable))
1010 return;
1013 // Try dsymForUUID in /usr/local/bin
1015 g_dsymForUUID_executable = FileSpec("/usr/local/bin/dsymForUUID");
1016 if (FileSystem::Instance().Exists(g_dsymForUUID_executable))
1017 return;
1020 // We couldn't find the dsymForUUID binary.
1021 g_dsymForUUID_executable = {};
1023 return g_dsymForUUID_executable;
1026 bool SymbolLocatorDebugSymbols::DownloadObjectAndSymbolFile(
1027 ModuleSpec &module_spec, Status &error, bool force_lookup,
1028 bool copy_executable) {
1029 const UUID *uuid_ptr = module_spec.GetUUIDPtr();
1030 const FileSpec *file_spec_ptr = module_spec.GetFileSpecPtr();
1032 // If \a dbgshell_command is set, the user has specified
1033 // forced symbol lookup via that command. We'll get the
1034 // path back from GetDsymForUUIDExecutable() later.
1035 llvm::StringRef dbgshell_command = GetDbgShellCommand();
1037 // If forced lookup isn't set, by the user's \a dbgshell_command or
1038 // by the \a force_lookup argument, exit this method.
1039 if (!force_lookup && dbgshell_command.empty())
1040 return false;
1042 // We need a UUID or valid existing FileSpec.
1043 if (!uuid_ptr &&
1044 (!file_spec_ptr || !FileSystem::Instance().Exists(*file_spec_ptr)))
1045 return false;
1047 // We need a dsymForUUID binary or an equivalent executable/script.
1048 FileSpec dsymForUUID_exe_spec = GetDsymForUUIDExecutable();
1049 if (!dsymForUUID_exe_spec)
1050 return false;
1052 // Create the dsymForUUID command.
1053 const std::string dsymForUUID_exe_path = dsymForUUID_exe_spec.GetPath();
1054 const std::string uuid_str = uuid_ptr ? uuid_ptr->GetAsString() : "";
1056 std::string lookup_arg = uuid_str;
1057 if (lookup_arg.empty())
1058 lookup_arg = file_spec_ptr ? file_spec_ptr->GetPath() : "";
1059 if (lookup_arg.empty())
1060 return false;
1062 StreamString command;
1063 command << dsymForUUID_exe_path << " --ignoreNegativeCache ";
1064 if (copy_executable)
1065 command << "--copyExecutable ";
1066 command << lookup_arg;
1068 // Log and report progress.
1069 std::string lookup_desc;
1070 if (uuid_ptr && file_spec_ptr)
1071 lookup_desc =
1072 llvm::formatv("{0} ({1})", file_spec_ptr->GetFilename().GetString(),
1073 uuid_ptr->GetAsString());
1074 else if (uuid_ptr)
1075 lookup_desc = uuid_ptr->GetAsString();
1076 else if (file_spec_ptr)
1077 lookup_desc = file_spec_ptr->GetFilename().GetString();
1079 Log *log = GetLog(LLDBLog::Host);
1080 LLDB_LOG(log, "Calling {0} for {1} to find dSYM: {2}", dsymForUUID_exe_path,
1081 lookup_desc, command.GetString());
1083 Progress progress("Downloading symbol file for", lookup_desc);
1085 // Invoke dsymForUUID.
1086 int exit_status = -1;
1087 int signo = -1;
1088 std::string command_output;
1089 error = Host::RunShellCommand(
1090 command.GetData(),
1091 FileSpec(), // current working directory
1092 &exit_status, // Exit status
1093 &signo, // Signal int *
1094 &command_output, // Command output
1095 std::chrono::seconds(
1096 640), // Large timeout to allow for long dsym download times
1097 false); // Don't run in a shell (we don't need shell expansion)
1099 if (error.Fail() || exit_status != 0 || command_output.empty()) {
1100 LLDB_LOGF(log, "'%s' failed (exit status: %d, error: '%s', output: '%s')",
1101 command.GetData(), exit_status, error.AsCString(),
1102 command_output.c_str());
1103 return false;
1106 CFCData data(
1107 CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)command_output.data(),
1108 command_output.size(), kCFAllocatorNull));
1110 CFCReleaser<CFDictionaryRef> plist(
1111 (CFDictionaryRef)::CFPropertyListCreateWithData(
1112 NULL, data.get(), kCFPropertyListImmutable, NULL, NULL));
1114 if (!plist.get()) {
1115 LLDB_LOGF(log, "'%s' failed: output is not a valid plist",
1116 command.GetData());
1117 return false;
1120 if (CFGetTypeID(plist.get()) != CFDictionaryGetTypeID()) {
1121 LLDB_LOGF(log, "'%s' failed: output plist is not a valid CFDictionary",
1122 command.GetData());
1123 return false;
1126 if (!uuid_str.empty()) {
1127 CFCString uuid_cfstr(uuid_str.c_str());
1128 CFDictionaryRef uuid_dict =
1129 (CFDictionaryRef)CFDictionaryGetValue(plist.get(), uuid_cfstr.get());
1130 return GetModuleSpecInfoFromUUIDDictionary(uuid_dict, module_spec, error,
1131 command.GetData());
1134 if (const CFIndex num_values = ::CFDictionaryGetCount(plist.get())) {
1135 std::vector<CFStringRef> keys(num_values, NULL);
1136 std::vector<CFDictionaryRef> values(num_values, NULL);
1137 ::CFDictionaryGetKeysAndValues(plist.get(), NULL,
1138 (const void **)&values[0]);
1139 if (num_values == 1) {
1140 return GetModuleSpecInfoFromUUIDDictionary(values[0], module_spec, error,
1141 command.GetData());
1144 for (CFIndex i = 0; i < num_values; ++i) {
1145 ModuleSpec curr_module_spec;
1146 if (GetModuleSpecInfoFromUUIDDictionary(values[i], curr_module_spec,
1147 error, command.GetData())) {
1148 if (module_spec.GetArchitecture().IsCompatibleMatch(
1149 curr_module_spec.GetArchitecture())) {
1150 module_spec = curr_module_spec;
1151 return true;
1157 return false;