//===-- 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 #include #include "llvm/Support/MachO.h" // C++ Includes // Other libraries and framework includes #include // Project includes #include "lldb/Core/ArchSpec.h" #include "lldb/Core/DataBuffer.h" #include "lldb/Core/DataExtractor.h" #include "lldb/Core/Timer.h" #include "lldb/Core/UUID.h" #include "Host/macosx/cfcpp/CFCReleaser.h" #include "mach/machine.h" using namespace lldb; using namespace lldb_private; using namespace llvm::MachO; extern "C" { CFURLRef DBGCopyFullDSYMURLForUUID (CFUUIDRef uuid, CFURLRef exec_url); CFDictionaryRef DBGCopyDSYMPropertyLists (CFURLRef dsym_url); } static bool SkinnyMachOFileContainsArchAndUUID ( const FileSpec &file_spec, const ArchSpec *arch, const UUID *uuid, // the UUID we are looking for off_t file_offset, DataExtractor& data, uint32_t data_offset, const uint32_t magic ) { assert(magic == HeaderMagic32 || magic == HeaderMagic32Swapped || magic == HeaderMagic64 || magic == HeaderMagic64Swapped); if (magic == HeaderMagic32 || magic == HeaderMagic64) data.SetByteOrder (eByteOrderHost); else if (eByteOrderHost == eByteOrderBig) data.SetByteOrder (eByteOrderLittle); else data.SetByteOrder (eByteOrderBig); uint32_t i; const uint32_t cputype = data.GetU32(&data_offset); // cpu specifier const uint32_t cpusubtype = data.GetU32(&data_offset); // machine specifier data_offset+=4; // Skip mach file type const uint32_t ncmds = data.GetU32(&data_offset); // number of load commands const uint32_t sizeofcmds = data.GetU32(&data_offset); // the size of all the load commands data_offset+=4; // Skip flags // Check the architecture if we have a valid arch pointer if (arch) { ArchSpec file_arch(eArchTypeMachO, cputype, cpusubtype); if (file_arch != *arch) return false; } // The file exists, and if a valid arch pointer was passed in we know // if already matches, so we can return if we aren't looking for a specific // UUID if (uuid == NULL) return true; if (magic == HeaderMagic64Swapped || magic == HeaderMagic64) data_offset += 4; // Skip reserved field for in mach_header_64 // Make sure we have enough data for all the load commands if (magic == HeaderMagic64Swapped || magic == HeaderMagic64) { if (data.GetByteSize() < sizeof(struct mach_header_64) + sizeofcmds) { DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, sizeof(struct mach_header_64) + sizeofcmds)); data.SetData (data_buffer_sp); } } else { if (data.GetByteSize() < sizeof(struct mach_header) + sizeofcmds) { DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, sizeof(struct mach_header) + sizeofcmds)); data.SetData (data_buffer_sp); } } for (i=0; iGetByteSize() > 0) { data.SetData(data_buffer_sp); uint32_t data_offset = 0; uint32_t magic = data.GetU32(&data_offset); switch (magic) { // 32 bit mach-o file case HeaderMagic32: case HeaderMagic32Swapped: case HeaderMagic64: case HeaderMagic64Swapped: return SkinnyMachOFileContainsArchAndUUID (file_spec, arch, uuid, file_offset, data, data_offset, magic); // fat mach-o file case UniversalMagic: case UniversalMagicSwapped: return UniversalMachOFileContainsArchAndUUID (file_spec, arch, uuid, file_offset, data, data_offset, magic); default: break; } } return false; } static FileSpec LocateDSYMMachFileInDSYMBundle ( const FileSpec& dsym_bundle_fspec, const 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); DIR* dirp = ::opendir(path); if (dirp != NULL) { dsym_fspec.GetDirectory().SetCString(path); struct dirent* dp; while ((dp = readdir(dirp)) != 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); if (FileAtPathContainsArchAndUUID (dsym_fspec, arch, uuid)) return dsym_fspec; } } } } dsym_fspec.Clear(); return dsym_fspec; } static int LocateMacOSXFilesUsingDebugSymbols ( const FileSpec *exec_fspec, // An executable path that may or may not be correct if UUID is specified const ArchSpec* arch, // Limit the search to files with this architecture if non-NULL const UUID *uuid, // Match the UUID value if non-NULL, FileSpec *out_exec_fspec, // If non-NULL, try and find the executable FileSpec *out_dsym_fspec // If non-NULL try and find the debug symbol file ) { int items_found = 0; if (out_exec_fspec) out_exec_fspec->Clear(); if (out_dsym_fspec) out_dsym_fspec->Clear(); 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 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 exec_url; 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 dsym_url (::DBGCopyFullDSYMURLForUUID(module_uuid_ref.get(), exec_url.get())); char path[PATH_MAX]; if (dsym_url.get()) { if (out_dsym_fspec) { if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1)) { out_dsym_fspec->SetFile(path, false); if (out_dsym_fspec->GetFileType () == FileSpec::eFileTypeDirectory) { *out_dsym_fspec = LocateDSYMMachFileInDSYMBundle (*out_dsym_fspec, uuid, arch); if (*out_dsym_fspec) ++items_found; } else { ++items_found; } } } if (out_exec_fspec) { CFCReleaser dict(::DBGCopyDSYMPropertyLists (dsym_url.get()));; if (dict.get()) { CFStringRef exec_cf_path = static_cast(::CFDictionaryGetValue (dict.get(), CFSTR("DBGSymbolRichExecutable"))); if (exec_cf_path && ::CFStringGetFileSystemRepresentation (exec_cf_path, path, sizeof(path))) { ++items_found; out_dsym_fspec->SetFile(path, false); } } } } } } } return items_found; } static bool LocateDSYMInVincinityOfExecutable (const FileSpec *exec_fspec, const ArchSpec* arch, const UUID *uuid, FileSpec &dsym_fspec) { if (exec_fspec) { char path[PATH_MAX]; if (exec_fspec->GetPath(path, sizeof(path))) { // Make sure the module isn't already just a dSYM file... if (strcasestr(path, ".dSYM/Contents/Resources/DWARF") == NULL) { size_t obj_file_path_length = strlen(path); strncat(path, ".dSYM/Contents/Resources/DWARF/", sizeof(path)); strncat(path, exec_fspec->GetFilename().AsCString(), sizeof(path)); dsym_fspec.SetFile(path, false); if (FileAtPathContainsArchAndUUID (dsym_fspec, arch, uuid)) { return true; } else { path[obj_file_path_length] = '\0'; char *last_dot = strrchr(path, '.'); while (last_dot != NULL && last_dot[0]) { char *next_slash = strchr(last_dot, '/'); if (next_slash != NULL) { *next_slash = '\0'; strncat(path, ".dSYM/Contents/Resources/DWARF/", sizeof(path)); strncat(path, exec_fspec->GetFilename().AsCString(), sizeof(path)); dsym_fspec.SetFile(path, false); if (dsym_fspec.Exists()) return true; else { *last_dot = '\0'; char *prev_slash = strrchr(path, '/'); if (prev_slash != NULL) *prev_slash = '\0'; else break; } } else { break; } } } } } } dsym_fspec.Clear(); return false; } FileSpec Symbols::LocateExecutableObjectFile (const FileSpec *exec_fspec, const ArchSpec* arch, const UUID *uuid) { Timer scoped_timer (__PRETTY_FUNCTION__, "LocateExecutableObjectFile (file = %s, arch = %s, uuid = %p)", exec_fspec ? exec_fspec->GetFilename().AsCString ("") : "", arch ? arch->AsCString() : "", uuid); FileSpec objfile_fspec; if (exec_fspec && FileAtPathContainsArchAndUUID (*exec_fspec, arch, uuid)) objfile_fspec = *exec_fspec; else LocateMacOSXFilesUsingDebugSymbols (exec_fspec, arch, uuid, &objfile_fspec, NULL); return objfile_fspec; } FileSpec Symbols::LocateExecutableSymbolFile (const FileSpec *exec_fspec, const ArchSpec* arch, const UUID *uuid) { Timer scoped_timer (__PRETTY_FUNCTION__, "LocateExecutableSymbolFile (file = %s, arch = %s, uuid = %p)", exec_fspec ? exec_fspec->GetFilename().AsCString ("") : "", arch ? arch->AsCString() : "", uuid); FileSpec symbol_fspec; // First try and find the dSYM in the same directory as the executable or in // an appropriate parent directory if (LocateDSYMInVincinityOfExecutable (exec_fspec, arch, uuid, symbol_fspec) == false) { // We failed to easily find the dSYM above, so use DebugSymbols LocateMacOSXFilesUsingDebugSymbols (exec_fspec, arch, uuid, NULL, &symbol_fspec); } return symbol_fspec; }