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//===----------------------------------------------------------------------===//
8
9#include "SymbolLocatorDebugSymbols.h"
10
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"
32
33#include "llvm/ADT/SmallSet.h"
34#include "llvm/Support/FileSystem.h"
35#include "llvm/Support/ThreadPool.h"
36
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"
41
42#include "mach/machine.h"
43
44#include <CoreFoundation/CoreFoundation.h>
45
46#include <cstring>
47#include <dirent.h>
48#include <dlfcn.h>
49#include <optional>
50#include <pwd.h>
51
52using namespace lldb;
53using namespace lldb_private;
54
55static CFURLRef (*g_dlsym_DBGCopyFullDSYMURLForUUID)(
56 CFUUIDRef uuid, CFURLRef exec_url) = nullptr;
57static CFDictionaryRef (*g_dlsym_DBGCopyDSYMPropertyLists)(CFURLRef dsym_url) =
58 nullptr;
59
60LLDB_PLUGIN_DEFINE(SymbolLocatorDebugSymbols)
61
62SymbolLocatorDebugSymbols::SymbolLocatorDebugSymbols() : SymbolLocator() {}
63
64void SymbolLocatorDebugSymbols::Initialize() {
65 PluginManager::RegisterPlugin(
66 name: GetPluginNameStatic(), description: GetPluginDescriptionStatic(), create_callback: CreateInstance,
67 locate_executable_object_file: LocateExecutableObjectFile, locate_executable_symbol_file: LocateExecutableSymbolFile,
68 download_object_symbol_file: DownloadObjectAndSymbolFile, find_symbol_file_in_bundle: FindSymbolFileInBundle);
69}
70
71void SymbolLocatorDebugSymbols::Terminate() {
72 PluginManager::UnregisterPlugin(create_callback: CreateInstance);
73}
74
75llvm::StringRef SymbolLocatorDebugSymbols::GetPluginDescriptionStatic() {
76 return "DebugSymbols symbol locator.";
77}
78
79SymbolLocator *SymbolLocatorDebugSymbols::CreateInstance() {
80 return new SymbolLocatorDebugSymbols();
81}
82
83std::optional<ModuleSpec> SymbolLocatorDebugSymbols::LocateExecutableObjectFile(
84 const ModuleSpec &module_spec) {
85 Log *log = GetLog(mask: LLDBLog::Host);
86 if (!ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup()) {
87 LLDB_LOGF(log, "Spotlight lookup for .dSYM bundles is disabled.");
88 return {};
89 }
90 ModuleSpec return_module_spec;
91 return_module_spec = module_spec;
92 return_module_spec.GetFileSpec().Clear();
93 return_module_spec.GetSymbolFileSpec().Clear();
94
95 const UUID *uuid = module_spec.GetUUIDPtr();
96 const ArchSpec *arch = module_spec.GetArchitecturePtr();
97
98 int items_found = 0;
99
100 if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr ||
101 g_dlsym_DBGCopyDSYMPropertyLists == nullptr) {
102 void *handle = dlopen(
103 file: "/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");
111 }
112 }
113
114 if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr ||
115 g_dlsym_DBGCopyDSYMPropertyLists == nullptr) {
116 return {};
117 }
118
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]));
128
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));
138 }
139
140 CFCReleaser<CFURLRef> dsym_url(g_dlsym_DBGCopyFullDSYMURLForUUID(
141 module_uuid_ref.get(), exec_url.get()));
142 char path[PATH_MAX];
143
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(file_spec&: dsym_filespec);
154
155 if (FileSystem::Instance().IsDirectory(file_spec: dsym_filespec)) {
156 dsym_filespec = PluginManager::FindSymbolFileInBundle(
157 dsym_bundle_fspec: dsym_filespec, uuid, arch);
158 ++items_found;
159 } else {
160 ++items_found;
161 }
162 return_module_spec.GetSymbolFileSpec() = dsym_filespec;
163 }
164
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());
173 }
174 }
175
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()));
183 }
184
185 // Check to see if we have the file on the local filesystem.
186 if (FileSystem::Instance().Exists(file_spec: 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(p: new Module(exe_spec));
192 if (module_sp && module_sp->GetObjectFile() &&
193 module_sp->MatchesModuleSpec(module_ref: 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;
200 }
201 }
202
203 // Check if the requested image is in our shared cache.
204 if (!success) {
205 SharedCacheImageInfo image_info = HostInfo::GetSharedCacheImageInfo(
206 image_name: module_spec.GetFileSpec().GetPath());
207
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;
220 }
221 }
222
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(file_spec&: exec_filespec);
236 if (FileSystem::Instance().Exists(file_spec: exec_filespec)) {
237 success = true;
238 return_module_spec.GetFileSpec() = exec_filespec;
239 }
240 }
241 }
242
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(haystack: path, needle: ".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(Path: file_spec.GetPath())) {
260
261 case file_type::directory_file: // Bundle directory?
262 {
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(file_spec&: bundle_exe_file_spec);
272 if (ObjectFile::GetModuleSpecifications(
273 file: bundle_exe_file_spec, file_offset: 0, file_size: 0, specs&: module_specs) &&
274 module_specs.FindMatchingModuleSpec(
275 module_spec, match_module_spec&: matched_module_spec))
276
277 {
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);
284 }
285 }
286 }
287 } break;
288
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;
294
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: file_spec, file_offset: 0, file_size: 0,
301 specs&: module_specs) &&
302 module_specs.FindMatchingModuleSpec(module_spec,
303 match_module_spec&: matched_module_spec))
304
305 {
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);
312 }
313 break;
314 }
315 }
316 }
317 }
318 }
319 }
320 }
321 }
322
323 if (items_found)
324 return return_module_spec;
325
326 return {};
327}
328
329std::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(path&: buffer, a: "Contents", b: "Resources", c: "DWARF");
334
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(Path: Iter->path());
342 if (Status->isDirectory())
343 continue;
344
345 FileSpec dsym_fspec(Iter->path());
346 ModuleSpecList module_specs;
347 if (ObjectFile::GetModuleSpecifications(file: dsym_fspec, file_offset: 0, file_size: 0, specs&: module_specs)) {
348 ModuleSpec spec;
349 for (size_t i = 0; i < module_specs.GetSize(); ++i) {
350 bool got_spec = module_specs.GetModuleSpecAtIndex(i, module_spec&: 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(rhs: *arch)))) {
358 return dsym_fspec;
359 }
360 }
361 }
362 }
363
364 return {};
365}
366
367static 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: file_fspec, file_offset: 0, file_size: 0, specs&: module_specs)) {
372 ModuleSpec spec;
373 for (size_t i = 0; i < module_specs.GetSize(); ++i) {
374 bool got_spec = module_specs.GetModuleSpecAtIndex(i, module_spec&: 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(rhs: *arch)))) {
381 return true;
382 }
383 }
384 }
385 return false;
386}
387
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.
394static 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();
400
401 std::string dsym_filename = filename.AsCString();
402 dsym_filename += ".dSYM";
403 dsym_directory.AppendPathComponent(component: dsym_filename);
404 dsym_directory.AppendPathComponent(component: "Contents");
405 dsym_directory.AppendPathComponent(component: "Resources");
406 dsym_directory.AppendPathComponent(component: "DWARF");
407
408 if (FileSystem::Instance().Exists(file_spec: dsym_directory)) {
409
410 // See if the binary name exists in the dSYM DWARF
411 // subdir.
412 dsym_fspec = dsym_directory;
413 dsym_fspec.AppendPathComponent(component: filename.AsCString());
414 if (FileSystem::Instance().Exists(file_spec: dsym_fspec) &&
415 FileAtPathContainsArchAndUUID(file_fspec: dsym_fspec, arch: mod_spec.GetArchitecturePtr(),
416 uuid: mod_spec.GetUUIDPtr())) {
417 return true;
418 }
419
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(c: '.');
426 if (last_dot != std::string::npos) {
427 binary_name.erase(pos: last_dot);
428 dsym_fspec = dsym_directory;
429 dsym_fspec.AppendPathComponent(component: binary_name);
430 if (FileSystem::Instance().Exists(file_spec: dsym_fspec) &&
431 FileAtPathContainsArchAndUUID(file_fspec: dsym_fspec,
432 arch: mod_spec.GetArchitecturePtr(),
433 uuid: mod_spec.GetUUIDPtr())) {
434 return true;
435 }
436 }
437 }
438
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(component: dsym_yaa_filename);
445
446 if (FileSystem::Instance().Exists(file_spec: dsym_yaa_fspec)) {
447 ModuleSpec mutable_mod_spec = mod_spec;
448 Status error;
449 if (PluginManager::DownloadObjectAndSymbolFile(module_spec&: mutable_mod_spec, error,
450 force_lookup: true) &&
451 FileSystem::Instance().Exists(file_spec: mutable_mod_spec.GetSymbolFileSpec())) {
452 dsym_fspec = mutable_mod_spec.GetSymbolFileSpec();
453 return true;
454 }
455 }
456
457 return false;
458}
459
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.
464//
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.
468//
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
471static bool LocateDSYMInVincinityOfExecutable(const ModuleSpec &module_spec,
472 FileSpec &dsym_fspec) {
473 Log *log = GetLog(mask: LLDBLog::Host);
474 const FileSpec &exec_fspec = module_spec.GetFileSpec();
475 if (exec_fspec) {
476 if (::LookForDsymNextToExecutablePath(mod_spec: 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());
481 }
482 return true;
483 } else {
484 FileSpec parent_dirs = exec_fspec;
485
486 // Remove the binary name from the FileSpec
487 parent_dirs.RemoveLastPathComponent();
488
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
493 //
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.
500
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(s: fn, c: '.') != nullptr) {
508 if (::LookForDsymNextToExecutablePath(mod_spec: module_spec, exec_fspec: 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());
513 }
514 return true;
515 }
516 }
517 parent_dirs.RemoveLastPathComponent();
518 }
519 }
520 }
521 dsym_fspec.Clear();
522 return false;
523}
524
525static int LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec &module_spec,
526 ModuleSpec &return_module_spec) {
527 Log *log = GetLog(mask: LLDBLog::Host);
528 if (!ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup()) {
529 LLDB_LOGF(log, "Spotlight lookup for .dSYM bundles is disabled.");
530 return 0;
531 }
532
533 return_module_spec = module_spec;
534 return_module_spec.GetFileSpec().Clear();
535 return_module_spec.GetSymbolFileSpec().Clear();
536
537 const UUID *uuid = module_spec.GetUUIDPtr();
538 const ArchSpec *arch = module_spec.GetArchitecturePtr();
539
540 int items_found = 0;
541
542 if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr ||
543 g_dlsym_DBGCopyDSYMPropertyLists == nullptr) {
544 void *handle = dlopen(
545 file: "/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");
553 }
554 }
555
556 if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr ||
557 g_dlsym_DBGCopyDSYMPropertyLists == nullptr) {
558 return items_found;
559 }
560
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]));
570
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));
580 }
581
582 CFCReleaser<CFURLRef> dsym_url(g_dlsym_DBGCopyFullDSYMURLForUUID(
583 module_uuid_ref.get(), exec_url.get()));
584 char path[PATH_MAX];
585
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(file_spec&: dsym_filespec);
596
597 if (FileSystem::Instance().IsDirectory(file_spec: dsym_filespec)) {
598 dsym_filespec = PluginManager::FindSymbolFileInBundle(
599 dsym_bundle_fspec: dsym_filespec, uuid, arch);
600 ++items_found;
601 } else {
602 ++items_found;
603 }
604 return_module_spec.GetSymbolFileSpec() = dsym_filespec;
605 }
606
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());
615 }
616 }
617
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()));
625 }
626
627 // Check to see if we have the file on the local filesystem.
628 if (FileSystem::Instance().Exists(file_spec: 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(p: new Module(exe_spec));
634 if (module_sp && module_sp->GetObjectFile() &&
635 module_sp->MatchesModuleSpec(module_ref: 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;
642 }
643 }
644
645 // Check if the requested image is in our shared cache.
646 if (!success) {
647 SharedCacheImageInfo image_info = HostInfo::GetSharedCacheImageInfo(
648 image_name: module_spec.GetFileSpec().GetPath());
649
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;
662 }
663 }
664
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(file_spec&: exec_filespec);
678 if (FileSystem::Instance().Exists(file_spec: exec_filespec)) {
679 success = true;
680 return_module_spec.GetFileSpec() = exec_filespec;
681 }
682 }
683 }
684
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(haystack: path, needle: ".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(Path: file_spec.GetPath())) {
702
703 case file_type::directory_file: // Bundle directory?
704 {
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(file_spec&: bundle_exe_file_spec);
714 if (ObjectFile::GetModuleSpecifications(
715 file: bundle_exe_file_spec, file_offset: 0, file_size: 0, specs&: module_specs) &&
716 module_specs.FindMatchingModuleSpec(
717 module_spec, match_module_spec&: matched_module_spec))
718
719 {
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);
726 }
727 }
728 }
729 } break;
730
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;
736
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: file_spec, file_offset: 0, file_size: 0,
743 specs&: module_specs) &&
744 module_specs.FindMatchingModuleSpec(module_spec,
745 match_module_spec&: matched_module_spec))
746
747 {
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);
754 }
755 break;
756 }
757 }
758 }
759 }
760 }
761 }
762 }
763 }
764
765 return items_found;
766}
767
768std::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();
773
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);
778
779 Progress progress(
780 "Locating external symbol file",
781 module_spec.GetFileSpec().GetFilename().AsCString(value_if_empty: "<Unknown>"));
782
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, dsym_fspec&: symbol_fspec)) {
788 // We failed to easily find the dSYM above, so use DebugSymbols
789 LocateMacOSXFilesUsingDebugSymbols(module_spec, return_module_spec&: dsym_module_spec);
790 } else {
791 dsym_module_spec.GetSymbolFileSpec() = symbol_fspec;
792 }
793
794 return dsym_module_spec.GetSymbolFileSpec();
795}
796
797static bool GetModuleSpecInfoFromUUIDDictionary(CFDictionaryRef uuid_dict,
798 ModuleSpec &module_spec,
799 Status &error,
800 const std::string &command) {
801 Log *log = GetLog(mask: 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;
807
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: cf_str, str)) {
812 std::string errorstr = command;
813 errorstr += ":\n";
814 errorstr += str;
815 error.SetErrorString(errorstr);
816 }
817 }
818
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: cf_str, str)) {
823 module_spec.GetFileSpec().SetFile(path: str.c_str(), style: FileSpec::Style::native);
824 FileSystem::Instance().Resolve(file_spec&: module_spec.GetFileSpec());
825 LLDB_LOGF(log,
826 "From dsymForUUID plist: Symbol rich executable is at '%s'",
827 str.c_str());
828 }
829 }
830
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: cf_str, str)) {
835 module_spec.GetSymbolFileSpec().SetFile(path: str.c_str(),
836 style: FileSpec::Style::native);
837 FileSystem::Instance().Resolve(file_spec&: module_spec.GetFileSpec());
838 success = true;
839 LLDB_LOGF(log, "From dsymForUUID plist: dSYM is at '%s'", str.c_str());
840 }
841 }
842
843 std::string DBGBuildSourcePath;
844 std::string DBGSourcePath;
845
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.
852 //
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: cf_str, str&: version);
866 if (!version.empty() && isdigit(version[0])) {
867 int version_number = atoi(nptr: version.c_str());
868 if (version_number > 1) {
869 new_style_source_remapping_dictionary = true;
870 }
871 if (version_number == 2) {
872 do_truncate_remapping_names = true;
873 }
874 }
875 }
876
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);
887 }
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(cf_str: keys[i], str&: DBGBuildSourcePath);
893 }
894 if (values[i] && CFGetTypeID(values[i]) == CFStringGetTypeID()) {
895 CFCString::FileSystemRepresentation(cf_str: values[i], str&: DBGSourcePath);
896 }
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;
905 }
906 if (DBGSourcePath[0] == '~') {
907 FileSpec resolved_source_path(DBGSourcePath.c_str());
908 FileSystem::Instance().Resolve(file_spec&: resolved_source_path);
909 DBGSourcePath = resolved_source_path.GetPath();
910 }
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(path: DBGBuildSourcePath,
916 replacement: DBGSourcePath, notify: 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 path: build_path.GetPath(), replacement: source_path.GetPath(), notify: true);
926 }
927 }
928 }
929 if (keys)
930 free(keys);
931 if (values)
932 free(values);
933 }
934 }
935
936 // If we have a DBGBuildSourcePath + DBGSourcePath pair, append them to the
937 // source remappings list.
938
939 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
940 CFSTR("DBGBuildSourcePath"));
941 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
942 CFCString::FileSystemRepresentation(cf_str: cf_str, str&: DBGBuildSourcePath);
943 }
944
945 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
946 CFSTR("DBGSourcePath"));
947 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
948 CFCString::FileSystemRepresentation(cf_str: cf_str, str&: DBGSourcePath);
949 }
950
951 if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) {
952 if (DBGSourcePath[0] == '~') {
953 FileSpec resolved_source_path(DBGSourcePath.c_str());
954 FileSystem::Instance().Resolve(file_spec&: resolved_source_path);
955 DBGSourcePath = resolved_source_path.GetPath();
956 }
957 module_spec.GetSourceMappingList().Append(path: DBGBuildSourcePath,
958 replacement: DBGSourcePath, notify: true);
959 }
960 }
961 return success;
962}
963
964/// It's expensive to check for the DBGShellCommands defaults setting. Only do
965/// it once per lldb run and cache the result.
966static llvm::StringRef GetDbgShellCommand() {
967 static std::once_flag g_once_flag;
968 static std::string g_dbgshell_command;
969 std::call_once(once&: g_once_flag, f: [&]() {
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;
978 }
979 }
980 if (defaults_setting) {
981 CFRelease(defaults_setting);
982 }
983 });
984 return g_dbgshell_command;
985}
986
987/// Get the dsymForUUID executable and cache the result so we don't end up
988/// stat'ing the binary over and over.
989static 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(name: "LLDB_APPLE_DSYMFORUUID_EXECUTABLE")) {
995 FileSpec dsymForUUID_executable(dsymForUUID_env);
996 FileSystem::Instance().Resolve(file_spec&: dsymForUUID_executable);
997 if (FileSystem::Instance().Exists(file_spec: dsymForUUID_executable))
998 return dsymForUUID_executable;
999 }
1000
1001 static std::once_flag g_once_flag;
1002 static FileSpec g_dsymForUUID_executable;
1003 std::call_once(once&: g_once_flag, f: [&]() {
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(file_spec&: g_dsymForUUID_executable);
1009 if (FileSystem::Instance().Exists(file_spec: g_dsymForUUID_executable))
1010 return;
1011 }
1012
1013 // Try dsymForUUID in /usr/local/bin
1014 {
1015 g_dsymForUUID_executable = FileSpec("/usr/local/bin/dsymForUUID");
1016 if (FileSystem::Instance().Exists(file_spec: g_dsymForUUID_executable))
1017 return;
1018 }
1019
1020 // We couldn't find the dsymForUUID binary.
1021 g_dsymForUUID_executable = {};
1022 });
1023 return g_dsymForUUID_executable;
1024}
1025
1026bool 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();
1031
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();
1036
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;
1041
1042 // We need a UUID or valid existing FileSpec.
1043 if (!uuid_ptr &&
1044 (!file_spec_ptr || !FileSystem::Instance().Exists(file_spec: *file_spec_ptr)))
1045 return false;
1046
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;
1051
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() : "";
1055
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;
1061
1062 StreamString command;
1063 command << dsymForUUID_exe_path << " --ignoreNegativeCache ";
1064 if (copy_executable)
1065 command << "--copyExecutable ";
1066 command << lookup_arg;
1067
1068 // Log and report progress.
1069 std::string lookup_desc;
1070 if (uuid_ptr && file_spec_ptr)
1071 lookup_desc =
1072 llvm::formatv(Fmt: "{0} ({1})", Vals: file_spec_ptr->GetFilename().GetString(),
1073 Vals: 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();
1078
1079 Log *log = GetLog(mask: LLDBLog::Host);
1080 LLDB_LOG(log, "Calling {0} for {1} to find dSYM: {2}", dsymForUUID_exe_path,
1081 lookup_desc, command.GetString());
1082
1083 Progress progress("Downloading symbol file for", lookup_desc);
1084
1085 // Invoke dsymForUUID.
1086 int exit_status = -1;
1087 int signo = -1;
1088 std::string command_output;
1089 error = Host::RunShellCommand(
1090 command: command.GetData(),
1091 working_dir: FileSpec(), // current working directory
1092 status_ptr: &exit_status, // Exit status
1093 signo_ptr: &signo, // Signal int *
1094 command_output: &command_output, // Command output
1095 timeout: std::chrono::seconds(
1096 640), // Large timeout to allow for long dsym download times
1097 run_in_shell: false); // Don't run in a shell (we don't need shell expansion)
1098
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;
1104 }
1105
1106 CFCData data(
1107 CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)command_output.data(),
1108 command_output.size(), kCFAllocatorNull));
1109
1110 CFCReleaser<CFDictionaryRef> plist(
1111 (CFDictionaryRef)::CFPropertyListCreateWithData(
1112 NULL, data.get(), kCFPropertyListImmutable, NULL, NULL));
1113
1114 if (!plist.get()) {
1115 LLDB_LOGF(log, "'%s' failed: output is not a valid plist",
1116 command.GetData());
1117 return false;
1118 }
1119
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;
1124 }
1125
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());
1132 }
1133
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());
1142 }
1143
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 rhs: curr_module_spec.GetArchitecture())) {
1150 module_spec = curr_module_spec;
1151 return true;
1152 }
1153 }
1154 }
1155 }
1156
1157 return false;
1158}
1159

source code of lldb/source/Plugins/SymbolLocator/DebugSymbols/SymbolLocatorDebugSymbols.cpp