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
52 using namespace lldb;
53 using namespace lldb_private;
54
55 static CFURLRef (*g_dlsym_DBGCopyFullDSYMURLForUUID)(
56 CFUUIDRef uuid, CFURLRef exec_url) = nullptr;
57 static CFDictionaryRef (*g_dlsym_DBGCopyDSYMPropertyLists)(CFURLRef dsym_url) =
58 nullptr;
59
LLDB_PLUGIN_DEFINE(SymbolLocatorDebugSymbols)60 LLDB_PLUGIN_DEFINE(SymbolLocatorDebugSymbols)
61
62 SymbolLocatorDebugSymbols::SymbolLocatorDebugSymbols() : SymbolLocator() {}
63
Initialize()64 void SymbolLocatorDebugSymbols::Initialize() {
65 PluginManager::RegisterPlugin(
66 GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance,
67 LocateExecutableObjectFile, LocateExecutableSymbolFile,
68 DownloadObjectAndSymbolFile, FindSymbolFileInBundle);
69 }
70
Terminate()71 void SymbolLocatorDebugSymbols::Terminate() {
72 PluginManager::UnregisterPlugin(CreateInstance);
73 }
74
GetPluginDescriptionStatic()75 llvm::StringRef SymbolLocatorDebugSymbols::GetPluginDescriptionStatic() {
76 return "DebugSymbols symbol locator.";
77 }
78
CreateInstance()79 SymbolLocator *SymbolLocatorDebugSymbols::CreateInstance() {
80 return new SymbolLocatorDebugSymbols();
81 }
82
LocateExecutableObjectFile(const ModuleSpec & module_spec)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 {};
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 "/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(dsym_filespec);
154
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;
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(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;
200 }
201 }
202
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());
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(exec_filespec);
236 if (FileSystem::Instance().Exists(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(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())) {
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(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))
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_spec, 0, 0,
301 module_specs) &&
302 module_specs.FindMatchingModuleSpec(module_spec,
303 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
FindSymbolFileInBundle(const FileSpec & dsym_bundle_fspec,const UUID * uuid,const ArchSpec * arch)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");
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(Iter->path());
342 if (Status->isDirectory())
343 continue;
344
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;
359 }
360 }
361 }
362 }
363
364 return {};
365 }
366
FileAtPathContainsArchAndUUID(const FileSpec & file_fspec,const ArchSpec * arch,const lldb_private::UUID * uuid)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;
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.
LookForDsymNextToExecutablePath(const ModuleSpec & mod_spec,const FileSpec & exec_fspec,FileSpec & 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();
400
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");
407
408 if (FileSystem::Instance().Exists(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(filename.AsCString());
414 if (FileSystem::Instance().Exists(dsym_fspec) &&
415 FileAtPathContainsArchAndUUID(dsym_fspec, mod_spec.GetArchitecturePtr(),
416 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('.');
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;
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(dsym_yaa_filename);
445
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;
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
LocateDSYMInVincinityOfExecutable(const ModuleSpec & module_spec,FileSpec & dsym_fspec)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());
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(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());
513 }
514 return true;
515 }
516 }
517 parent_dirs.RemoveLastPathComponent();
518 }
519 }
520 }
521 dsym_fspec.Clear();
522 return false;
523 }
524
LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec & module_spec,ModuleSpec & return_module_spec)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;
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 "/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(dsym_filespec);
596
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;
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(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;
642 }
643 }
644
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());
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(exec_filespec);
678 if (FileSystem::Instance().Exists(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(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())) {
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(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))
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_spec, 0, 0,
743 module_specs) &&
744 module_specs.FindMatchingModuleSpec(module_spec,
745 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
LocateExecutableSymbolFile(const ModuleSpec & module_spec,const FileSpecList & default_search_paths)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();
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 FileSpec symbol_fspec;
780 ModuleSpec dsym_module_spec;
781 // First try and find the dSYM in the same directory as the executable or in
782 // an appropriate parent directory
783 if (!LocateDSYMInVincinityOfExecutable(module_spec, symbol_fspec)) {
784 // We failed to easily find the dSYM above, so use DebugSymbols
785 LocateMacOSXFilesUsingDebugSymbols(module_spec, dsym_module_spec);
786 } else {
787 dsym_module_spec.GetSymbolFileSpec() = symbol_fspec;
788 }
789
790 return dsym_module_spec.GetSymbolFileSpec();
791 }
792
GetModuleSpecInfoFromUUIDDictionary(CFDictionaryRef uuid_dict,ModuleSpec & module_spec,Status & error,const std::string & command)793 static bool GetModuleSpecInfoFromUUIDDictionary(CFDictionaryRef uuid_dict,
794 ModuleSpec &module_spec,
795 Status &error,
796 const std::string &command) {
797 Log *log = GetLog(LLDBLog::Host);
798 bool success = false;
799 if (uuid_dict != NULL && CFGetTypeID(uuid_dict) == CFDictionaryGetTypeID()) {
800 std::string str;
801 CFStringRef cf_str;
802 CFDictionaryRef cf_dict;
803
804 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
805 CFSTR("DBGError"));
806 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
807 if (CFCString::FileSystemRepresentation(cf_str, str)) {
808 std::string errorstr = command;
809 errorstr += ":\n";
810 errorstr += str;
811 error.SetErrorString(errorstr);
812 }
813 }
814
815 cf_str = (CFStringRef)CFDictionaryGetValue(
816 (CFDictionaryRef)uuid_dict, CFSTR("DBGSymbolRichExecutable"));
817 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
818 if (CFCString::FileSystemRepresentation(cf_str, str)) {
819 module_spec.GetFileSpec().SetFile(str.c_str(), FileSpec::Style::native);
820 FileSystem::Instance().Resolve(module_spec.GetFileSpec());
821 LLDB_LOGF(log,
822 "From dsymForUUID plist: Symbol rich executable is at '%s'",
823 str.c_str());
824 }
825 }
826
827 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
828 CFSTR("DBGDSYMPath"));
829 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
830 if (CFCString::FileSystemRepresentation(cf_str, str)) {
831 module_spec.GetSymbolFileSpec().SetFile(str.c_str(),
832 FileSpec::Style::native);
833 FileSystem::Instance().Resolve(module_spec.GetFileSpec());
834 success = true;
835 LLDB_LOGF(log, "From dsymForUUID plist: dSYM is at '%s'", str.c_str());
836 }
837 }
838
839 std::string DBGBuildSourcePath;
840 std::string DBGSourcePath;
841
842 // If DBGVersion 1 or DBGVersion missing, ignore DBGSourcePathRemapping.
843 // If DBGVersion 2, strip last two components of path remappings from
844 // entries to fix an issue with a specific set of
845 // DBGSourcePathRemapping entries that lldb worked
846 // with.
847 // If DBGVersion 3, trust & use the source path remappings as-is.
848 //
849 cf_dict = (CFDictionaryRef)CFDictionaryGetValue(
850 (CFDictionaryRef)uuid_dict, CFSTR("DBGSourcePathRemapping"));
851 if (cf_dict && CFGetTypeID(cf_dict) == CFDictionaryGetTypeID()) {
852 // If we see DBGVersion with a value of 2 or higher, this is a new style
853 // DBGSourcePathRemapping dictionary
854 bool new_style_source_remapping_dictionary = false;
855 bool do_truncate_remapping_names = false;
856 std::string original_DBGSourcePath_value = DBGSourcePath;
857 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
858 CFSTR("DBGVersion"));
859 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
860 std::string version;
861 CFCString::FileSystemRepresentation(cf_str, version);
862 if (!version.empty() && isdigit(version[0])) {
863 int version_number = atoi(version.c_str());
864 if (version_number > 1) {
865 new_style_source_remapping_dictionary = true;
866 }
867 if (version_number == 2) {
868 do_truncate_remapping_names = true;
869 }
870 }
871 }
872
873 CFIndex kv_pair_count = CFDictionaryGetCount((CFDictionaryRef)uuid_dict);
874 if (kv_pair_count > 0) {
875 CFStringRef *keys =
876 (CFStringRef *)malloc(kv_pair_count * sizeof(CFStringRef));
877 CFStringRef *values =
878 (CFStringRef *)malloc(kv_pair_count * sizeof(CFStringRef));
879 if (keys != nullptr && values != nullptr) {
880 CFDictionaryGetKeysAndValues((CFDictionaryRef)uuid_dict,
881 (const void **)keys,
882 (const void **)values);
883 }
884 for (CFIndex i = 0; i < kv_pair_count; i++) {
885 DBGBuildSourcePath.clear();
886 DBGSourcePath.clear();
887 if (keys[i] && CFGetTypeID(keys[i]) == CFStringGetTypeID()) {
888 CFCString::FileSystemRepresentation(keys[i], DBGBuildSourcePath);
889 }
890 if (values[i] && CFGetTypeID(values[i]) == CFStringGetTypeID()) {
891 CFCString::FileSystemRepresentation(values[i], DBGSourcePath);
892 }
893 if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) {
894 // In the "old style" DBGSourcePathRemapping dictionary, the
895 // DBGSourcePath values (the "values" half of key-value path pairs)
896 // were wrong. Ignore them and use the universal DBGSourcePath
897 // string from earlier.
898 if (new_style_source_remapping_dictionary &&
899 !original_DBGSourcePath_value.empty()) {
900 DBGSourcePath = original_DBGSourcePath_value;
901 }
902 if (DBGSourcePath[0] == '~') {
903 FileSpec resolved_source_path(DBGSourcePath.c_str());
904 FileSystem::Instance().Resolve(resolved_source_path);
905 DBGSourcePath = resolved_source_path.GetPath();
906 }
907 // With version 2 of DBGSourcePathRemapping, we can chop off the
908 // last two filename parts from the source remapping and get a more
909 // general source remapping that still works. Add this as another
910 // option in addition to the full source path remap.
911 module_spec.GetSourceMappingList().Append(DBGBuildSourcePath,
912 DBGSourcePath, true);
913 if (do_truncate_remapping_names) {
914 FileSpec build_path(DBGBuildSourcePath.c_str());
915 FileSpec source_path(DBGSourcePath.c_str());
916 build_path.RemoveLastPathComponent();
917 build_path.RemoveLastPathComponent();
918 source_path.RemoveLastPathComponent();
919 source_path.RemoveLastPathComponent();
920 module_spec.GetSourceMappingList().Append(
921 build_path.GetPath(), source_path.GetPath(), true);
922 }
923 }
924 }
925 if (keys)
926 free(keys);
927 if (values)
928 free(values);
929 }
930 }
931
932 // If we have a DBGBuildSourcePath + DBGSourcePath pair, append them to the
933 // source remappings list.
934
935 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
936 CFSTR("DBGBuildSourcePath"));
937 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
938 CFCString::FileSystemRepresentation(cf_str, DBGBuildSourcePath);
939 }
940
941 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
942 CFSTR("DBGSourcePath"));
943 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
944 CFCString::FileSystemRepresentation(cf_str, DBGSourcePath);
945 }
946
947 if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) {
948 if (DBGSourcePath[0] == '~') {
949 FileSpec resolved_source_path(DBGSourcePath.c_str());
950 FileSystem::Instance().Resolve(resolved_source_path);
951 DBGSourcePath = resolved_source_path.GetPath();
952 }
953 module_spec.GetSourceMappingList().Append(DBGBuildSourcePath,
954 DBGSourcePath, true);
955 }
956 }
957 return success;
958 }
959
960 /// It's expensive to check for the DBGShellCommands defaults setting. Only do
961 /// it once per lldb run and cache the result.
GetDbgShellCommand()962 static llvm::StringRef GetDbgShellCommand() {
963 static std::once_flag g_once_flag;
964 static std::string g_dbgshell_command;
965 std::call_once(g_once_flag, [&]() {
966 CFTypeRef defaults_setting = CFPreferencesCopyAppValue(
967 CFSTR("DBGShellCommands"), CFSTR("com.apple.DebugSymbols"));
968 if (defaults_setting &&
969 CFGetTypeID(defaults_setting) == CFStringGetTypeID()) {
970 char buffer[PATH_MAX];
971 if (CFStringGetCString((CFStringRef)defaults_setting, buffer,
972 sizeof(buffer), kCFStringEncodingUTF8)) {
973 g_dbgshell_command = buffer;
974 }
975 }
976 if (defaults_setting) {
977 CFRelease(defaults_setting);
978 }
979 });
980 return g_dbgshell_command;
981 }
982
983 /// Get the dsymForUUID executable and cache the result so we don't end up
984 /// stat'ing the binary over and over.
GetDsymForUUIDExecutable()985 static FileSpec GetDsymForUUIDExecutable() {
986 // The LLDB_APPLE_DSYMFORUUID_EXECUTABLE environment variable is used by the
987 // test suite to override the dsymForUUID location. Because we must be able
988 // to change the value within a single test, don't bother caching it.
989 if (const char *dsymForUUID_env =
990 getenv("LLDB_APPLE_DSYMFORUUID_EXECUTABLE")) {
991 FileSpec dsymForUUID_executable(dsymForUUID_env);
992 FileSystem::Instance().Resolve(dsymForUUID_executable);
993 if (FileSystem::Instance().Exists(dsymForUUID_executable))
994 return dsymForUUID_executable;
995 }
996
997 static std::once_flag g_once_flag;
998 static FileSpec g_dsymForUUID_executable;
999 std::call_once(g_once_flag, [&]() {
1000 // Try the DBGShellCommand.
1001 llvm::StringRef dbgshell_command = GetDbgShellCommand();
1002 if (!dbgshell_command.empty()) {
1003 g_dsymForUUID_executable = FileSpec(dbgshell_command);
1004 FileSystem::Instance().Resolve(g_dsymForUUID_executable);
1005 if (FileSystem::Instance().Exists(g_dsymForUUID_executable))
1006 return;
1007 }
1008
1009 // Try dsymForUUID in /usr/local/bin
1010 {
1011 g_dsymForUUID_executable = FileSpec("/usr/local/bin/dsymForUUID");
1012 if (FileSystem::Instance().Exists(g_dsymForUUID_executable))
1013 return;
1014 }
1015
1016 // We couldn't find the dsymForUUID binary.
1017 g_dsymForUUID_executable = {};
1018 });
1019 return g_dsymForUUID_executable;
1020 }
1021
DownloadObjectAndSymbolFile(ModuleSpec & module_spec,Status & error,bool force_lookup,bool copy_executable)1022 bool SymbolLocatorDebugSymbols::DownloadObjectAndSymbolFile(
1023 ModuleSpec &module_spec, Status &error, bool force_lookup,
1024 bool copy_executable) {
1025 const UUID *uuid_ptr = module_spec.GetUUIDPtr();
1026 const FileSpec *file_spec_ptr = module_spec.GetFileSpecPtr();
1027
1028 // If \a dbgshell_command is set, the user has specified
1029 // forced symbol lookup via that command. We'll get the
1030 // path back from GetDsymForUUIDExecutable() later.
1031 llvm::StringRef dbgshell_command = GetDbgShellCommand();
1032
1033 // If forced lookup isn't set, by the user's \a dbgshell_command or
1034 // by the \a force_lookup argument, exit this method.
1035 if (!force_lookup && dbgshell_command.empty())
1036 return false;
1037
1038 // We need a UUID or valid existing FileSpec.
1039 if (!uuid_ptr &&
1040 (!file_spec_ptr || !FileSystem::Instance().Exists(*file_spec_ptr)))
1041 return false;
1042
1043 // We need a dsymForUUID binary or an equivalent executable/script.
1044 FileSpec dsymForUUID_exe_spec = GetDsymForUUIDExecutable();
1045 if (!dsymForUUID_exe_spec)
1046 return false;
1047
1048 const std::string dsymForUUID_exe_path = dsymForUUID_exe_spec.GetPath();
1049 const std::string uuid_str = uuid_ptr ? uuid_ptr->GetAsString() : "";
1050 const std::string file_path_str =
1051 file_spec_ptr ? file_spec_ptr->GetPath() : "";
1052
1053 Log *log = GetLog(LLDBLog::Host);
1054
1055 // Create the dsymForUUID command.
1056 StreamString command;
1057 const char *copy_executable_arg = copy_executable ? "--copyExecutable " : "";
1058 if (!uuid_str.empty()) {
1059 command.Printf("%s --ignoreNegativeCache %s%s",
1060 dsymForUUID_exe_path.c_str(), copy_executable_arg,
1061 uuid_str.c_str());
1062 LLDB_LOGF(log, "Calling %s with UUID %s to find dSYM: %s",
1063 dsymForUUID_exe_path.c_str(), uuid_str.c_str(),
1064 command.GetString().data());
1065 } else if (!file_path_str.empty()) {
1066 command.Printf("%s --ignoreNegativeCache %s%s",
1067 dsymForUUID_exe_path.c_str(), copy_executable_arg,
1068 file_path_str.c_str());
1069 LLDB_LOGF(log, "Calling %s with file %s to find dSYM: %s",
1070 dsymForUUID_exe_path.c_str(), file_path_str.c_str(),
1071 command.GetString().data());
1072 } else {
1073 return false;
1074 }
1075
1076 // Invoke dsymForUUID.
1077 int exit_status = -1;
1078 int signo = -1;
1079 std::string command_output;
1080 error = Host::RunShellCommand(
1081 command.GetData(),
1082 FileSpec(), // current working directory
1083 &exit_status, // Exit status
1084 &signo, // Signal int *
1085 &command_output, // Command output
1086 std::chrono::seconds(
1087 640), // Large timeout to allow for long dsym download times
1088 false); // Don't run in a shell (we don't need shell expansion)
1089
1090 if (error.Fail() || exit_status != 0 || command_output.empty()) {
1091 LLDB_LOGF(log, "'%s' failed (exit status: %d, error: '%s', output: '%s')",
1092 command.GetData(), exit_status, error.AsCString(),
1093 command_output.c_str());
1094 return false;
1095 }
1096
1097 CFCData data(
1098 CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)command_output.data(),
1099 command_output.size(), kCFAllocatorNull));
1100
1101 CFCReleaser<CFDictionaryRef> plist(
1102 (CFDictionaryRef)::CFPropertyListCreateWithData(
1103 NULL, data.get(), kCFPropertyListImmutable, NULL, NULL));
1104
1105 if (!plist.get()) {
1106 LLDB_LOGF(log, "'%s' failed: output is not a valid plist",
1107 command.GetData());
1108 return false;
1109 }
1110
1111 if (CFGetTypeID(plist.get()) != CFDictionaryGetTypeID()) {
1112 LLDB_LOGF(log, "'%s' failed: output plist is not a valid CFDictionary",
1113 command.GetData());
1114 return false;
1115 }
1116
1117 if (!uuid_str.empty()) {
1118 CFCString uuid_cfstr(uuid_str.c_str());
1119 CFDictionaryRef uuid_dict =
1120 (CFDictionaryRef)CFDictionaryGetValue(plist.get(), uuid_cfstr.get());
1121 return GetModuleSpecInfoFromUUIDDictionary(uuid_dict, module_spec, error,
1122 command.GetData());
1123 }
1124
1125 if (const CFIndex num_values = ::CFDictionaryGetCount(plist.get())) {
1126 std::vector<CFStringRef> keys(num_values, NULL);
1127 std::vector<CFDictionaryRef> values(num_values, NULL);
1128 ::CFDictionaryGetKeysAndValues(plist.get(), NULL,
1129 (const void **)&values[0]);
1130 if (num_values == 1) {
1131 return GetModuleSpecInfoFromUUIDDictionary(values[0], module_spec, error,
1132 command.GetData());
1133 }
1134
1135 for (CFIndex i = 0; i < num_values; ++i) {
1136 ModuleSpec curr_module_spec;
1137 if (GetModuleSpecInfoFromUUIDDictionary(values[i], curr_module_spec,
1138 error, command.GetData())) {
1139 if (module_spec.GetArchitecture().IsCompatibleMatch(
1140 curr_module_spec.GetArchitecture())) {
1141 module_spec = curr_module_spec;
1142 return true;
1143 }
1144 }
1145 }
1146 }
1147
1148 return false;
1149 }
1150