1 //===-- SymbolLocatorDebugSymbols.cpp -------------------------------------===//
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
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>
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
) =
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.");
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();
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
);
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) {
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();
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
),
140 CFCReleaser
<CFURLRef
> dsym_url(g_dlsym_DBGCopyFullDSYMURLForUUID(
141 module_uuid_ref
.get(), exec_url
.get()));
144 if (dsym_url
.get()) {
145 if (::CFURLGetFileSystemRepresentation(
146 dsym_url
.get(), true, (UInt8
*)path
, sizeof(path
) - 1)) {
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
);
153 FileSystem::Instance().Resolve(dsym_filespec
);
155 if (FileSystem::Instance().IsDirectory(dsym_filespec
)) {
156 dsym_filespec
= PluginManager::FindSymbolFileInBundle(
157 dsym_filespec
, uuid
, arch
);
162 return_module_spec
.GetSymbolFileSpec() = dsym_filespec
;
165 bool success
= false;
167 if (::CFURLGetFileSystemRepresentation(
168 dsym_url
.get(), true, (UInt8
*)path
, sizeof(path
) - 1)) {
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
;
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())) {
188 exe_spec
.GetFileSpec() = module_spec
.GetFileSpec();
189 exe_spec
.GetUUID() = module_spec
.GetUUID();
191 module_sp
.reset(new Module(exe_spec
));
192 if (module_sp
&& module_sp
->GetObjectFile() &&
193 module_sp
->MatchesModuleSpec(exe_spec
)) {
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());
203 // Check if the requested image is in our shared cache.
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
)) {
213 return_module_spec
.GetFileSpec() = module_spec
.GetFileSpec();
215 "using binary from shared cache for filepath %s for "
217 module_spec
.GetFileSpec().GetPath().c_str(),
218 uuid
->GetAsString().c_str());
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());
233 FileSpec
exec_filespec(path
);
235 FileSystem::Instance().Resolve(exec_filespec
);
236 if (FileSystem::Instance().Exists(exec_filespec
)) {
238 return_module_spec
.GetFileSpec() = exec_filespec
;
243 // Look next to the dSYM for the binary file.
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';
251 "Looking for executable binary next to dSYM "
252 "bundle with name with name %s",
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(),
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
))
279 return_module_spec
.GetFileSpec() = bundle_exe_file_spec
;
281 "Executable binary %s next to dSYM is "
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
:
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,
302 module_specs
.FindMatchingModuleSpec(module_spec
,
303 matched_module_spec
))
307 return_module_spec
.GetFileSpec() = file_spec
;
309 "Executable binary %s next to dSYM is "
324 return return_module_spec
;
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");
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())
345 FileSpec
dsym_fspec(Iter
->path());
346 ModuleSpecList module_specs
;
347 if (ObjectFile::GetModuleSpecifications(dsym_fspec
, 0, 0, module_specs
)) {
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
)) &&
356 (spec
.GetArchitecturePtr() &&
357 spec
.GetArchitecture().IsCompatibleMatch(*arch
)))) {
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
)) {
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
);
377 if ((uuid
== nullptr || (spec
.GetUUIDPtr() && spec
.GetUUID() == *uuid
)) &&
379 (spec
.GetArchitecturePtr() &&
380 spec
.GetArchitecture().IsCompatibleMatch(*arch
)))) {
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
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())) {
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())) {
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
;
449 if (PluginManager::DownloadObjectAndSymbolFile(mutable_mod_spec
, error
,
451 FileSystem::Instance().Exists(mutable_mod_spec
.GetSymbolFileSpec())) {
452 dsym_fspec
= mutable_mod_spec
.GetSymbolFileSpec();
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
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();
476 if (::LookForDsymNextToExecutablePath(module_spec
, exec_fspec
,
479 LLDB_LOGF(log
, "dSYM with matching UUID & arch found at %s",
480 dsym_fspec
.GetPath().c_str());
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
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();
507 if (::strchr(fn
, '.') != nullptr) {
508 if (::LookForDsymNextToExecutablePath(module_spec
, parent_dirs
,
511 LLDB_LOGF(log
, "dSYM with matching UUID & arch found at %s",
512 dsym_fspec
.GetPath().c_str());
517 parent_dirs
.RemoveLastPathComponent();
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.");
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();
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
);
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) {
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();
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
),
582 CFCReleaser
<CFURLRef
> dsym_url(g_dlsym_DBGCopyFullDSYMURLForUUID(
583 module_uuid_ref
.get(), exec_url
.get()));
586 if (dsym_url
.get()) {
587 if (::CFURLGetFileSystemRepresentation(
588 dsym_url
.get(), true, (UInt8
*)path
, sizeof(path
) - 1)) {
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
);
595 FileSystem::Instance().Resolve(dsym_filespec
);
597 if (FileSystem::Instance().IsDirectory(dsym_filespec
)) {
598 dsym_filespec
= PluginManager::FindSymbolFileInBundle(
599 dsym_filespec
, uuid
, arch
);
604 return_module_spec
.GetSymbolFileSpec() = dsym_filespec
;
607 bool success
= false;
609 if (::CFURLGetFileSystemRepresentation(
610 dsym_url
.get(), true, (UInt8
*)path
, sizeof(path
) - 1)) {
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
;
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())) {
630 exe_spec
.GetFileSpec() = module_spec
.GetFileSpec();
631 exe_spec
.GetUUID() = module_spec
.GetUUID();
633 module_sp
.reset(new Module(exe_spec
));
634 if (module_sp
&& module_sp
->GetObjectFile() &&
635 module_sp
->MatchesModuleSpec(exe_spec
)) {
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());
645 // Check if the requested image is in our shared cache.
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
)) {
655 return_module_spec
.GetFileSpec() = module_spec
.GetFileSpec();
657 "using binary from shared cache for filepath %s for "
659 module_spec
.GetFileSpec().GetPath().c_str(),
660 uuid
->GetAsString().c_str());
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());
675 FileSpec
exec_filespec(path
);
677 FileSystem::Instance().Resolve(exec_filespec
);
678 if (FileSystem::Instance().Exists(exec_filespec
)) {
680 return_module_spec
.GetFileSpec() = exec_filespec
;
685 // Look next to the dSYM for the binary file.
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';
693 "Looking for executable binary next to dSYM "
694 "bundle with name with name %s",
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(),
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
))
721 return_module_spec
.GetFileSpec() = bundle_exe_file_spec
;
723 "Executable binary %s next to dSYM is "
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
:
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,
744 module_specs
.FindMatchingModuleSpec(module_spec
,
745 matched_module_spec
))
749 return_module_spec
.GetFileSpec() = file_spec
;
751 "Executable binary %s next to dSYM is "
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();
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
);
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
);
791 dsym_module_spec
.GetSymbolFileSpec() = symbol_fspec
;
794 return dsym_module_spec
.GetSymbolFileSpec();
797 static bool GetModuleSpecInfoFromUUIDDictionary(CFDictionaryRef uuid_dict
,
798 ModuleSpec
&module_spec
,
800 const std::string
&command
) {
801 Log
*log
= GetLog(LLDBLog::Host
);
802 bool success
= false;
803 if (uuid_dict
!= NULL
&& CFGetTypeID(uuid_dict
) == CFDictionaryGetTypeID()) {
806 CFDictionaryRef cf_dict
;
808 cf_str
= (CFStringRef
)CFDictionaryGetValue((CFDictionaryRef
)uuid_dict
,
810 if (cf_str
&& CFGetTypeID(cf_str
) == CFStringGetTypeID()) {
811 if (CFCString::FileSystemRepresentation(cf_str
, str
)) {
812 std::string errorstr
= command
;
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());
826 "From dsymForUUID plist: Symbol rich executable is at '%s'",
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());
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
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()) {
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) {
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
,
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);
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);
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
))
1013 // Try dsymForUUID in /usr/local/bin
1015 g_dsymForUUID_executable
= FileSpec("/usr/local/bin/dsymForUUID");
1016 if (FileSystem::Instance().Exists(g_dsymForUUID_executable
))
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())
1042 // We need a UUID or valid existing FileSpec.
1044 (!file_spec_ptr
|| !FileSystem::Instance().Exists(*file_spec_ptr
)))
1047 // We need a dsymForUUID binary or an equivalent executable/script.
1048 FileSpec dsymForUUID_exe_spec
= GetDsymForUUIDExecutable();
1049 if (!dsymForUUID_exe_spec
)
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())
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
)
1072 llvm::formatv("{0} ({1})", file_spec_ptr
->GetFilename().GetString(),
1073 uuid_ptr
->GetAsString());
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;
1088 std::string command_output
;
1089 error
= Host::RunShellCommand(
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());
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
));
1115 LLDB_LOGF(log
, "'%s' failed: output is not a valid plist",
1120 if (CFGetTypeID(plist
.get()) != CFDictionaryGetTypeID()) {
1121 LLDB_LOGF(log
, "'%s' failed: output plist is not a valid CFDictionary",
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
,
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
,
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
;