Symbols.cpp revision 1674b12bbc3dae7b9543b8c5f958e90ddc767fa4
1//===-- Symbols.cpp ---------------------------------------------*- C++ -*-===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9 10#include "lldb/Host/Symbols.h" 11 12// C Includes 13#include <dirent.h> 14#include "llvm/Support/MachO.h" 15 16// C++ Includes 17// Other libraries and framework includes 18#include <CoreFoundation/CoreFoundation.h> 19 20// Project includes 21#include "lldb/Core/ArchSpec.h" 22#include "lldb/Core/DataBuffer.h" 23#include "lldb/Core/DataExtractor.h" 24#include "lldb/Core/Timer.h" 25#include "lldb/Core/UUID.h" 26 27#include "Host/macosx/cfcpp/CFCReleaser.h" 28 29using namespace lldb; 30using namespace lldb_private; 31using namespace llvm::MachO; 32 33extern "C" { 34 35CFURLRef DBGCopyFullDSYMURLForUUID (CFUUIDRef uuid, CFURLRef exec_url); 36CFDictionaryRef DBGCopyDSYMPropertyLists (CFURLRef dsym_url); 37 38} 39 40static bool 41SkinnyMachOFileContainsArchAndUUID 42( 43 const FileSpec &file_spec, 44 const ArchSpec *arch, 45 const UUID *uuid, // the UUID we are looking for 46 off_t file_offset, 47 DataExtractor& data, 48 uint32_t data_offset, 49 const uint32_t magic 50) 51{ 52 assert(magic == HeaderMagic32 || magic == HeaderMagic32Swapped || magic == HeaderMagic64 || magic == HeaderMagic64Swapped); 53 if (magic == HeaderMagic32 || magic == HeaderMagic64) 54 data.SetByteOrder (eByteOrderHost); 55 else if (eByteOrderHost == eByteOrderBig) 56 data.SetByteOrder (eByteOrderLittle); 57 else 58 data.SetByteOrder (eByteOrderBig); 59 60 uint32_t i; 61 const uint32_t cputype = data.GetU32(&data_offset); // cpu specifier 62 const uint32_t cpusubtype = data.GetU32(&data_offset); // machine specifier 63 data_offset+=4; // Skip mach file type 64 const uint32_t ncmds = data.GetU32(&data_offset); // number of load commands 65 const uint32_t sizeofcmds = data.GetU32(&data_offset); // the size of all the load commands 66 data_offset+=4; // Skip flags 67 68 // Check the architecture if we have a valid arch pointer 69 if (arch) 70 { 71 ArchSpec file_arch(eArchTypeMachO, cputype, cpusubtype); 72 73 if (file_arch != *arch) 74 return false; 75 } 76 77 // The file exists, and if a valid arch pointer was passed in we know 78 // if already matches, so we can return if we aren't looking for a specific 79 // UUID 80 if (uuid == NULL) 81 return true; 82 83 if (magic == HeaderMagic64Swapped || magic == HeaderMagic64) 84 data_offset += 4; // Skip reserved field for in mach_header_64 85 86 // Make sure we have enough data for all the load commands 87 if (magic == HeaderMagic64Swapped || magic == HeaderMagic64) 88 { 89 if (data.GetByteSize() < sizeof(struct mach_header_64) + sizeofcmds) 90 { 91 DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, sizeof(struct mach_header_64) + sizeofcmds)); 92 data.SetData (data_buffer_sp); 93 } 94 } 95 else 96 { 97 if (data.GetByteSize() < sizeof(struct mach_header) + sizeofcmds) 98 { 99 DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, sizeof(struct mach_header) + sizeofcmds)); 100 data.SetData (data_buffer_sp); 101 } 102 } 103 104 for (i=0; i<ncmds; i++) 105 { 106 const uint32_t cmd_offset = data_offset; // Save this data_offset in case parsing of the segment goes awry! 107 uint32_t cmd = data.GetU32(&data_offset); 108 uint32_t cmd_size = data.GetU32(&data_offset); 109 if (cmd == LoadCommandUUID) 110 { 111 UUID file_uuid (data.GetData(&data_offset, 16), 16); 112 return file_uuid == *uuid; 113 } 114 data_offset = cmd_offset + cmd_size; 115 } 116 return false; 117} 118 119bool 120UniversalMachOFileContainsArchAndUUID 121( 122 const FileSpec &file_spec, 123 const ArchSpec *arch, 124 const UUID *uuid, 125 off_t file_offset, 126 DataExtractor& data, 127 uint32_t data_offset, 128 const uint32_t magic 129) 130{ 131 assert(magic == UniversalMagic || magic == UniversalMagicSwapped); 132 133 // Universal mach-o files always have their headers encoded as BIG endian 134 data.SetByteOrder(eByteOrderBig); 135 136 uint32_t i; 137 const uint32_t nfat_arch = data.GetU32(&data_offset); // number of structs that follow 138 const uint32_t fat_header_and_arch_size = sizeof(struct fat_header) + nfat_arch * sizeof(struct fat_arch); 139 if (data.GetByteSize() < fat_header_and_arch_size) 140 { 141 DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, fat_header_and_arch_size)); 142 data.SetData (data_buffer_sp); 143 } 144 145 for (i=0; i<nfat_arch; i++) 146 { 147 cpu_type_t arch_cputype = data.GetU32(&data_offset); // cpu specifier (int) 148 cpu_subtype_t arch_cpusubtype = data.GetU32(&data_offset); // machine specifier (int) 149 uint32_t arch_offset = data.GetU32(&data_offset); // file offset to this object file 150 // uint32_t arch_size = data.GetU32(&data_offset); // size of this object file 151 // uint32_t arch_align = data.GetU32(&data_offset); // alignment as a power of 2 152 data_offset += 8; // Skip size and align as we don't need those 153 // Only process this slice if the cpu type/subtype matches 154 if (arch) 155 { 156 ArchSpec fat_arch(eArchTypeMachO, arch_cputype, arch_cpusubtype); 157 if (fat_arch != *arch) 158 continue; 159 } 160 161 // Create a buffer with only the arch slice date in it 162 DataExtractor arch_data; 163 DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset + arch_offset, 0x1000)); 164 arch_data.SetData(data_buffer_sp); 165 uint32_t arch_data_offset = 0; 166 uint32_t arch_magic = arch_data.GetU32(&arch_data_offset); 167 168 switch (arch_magic) 169 { 170 case HeaderMagic32: 171 case HeaderMagic32Swapped: 172 case HeaderMagic64: 173 case HeaderMagic64Swapped: 174 if (SkinnyMachOFileContainsArchAndUUID (file_spec, arch, uuid, file_offset + arch_offset, arch_data, arch_data_offset, arch_magic)) 175 return true; 176 break; 177 } 178 } 179 return false; 180} 181 182static bool 183FileAtPathContainsArchAndUUID 184( 185 const FileSpec &file_spec, 186 const ArchSpec *arch, 187 const UUID *uuid 188) 189{ 190 DataExtractor data; 191 off_t file_offset = 0; 192 DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, 0x1000)); 193 194 if (data_buffer_sp && data_buffer_sp->GetByteSize() > 0) 195 { 196 data.SetData(data_buffer_sp); 197 198 uint32_t data_offset = 0; 199 uint32_t magic = data.GetU32(&data_offset); 200 201 switch (magic) 202 { 203 // 32 bit mach-o file 204 case HeaderMagic32: 205 case HeaderMagic32Swapped: 206 case HeaderMagic64: 207 case HeaderMagic64Swapped: 208 return SkinnyMachOFileContainsArchAndUUID (file_spec, arch, uuid, file_offset, data, data_offset, magic); 209 210 // fat mach-o file 211 case UniversalMagic: 212 case UniversalMagicSwapped: 213 return UniversalMachOFileContainsArchAndUUID (file_spec, arch, uuid, file_offset, data, data_offset, magic); 214 215 default: 216 break; 217 } 218 } 219 return false; 220} 221 222static FileSpec 223LocateDSYMMachFileInDSYMBundle 224( 225 const FileSpec& dsym_bundle_fspec, 226 const UUID *uuid, 227 const ArchSpec *arch) 228{ 229 char path[PATH_MAX]; 230 231 FileSpec dsym_fspec; 232 233 if (dsym_bundle_fspec.GetPath(path, sizeof(path))) 234 { 235 ::strncat (path, "/Contents/Resources/DWARF", sizeof(path) - strlen(path) - 1); 236 237 DIR* dirp = ::opendir(path); 238 if (dirp != NULL) 239 { 240 const size_t path_len = strlen(path); 241 const int bytes_left = sizeof(path) - path_len - 1; 242 struct dirent* dp; 243 while ((dp = readdir(dirp)) != NULL) 244 { 245 // Only search directories 246 if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN) 247 { 248 if (dp->d_namlen == 1 && dp->d_name[0] == '.') 249 continue; 250 251 if (dp->d_namlen == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.') 252 continue; 253 } 254 255 if (dp->d_type == DT_REG || dp->d_type == DT_UNKNOWN) 256 { 257 ::strncpy (&path[path_len], dp->d_name, bytes_left); 258 259 dsym_fspec.SetFile(path); 260 if (FileAtPathContainsArchAndUUID (dsym_fspec, arch, uuid)) 261 return dsym_fspec; 262 } 263 } 264 } 265 } 266 dsym_fspec.Clear(); 267 return dsym_fspec; 268} 269 270static int 271LocateMacOSXFilesUsingDebugSymbols 272( 273 const FileSpec *exec_fspec, // An executable path that may or may not be correct if UUID is specified 274 const ArchSpec* arch, // Limit the search to files with this architecture if non-NULL 275 const UUID *uuid, // Match the UUID value if non-NULL, 276 FileSpec *out_exec_fspec, // If non-NULL, try and find the executable 277 FileSpec *out_dsym_fspec // If non-NULL try and find the debug symbol file 278) 279{ 280 int items_found = 0; 281 282 if (out_exec_fspec) 283 out_exec_fspec->Clear(); 284 285 if (out_dsym_fspec) 286 out_dsym_fspec->Clear(); 287 288 if (uuid && uuid->IsValid()) 289 { 290 // Try and locate the dSYM file using DebugSymbols first 291 const UInt8 *module_uuid = (const UInt8 *)uuid->GetBytes(); 292 if (module_uuid != NULL) 293 { 294 CFCReleaser<CFUUIDRef> module_uuid_ref(::CFUUIDCreateWithBytes ( NULL, 295 module_uuid[0], 296 module_uuid[1], 297 module_uuid[2], 298 module_uuid[3], 299 module_uuid[4], 300 module_uuid[5], 301 module_uuid[6], 302 module_uuid[7], 303 module_uuid[8], 304 module_uuid[9], 305 module_uuid[10], 306 module_uuid[11], 307 module_uuid[12], 308 module_uuid[13], 309 module_uuid[14], 310 module_uuid[15])); 311 312 if (module_uuid_ref.get()) 313 { 314 CFCReleaser<CFURLRef> exec_url; 315 316 if (exec_fspec) 317 { 318 char exec_cf_path[PATH_MAX]; 319 if (exec_fspec->GetPath(exec_cf_path, sizeof(exec_cf_path))) 320 exec_url.reset(::CFURLCreateFromFileSystemRepresentation (NULL, 321 (const UInt8 *)exec_cf_path, 322 strlen(exec_cf_path), 323 FALSE)); 324 } 325 326 CFCReleaser<CFURLRef> dsym_url (::DBGCopyFullDSYMURLForUUID(module_uuid_ref.get(), exec_url.get())); 327 char path[PATH_MAX]; 328 329 if (dsym_url.get()) 330 { 331 if (out_dsym_fspec) 332 { 333 if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1)) 334 { 335 out_dsym_fspec->SetFile(path); 336 337 if (out_dsym_fspec->GetFileType () == FileSpec::eFileTypeDirectory) 338 { 339 *out_dsym_fspec = LocateDSYMMachFileInDSYMBundle (*out_dsym_fspec, uuid, arch); 340 if (*out_dsym_fspec) 341 ++items_found; 342 } 343 else 344 { 345 ++items_found; 346 } 347 } 348 } 349 350 if (out_exec_fspec) 351 { 352 CFCReleaser<CFDictionaryRef> dict(::DBGCopyDSYMPropertyLists (dsym_url.get()));; 353 if (dict.get()) 354 { 355 CFStringRef exec_cf_path = static_cast<CFStringRef>(::CFDictionaryGetValue (dict.get(), CFSTR("DBGSymbolRichExecutable"))); 356 if (exec_cf_path && ::CFStringGetFileSystemRepresentation (exec_cf_path, path, sizeof(path))) 357 { 358 ++items_found; 359 out_dsym_fspec->SetFile(path); 360 } 361 } 362 } 363 } 364 } 365 } 366 } 367 return items_found; 368} 369 370static bool 371LocateDSYMInVincinityOfExecutable (const FileSpec *exec_fspec, const ArchSpec* arch, const UUID *uuid, FileSpec &dsym_fspec) 372{ 373 if (exec_fspec) 374 { 375 char path[PATH_MAX]; 376 if (exec_fspec->GetPath(path, sizeof(path))) 377 { 378 // Make sure the module isn't already just a dSYM file... 379 if (strcasestr(path, ".dSYM/Contents/Resources/DWARF") == NULL) 380 { 381 size_t obj_file_path_length = strlen(path); 382 strncat(path, ".dSYM/Contents/Resources/DWARF/", sizeof(path)); 383 strncat(path, exec_fspec->GetFilename().AsCString(), sizeof(path)); 384 385 dsym_fspec.SetFile(path); 386 387 if (FileAtPathContainsArchAndUUID (dsym_fspec, arch, uuid)) 388 { 389 return true; 390 } 391 else 392 { 393 path[obj_file_path_length] = '\0'; 394 395 char *last_dot = strrchr(path, '.'); 396 while (last_dot != NULL && last_dot[0]) 397 { 398 char *next_slash = strchr(last_dot, '/'); 399 if (next_slash != NULL) 400 { 401 *next_slash = '\0'; 402 strncat(path, ".dSYM/Contents/Resources/DWARF/", sizeof(path)); 403 strncat(path, exec_fspec->GetFilename().AsCString(), sizeof(path)); 404 dsym_fspec.SetFile(path); 405 if (dsym_fspec.Exists()) 406 return true; 407 else 408 { 409 *last_dot = '\0'; 410 char *prev_slash = strrchr(path, '/'); 411 if (prev_slash != NULL) 412 *prev_slash = '\0'; 413 else 414 break; 415 } 416 } 417 else 418 { 419 break; 420 } 421 } 422 } 423 } 424 } 425 } 426 dsym_fspec.Clear(); 427 return false; 428} 429 430FileSpec 431Symbols::LocateExecutableObjectFile (const FileSpec *exec_fspec, const ArchSpec* arch, const UUID *uuid) 432{ 433 Timer scoped_timer (__PRETTY_FUNCTION__, 434 "LocateExecutableObjectFile (file = %s, arch = %s, uuid = %p)", 435 exec_fspec ? exec_fspec->GetFilename().AsCString ("<NULL>") : "<NULL>", 436 arch ? arch->AsCString() : "<NULL>", 437 uuid); 438 439 FileSpec objfile_fspec; 440 if (exec_fspec && FileAtPathContainsArchAndUUID (*exec_fspec, arch, uuid)) 441 objfile_fspec = *exec_fspec; 442 else 443 LocateMacOSXFilesUsingDebugSymbols (exec_fspec, arch, uuid, &objfile_fspec, NULL); 444 return objfile_fspec; 445} 446 447FileSpec 448Symbols::LocateExecutableSymbolFile (const FileSpec *exec_fspec, const ArchSpec* arch, const UUID *uuid) 449{ 450 Timer scoped_timer (__PRETTY_FUNCTION__, 451 "LocateExecutableSymbolFile (file = %s, arch = %s, uuid = %p)", 452 exec_fspec ? exec_fspec->GetFilename().AsCString ("<NULL>") : "<NULL>", 453 arch ? arch->AsCString() : "<NULL>", 454 uuid); 455 456 FileSpec symbol_fspec; 457 // First try and find the dSYM in the same directory as the executable or in 458 // an appropriate parent directory 459 if (LocateDSYMInVincinityOfExecutable (exec_fspec, arch, uuid, symbol_fspec) == false) 460 { 461 // We failed to easily find the dSYM above, so use DebugSymbols 462 LocateMacOSXFilesUsingDebugSymbols (exec_fspec, arch, uuid, NULL, &symbol_fspec); 463 } 464 return symbol_fspec; 465} 466