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 0000000000000..f6a18febe6da9 --- /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; +} + | 
