1 //===-- Symbols.cpp ---------------------------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #include "lldb/Host/Symbols.h"
15 #include "lldb/Utility/SafeMachO.h"
18 // Other libraries and framework includes
19 #include <CoreFoundation/CoreFoundation.h>
22 #include "lldb/Core/ArchSpec.h"
23 #include "lldb/Core/DataBuffer.h"
24 #include "lldb/Core/DataExtractor.h"
25 #include "lldb/Core/Log.h"
26 #include "lldb/Core/Module.h"
27 #include "lldb/Core/ModuleSpec.h"
28 #include "lldb/Core/StreamString.h"
29 #include "lldb/Core/Timer.h"
30 #include "lldb/Core/UUID.h"
31 #include "lldb/Host/Endian.h"
32 #include "lldb/Host/Host.h"
33 #include "lldb/Symbol/ObjectFile.h"
34 #include "lldb/Utility/CleanUp.h"
35 #include "Host/macosx/cfcpp/CFCBundle.h"
36 #include "Host/macosx/cfcpp/CFCData.h"
37 #include "Host/macosx/cfcpp/CFCReleaser.h"
38 #include "Host/macosx/cfcpp/CFCString.h"
39 #include "mach/machine.h"
42 using namespace lldb_private;
43 using namespace llvm::MachO;
45 #if !defined (__arm__) && !defined (__arm64__) && !defined (__aarch64__) // No DebugSymbols on the iOS devices
48 CFURLRef DBGCopyFullDSYMURLForUUID (CFUUIDRef uuid, CFURLRef exec_url);
49 CFDictionaryRef DBGCopyDSYMPropertyLists (CFURLRef dsym_url);
55 LocateMacOSXFilesUsingDebugSymbols
57 const ModuleSpec &module_spec,
58 ModuleSpec &return_module_spec
61 return_module_spec = module_spec;
62 return_module_spec.GetFileSpec().Clear();
63 return_module_spec.GetSymbolFileSpec().Clear();
67 #if !defined (__arm__) && !defined (__arm64__) && !defined (__aarch64__) // No DebugSymbols on the iOS devices
69 const UUID *uuid = module_spec.GetUUIDPtr();
70 const ArchSpec *arch = module_spec.GetArchitecturePtr();
72 if (uuid && uuid->IsValid())
74 // Try and locate the dSYM file using DebugSymbols first
75 const UInt8 *module_uuid = (const UInt8 *)uuid->GetBytes();
76 if (module_uuid != NULL)
78 CFCReleaser<CFUUIDRef> module_uuid_ref(::CFUUIDCreateWithBytes (NULL,
96 if (module_uuid_ref.get())
98 CFCReleaser<CFURLRef> exec_url;
99 const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();
100 Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
103 char exec_cf_path[PATH_MAX];
104 if (exec_fspec->GetPath(exec_cf_path, sizeof(exec_cf_path)))
105 exec_url.reset(::CFURLCreateFromFileSystemRepresentation (NULL,
106 (const UInt8 *)exec_cf_path,
107 strlen(exec_cf_path),
111 CFCReleaser<CFURLRef> dsym_url (::DBGCopyFullDSYMURLForUUID(module_uuid_ref.get(), exec_url.get()));
116 if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1))
120 log->Printf ("DebugSymbols framework returned dSYM path of %s for UUID %s -- looking for the dSYM", path, uuid->GetAsString().c_str());
122 FileSpec dsym_filespec(path, path[0] == '~');
124 if (dsym_filespec.GetFileType () == FileSpec::eFileTypeDirectory)
126 dsym_filespec = Symbols::FindSymbolFileInBundle (dsym_filespec, uuid, arch);
133 return_module_spec.GetSymbolFileSpec() = dsym_filespec;
136 bool success = false;
139 if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1))
141 log->Printf ("DebugSymbols framework returned dSYM path of %s for UUID %s -- looking for an exec file", path, uuid->GetAsString().c_str());
146 CFCReleaser<CFDictionaryRef> dict(::DBGCopyDSYMPropertyLists (dsym_url.get()));
147 CFDictionaryRef uuid_dict = NULL;
150 CFCString uuid_cfstr (uuid->GetAsString().c_str());
151 uuid_dict = static_cast<CFDictionaryRef>(::CFDictionaryGetValue (dict.get(), uuid_cfstr.get()));
155 CFStringRef exec_cf_path = static_cast<CFStringRef>(::CFDictionaryGetValue (uuid_dict, CFSTR("DBGSymbolRichExecutable")));
156 if (exec_cf_path && ::CFStringGetFileSystemRepresentation (exec_cf_path, path, sizeof(path)))
160 log->Printf ("plist bundle has exec path of %s for UUID %s", path, uuid->GetAsString().c_str());
163 FileSpec exec_filespec (path, path[0] == '~');
164 if (exec_filespec.Exists())
167 return_module_spec.GetFileSpec() = exec_filespec;
174 // No dictionary, check near the dSYM bundle for an executable that matches...
175 if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1))
177 char *dsym_extension_pos = ::strstr (path, ".dSYM");
178 if (dsym_extension_pos)
180 *dsym_extension_pos = '\0';
183 log->Printf ("Looking for executable binary next to dSYM bundle with name with name %s", path);
185 FileSpec file_spec (path, true);
186 ModuleSpecList module_specs;
187 ModuleSpec matched_module_spec;
188 switch (file_spec.GetFileType())
190 case FileSpec::eFileTypeDirectory: // Bundle directory?
192 CFCBundle bundle (path);
193 CFCReleaser<CFURLRef> bundle_exe_url (bundle.CopyExecutableURL ());
194 if (bundle_exe_url.get())
196 if (::CFURLGetFileSystemRepresentation (bundle_exe_url.get(), true, (UInt8*)path, sizeof(path)-1))
198 FileSpec bundle_exe_file_spec (path, true);
199 if (ObjectFile::GetModuleSpecifications(bundle_exe_file_spec, 0, 0, module_specs) &&
200 module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec))
204 return_module_spec.GetFileSpec() = bundle_exe_file_spec;
207 log->Printf ("Executable binary %s next to dSYM is compatible; using", path);
215 case FileSpec::eFileTypePipe: // Forget pipes
216 case FileSpec::eFileTypeSocket: // We can't process socket files
217 case FileSpec::eFileTypeInvalid: // File doesn't exist...
220 case FileSpec::eFileTypeUnknown:
221 case FileSpec::eFileTypeRegular:
222 case FileSpec::eFileTypeSymbolicLink:
223 case FileSpec::eFileTypeOther:
224 if (ObjectFile::GetModuleSpecifications(file_spec, 0, 0, module_specs) &&
225 module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec))
229 return_module_spec.GetFileSpec() = file_spec;
232 log->Printf ("Executable binary %s next to dSYM is compatible; using", path);
244 #endif // #if !defined (__arm__) && !defined (__arm64__) && !defined (__aarch64__)
250 Symbols::FindSymbolFileInBundle (const FileSpec& dsym_bundle_fspec,
251 const lldb_private::UUID *uuid,
252 const ArchSpec *arch)
258 if (dsym_bundle_fspec.GetPath(path, sizeof(path)))
260 ::strncat (path, "/Contents/Resources/DWARF", sizeof(path) - strlen(path) - 1);
262 lldb_utility::CleanUp <DIR *, int> dirp (opendir(path), NULL, closedir);
265 dsym_fspec.GetDirectory().SetCString(path);
267 while ((dp = readdir(dirp.get())) != NULL)
269 // Only search directories
270 if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN)
272 if (dp->d_namlen == 1 && dp->d_name[0] == '.')
275 if (dp->d_namlen == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.')
279 if (dp->d_type == DT_REG || dp->d_type == DT_UNKNOWN)
281 dsym_fspec.GetFilename().SetCString(dp->d_name);
282 ModuleSpecList module_specs;
283 if (ObjectFile::GetModuleSpecifications(dsym_fspec, 0, 0, module_specs))
286 for (size_t i = 0; i < module_specs.GetSize(); ++i)
288 assert(module_specs.GetModuleSpecAtIndex(i, spec));
289 if ((uuid == NULL || (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) &&
290 (arch == NULL || (spec.GetArchitecturePtr() && spec.GetArchitecture().IsCompatibleMatch(*arch))))
305 GetModuleSpecInfoFromUUIDDictionary (CFDictionaryRef uuid_dict, ModuleSpec &module_spec)
307 Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
308 bool success = false;
309 if (uuid_dict != NULL && CFGetTypeID (uuid_dict) == CFDictionaryGetTypeID ())
314 cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGSymbolRichExecutable"));
315 if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ())
317 if (CFCString::FileSystemRepresentation(cf_str, str))
319 module_spec.GetFileSpec().SetFile (str.c_str(), true);
322 log->Printf ("From dsymForUUID plist: Symbol rich executable is at '%s'", str.c_str());
327 cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGDSYMPath"));
328 if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ())
330 if (CFCString::FileSystemRepresentation(cf_str, str))
332 module_spec.GetSymbolFileSpec().SetFile (str.c_str(), true);
336 log->Printf ("From dsymForUUID plist: dSYM is at '%s'", str.c_str());
341 cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGArchitecture"));
342 if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ())
344 if (CFCString::FileSystemRepresentation(cf_str, str))
345 module_spec.GetArchitecture().SetTriple(str.c_str());
348 std::string DBGBuildSourcePath;
349 std::string DBGSourcePath;
351 cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGBuildSourcePath"));
352 if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ())
354 CFCString::FileSystemRepresentation(cf_str, DBGBuildSourcePath);
357 cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGSourcePath"));
358 if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ())
360 CFCString::FileSystemRepresentation(cf_str, DBGSourcePath);
363 if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty())
365 module_spec.GetSourceMappingList().Append (ConstString(DBGBuildSourcePath.c_str()), ConstString(DBGSourcePath.c_str()), true);
373 Symbols::DownloadObjectAndSymbolFile (ModuleSpec &module_spec, bool force_lookup)
375 bool success = false;
376 const UUID *uuid_ptr = module_spec.GetUUIDPtr();
377 const FileSpec *file_spec_ptr = module_spec.GetFileSpecPtr();
379 // It's expensive to check for the DBGShellCommands defaults setting, only do it once per
380 // lldb run and cache the result.
381 static bool g_have_checked_for_dbgshell_command = false;
382 static const char *g_dbgshell_command = NULL;
383 if (g_have_checked_for_dbgshell_command == false)
385 g_have_checked_for_dbgshell_command = true;
386 CFTypeRef defaults_setting = CFPreferencesCopyAppValue (CFSTR ("DBGShellCommands"), CFSTR ("com.apple.DebugSymbols"));
387 if (defaults_setting && CFGetTypeID (defaults_setting) == CFStringGetTypeID())
389 char cstr_buf[PATH_MAX];
390 if (CFStringGetCString ((CFStringRef) defaults_setting, cstr_buf, sizeof (cstr_buf), kCFStringEncodingUTF8))
392 g_dbgshell_command = strdup (cstr_buf); // this malloc'ed memory will never be freed
395 if (defaults_setting)
397 CFRelease (defaults_setting);
401 // When g_dbgshell_command is NULL, the user has not enabled the use of an external program
402 // to find the symbols, don't run it for them.
403 if (force_lookup == false && g_dbgshell_command == NULL)
408 if (uuid_ptr || (file_spec_ptr && file_spec_ptr->Exists()))
410 static bool g_located_dsym_for_uuid_exe = false;
411 static bool g_dsym_for_uuid_exe_exists = false;
412 static char g_dsym_for_uuid_exe_path[PATH_MAX];
413 if (!g_located_dsym_for_uuid_exe)
415 g_located_dsym_for_uuid_exe = true;
416 const char *dsym_for_uuid_exe_path_cstr = getenv("LLDB_APPLE_DSYMFORUUID_EXECUTABLE");
417 FileSpec dsym_for_uuid_exe_spec;
418 if (dsym_for_uuid_exe_path_cstr)
420 dsym_for_uuid_exe_spec.SetFile(dsym_for_uuid_exe_path_cstr, true);
421 g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists();
424 if (!g_dsym_for_uuid_exe_exists)
426 dsym_for_uuid_exe_spec.SetFile("/usr/local/bin/dsymForUUID", false);
427 g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists();
428 if (!g_dsym_for_uuid_exe_exists)
431 if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) != -1)
433 char buffer[bufsize];
435 struct passwd *tilde_rc = NULL;
436 // we are a library so we need to use the reentrant version of getpwnam()
437 if (getpwnam_r ("rc", &pwd, buffer, bufsize, &tilde_rc) == 0
441 std::string dsymforuuid_path(tilde_rc->pw_dir);
442 dsymforuuid_path += "/bin/dsymForUUID";
443 dsym_for_uuid_exe_spec.SetFile(dsymforuuid_path.c_str(), false);
444 g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists();
449 if (!g_dsym_for_uuid_exe_exists && g_dbgshell_command != NULL)
451 dsym_for_uuid_exe_spec.SetFile(g_dbgshell_command, true);
452 g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists();
455 if (g_dsym_for_uuid_exe_exists)
456 dsym_for_uuid_exe_spec.GetPath (g_dsym_for_uuid_exe_path, sizeof(g_dsym_for_uuid_exe_path));
458 if (g_dsym_for_uuid_exe_exists)
460 std::string uuid_str;
461 char file_path[PATH_MAX];
465 uuid_str = uuid_ptr->GetAsString();
468 file_spec_ptr->GetPath(file_path, sizeof(file_path));
470 StreamString command;
471 if (!uuid_str.empty())
472 command.Printf("%s --ignoreNegativeCache --copyExecutable %s", g_dsym_for_uuid_exe_path, uuid_str.c_str());
473 else if (file_path[0] != '\0')
474 command.Printf("%s --ignoreNegativeCache --copyExecutable %s", g_dsym_for_uuid_exe_path, file_path);
476 if (!command.GetString().empty())
478 Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
479 int exit_status = -1;
481 std::string command_output;
484 if (!uuid_str.empty())
485 log->Printf("Calling %s with UUID %s to find dSYM", g_dsym_for_uuid_exe_path, uuid_str.c_str());
486 else if (file_path[0] != '\0')
487 log->Printf("Calling %s with file %s to find dSYM", g_dsym_for_uuid_exe_path, file_path);
489 Error error = Host::RunShellCommand (command.GetData(),
490 NULL, // current working directory
491 &exit_status, // Exit status
492 &signo, // Signal int *
493 &command_output, // Command output
494 30, // Large timeout to allow for long dsym download times
495 false); // Don't run in a shell (we don't need shell expansion)
496 if (error.Success() && exit_status == 0 && !command_output.empty())
498 CFCData data (CFDataCreateWithBytesNoCopy (NULL,
499 (const UInt8 *)command_output.data(),
500 command_output.size(),
503 CFCReleaser<CFDictionaryRef> plist((CFDictionaryRef)::CFPropertyListCreateFromXMLData (NULL, data.get(), kCFPropertyListImmutable, NULL));
505 if (plist.get() && CFGetTypeID (plist.get()) == CFDictionaryGetTypeID ())
507 if (!uuid_str.empty())
509 CFCString uuid_cfstr(uuid_str.c_str());
510 CFDictionaryRef uuid_dict = (CFDictionaryRef)CFDictionaryGetValue (plist.get(), uuid_cfstr.get());
511 success = GetModuleSpecInfoFromUUIDDictionary (uuid_dict, module_spec);
515 const CFIndex num_values = ::CFDictionaryGetCount(plist.get());
518 std::vector<CFStringRef> keys (num_values, NULL);
519 std::vector<CFDictionaryRef> values (num_values, NULL);
520 ::CFDictionaryGetKeysAndValues(plist.get(), NULL, (const void **)&values[0]);
523 return GetModuleSpecInfoFromUUIDDictionary (values[0], module_spec);
527 for (CFIndex i=0; i<num_values; ++i)
529 ModuleSpec curr_module_spec;
530 if (GetModuleSpecInfoFromUUIDDictionary (values[i], curr_module_spec))
532 if (module_spec.GetArchitecture().IsCompatibleMatch(curr_module_spec.GetArchitecture()))
534 module_spec = curr_module_spec;
548 if (!uuid_str.empty())
549 log->Printf("Called %s on %s, no matches", g_dsym_for_uuid_exe_path, uuid_str.c_str());
550 else if (file_path[0] != '\0')
551 log->Printf("Called %s on %s, no matches", g_dsym_for_uuid_exe_path, file_path);