You've already forked linux-packaging-mono
							
							
		
			
				
	
	
		
			624 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			624 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| //===-- 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 "lldb/Utility/SafeMachO.h"
 | |
| #include <dirent.h>
 | |
| #include <pwd.h>
 | |
| 
 | |
| // C++ Includes
 | |
| // Other libraries and framework includes
 | |
| #include <CoreFoundation/CoreFoundation.h>
 | |
| 
 | |
| // Project includes
 | |
| #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 "lldb/Core/Module.h"
 | |
| #include "lldb/Core/ModuleSpec.h"
 | |
| #include "lldb/Host/Host.h"
 | |
| #include "lldb/Symbol/ObjectFile.h"
 | |
| #include "lldb/Utility/ArchSpec.h"
 | |
| #include "lldb/Utility/CleanUp.h"
 | |
| #include "lldb/Utility/DataBuffer.h"
 | |
| #include "lldb/Utility/DataExtractor.h"
 | |
| #include "lldb/Utility/Endian.h"
 | |
| #include "lldb/Utility/Log.h"
 | |
| #include "lldb/Utility/StreamString.h"
 | |
| #include "lldb/Utility/Timer.h"
 | |
| #include "lldb/Utility/UUID.h"
 | |
| #include "mach/machine.h"
 | |
| 
 | |
| #include "llvm/Support/FileSystem.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 (llvm::sys::fs::is_directory(dsym_filespec.GetPath())) {
 | |
|               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;
 | |
|                 using namespace llvm::sys::fs;
 | |
|                 switch (get_file_type(file_spec.GetPath())) {
 | |
| 
 | |
|                 case file_type::directory_file: // 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 file_type::fifo_file:      // Forget pipes
 | |
|                 case file_type::socket_file:    // We can't process socket files
 | |
|                 case file_type::file_not_found: // File doesn't exist...
 | |
|                 case file_type::status_error:
 | |
|                   break;
 | |
| 
 | |
|                 case file_type::type_unknown:
 | |
|                 case file_type::regular_file:
 | |
|                 case file_type::symlink_file:
 | |
|                 case file_type::block_file:
 | |
|                 case file_type::character_file:
 | |
|                   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) {
 | |
|               bool got_spec = module_specs.GetModuleSpecAtIndex(i, spec);
 | |
|               UNUSED_IF_ASSERT_DISABLED(got_spec);
 | |
|               assert(got_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;
 | |
|     CFDictionaryRef cf_dict;
 | |
| 
 | |
|     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()) {
 | |
|       if (DBGSourcePath[0] == '~') {
 | |
|         FileSpec resolved_source_path(DBGSourcePath.c_str(), true);
 | |
|         DBGSourcePath = resolved_source_path.GetPath();
 | |
|       }
 | |
|       module_spec.GetSourceMappingList().Append(
 | |
|           ConstString(DBGBuildSourcePath.c_str()),
 | |
|           ConstString(DBGSourcePath.c_str()), true);
 | |
|     }
 | |
| 
 | |
|     cf_dict = (CFDictionaryRef)CFDictionaryGetValue(
 | |
|         (CFDictionaryRef)uuid_dict, CFSTR("DBGSourcePathRemapping"));
 | |
|     if (cf_dict && CFGetTypeID(cf_dict) == CFDictionaryGetTypeID()) {
 | |
|       // If we see DBGVersion with a value of 2 or higher, this is a new style
 | |
|       // DBGSourcePathRemapping dictionary
 | |
|       bool new_style_source_remapping_dictionary = false;
 | |
|       bool do_truncate_remapping_names = false;
 | |
|       std::string original_DBGSourcePath_value = DBGSourcePath;
 | |
|       cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
 | |
|                                                  CFSTR("DBGVersion"));
 | |
|       if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
 | |
|         std::string version;
 | |
|         CFCString::FileSystemRepresentation(cf_str, version);
 | |
|         if (!version.empty() && isdigit(version[0])) {
 | |
|           int version_number = atoi(version.c_str());
 | |
|           if (version_number > 1) {
 | |
|             new_style_source_remapping_dictionary = true;
 | |
|           }
 | |
|           if (version_number == 2) {
 | |
|             do_truncate_remapping_names = true;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       CFIndex kv_pair_count = CFDictionaryGetCount((CFDictionaryRef)uuid_dict);
 | |
|       if (kv_pair_count > 0) {
 | |
|         CFStringRef *keys =
 | |
|             (CFStringRef *)malloc(kv_pair_count * sizeof(CFStringRef));
 | |
|         CFStringRef *values =
 | |
|             (CFStringRef *)malloc(kv_pair_count * sizeof(CFStringRef));
 | |
|         if (keys != nullptr && values != nullptr) {
 | |
|           CFDictionaryGetKeysAndValues((CFDictionaryRef)uuid_dict,
 | |
|                                        (const void **)keys,
 | |
|                                        (const void **)values);
 | |
|         }
 | |
|         for (CFIndex i = 0; i < kv_pair_count; i++) {
 | |
|           DBGBuildSourcePath.clear();
 | |
|           DBGSourcePath.clear();
 | |
|           if (keys[i] && CFGetTypeID(keys[i]) == CFStringGetTypeID()) {
 | |
|             CFCString::FileSystemRepresentation(keys[i], DBGBuildSourcePath);
 | |
|           }
 | |
|           if (values[i] && CFGetTypeID(values[i]) == CFStringGetTypeID()) {
 | |
|             CFCString::FileSystemRepresentation(values[i], DBGSourcePath);
 | |
|           }
 | |
|           if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) {
 | |
|             // In the "old style" DBGSourcePathRemapping dictionary, the
 | |
|             // DBGSourcePath values
 | |
|             // (the "values" half of key-value path pairs) were wrong.  Ignore
 | |
|             // them and use the
 | |
|             // universal DBGSourcePath string from earlier.
 | |
|             if (new_style_source_remapping_dictionary == true &&
 | |
|                 !original_DBGSourcePath_value.empty()) {
 | |
|               DBGSourcePath = original_DBGSourcePath_value;
 | |
|             }
 | |
|             if (DBGSourcePath[0] == '~') {
 | |
|               FileSpec resolved_source_path(DBGSourcePath.c_str(), true);
 | |
|               DBGSourcePath = resolved_source_path.GetPath();
 | |
|             }
 | |
|             // With version 2 of DBGSourcePathRemapping, we can chop off the
 | |
|             // last two filename parts from the source remapping and get a
 | |
|             // more general source remapping that still works. Add this as
 | |
|             // another option in addition to the full source path remap.
 | |
|             module_spec.GetSourceMappingList().Append(
 | |
|                 ConstString(DBGBuildSourcePath.c_str()),
 | |
|                 ConstString(DBGSourcePath.c_str()), true);
 | |
|             if (do_truncate_remapping_names) {
 | |
|               FileSpec build_path(DBGBuildSourcePath.c_str(), false);
 | |
|               FileSpec source_path(DBGSourcePath.c_str(), false);
 | |
|               build_path.RemoveLastPathComponent();
 | |
|               build_path.RemoveLastPathComponent();
 | |
|               source_path.RemoveLastPathComponent();
 | |
|               source_path.RemoveLastPathComponent();
 | |
|               module_spec.GetSourceMappingList().Append(
 | |
|                 ConstString(build_path.GetPath().c_str()),
 | |
|                 ConstString(source_path.GetPath().c_str()), true);
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|         if (keys)
 | |
|           free(keys);
 | |
|         if (values)
 | |
|           free(values);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   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);
 | |
|         }
 | |
|         Status 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;
 | |
| }
 |