diff options
Diffstat (limited to 'source/Host/macosx/Symbols.cpp')
-rw-r--r-- | source/Host/macosx/Symbols.cpp | 559 |
1 files changed, 559 insertions, 0 deletions
diff --git a/source/Host/macosx/Symbols.cpp b/source/Host/macosx/Symbols.cpp new file mode 100644 index 000000000000..f6a18febe6da --- /dev/null +++ b/source/Host/macosx/Symbols.cpp @@ -0,0 +1,559 @@ +//===-- 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; +} + |