| //===-- Symbols.cpp ---------------------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "lldb/Host/Symbols.h" |
| |
| // C Includes |
| #include <dirent.h> |
| #include <pwd.h> |
| #include "lldb/Utility/SafeMachO.h" |
| |
| // C++ Includes |
| // Other libraries and framework includes |
| #include <CoreFoundation/CoreFoundation.h> |
| |
| // Project includes |
| #include "lldb/Core/ArchSpec.h" |
| #include "lldb/Core/DataBuffer.h" |
| #include "lldb/Core/DataExtractor.h" |
| #include "lldb/Core/Log.h" |
| #include "lldb/Core/Module.h" |
| #include "lldb/Core/ModuleSpec.h" |
| #include "lldb/Core/StreamString.h" |
| #include "lldb/Core/Timer.h" |
| #include "lldb/Core/UUID.h" |
| #include "lldb/Host/Endian.h" |
| #include "lldb/Host/Host.h" |
| #include "lldb/Symbol/ObjectFile.h" |
| #include "lldb/Utility/CleanUp.h" |
| #include "Host/macosx/cfcpp/CFCBundle.h" |
| #include "Host/macosx/cfcpp/CFCData.h" |
| #include "Host/macosx/cfcpp/CFCReleaser.h" |
| #include "Host/macosx/cfcpp/CFCString.h" |
| #include "mach/machine.h" |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| using namespace llvm::MachO; |
| |
| #if !defined (__arm__) && !defined (__arm64__) && !defined (__aarch64__) // No DebugSymbols on the iOS devices |
| extern "C" { |
| |
| CFURLRef DBGCopyFullDSYMURLForUUID (CFUUIDRef uuid, CFURLRef exec_url); |
| CFDictionaryRef DBGCopyDSYMPropertyLists (CFURLRef dsym_url); |
| |
| } |
| #endif |
| |
| int |
| LocateMacOSXFilesUsingDebugSymbols |
| ( |
| const ModuleSpec &module_spec, |
| ModuleSpec &return_module_spec |
| ) |
| { |
| return_module_spec = module_spec; |
| return_module_spec.GetFileSpec().Clear(); |
| return_module_spec.GetSymbolFileSpec().Clear(); |
| |
| int items_found = 0; |
| |
| #if !defined (__arm__) && !defined (__arm64__) && !defined (__aarch64__) // No DebugSymbols on the iOS devices |
| |
| const UUID *uuid = module_spec.GetUUIDPtr(); |
| const ArchSpec *arch = module_spec.GetArchitecturePtr(); |
| |
| if (uuid && uuid->IsValid()) |
| { |
| // Try and locate the dSYM file using DebugSymbols first |
| const UInt8 *module_uuid = (const UInt8 *)uuid->GetBytes(); |
| if (module_uuid != NULL) |
| { |
| CFCReleaser<CFUUIDRef> module_uuid_ref(::CFUUIDCreateWithBytes (NULL, |
| module_uuid[0], |
| module_uuid[1], |
| module_uuid[2], |
| module_uuid[3], |
| module_uuid[4], |
| module_uuid[5], |
| module_uuid[6], |
| module_uuid[7], |
| module_uuid[8], |
| module_uuid[9], |
| module_uuid[10], |
| module_uuid[11], |
| module_uuid[12], |
| module_uuid[13], |
| module_uuid[14], |
| module_uuid[15])); |
| |
| if (module_uuid_ref.get()) |
| { |
| CFCReleaser<CFURLRef> exec_url; |
| const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); |
| Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); |
| if (exec_fspec) |
| { |
| char exec_cf_path[PATH_MAX]; |
| if (exec_fspec->GetPath(exec_cf_path, sizeof(exec_cf_path))) |
| exec_url.reset(::CFURLCreateFromFileSystemRepresentation (NULL, |
| (const UInt8 *)exec_cf_path, |
| strlen(exec_cf_path), |
| FALSE)); |
| } |
| |
| CFCReleaser<CFURLRef> dsym_url (::DBGCopyFullDSYMURLForUUID(module_uuid_ref.get(), exec_url.get())); |
| char path[PATH_MAX]; |
| |
| if (dsym_url.get()) |
| { |
| if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1)) |
| { |
| if (log) |
| { |
| log->Printf ("DebugSymbols framework returned dSYM path of %s for UUID %s -- looking for the dSYM", path, uuid->GetAsString().c_str()); |
| } |
| FileSpec dsym_filespec(path, path[0] == '~'); |
| |
| if (dsym_filespec.GetFileType () == FileSpec::eFileTypeDirectory) |
| { |
| dsym_filespec = Symbols::FindSymbolFileInBundle (dsym_filespec, uuid, arch); |
| ++items_found; |
| } |
| else |
| { |
| ++items_found; |
| } |
| return_module_spec.GetSymbolFileSpec() = dsym_filespec; |
| } |
| |
| bool success = false; |
| if (log) |
| { |
| if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1)) |
| { |
| log->Printf ("DebugSymbols framework returned dSYM path of %s for UUID %s -- looking for an exec file", path, uuid->GetAsString().c_str()); |
| } |
| |
| } |
| |
| CFCReleaser<CFDictionaryRef> dict(::DBGCopyDSYMPropertyLists (dsym_url.get())); |
| CFDictionaryRef uuid_dict = NULL; |
| if (dict.get()) |
| { |
| CFCString uuid_cfstr (uuid->GetAsString().c_str()); |
| uuid_dict = static_cast<CFDictionaryRef>(::CFDictionaryGetValue (dict.get(), uuid_cfstr.get())); |
| } |
| if (uuid_dict) |
| { |
| CFStringRef exec_cf_path = static_cast<CFStringRef>(::CFDictionaryGetValue (uuid_dict, CFSTR("DBGSymbolRichExecutable"))); |
| if (exec_cf_path && ::CFStringGetFileSystemRepresentation (exec_cf_path, path, sizeof(path))) |
| { |
| if (log) |
| { |
| log->Printf ("plist bundle has exec path of %s for UUID %s", path, uuid->GetAsString().c_str()); |
| } |
| ++items_found; |
| FileSpec exec_filespec (path, path[0] == '~'); |
| if (exec_filespec.Exists()) |
| { |
| success = true; |
| return_module_spec.GetFileSpec() = exec_filespec; |
| } |
| } |
| } |
| |
| if (!success) |
| { |
| // No dictionary, check near the dSYM bundle for an executable that matches... |
| if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1)) |
| { |
| char *dsym_extension_pos = ::strstr (path, ".dSYM"); |
| if (dsym_extension_pos) |
| { |
| *dsym_extension_pos = '\0'; |
| if (log) |
| { |
| log->Printf ("Looking for executable binary next to dSYM bundle with name with name %s", path); |
| } |
| FileSpec file_spec (path, true); |
| ModuleSpecList module_specs; |
| ModuleSpec matched_module_spec; |
| switch (file_spec.GetFileType()) |
| { |
| case FileSpec::eFileTypeDirectory: // Bundle directory? |
| { |
| CFCBundle bundle (path); |
| CFCReleaser<CFURLRef> bundle_exe_url (bundle.CopyExecutableURL ()); |
| if (bundle_exe_url.get()) |
| { |
| if (::CFURLGetFileSystemRepresentation (bundle_exe_url.get(), true, (UInt8*)path, sizeof(path)-1)) |
| { |
| FileSpec bundle_exe_file_spec (path, true); |
| if (ObjectFile::GetModuleSpecifications(bundle_exe_file_spec, 0, 0, module_specs) && |
| module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec)) |
| |
| { |
| ++items_found; |
| return_module_spec.GetFileSpec() = bundle_exe_file_spec; |
| if (log) |
| { |
| log->Printf ("Executable binary %s next to dSYM is compatible; using", path); |
| } |
| } |
| } |
| } |
| } |
| break; |
| |
| case FileSpec::eFileTypePipe: // Forget pipes |
| case FileSpec::eFileTypeSocket: // We can't process socket files |
| case FileSpec::eFileTypeInvalid: // File doesn't exist... |
| break; |
| |
| case FileSpec::eFileTypeUnknown: |
| case FileSpec::eFileTypeRegular: |
| case FileSpec::eFileTypeSymbolicLink: |
| case FileSpec::eFileTypeOther: |
| if (ObjectFile::GetModuleSpecifications(file_spec, 0, 0, module_specs) && |
| module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec)) |
| |
| { |
| ++items_found; |
| return_module_spec.GetFileSpec() = file_spec; |
| if (log) |
| { |
| log->Printf ("Executable binary %s next to dSYM is compatible; using", path); |
| } |
| } |
| break; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| #endif // #if !defined (__arm__) && !defined (__arm64__) && !defined (__aarch64__) |
| |
| return items_found; |
| } |
| |
| FileSpec |
| Symbols::FindSymbolFileInBundle (const FileSpec& dsym_bundle_fspec, |
| const lldb_private::UUID *uuid, |
| const ArchSpec *arch) |
| { |
| char path[PATH_MAX]; |
| |
| FileSpec dsym_fspec; |
| |
| if (dsym_bundle_fspec.GetPath(path, sizeof(path))) |
| { |
| ::strncat (path, "/Contents/Resources/DWARF", sizeof(path) - strlen(path) - 1); |
| |
| lldb_utility::CleanUp <DIR *, int> dirp (opendir(path), NULL, closedir); |
| if (dirp.is_valid()) |
| { |
| dsym_fspec.GetDirectory().SetCString(path); |
| struct dirent* dp; |
| while ((dp = readdir(dirp.get())) != NULL) |
| { |
| // Only search directories |
| if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN) |
| { |
| if (dp->d_namlen == 1 && dp->d_name[0] == '.') |
| continue; |
| |
| if (dp->d_namlen == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.') |
| continue; |
| } |
| |
| if (dp->d_type == DT_REG || dp->d_type == DT_UNKNOWN) |
| { |
| dsym_fspec.GetFilename().SetCString(dp->d_name); |
| ModuleSpecList module_specs; |
| if (ObjectFile::GetModuleSpecifications(dsym_fspec, 0, 0, module_specs)) |
| { |
| ModuleSpec spec; |
| for (size_t i = 0; i < module_specs.GetSize(); ++i) |
| { |
| assert(module_specs.GetModuleSpecAtIndex(i, spec)); |
| if ((uuid == NULL || (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) && |
| (arch == NULL || (spec.GetArchitecturePtr() && spec.GetArchitecture().IsCompatibleMatch(*arch)))) |
| { |
| return dsym_fspec; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| dsym_fspec.Clear(); |
| return dsym_fspec; |
| } |
| |
| static bool |
| GetModuleSpecInfoFromUUIDDictionary (CFDictionaryRef uuid_dict, ModuleSpec &module_spec) |
| { |
| Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); |
| bool success = false; |
| if (uuid_dict != NULL && CFGetTypeID (uuid_dict) == CFDictionaryGetTypeID ()) |
| { |
| std::string str; |
| CFStringRef cf_str; |
| |
| cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGSymbolRichExecutable")); |
| if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ()) |
| { |
| if (CFCString::FileSystemRepresentation(cf_str, str)) |
| { |
| module_spec.GetFileSpec().SetFile (str.c_str(), true); |
| if (log) |
| { |
| log->Printf ("From dsymForUUID plist: Symbol rich executable is at '%s'", str.c_str()); |
| } |
| } |
| } |
| |
| cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGDSYMPath")); |
| if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ()) |
| { |
| if (CFCString::FileSystemRepresentation(cf_str, str)) |
| { |
| module_spec.GetSymbolFileSpec().SetFile (str.c_str(), true); |
| success = true; |
| if (log) |
| { |
| log->Printf ("From dsymForUUID plist: dSYM is at '%s'", str.c_str()); |
| } |
| } |
| } |
| |
| cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGArchitecture")); |
| if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ()) |
| { |
| if (CFCString::FileSystemRepresentation(cf_str, str)) |
| module_spec.GetArchitecture().SetTriple(str.c_str()); |
| } |
| |
| std::string DBGBuildSourcePath; |
| std::string DBGSourcePath; |
| |
| cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGBuildSourcePath")); |
| if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ()) |
| { |
| CFCString::FileSystemRepresentation(cf_str, DBGBuildSourcePath); |
| } |
| |
| cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGSourcePath")); |
| if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ()) |
| { |
| CFCString::FileSystemRepresentation(cf_str, DBGSourcePath); |
| } |
| |
| if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) |
| { |
| module_spec.GetSourceMappingList().Append (ConstString(DBGBuildSourcePath.c_str()), ConstString(DBGSourcePath.c_str()), true); |
| } |
| } |
| return success; |
| } |
| |
| |
| bool |
| Symbols::DownloadObjectAndSymbolFile (ModuleSpec &module_spec, bool force_lookup) |
| { |
| bool success = false; |
| const UUID *uuid_ptr = module_spec.GetUUIDPtr(); |
| const FileSpec *file_spec_ptr = module_spec.GetFileSpecPtr(); |
| |
| // It's expensive to check for the DBGShellCommands defaults setting, only do it once per |
| // lldb run and cache the result. |
| static bool g_have_checked_for_dbgshell_command = false; |
| static const char *g_dbgshell_command = NULL; |
| if (g_have_checked_for_dbgshell_command == false) |
| { |
| g_have_checked_for_dbgshell_command = true; |
| CFTypeRef defaults_setting = CFPreferencesCopyAppValue (CFSTR ("DBGShellCommands"), CFSTR ("com.apple.DebugSymbols")); |
| if (defaults_setting && CFGetTypeID (defaults_setting) == CFStringGetTypeID()) |
| { |
| char cstr_buf[PATH_MAX]; |
| if (CFStringGetCString ((CFStringRef) defaults_setting, cstr_buf, sizeof (cstr_buf), kCFStringEncodingUTF8)) |
| { |
| g_dbgshell_command = strdup (cstr_buf); // this malloc'ed memory will never be freed |
| } |
| } |
| if (defaults_setting) |
| { |
| CFRelease (defaults_setting); |
| } |
| } |
| |
| // When g_dbgshell_command is NULL, the user has not enabled the use of an external program |
| // to find the symbols, don't run it for them. |
| if (force_lookup == false && g_dbgshell_command == NULL) |
| { |
| return false; |
| } |
| |
| if (uuid_ptr || (file_spec_ptr && file_spec_ptr->Exists())) |
| { |
| static bool g_located_dsym_for_uuid_exe = false; |
| static bool g_dsym_for_uuid_exe_exists = false; |
| static char g_dsym_for_uuid_exe_path[PATH_MAX]; |
| if (!g_located_dsym_for_uuid_exe) |
| { |
| g_located_dsym_for_uuid_exe = true; |
| const char *dsym_for_uuid_exe_path_cstr = getenv("LLDB_APPLE_DSYMFORUUID_EXECUTABLE"); |
| FileSpec dsym_for_uuid_exe_spec; |
| if (dsym_for_uuid_exe_path_cstr) |
| { |
| dsym_for_uuid_exe_spec.SetFile(dsym_for_uuid_exe_path_cstr, true); |
| g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists(); |
| } |
| |
| if (!g_dsym_for_uuid_exe_exists) |
| { |
| dsym_for_uuid_exe_spec.SetFile("/usr/local/bin/dsymForUUID", false); |
| g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists(); |
| if (!g_dsym_for_uuid_exe_exists) |
| { |
| long bufsize; |
| if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) != -1) |
| { |
| char buffer[bufsize]; |
| struct passwd pwd; |
| struct passwd *tilde_rc = NULL; |
| // we are a library so we need to use the reentrant version of getpwnam() |
| if (getpwnam_r ("rc", &pwd, buffer, bufsize, &tilde_rc) == 0 |
| && tilde_rc |
| && tilde_rc->pw_dir) |
| { |
| std::string dsymforuuid_path(tilde_rc->pw_dir); |
| dsymforuuid_path += "/bin/dsymForUUID"; |
| dsym_for_uuid_exe_spec.SetFile(dsymforuuid_path.c_str(), false); |
| g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists(); |
| } |
| } |
| } |
| } |
| if (!g_dsym_for_uuid_exe_exists && g_dbgshell_command != NULL) |
| { |
| dsym_for_uuid_exe_spec.SetFile(g_dbgshell_command, true); |
| g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists(); |
| } |
| |
| if (g_dsym_for_uuid_exe_exists) |
| dsym_for_uuid_exe_spec.GetPath (g_dsym_for_uuid_exe_path, sizeof(g_dsym_for_uuid_exe_path)); |
| } |
| if (g_dsym_for_uuid_exe_exists) |
| { |
| std::string uuid_str; |
| char file_path[PATH_MAX]; |
| file_path[0] = '\0'; |
| |
| if (uuid_ptr) |
| uuid_str = uuid_ptr->GetAsString(); |
| |
| if (file_spec_ptr) |
| file_spec_ptr->GetPath(file_path, sizeof(file_path)); |
| |
| StreamString command; |
| if (!uuid_str.empty()) |
| command.Printf("%s --ignoreNegativeCache --copyExecutable %s", g_dsym_for_uuid_exe_path, uuid_str.c_str()); |
| else if (file_path[0] != '\0') |
| command.Printf("%s --ignoreNegativeCache --copyExecutable %s", g_dsym_for_uuid_exe_path, file_path); |
| |
| if (!command.GetString().empty()) |
| { |
| Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); |
| int exit_status = -1; |
| int signo = -1; |
| std::string command_output; |
| if (log) |
| { |
| if (!uuid_str.empty()) |
| log->Printf("Calling %s with UUID %s to find dSYM", g_dsym_for_uuid_exe_path, uuid_str.c_str()); |
| else if (file_path[0] != '\0') |
| log->Printf("Calling %s with file %s to find dSYM", g_dsym_for_uuid_exe_path, file_path); |
| } |
| Error error = Host::RunShellCommand (command.GetData(), |
| NULL, // current working directory |
| &exit_status, // Exit status |
| &signo, // Signal int * |
| &command_output, // Command output |
| 30, // Large timeout to allow for long dsym download times |
| false); // Don't run in a shell (we don't need shell expansion) |
| if (error.Success() && exit_status == 0 && !command_output.empty()) |
| { |
| CFCData data (CFDataCreateWithBytesNoCopy (NULL, |
| (const UInt8 *)command_output.data(), |
| command_output.size(), |
| kCFAllocatorNull)); |
| |
| CFCReleaser<CFDictionaryRef> plist((CFDictionaryRef)::CFPropertyListCreateFromXMLData (NULL, data.get(), kCFPropertyListImmutable, NULL)); |
| |
| if (plist.get() && CFGetTypeID (plist.get()) == CFDictionaryGetTypeID ()) |
| { |
| if (!uuid_str.empty()) |
| { |
| CFCString uuid_cfstr(uuid_str.c_str()); |
| CFDictionaryRef uuid_dict = (CFDictionaryRef)CFDictionaryGetValue (plist.get(), uuid_cfstr.get()); |
| success = GetModuleSpecInfoFromUUIDDictionary (uuid_dict, module_spec); |
| } |
| else |
| { |
| const CFIndex num_values = ::CFDictionaryGetCount(plist.get()); |
| if (num_values > 0) |
| { |
| std::vector<CFStringRef> keys (num_values, NULL); |
| std::vector<CFDictionaryRef> values (num_values, NULL); |
| ::CFDictionaryGetKeysAndValues(plist.get(), NULL, (const void **)&values[0]); |
| if (num_values == 1) |
| { |
| return GetModuleSpecInfoFromUUIDDictionary (values[0], module_spec); |
| } |
| else |
| { |
| for (CFIndex i=0; i<num_values; ++i) |
| { |
| ModuleSpec curr_module_spec; |
| if (GetModuleSpecInfoFromUUIDDictionary (values[i], curr_module_spec)) |
| { |
| if (module_spec.GetArchitecture().IsCompatibleMatch(curr_module_spec.GetArchitecture())) |
| { |
| module_spec = curr_module_spec; |
| return true; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| else |
| { |
| if (log) |
| { |
| if (!uuid_str.empty()) |
| log->Printf("Called %s on %s, no matches", g_dsym_for_uuid_exe_path, uuid_str.c_str()); |
| else if (file_path[0] != '\0') |
| log->Printf("Called %s on %s, no matches", g_dsym_for_uuid_exe_path, file_path); |
| } |
| } |
| } |
| } |
| } |
| return success; |
| } |
| |