1 //===-- LocateSymbolFileMacOSX.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 "lldb/Symbol/LocateSymbolFile.h"
10
11 #include <dirent.h>
12 #include <dlfcn.h>
13 #include <pwd.h>
14
15 #include <CoreFoundation/CoreFoundation.h>
16
17 #include "Host/macosx/cfcpp/CFCBundle.h"
18 #include "Host/macosx/cfcpp/CFCData.h"
19 #include "Host/macosx/cfcpp/CFCReleaser.h"
20 #include "Host/macosx/cfcpp/CFCString.h"
21 #include "lldb/Core/ModuleList.h"
22 #include "lldb/Core/ModuleSpec.h"
23 #include "lldb/Host/Host.h"
24 #include "lldb/Symbol/ObjectFile.h"
25 #include "lldb/Utility/ArchSpec.h"
26 #include "lldb/Utility/DataBuffer.h"
27 #include "lldb/Utility/DataExtractor.h"
28 #include "lldb/Utility/Endian.h"
29 #include "lldb/Utility/Log.h"
30 #include "lldb/Utility/ReproducerProvider.h"
31 #include "lldb/Utility/StreamString.h"
32 #include "lldb/Utility/Timer.h"
33 #include "lldb/Utility/UUID.h"
34 #include "mach/machine.h"
35
36 #include "llvm/ADT/ScopeExit.h"
37 #include "llvm/Support/FileSystem.h"
38
39 using namespace lldb;
40 using namespace lldb_private;
41
42 static CFURLRef (*g_dlsym_DBGCopyFullDSYMURLForUUID)(CFUUIDRef uuid, CFURLRef exec_url) = nullptr;
43 static CFDictionaryRef (*g_dlsym_DBGCopyDSYMPropertyLists)(CFURLRef dsym_url) = nullptr;
44
LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec & module_spec,ModuleSpec & return_module_spec)45 int LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec &module_spec,
46 ModuleSpec &return_module_spec) {
47 Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
48 if (!ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup()) {
49 LLDB_LOGF(log, "Spotlight lookup for .dSYM bundles is disabled.");
50 return 0;
51 }
52
53 return_module_spec = module_spec;
54 return_module_spec.GetFileSpec().Clear();
55 return_module_spec.GetSymbolFileSpec().Clear();
56
57 const UUID *uuid = module_spec.GetUUIDPtr();
58 const ArchSpec *arch = module_spec.GetArchitecturePtr();
59
60 if (repro::Loader *l = repro::Reproducer::Instance().GetLoader()) {
61 static repro::SymbolFileLoader symbol_file_loader(l);
62 std::pair<FileSpec, FileSpec> paths = symbol_file_loader.GetPaths(uuid);
63 return_module_spec.GetFileSpec() = paths.first;
64 return_module_spec.GetSymbolFileSpec() = paths.second;
65 return 1;
66 }
67
68 int items_found = 0;
69
70 if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr ||
71 g_dlsym_DBGCopyDSYMPropertyLists == nullptr) {
72 void *handle = dlopen ("/System/Library/PrivateFrameworks/DebugSymbols.framework/DebugSymbols", RTLD_LAZY | RTLD_LOCAL);
73 if (handle) {
74 g_dlsym_DBGCopyFullDSYMURLForUUID = (CFURLRef (*)(CFUUIDRef, CFURLRef)) dlsym (handle, "DBGCopyFullDSYMURLForUUID");
75 g_dlsym_DBGCopyDSYMPropertyLists = (CFDictionaryRef (*)(CFURLRef)) dlsym (handle, "DBGCopyDSYMPropertyLists");
76 }
77 }
78
79 if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr ||
80 g_dlsym_DBGCopyDSYMPropertyLists == nullptr) {
81 return items_found;
82 }
83
84 if (uuid && uuid->IsValid()) {
85 // Try and locate the dSYM file using DebugSymbols first
86 llvm::ArrayRef<uint8_t> module_uuid = uuid->GetBytes();
87 if (module_uuid.size() == 16) {
88 CFCReleaser<CFUUIDRef> module_uuid_ref(::CFUUIDCreateWithBytes(
89 NULL, module_uuid[0], module_uuid[1], module_uuid[2], module_uuid[3],
90 module_uuid[4], module_uuid[5], module_uuid[6], module_uuid[7],
91 module_uuid[8], module_uuid[9], module_uuid[10], module_uuid[11],
92 module_uuid[12], module_uuid[13], module_uuid[14], module_uuid[15]));
93
94 if (module_uuid_ref.get()) {
95 CFCReleaser<CFURLRef> exec_url;
96 const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();
97 if (exec_fspec) {
98 char exec_cf_path[PATH_MAX];
99 if (exec_fspec->GetPath(exec_cf_path, sizeof(exec_cf_path)))
100 exec_url.reset(::CFURLCreateFromFileSystemRepresentation(
101 NULL, (const UInt8 *)exec_cf_path, strlen(exec_cf_path),
102 FALSE));
103 }
104
105 CFCReleaser<CFURLRef> dsym_url(
106 g_dlsym_DBGCopyFullDSYMURLForUUID(module_uuid_ref.get(), exec_url.get()));
107 char path[PATH_MAX];
108
109 if (dsym_url.get()) {
110 if (::CFURLGetFileSystemRepresentation(
111 dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {
112 if (log) {
113 LLDB_LOGF(log,
114 "DebugSymbols framework returned dSYM path of %s for "
115 "UUID %s -- looking for the dSYM",
116 path, uuid->GetAsString().c_str());
117 }
118 FileSpec dsym_filespec(path);
119 if (path[0] == '~')
120 FileSystem::Instance().Resolve(dsym_filespec);
121
122 if (FileSystem::Instance().IsDirectory(dsym_filespec)) {
123 dsym_filespec =
124 Symbols::FindSymbolFileInBundle(dsym_filespec, uuid, arch);
125 ++items_found;
126 } else {
127 ++items_found;
128 }
129 return_module_spec.GetSymbolFileSpec() = dsym_filespec;
130 }
131
132 bool success = false;
133 if (log) {
134 if (::CFURLGetFileSystemRepresentation(
135 dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {
136 LLDB_LOGF(log,
137 "DebugSymbols framework returned dSYM path of %s for "
138 "UUID %s -- looking for an exec file",
139 path, uuid->GetAsString().c_str());
140 }
141 }
142
143 CFCReleaser<CFDictionaryRef> dict(
144 g_dlsym_DBGCopyDSYMPropertyLists(dsym_url.get()));
145 CFDictionaryRef uuid_dict = NULL;
146 if (dict.get()) {
147 CFCString uuid_cfstr(uuid->GetAsString().c_str());
148 uuid_dict = static_cast<CFDictionaryRef>(
149 ::CFDictionaryGetValue(dict.get(), uuid_cfstr.get()));
150 }
151 if (uuid_dict) {
152 CFStringRef exec_cf_path =
153 static_cast<CFStringRef>(::CFDictionaryGetValue(
154 uuid_dict, CFSTR("DBGSymbolRichExecutable")));
155 if (exec_cf_path && ::CFStringGetFileSystemRepresentation(
156 exec_cf_path, path, sizeof(path))) {
157 if (log) {
158 LLDB_LOGF(log, "plist bundle has exec path of %s for UUID %s",
159 path, uuid->GetAsString().c_str());
160 }
161 ++items_found;
162 FileSpec exec_filespec(path);
163 if (path[0] == '~')
164 FileSystem::Instance().Resolve(exec_filespec);
165 if (FileSystem::Instance().Exists(exec_filespec)) {
166 success = true;
167 return_module_spec.GetFileSpec() = exec_filespec;
168 }
169 }
170 }
171
172 if (!success) {
173 // No dictionary, check near the dSYM bundle for an executable that
174 // matches...
175 if (::CFURLGetFileSystemRepresentation(
176 dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {
177 char *dsym_extension_pos = ::strstr(path, ".dSYM");
178 if (dsym_extension_pos) {
179 *dsym_extension_pos = '\0';
180 if (log) {
181 LLDB_LOGF(log,
182 "Looking for executable binary next to dSYM "
183 "bundle with name with name %s",
184 path);
185 }
186 FileSpec file_spec(path);
187 FileSystem::Instance().Resolve(file_spec);
188 ModuleSpecList module_specs;
189 ModuleSpec matched_module_spec;
190 using namespace llvm::sys::fs;
191 switch (get_file_type(file_spec.GetPath())) {
192
193 case file_type::directory_file: // Bundle directory?
194 {
195 CFCBundle bundle(path);
196 CFCReleaser<CFURLRef> bundle_exe_url(
197 bundle.CopyExecutableURL());
198 if (bundle_exe_url.get()) {
199 if (::CFURLGetFileSystemRepresentation(bundle_exe_url.get(),
200 true, (UInt8 *)path,
201 sizeof(path) - 1)) {
202 FileSpec bundle_exe_file_spec(path);
203 FileSystem::Instance().Resolve(bundle_exe_file_spec);
204 if (ObjectFile::GetModuleSpecifications(
205 bundle_exe_file_spec, 0, 0, module_specs) &&
206 module_specs.FindMatchingModuleSpec(
207 module_spec, matched_module_spec))
208
209 {
210 ++items_found;
211 return_module_spec.GetFileSpec() = bundle_exe_file_spec;
212 if (log) {
213 LLDB_LOGF(log,
214 "Executable binary %s next to dSYM is "
215 "compatible; using",
216 path);
217 }
218 }
219 }
220 }
221 } break;
222
223 case file_type::fifo_file: // Forget pipes
224 case file_type::socket_file: // We can't process socket files
225 case file_type::file_not_found: // File doesn't exist...
226 case file_type::status_error:
227 break;
228
229 case file_type::type_unknown:
230 case file_type::regular_file:
231 case file_type::symlink_file:
232 case file_type::block_file:
233 case file_type::character_file:
234 if (ObjectFile::GetModuleSpecifications(file_spec, 0, 0,
235 module_specs) &&
236 module_specs.FindMatchingModuleSpec(module_spec,
237 matched_module_spec))
238
239 {
240 ++items_found;
241 return_module_spec.GetFileSpec() = file_spec;
242 if (log) {
243 LLDB_LOGF(log,
244 "Executable binary %s next to dSYM is "
245 "compatible; using",
246 path);
247 }
248 }
249 break;
250 }
251 }
252 }
253 }
254 }
255 }
256 }
257 }
258
259 if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator()) {
260 g->GetOrCreate<repro::SymbolFileProvider>().AddSymbolFile(
261 uuid, return_module_spec.GetFileSpec(),
262 return_module_spec.GetSymbolFileSpec());
263 }
264
265 return items_found;
266 }
267
FindSymbolFileInBundle(const FileSpec & dsym_bundle_fspec,const lldb_private::UUID * uuid,const ArchSpec * arch)268 FileSpec Symbols::FindSymbolFileInBundle(const FileSpec &dsym_bundle_fspec,
269 const lldb_private::UUID *uuid,
270 const ArchSpec *arch) {
271 std::string dsym_bundle_path = dsym_bundle_fspec.GetPath();
272 llvm::SmallString<128> buffer(dsym_bundle_path);
273 llvm::sys::path::append(buffer, "Contents", "Resources", "DWARF");
274
275 std::error_code EC;
276 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs =
277 FileSystem::Instance().GetVirtualFileSystem();
278 llvm::vfs::recursive_directory_iterator Iter(*vfs, buffer.str(), EC);
279 llvm::vfs::recursive_directory_iterator End;
280 for (; Iter != End && !EC; Iter.increment(EC)) {
281 llvm::ErrorOr<llvm::vfs::Status> Status = vfs->status(Iter->path());
282 if (Status->isDirectory())
283 continue;
284
285 FileSpec dsym_fspec(Iter->path());
286 ModuleSpecList module_specs;
287 if (ObjectFile::GetModuleSpecifications(dsym_fspec, 0, 0, module_specs)) {
288 ModuleSpec spec;
289 for (size_t i = 0; i < module_specs.GetSize(); ++i) {
290 bool got_spec = module_specs.GetModuleSpecAtIndex(i, spec);
291 assert(got_spec); // The call has side-effects so can't be inlined.
292 UNUSED_IF_ASSERT_DISABLED(got_spec);
293 if ((uuid == nullptr ||
294 (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) &&
295 (arch == nullptr ||
296 (spec.GetArchitecturePtr() &&
297 spec.GetArchitecture().IsCompatibleMatch(*arch)))) {
298 return dsym_fspec;
299 }
300 }
301 }
302 }
303
304 return {};
305 }
306
GetModuleSpecInfoFromUUIDDictionary(CFDictionaryRef uuid_dict,ModuleSpec & module_spec)307 static bool GetModuleSpecInfoFromUUIDDictionary(CFDictionaryRef uuid_dict,
308 ModuleSpec &module_spec) {
309 Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
310 bool success = false;
311 if (uuid_dict != NULL && CFGetTypeID(uuid_dict) == CFDictionaryGetTypeID()) {
312 std::string str;
313 CFStringRef cf_str;
314 CFDictionaryRef cf_dict;
315
316 cf_str = (CFStringRef)CFDictionaryGetValue(
317 (CFDictionaryRef)uuid_dict, CFSTR("DBGSymbolRichExecutable"));
318 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
319 if (CFCString::FileSystemRepresentation(cf_str, str)) {
320 module_spec.GetFileSpec().SetFile(str.c_str(), FileSpec::Style::native);
321 FileSystem::Instance().Resolve(module_spec.GetFileSpec());
322 if (log) {
323 LLDB_LOGF(log,
324 "From dsymForUUID plist: Symbol rich executable is at '%s'",
325 str.c_str());
326 }
327 }
328 }
329
330 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
331 CFSTR("DBGDSYMPath"));
332 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
333 if (CFCString::FileSystemRepresentation(cf_str, str)) {
334 module_spec.GetSymbolFileSpec().SetFile(str.c_str(),
335 FileSpec::Style::native);
336 FileSystem::Instance().Resolve(module_spec.GetFileSpec());
337 success = true;
338 if (log) {
339 LLDB_LOGF(log, "From dsymForUUID plist: dSYM is at '%s'",
340 str.c_str());
341 }
342 }
343 }
344
345 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
346 CFSTR("DBGArchitecture"));
347 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
348 if (CFCString::FileSystemRepresentation(cf_str, str))
349 module_spec.GetArchitecture().SetTriple(str.c_str());
350 }
351
352 std::string DBGBuildSourcePath;
353 std::string DBGSourcePath;
354
355 // If DBGVersion 1 or DBGVersion missing, ignore DBGSourcePathRemapping.
356 // If DBGVersion 2, strip last two components of path remappings from
357 // entries to fix an issue with a specific set of
358 // DBGSourcePathRemapping entries that lldb worked
359 // with.
360 // If DBGVersion 3, trust & use the source path remappings as-is.
361 //
362 cf_dict = (CFDictionaryRef)CFDictionaryGetValue(
363 (CFDictionaryRef)uuid_dict, CFSTR("DBGSourcePathRemapping"));
364 if (cf_dict && CFGetTypeID(cf_dict) == CFDictionaryGetTypeID()) {
365 // If we see DBGVersion with a value of 2 or higher, this is a new style
366 // DBGSourcePathRemapping dictionary
367 bool new_style_source_remapping_dictionary = false;
368 bool do_truncate_remapping_names = false;
369 std::string original_DBGSourcePath_value = DBGSourcePath;
370 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
371 CFSTR("DBGVersion"));
372 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
373 std::string version;
374 CFCString::FileSystemRepresentation(cf_str, version);
375 if (!version.empty() && isdigit(version[0])) {
376 int version_number = atoi(version.c_str());
377 if (version_number > 1) {
378 new_style_source_remapping_dictionary = true;
379 }
380 if (version_number == 2) {
381 do_truncate_remapping_names = true;
382 }
383 }
384 }
385
386 CFIndex kv_pair_count = CFDictionaryGetCount((CFDictionaryRef)uuid_dict);
387 if (kv_pair_count > 0) {
388 CFStringRef *keys =
389 (CFStringRef *)malloc(kv_pair_count * sizeof(CFStringRef));
390 CFStringRef *values =
391 (CFStringRef *)malloc(kv_pair_count * sizeof(CFStringRef));
392 if (keys != nullptr && values != nullptr) {
393 CFDictionaryGetKeysAndValues((CFDictionaryRef)uuid_dict,
394 (const void **)keys,
395 (const void **)values);
396 }
397 for (CFIndex i = 0; i < kv_pair_count; i++) {
398 DBGBuildSourcePath.clear();
399 DBGSourcePath.clear();
400 if (keys[i] && CFGetTypeID(keys[i]) == CFStringGetTypeID()) {
401 CFCString::FileSystemRepresentation(keys[i], DBGBuildSourcePath);
402 }
403 if (values[i] && CFGetTypeID(values[i]) == CFStringGetTypeID()) {
404 CFCString::FileSystemRepresentation(values[i], DBGSourcePath);
405 }
406 if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) {
407 // In the "old style" DBGSourcePathRemapping dictionary, the
408 // DBGSourcePath values (the "values" half of key-value path pairs)
409 // were wrong. Ignore them and use the universal DBGSourcePath
410 // string from earlier.
411 if (new_style_source_remapping_dictionary &&
412 !original_DBGSourcePath_value.empty()) {
413 DBGSourcePath = original_DBGSourcePath_value;
414 }
415 if (DBGSourcePath[0] == '~') {
416 FileSpec resolved_source_path(DBGSourcePath.c_str());
417 FileSystem::Instance().Resolve(resolved_source_path);
418 DBGSourcePath = resolved_source_path.GetPath();
419 }
420 // With version 2 of DBGSourcePathRemapping, we can chop off the
421 // last two filename parts from the source remapping and get a more
422 // general source remapping that still works. Add this as another
423 // option in addition to the full source path remap.
424 module_spec.GetSourceMappingList().Append(
425 ConstString(DBGBuildSourcePath.c_str()),
426 ConstString(DBGSourcePath.c_str()), true);
427 if (do_truncate_remapping_names) {
428 FileSpec build_path(DBGBuildSourcePath.c_str());
429 FileSpec source_path(DBGSourcePath.c_str());
430 build_path.RemoveLastPathComponent();
431 build_path.RemoveLastPathComponent();
432 source_path.RemoveLastPathComponent();
433 source_path.RemoveLastPathComponent();
434 module_spec.GetSourceMappingList().Append(
435 ConstString(build_path.GetPath().c_str()),
436 ConstString(source_path.GetPath().c_str()), true);
437 }
438 }
439 }
440 if (keys)
441 free(keys);
442 if (values)
443 free(values);
444 }
445 }
446
447 // If we have a DBGBuildSourcePath + DBGSourcePath pair, append them to the
448 // source remappings list.
449
450 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
451 CFSTR("DBGBuildSourcePath"));
452 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
453 CFCString::FileSystemRepresentation(cf_str, DBGBuildSourcePath);
454 }
455
456 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
457 CFSTR("DBGSourcePath"));
458 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
459 CFCString::FileSystemRepresentation(cf_str, DBGSourcePath);
460 }
461
462 if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) {
463 if (DBGSourcePath[0] == '~') {
464 FileSpec resolved_source_path(DBGSourcePath.c_str());
465 FileSystem::Instance().Resolve(resolved_source_path);
466 DBGSourcePath = resolved_source_path.GetPath();
467 }
468 module_spec.GetSourceMappingList().Append(
469 ConstString(DBGBuildSourcePath.c_str()),
470 ConstString(DBGSourcePath.c_str()), true);
471 }
472 }
473 return success;
474 }
475
DownloadObjectAndSymbolFile(ModuleSpec & module_spec,bool force_lookup)476 bool Symbols::DownloadObjectAndSymbolFile(ModuleSpec &module_spec,
477 bool force_lookup) {
478 bool success = false;
479 const UUID *uuid_ptr = module_spec.GetUUIDPtr();
480 const FileSpec *file_spec_ptr = module_spec.GetFileSpecPtr();
481
482 if (repro::Loader *l = repro::Reproducer::Instance().GetLoader()) {
483 static repro::SymbolFileLoader symbol_file_loader(l);
484 std::pair<FileSpec, FileSpec> paths = symbol_file_loader.GetPaths(uuid_ptr);
485 if (paths.first)
486 module_spec.GetFileSpec() = paths.first;
487 if (paths.second)
488 module_spec.GetSymbolFileSpec() = paths.second;
489 return true;
490 }
491
492 // Lambda to capture the state of module_spec before returning from this
493 // function.
494 auto RecordResult = [&]() {
495 if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator()) {
496 g->GetOrCreate<repro::SymbolFileProvider>().AddSymbolFile(
497 uuid_ptr, module_spec.GetFileSpec(), module_spec.GetSymbolFileSpec());
498 }
499 };
500
501 // It's expensive to check for the DBGShellCommands defaults setting, only do
502 // it once per lldb run and cache the result.
503 static bool g_have_checked_for_dbgshell_command = false;
504 static const char *g_dbgshell_command = NULL;
505 if (!g_have_checked_for_dbgshell_command) {
506 g_have_checked_for_dbgshell_command = true;
507 CFTypeRef defaults_setting = CFPreferencesCopyAppValue(
508 CFSTR("DBGShellCommands"), CFSTR("com.apple.DebugSymbols"));
509 if (defaults_setting &&
510 CFGetTypeID(defaults_setting) == CFStringGetTypeID()) {
511 char cstr_buf[PATH_MAX];
512 if (CFStringGetCString((CFStringRef)defaults_setting, cstr_buf,
513 sizeof(cstr_buf), kCFStringEncodingUTF8)) {
514 g_dbgshell_command =
515 strdup(cstr_buf); // this malloc'ed memory will never be freed
516 }
517 }
518 if (defaults_setting) {
519 CFRelease(defaults_setting);
520 }
521 }
522
523 // When g_dbgshell_command is NULL, the user has not enabled the use of an
524 // external program to find the symbols, don't run it for them.
525 if (!force_lookup && g_dbgshell_command == NULL) {
526 RecordResult();
527 return false;
528 }
529
530 if (uuid_ptr ||
531 (file_spec_ptr && FileSystem::Instance().Exists(*file_spec_ptr))) {
532 static bool g_located_dsym_for_uuid_exe = false;
533 static bool g_dsym_for_uuid_exe_exists = false;
534 static char g_dsym_for_uuid_exe_path[PATH_MAX];
535 if (!g_located_dsym_for_uuid_exe) {
536 g_located_dsym_for_uuid_exe = true;
537 const char *dsym_for_uuid_exe_path_cstr =
538 getenv("LLDB_APPLE_DSYMFORUUID_EXECUTABLE");
539 FileSpec dsym_for_uuid_exe_spec;
540 if (dsym_for_uuid_exe_path_cstr) {
541 dsym_for_uuid_exe_spec.SetFile(dsym_for_uuid_exe_path_cstr,
542 FileSpec::Style::native);
543 FileSystem::Instance().Resolve(dsym_for_uuid_exe_spec);
544 g_dsym_for_uuid_exe_exists =
545 FileSystem::Instance().Exists(dsym_for_uuid_exe_spec);
546 }
547
548 if (!g_dsym_for_uuid_exe_exists) {
549 dsym_for_uuid_exe_spec.SetFile("/usr/local/bin/dsymForUUID",
550 FileSpec::Style::native);
551 g_dsym_for_uuid_exe_exists =
552 FileSystem::Instance().Exists(dsym_for_uuid_exe_spec);
553 if (!g_dsym_for_uuid_exe_exists) {
554 long bufsize;
555 if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) != -1) {
556 char buffer[bufsize];
557 struct passwd pwd;
558 struct passwd *tilde_rc = NULL;
559 // we are a library so we need to use the reentrant version of
560 // getpwnam()
561 if (getpwnam_r("rc", &pwd, buffer, bufsize, &tilde_rc) == 0 &&
562 tilde_rc && tilde_rc->pw_dir) {
563 std::string dsymforuuid_path(tilde_rc->pw_dir);
564 dsymforuuid_path += "/bin/dsymForUUID";
565 dsym_for_uuid_exe_spec.SetFile(dsymforuuid_path.c_str(),
566 FileSpec::Style::native);
567 g_dsym_for_uuid_exe_exists =
568 FileSystem::Instance().Exists(dsym_for_uuid_exe_spec);
569 }
570 }
571 }
572 }
573 if (!g_dsym_for_uuid_exe_exists && g_dbgshell_command != NULL) {
574 dsym_for_uuid_exe_spec.SetFile(g_dbgshell_command,
575 FileSpec::Style::native);
576 FileSystem::Instance().Resolve(dsym_for_uuid_exe_spec);
577 g_dsym_for_uuid_exe_exists =
578 FileSystem::Instance().Exists(dsym_for_uuid_exe_spec);
579 }
580
581 if (g_dsym_for_uuid_exe_exists)
582 dsym_for_uuid_exe_spec.GetPath(g_dsym_for_uuid_exe_path,
583 sizeof(g_dsym_for_uuid_exe_path));
584 }
585 if (g_dsym_for_uuid_exe_exists) {
586 std::string uuid_str;
587 char file_path[PATH_MAX];
588 file_path[0] = '\0';
589
590 if (uuid_ptr)
591 uuid_str = uuid_ptr->GetAsString();
592
593 if (file_spec_ptr)
594 file_spec_ptr->GetPath(file_path, sizeof(file_path));
595
596 StreamString command;
597 if (!uuid_str.empty())
598 command.Printf("%s --ignoreNegativeCache --copyExecutable %s",
599 g_dsym_for_uuid_exe_path, uuid_str.c_str());
600 else if (file_path[0] != '\0')
601 command.Printf("%s --ignoreNegativeCache --copyExecutable %s",
602 g_dsym_for_uuid_exe_path, file_path);
603
604 if (!command.GetString().empty()) {
605 Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
606 int exit_status = -1;
607 int signo = -1;
608 std::string command_output;
609 if (log) {
610 if (!uuid_str.empty())
611 LLDB_LOGF(log, "Calling %s with UUID %s to find dSYM",
612 g_dsym_for_uuid_exe_path, uuid_str.c_str());
613 else if (file_path[0] != '\0')
614 LLDB_LOGF(log, "Calling %s with file %s to find dSYM",
615 g_dsym_for_uuid_exe_path, file_path);
616 }
617 Status error = Host::RunShellCommand(
618 command.GetData(),
619 FileSpec(), // current working directory
620 &exit_status, // Exit status
621 &signo, // Signal int *
622 &command_output, // Command output
623 std::chrono::seconds(
624 640), // Large timeout to allow for long dsym download times
625 false); // Don't run in a shell (we don't need shell expansion)
626 if (error.Success() && exit_status == 0 && !command_output.empty()) {
627 CFCData data(CFDataCreateWithBytesNoCopy(
628 NULL, (const UInt8 *)command_output.data(), command_output.size(),
629 kCFAllocatorNull));
630
631 CFCReleaser<CFDictionaryRef> plist(
632 (CFDictionaryRef)::CFPropertyListCreateFromXMLData(
633 NULL, data.get(), kCFPropertyListImmutable, NULL));
634
635 if (plist.get() &&
636 CFGetTypeID(plist.get()) == CFDictionaryGetTypeID()) {
637 if (!uuid_str.empty()) {
638 CFCString uuid_cfstr(uuid_str.c_str());
639 CFDictionaryRef uuid_dict = (CFDictionaryRef)CFDictionaryGetValue(
640 plist.get(), uuid_cfstr.get());
641 success =
642 GetModuleSpecInfoFromUUIDDictionary(uuid_dict, module_spec);
643 } else {
644 const CFIndex num_values = ::CFDictionaryGetCount(plist.get());
645 if (num_values > 0) {
646 std::vector<CFStringRef> keys(num_values, NULL);
647 std::vector<CFDictionaryRef> values(num_values, NULL);
648 ::CFDictionaryGetKeysAndValues(plist.get(), NULL,
649 (const void **)&values[0]);
650 if (num_values == 1) {
651 success = GetModuleSpecInfoFromUUIDDictionary(values[0],
652 module_spec);
653 RecordResult();
654 return success;
655 } else {
656 for (CFIndex i = 0; i < num_values; ++i) {
657 ModuleSpec curr_module_spec;
658 if (GetModuleSpecInfoFromUUIDDictionary(values[i],
659 curr_module_spec)) {
660 if (module_spec.GetArchitecture().IsCompatibleMatch(
661 curr_module_spec.GetArchitecture())) {
662 module_spec = curr_module_spec;
663 RecordResult();
664 return true;
665 }
666 }
667 }
668 }
669 }
670 }
671 }
672 } else {
673 if (log) {
674 if (!uuid_str.empty())
675 LLDB_LOGF(log, "Called %s on %s, no matches",
676 g_dsym_for_uuid_exe_path, uuid_str.c_str());
677 else if (file_path[0] != '\0')
678 LLDB_LOGF(log, "Called %s on %s, no matches",
679 g_dsym_for_uuid_exe_path, file_path);
680 }
681 }
682 }
683 }
684 }
685 RecordResult();
686 return success;
687 }
688