Symbols.cpp revision 964deba8853eb794e59263322b59b09b43669618
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/Module.h" 25#include "lldb/Core/Timer.h" 26#include "lldb/Core/UUID.h" 27#include "lldb/Host/Endian.h" 28#include "lldb/Host/Host.h" 29#include "lldb/Utility/CleanUp.h" 30#include "Host/macosx/cfcpp/CFCBundle.h" 31#include "Host/macosx/cfcpp/CFCReleaser.h" 32#include "Host/macosx/cfcpp/CFCString.h" 33#include "mach/machine.h" 34 35 36using namespace lldb; 37using namespace lldb_private; 38using namespace llvm::MachO; 39 40#if !defined (__arm__) // No DebugSymbols on the iOS devices 41extern "C" { 42 43CFURLRef DBGCopyFullDSYMURLForUUID (CFUUIDRef uuid, CFURLRef exec_url); 44CFDictionaryRef DBGCopyDSYMPropertyLists (CFURLRef dsym_url); 45 46} 47#endif 48 49static bool 50SkinnyMachOFileContainsArchAndUUID 51( 52 const FileSpec &file_spec, 53 const ArchSpec *arch, 54 const lldb_private::UUID *uuid, // the UUID we are looking for 55 off_t file_offset, 56 DataExtractor& data, 57 uint32_t data_offset, 58 const uint32_t magic 59) 60{ 61 assert(magic == HeaderMagic32 || magic == HeaderMagic32Swapped || magic == HeaderMagic64 || magic == HeaderMagic64Swapped); 62 if (magic == HeaderMagic32 || magic == HeaderMagic64) 63 data.SetByteOrder (lldb::endian::InlHostByteOrder()); 64 else if (lldb::endian::InlHostByteOrder() == eByteOrderBig) 65 data.SetByteOrder (eByteOrderLittle); 66 else 67 data.SetByteOrder (eByteOrderBig); 68 69 uint32_t i; 70 const uint32_t cputype = data.GetU32(&data_offset); // cpu specifier 71 const uint32_t cpusubtype = data.GetU32(&data_offset); // machine specifier 72 data_offset+=4; // Skip mach file type 73 const uint32_t ncmds = data.GetU32(&data_offset); // number of load commands 74 const uint32_t sizeofcmds = data.GetU32(&data_offset); // the size of all the load commands 75 data_offset+=4; // Skip flags 76 77 // Check the architecture if we have a valid arch pointer 78 if (arch) 79 { 80 ArchSpec file_arch(eArchTypeMachO, cputype, cpusubtype); 81 82 if (file_arch != *arch) 83 return false; 84 } 85 86 // The file exists, and if a valid arch pointer was passed in we know 87 // if already matches, so we can return if we aren't looking for a specific 88 // UUID 89 if (uuid == NULL) 90 return true; 91 92 if (magic == HeaderMagic64Swapped || magic == HeaderMagic64) 93 data_offset += 4; // Skip reserved field for in mach_header_64 94 95 // Make sure we have enough data for all the load commands 96 if (magic == HeaderMagic64Swapped || magic == HeaderMagic64) 97 { 98 if (data.GetByteSize() < sizeof(struct mach_header_64) + sizeofcmds) 99 { 100 DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, sizeof(struct mach_header_64) + sizeofcmds)); 101 data.SetData (data_buffer_sp); 102 } 103 } 104 else 105 { 106 if (data.GetByteSize() < sizeof(struct mach_header) + sizeofcmds) 107 { 108 DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, sizeof(struct mach_header) + sizeofcmds)); 109 data.SetData (data_buffer_sp); 110 } 111 } 112 113 for (i=0; i<ncmds; i++) 114 { 115 const uint32_t cmd_offset = data_offset; // Save this data_offset in case parsing of the segment goes awry! 116 uint32_t cmd = data.GetU32(&data_offset); 117 uint32_t cmd_size = data.GetU32(&data_offset); 118 if (cmd == LoadCommandUUID) 119 { 120 lldb_private::UUID file_uuid (data.GetData(&data_offset, 16), 16); 121 if (file_uuid == *uuid) 122 return true; 123 124 // Emit some warning messages since the UUIDs do not match! 125 char path_buf[PATH_MAX]; 126 path_buf[0] = '\0'; 127 const char *path = file_spec.GetPath(path_buf, PATH_MAX) ? path_buf 128 : file_spec.GetFilename().AsCString(); 129 Host::SystemLog (Host::eSystemLogWarning, 130 "warning: UUID mismatch detected between binary and:\n\t'%s'\n", 131 path); 132 return false; 133 } 134 data_offset = cmd_offset + cmd_size; 135 } 136 return false; 137} 138 139bool 140UniversalMachOFileContainsArchAndUUID 141( 142 const FileSpec &file_spec, 143 const ArchSpec *arch, 144 const lldb_private::UUID *uuid, 145 off_t file_offset, 146 DataExtractor& data, 147 uint32_t data_offset, 148 const uint32_t magic 149) 150{ 151 assert(magic == UniversalMagic || magic == UniversalMagicSwapped); 152 153 // Universal mach-o files always have their headers encoded as BIG endian 154 data.SetByteOrder(eByteOrderBig); 155 156 uint32_t i; 157 const uint32_t nfat_arch = data.GetU32(&data_offset); // number of structs that follow 158 const uint32_t fat_header_and_arch_size = sizeof(struct fat_header) + nfat_arch * sizeof(struct fat_arch); 159 if (data.GetByteSize() < fat_header_and_arch_size) 160 { 161 DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, fat_header_and_arch_size)); 162 data.SetData (data_buffer_sp); 163 } 164 165 for (i=0; i<nfat_arch; i++) 166 { 167 cpu_type_t arch_cputype = data.GetU32(&data_offset); // cpu specifier (int) 168 cpu_subtype_t arch_cpusubtype = data.GetU32(&data_offset); // machine specifier (int) 169 uint32_t arch_offset = data.GetU32(&data_offset); // file offset to this object file 170 // uint32_t arch_size = data.GetU32(&data_offset); // size of this object file 171 // uint32_t arch_align = data.GetU32(&data_offset); // alignment as a power of 2 172 data_offset += 8; // Skip size and align as we don't need those 173 // Only process this slice if the cpu type/subtype matches 174 if (arch) 175 { 176 ArchSpec fat_arch(eArchTypeMachO, arch_cputype, arch_cpusubtype); 177 if (fat_arch != *arch) 178 continue; 179 } 180 181 // Create a buffer with only the arch slice date in it 182 DataExtractor arch_data; 183 DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset + arch_offset, 0x1000)); 184 arch_data.SetData(data_buffer_sp); 185 uint32_t arch_data_offset = 0; 186 uint32_t arch_magic = arch_data.GetU32(&arch_data_offset); 187 188 switch (arch_magic) 189 { 190 case HeaderMagic32: 191 case HeaderMagic32Swapped: 192 case HeaderMagic64: 193 case HeaderMagic64Swapped: 194 if (SkinnyMachOFileContainsArchAndUUID (file_spec, arch, uuid, file_offset + arch_offset, arch_data, arch_data_offset, arch_magic)) 195 return true; 196 break; 197 } 198 } 199 return false; 200} 201 202static bool 203FileAtPathContainsArchAndUUID 204( 205 const FileSpec &file_spec, 206 const ArchSpec *arch, 207 const lldb_private::UUID *uuid 208) 209{ 210 DataExtractor data; 211 off_t file_offset = 0; 212 DataBufferSP data_buffer_sp (file_spec.ReadFileContents (file_offset, 0x1000)); 213 214 if (data_buffer_sp && data_buffer_sp->GetByteSize() > 0) 215 { 216 data.SetData(data_buffer_sp); 217 218 uint32_t data_offset = 0; 219 uint32_t magic = data.GetU32(&data_offset); 220 221 switch (magic) 222 { 223 // 32 bit mach-o file 224 case HeaderMagic32: 225 case HeaderMagic32Swapped: 226 case HeaderMagic64: 227 case HeaderMagic64Swapped: 228 return SkinnyMachOFileContainsArchAndUUID (file_spec, arch, uuid, file_offset, data, data_offset, magic); 229 230 // fat mach-o file 231 case UniversalMagic: 232 case UniversalMagicSwapped: 233 return UniversalMachOFileContainsArchAndUUID (file_spec, arch, uuid, file_offset, data, data_offset, magic); 234 235 default: 236 break; 237 } 238 } 239 return false; 240} 241 242static FileSpec 243LocateDSYMMachFileInDSYMBundle 244( 245 const FileSpec& dsym_bundle_fspec, 246 const lldb_private::UUID *uuid, 247 const ArchSpec *arch) 248{ 249 char path[PATH_MAX]; 250 251 FileSpec dsym_fspec; 252 253 if (dsym_bundle_fspec.GetPath(path, sizeof(path))) 254 { 255 ::strncat (path, "/Contents/Resources/DWARF", sizeof(path) - strlen(path) - 1); 256 257 lldb_utility::CleanUp <DIR *, int> dirp (opendir(path), NULL, closedir); 258 if (dirp.is_valid()) 259 { 260 dsym_fspec.GetDirectory().SetCString(path); 261 struct dirent* dp; 262 while ((dp = readdir(dirp.get())) != NULL) 263 { 264 // Only search directories 265 if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN) 266 { 267 if (dp->d_namlen == 1 && dp->d_name[0] == '.') 268 continue; 269 270 if (dp->d_namlen == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.') 271 continue; 272 } 273 274 if (dp->d_type == DT_REG || dp->d_type == DT_UNKNOWN) 275 { 276 dsym_fspec.GetFilename().SetCString(dp->d_name); 277 if (FileAtPathContainsArchAndUUID (dsym_fspec, arch, uuid)) 278 return dsym_fspec; 279 } 280 } 281 } 282 } 283 dsym_fspec.Clear(); 284 return dsym_fspec; 285} 286 287static int 288LocateMacOSXFilesUsingDebugSymbols 289( 290 const ModuleSpec &module_spec, 291 FileSpec *out_exec_fspec, // If non-NULL, try and find the executable 292 FileSpec *out_dsym_fspec // If non-NULL try and find the debug symbol file 293) 294{ 295 int items_found = 0; 296 297 if (out_exec_fspec) 298 out_exec_fspec->Clear(); 299 300 if (out_dsym_fspec) 301 out_dsym_fspec->Clear(); 302 303#if !defined (__arm__) // No DebugSymbols on the iOS devices 304 305 const UUID *uuid = module_spec.GetUUIDPtr(); 306 const ArchSpec *arch = module_spec.GetArchitecturePtr(); 307 308 if (uuid && uuid->IsValid()) 309 { 310 // Try and locate the dSYM file using DebugSymbols first 311 const UInt8 *module_uuid = (const UInt8 *)uuid->GetBytes(); 312 if (module_uuid != NULL) 313 { 314 CFCReleaser<CFUUIDRef> module_uuid_ref(::CFUUIDCreateWithBytes (NULL, 315 module_uuid[0], 316 module_uuid[1], 317 module_uuid[2], 318 module_uuid[3], 319 module_uuid[4], 320 module_uuid[5], 321 module_uuid[6], 322 module_uuid[7], 323 module_uuid[8], 324 module_uuid[9], 325 module_uuid[10], 326 module_uuid[11], 327 module_uuid[12], 328 module_uuid[13], 329 module_uuid[14], 330 module_uuid[15])); 331 332 if (module_uuid_ref.get()) 333 { 334 CFCReleaser<CFURLRef> exec_url; 335 const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); 336 if (exec_fspec) 337 { 338 char exec_cf_path[PATH_MAX]; 339 if (exec_fspec->GetPath(exec_cf_path, sizeof(exec_cf_path))) 340 exec_url.reset(::CFURLCreateFromFileSystemRepresentation (NULL, 341 (const UInt8 *)exec_cf_path, 342 strlen(exec_cf_path), 343 FALSE)); 344 } 345 346 CFCReleaser<CFURLRef> dsym_url (::DBGCopyFullDSYMURLForUUID(module_uuid_ref.get(), exec_url.get())); 347 char path[PATH_MAX]; 348 349 if (dsym_url.get()) 350 { 351 if (out_dsym_fspec) 352 { 353 if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1)) 354 { 355 out_dsym_fspec->SetFile(path, false); 356 357 if (out_dsym_fspec->GetFileType () == FileSpec::eFileTypeDirectory) 358 { 359 *out_dsym_fspec = LocateDSYMMachFileInDSYMBundle (*out_dsym_fspec, uuid, arch); 360 if (*out_dsym_fspec) 361 ++items_found; 362 } 363 else 364 { 365 ++items_found; 366 } 367 } 368 } 369 370 CFCReleaser<CFDictionaryRef> dict(::DBGCopyDSYMPropertyLists (dsym_url.get())); 371 CFDictionaryRef uuid_dict = NULL; 372 if (dict.get()) 373 { 374 char uuid_cstr_buf[64]; 375 const char *uuid_cstr = uuid->GetAsCString (uuid_cstr_buf, sizeof(uuid_cstr_buf)); 376 CFCString uuid_cfstr (uuid_cstr); 377 CFDictionaryRef uuid_dict = static_cast<CFDictionaryRef>(::CFDictionaryGetValue (dict.get(), uuid_cfstr.get())); 378 if (uuid_dict) 379 { 380 381 CFStringRef actual_src_cfpath = static_cast<CFStringRef>(::CFDictionaryGetValue (uuid_dict, CFSTR("DBGSourcePath"))); 382 if (actual_src_cfpath) 383 { 384 CFStringRef build_src_cfpath = static_cast<CFStringRef>(::CFDictionaryGetValue (uuid_dict, CFSTR("DBGBuildSourcePath"))); 385 if (build_src_cfpath) 386 { 387 char actual_src_path[PATH_MAX]; 388 char build_src_path[PATH_MAX]; 389 ::CFStringGetFileSystemRepresentation (actual_src_cfpath, actual_src_path, sizeof(actual_src_path)); 390 ::CFStringGetFileSystemRepresentation (build_src_cfpath, build_src_path, sizeof(build_src_path)); 391 module_spec.GetSourceMappingList().Append (ConstString(build_src_path), ConstString(actual_src_path), true); 392 } 393 } 394 } 395 } 396 397 if (out_exec_fspec) 398 { 399 bool success = false; 400 if (uuid_dict) 401 { 402 CFStringRef exec_cf_path = static_cast<CFStringRef>(::CFDictionaryGetValue (uuid_dict, CFSTR("DBGSymbolRichExecutable"))); 403 if (exec_cf_path && ::CFStringGetFileSystemRepresentation (exec_cf_path, path, sizeof(path))) 404 { 405 ++items_found; 406 out_exec_fspec->SetFile(path, path[0] == '~'); 407 if (out_exec_fspec->Exists()) 408 success = true; 409 } 410 } 411 412 if (!success) 413 { 414 // No dictionary, check near the dSYM bundle for an executable that matches... 415 if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1)) 416 { 417 char *dsym_extension_pos = ::strstr (path, ".dSYM"); 418 if (dsym_extension_pos) 419 { 420 *dsym_extension_pos = '\0'; 421 FileSpec file_spec (path, true); 422 switch (file_spec.GetFileType()) 423 { 424 case FileSpec::eFileTypeDirectory: // Bundle directory? 425 { 426 CFCBundle bundle (path); 427 CFCReleaser<CFURLRef> bundle_exe_url (bundle.CopyExecutableURL ()); 428 if (bundle_exe_url.get()) 429 { 430 if (::CFURLGetFileSystemRepresentation (bundle_exe_url.get(), true, (UInt8*)path, sizeof(path)-1)) 431 { 432 FileSpec bundle_exe_file_spec (path, true); 433 434 if (FileAtPathContainsArchAndUUID (bundle_exe_file_spec, arch, uuid)) 435 { 436 ++items_found; 437 *out_exec_fspec = bundle_exe_file_spec; 438 } 439 } 440 } 441 } 442 break; 443 444 case FileSpec::eFileTypePipe: // Forget pipes 445 case FileSpec::eFileTypeSocket: // We can't process socket files 446 case FileSpec::eFileTypeInvalid: // File doesn't exist... 447 break; 448 449 case FileSpec::eFileTypeUnknown: 450 case FileSpec::eFileTypeRegular: 451 case FileSpec::eFileTypeSymbolicLink: 452 case FileSpec::eFileTypeOther: 453 if (FileAtPathContainsArchAndUUID (file_spec, arch, uuid)) 454 { 455 ++items_found; 456 *out_exec_fspec = file_spec; 457 } 458 break; 459 } 460 } 461 } 462 } 463 } 464 } 465 } 466 } 467 } 468#endif // #if !defined (__arm__) 469 470 return items_found; 471} 472 473static bool 474LocateDSYMInVincinityOfExecutable (const ModuleSpec &module_spec, FileSpec &dsym_fspec) 475{ 476 const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); 477 if (exec_fspec) 478 { 479 char path[PATH_MAX]; 480 if (exec_fspec->GetPath(path, sizeof(path))) 481 { 482 // Make sure the module isn't already just a dSYM file... 483 if (strcasestr(path, ".dSYM/Contents/Resources/DWARF") == NULL) 484 { 485 size_t obj_file_path_length = strlen(path); 486 strncat(path, ".dSYM/Contents/Resources/DWARF/", sizeof(path)); 487 strncat(path, exec_fspec->GetFilename().AsCString(), sizeof(path)); 488 489 dsym_fspec.SetFile(path, false); 490 491 if (dsym_fspec.Exists() && FileAtPathContainsArchAndUUID (dsym_fspec, module_spec.GetArchitecturePtr(), module_spec.GetUUIDPtr())) 492 { 493 return true; 494 } 495 else 496 { 497 path[obj_file_path_length] = '\0'; 498 499 char *last_dot = strrchr(path, '.'); 500 while (last_dot != NULL && last_dot[0]) 501 { 502 char *next_slash = strchr(last_dot, '/'); 503 if (next_slash != NULL) 504 { 505 *next_slash = '\0'; 506 strncat(path, ".dSYM/Contents/Resources/DWARF/", sizeof(path)); 507 strncat(path, exec_fspec->GetFilename().AsCString(), sizeof(path)); 508 dsym_fspec.SetFile(path, false); 509 if (dsym_fspec.Exists() && FileAtPathContainsArchAndUUID (dsym_fspec, module_spec.GetArchitecturePtr(), module_spec.GetUUIDPtr())) 510 return true; 511 else 512 { 513 *last_dot = '\0'; 514 char *prev_slash = strrchr(path, '/'); 515 if (prev_slash != NULL) 516 *prev_slash = '\0'; 517 else 518 break; 519 } 520 } 521 else 522 { 523 break; 524 } 525 } 526 } 527 } 528 } 529 } 530 dsym_fspec.Clear(); 531 return false; 532} 533 534FileSpec 535Symbols::LocateExecutableObjectFile (const ModuleSpec &module_spec) 536{ 537 const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); 538 const ArchSpec *arch = module_spec.GetArchitecturePtr(); 539 const UUID *uuid = module_spec.GetUUIDPtr(); 540 Timer scoped_timer (__PRETTY_FUNCTION__, 541 "LocateExecutableObjectFile (file = %s, arch = %s, uuid = %p)", 542 exec_fspec ? exec_fspec->GetFilename().AsCString ("<NULL>") : "<NULL>", 543 arch ? arch->GetArchitectureName() : "<NULL>", 544 uuid); 545 546 FileSpec objfile_fspec; 547 if (exec_fspec && FileAtPathContainsArchAndUUID (exec_fspec, arch, uuid)) 548 objfile_fspec = exec_fspec; 549 else 550 LocateMacOSXFilesUsingDebugSymbols (module_spec, &objfile_fspec, NULL); 551 return objfile_fspec; 552} 553 554FileSpec 555Symbols::LocateExecutableSymbolFile (const ModuleSpec &module_spec) 556{ 557 const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); 558 const ArchSpec *arch = module_spec.GetArchitecturePtr(); 559 const UUID *uuid = module_spec.GetUUIDPtr(); 560 561 Timer scoped_timer (__PRETTY_FUNCTION__, 562 "LocateExecutableSymbolFile (file = %s, arch = %s, uuid = %p)", 563 exec_fspec ? exec_fspec->GetFilename().AsCString ("<NULL>") : "<NULL>", 564 arch ? arch->GetArchitectureName() : "<NULL>", 565 uuid); 566 567 FileSpec symbol_fspec; 568 // First try and find the dSYM in the same directory as the executable or in 569 // an appropriate parent directory 570 if (LocateDSYMInVincinityOfExecutable (module_spec, symbol_fspec) == false) 571 { 572 // We failed to easily find the dSYM above, so use DebugSymbols 573 LocateMacOSXFilesUsingDebugSymbols (module_spec, NULL, &symbol_fspec); 574 } 575 return symbol_fspec; 576} 577