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