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