Symbols.cpp revision 4d8c5434779393d5502b45f42a9371477604aab7
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 if (actual_src_path[0] == '~') 392 { 393 FileSpec resolved_source_path(actual_src_path, true); 394 resolved_source_path.GetPath(actual_src_path, sizeof(actual_src_path)); 395 } 396 module_spec.GetSourceMappingList().Append (ConstString(build_src_path), ConstString(actual_src_path), true); 397 } 398 } 399 } 400 } 401 402 if (out_exec_fspec) 403 { 404 bool success = false; 405 if (uuid_dict) 406 { 407 CFStringRef exec_cf_path = static_cast<CFStringRef>(::CFDictionaryGetValue (uuid_dict, CFSTR("DBGSymbolRichExecutable"))); 408 if (exec_cf_path && ::CFStringGetFileSystemRepresentation (exec_cf_path, path, sizeof(path))) 409 { 410 ++items_found; 411 out_exec_fspec->SetFile(path, path[0] == '~'); 412 if (out_exec_fspec->Exists()) 413 success = true; 414 } 415 } 416 417 if (!success) 418 { 419 // No dictionary, check near the dSYM bundle for an executable that matches... 420 if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1)) 421 { 422 char *dsym_extension_pos = ::strstr (path, ".dSYM"); 423 if (dsym_extension_pos) 424 { 425 *dsym_extension_pos = '\0'; 426 FileSpec file_spec (path, true); 427 switch (file_spec.GetFileType()) 428 { 429 case FileSpec::eFileTypeDirectory: // Bundle directory? 430 { 431 CFCBundle bundle (path); 432 CFCReleaser<CFURLRef> bundle_exe_url (bundle.CopyExecutableURL ()); 433 if (bundle_exe_url.get()) 434 { 435 if (::CFURLGetFileSystemRepresentation (bundle_exe_url.get(), true, (UInt8*)path, sizeof(path)-1)) 436 { 437 FileSpec bundle_exe_file_spec (path, true); 438 439 if (FileAtPathContainsArchAndUUID (bundle_exe_file_spec, arch, uuid)) 440 { 441 ++items_found; 442 *out_exec_fspec = bundle_exe_file_spec; 443 } 444 } 445 } 446 } 447 break; 448 449 case FileSpec::eFileTypePipe: // Forget pipes 450 case FileSpec::eFileTypeSocket: // We can't process socket files 451 case FileSpec::eFileTypeInvalid: // File doesn't exist... 452 break; 453 454 case FileSpec::eFileTypeUnknown: 455 case FileSpec::eFileTypeRegular: 456 case FileSpec::eFileTypeSymbolicLink: 457 case FileSpec::eFileTypeOther: 458 if (FileAtPathContainsArchAndUUID (file_spec, arch, uuid)) 459 { 460 ++items_found; 461 *out_exec_fspec = file_spec; 462 } 463 break; 464 } 465 } 466 } 467 } 468 } 469 } 470 } 471 } 472 } 473#endif // #if !defined (__arm__) 474 475 return items_found; 476} 477 478static bool 479LocateDSYMInVincinityOfExecutable (const ModuleSpec &module_spec, FileSpec &dsym_fspec) 480{ 481 const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); 482 if (exec_fspec) 483 { 484 char path[PATH_MAX]; 485 if (exec_fspec->GetPath(path, sizeof(path))) 486 { 487 // Make sure the module isn't already just a dSYM file... 488 if (strcasestr(path, ".dSYM/Contents/Resources/DWARF") == NULL) 489 { 490 size_t obj_file_path_length = strlen(path); 491 strlcat(path, ".dSYM/Contents/Resources/DWARF/", sizeof(path)); 492 strlcat(path, exec_fspec->GetFilename().AsCString(), sizeof(path)); 493 494 dsym_fspec.SetFile(path, false); 495 496 if (dsym_fspec.Exists() && FileAtPathContainsArchAndUUID (dsym_fspec, module_spec.GetArchitecturePtr(), module_spec.GetUUIDPtr())) 497 { 498 return true; 499 } 500 else 501 { 502 path[obj_file_path_length] = '\0'; 503 504 char *last_dot = strrchr(path, '.'); 505 while (last_dot != NULL && last_dot[0]) 506 { 507 char *next_slash = strchr(last_dot, '/'); 508 if (next_slash != NULL) 509 { 510 *next_slash = '\0'; 511 strlcat(path, ".dSYM/Contents/Resources/DWARF/", sizeof(path)); 512 strlcat(path, exec_fspec->GetFilename().AsCString(), sizeof(path)); 513 dsym_fspec.SetFile(path, false); 514 if (dsym_fspec.Exists() && FileAtPathContainsArchAndUUID (dsym_fspec, module_spec.GetArchitecturePtr(), module_spec.GetUUIDPtr())) 515 return true; 516 else 517 { 518 *last_dot = '\0'; 519 char *prev_slash = strrchr(path, '/'); 520 if (prev_slash != NULL) 521 *prev_slash = '\0'; 522 else 523 break; 524 } 525 } 526 else 527 { 528 break; 529 } 530 } 531 } 532 } 533 } 534 } 535 dsym_fspec.Clear(); 536 return false; 537} 538 539FileSpec 540Symbols::LocateExecutableObjectFile (const ModuleSpec &module_spec) 541{ 542 const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); 543 const ArchSpec *arch = module_spec.GetArchitecturePtr(); 544 const UUID *uuid = module_spec.GetUUIDPtr(); 545 Timer scoped_timer (__PRETTY_FUNCTION__, 546 "LocateExecutableObjectFile (file = %s, arch = %s, uuid = %p)", 547 exec_fspec ? exec_fspec->GetFilename().AsCString ("<NULL>") : "<NULL>", 548 arch ? arch->GetArchitectureName() : "<NULL>", 549 uuid); 550 551 FileSpec objfile_fspec; 552 if (exec_fspec && FileAtPathContainsArchAndUUID (exec_fspec, arch, uuid)) 553 objfile_fspec = exec_fspec; 554 else 555 LocateMacOSXFilesUsingDebugSymbols (module_spec, &objfile_fspec, NULL); 556 return objfile_fspec; 557} 558 559FileSpec 560Symbols::LocateExecutableSymbolFile (const ModuleSpec &module_spec) 561{ 562 const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); 563 const ArchSpec *arch = module_spec.GetArchitecturePtr(); 564 const UUID *uuid = module_spec.GetUUIDPtr(); 565 566 Timer scoped_timer (__PRETTY_FUNCTION__, 567 "LocateExecutableSymbolFile (file = %s, arch = %s, uuid = %p)", 568 exec_fspec ? exec_fspec->GetFilename().AsCString ("<NULL>") : "<NULL>", 569 arch ? arch->GetArchitectureName() : "<NULL>", 570 uuid); 571 572 FileSpec symbol_fspec; 573 // First try and find the dSYM in the same directory as the executable or in 574 // an appropriate parent directory 575 if (LocateDSYMInVincinityOfExecutable (module_spec, symbol_fspec) == false) 576 { 577 // We failed to easily find the dSYM above, so use DebugSymbols 578 LocateMacOSXFilesUsingDebugSymbols (module_spec, NULL, &symbol_fspec); 579 } 580 return symbol_fspec; 581} 582