1 //===-- ObjectContainerBSDArchive.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 "ObjectContainerBSDArchive.h" 11 12 #ifdef _WIN32 13 // Defines from ar, missing on Windows 14 #define ARMAG "!<arch>\n" 15 #define SARMAG 8 16 #define ARFMAG "`\n" 17 18 typedef struct ar_hdr 19 { 20 char ar_name[16]; 21 char ar_date[12]; 22 char ar_uid[6], ar_gid[6]; 23 char ar_mode[8]; 24 char ar_size[10]; 25 char ar_fmag[2]; 26 } ar_hdr; 27 #else 28 #include <ar.h> 29 #endif 30 31 #include "lldb/Core/ArchSpec.h" 32 #include "lldb/Core/DataBuffer.h" 33 #include "lldb/Core/Module.h" 34 #include "lldb/Core/ModuleSpec.h" 35 #include "lldb/Core/PluginManager.h" 36 #include "lldb/Core/Stream.h" 37 #include "lldb/Core/Timer.h" 38 #include "lldb/Host/Mutex.h" 39 #include "lldb/Symbol/ObjectFile.h" 40 41 using namespace lldb; 42 using namespace lldb_private; 43 44 45 46 ObjectContainerBSDArchive::Object::Object() : 47 ar_name(), 48 ar_date(0), 49 ar_uid(0), 50 ar_gid(0), 51 ar_mode(0), 52 ar_size(0), 53 ar_file_offset(0), 54 ar_file_size(0) 55 { 56 } 57 58 void 59 ObjectContainerBSDArchive::Object::Clear() 60 { 61 ar_name.Clear(); 62 ar_date = 0; 63 ar_uid = 0; 64 ar_gid = 0; 65 ar_mode = 0; 66 ar_size = 0; 67 ar_file_offset = 0; 68 ar_file_size = 0; 69 } 70 71 lldb::offset_t 72 ObjectContainerBSDArchive::Object::Extract (const DataExtractor& data, lldb::offset_t offset) 73 { 74 size_t ar_name_len = 0; 75 std::string str; 76 char *err; 77 str.assign ((const char *)data.GetData(&offset, 16), 16); 78 if (str.find("#1/") == 0) 79 { 80 // If the name is longer than 16 bytes, or contains an embedded space 81 // then it will use this format where the length of the name is 82 // here and the name characters are after this header. 83 ar_name_len = strtoul(str.c_str() + 3, &err, 10); 84 } 85 else 86 { 87 // Strip off any spaces (if the object file name contains spaces it 88 // will use the extended format above). 89 str.erase (str.find(' ')); 90 ar_name.SetCString(str.c_str()); 91 } 92 93 str.assign ((const char *)data.GetData(&offset, 12), 12); 94 ar_date = strtoul(str.c_str(), &err, 10); 95 96 str.assign ((const char *)data.GetData(&offset, 6), 6); 97 ar_uid = strtoul(str.c_str(), &err, 10); 98 99 str.assign ((const char *)data.GetData(&offset, 6), 6); 100 ar_gid = strtoul(str.c_str(), &err, 10); 101 102 str.assign ((const char *)data.GetData(&offset, 8), 8); 103 ar_mode = strtoul(str.c_str(), &err, 8); 104 105 str.assign ((const char *)data.GetData(&offset, 10), 10); 106 ar_size = strtoul(str.c_str(), &err, 10); 107 108 str.assign ((const char *)data.GetData(&offset, 2), 2); 109 if (str == ARFMAG) 110 { 111 if (ar_name_len > 0) 112 { 113 str.assign ((const char *)data.GetData(&offset, ar_name_len), ar_name_len); 114 ar_name.SetCString (str.c_str()); 115 } 116 ar_file_offset = offset; 117 ar_file_size = ar_size - ar_name_len; 118 return offset; 119 } 120 return LLDB_INVALID_OFFSET; 121 } 122 123 ObjectContainerBSDArchive::Archive::Archive 124 ( 125 const lldb_private::ArchSpec &arch, 126 const lldb_private::TimeValue &time, 127 lldb::offset_t file_offset, 128 lldb_private::DataExtractor &data 129 ) : 130 m_arch (arch), 131 m_time (time), 132 m_file_offset (file_offset), 133 m_objects(), 134 m_data (data) 135 { 136 } 137 138 ObjectContainerBSDArchive::Archive::~Archive () 139 { 140 } 141 142 size_t 143 ObjectContainerBSDArchive::Archive::ParseObjects () 144 { 145 DataExtractor &data = m_data; 146 std::string str; 147 lldb::offset_t offset = 0; 148 str.assign((const char *)data.GetData(&offset, SARMAG), SARMAG); 149 if (str == ARMAG) 150 { 151 Object obj; 152 do 153 { 154 offset = obj.Extract (data, offset); 155 if (offset == LLDB_INVALID_OFFSET) 156 break; 157 size_t obj_idx = m_objects.size(); 158 m_objects.push_back(obj); 159 // Insert all of the C strings out of order for now... 160 m_object_name_to_index_map.Append (obj.ar_name.GetCString(), obj_idx); 161 offset += obj.ar_file_size; 162 obj.Clear(); 163 } while (data.ValidOffset(offset)); 164 165 // Now sort all of the object name pointers 166 m_object_name_to_index_map.Sort (); 167 } 168 return m_objects.size(); 169 } 170 171 ObjectContainerBSDArchive::Object * 172 ObjectContainerBSDArchive::Archive::FindObject (const ConstString &object_name, const TimeValue &object_mod_time) 173 { 174 const ObjectNameToIndexMap::Entry *match = m_object_name_to_index_map.FindFirstValueForName (object_name.GetCString()); 175 if (match) 176 { 177 if (object_mod_time.IsValid()) 178 { 179 const uint64_t object_date = object_mod_time.GetAsSecondsSinceJan1_1970(); 180 if (m_objects[match->value].ar_date == object_date) 181 return &m_objects[match->value]; 182 const ObjectNameToIndexMap::Entry *next_match = m_object_name_to_index_map.FindNextValueForName (match); 183 while (next_match) 184 { 185 if (m_objects[next_match->value].ar_date == object_date) 186 return &m_objects[next_match->value]; 187 next_match = m_object_name_to_index_map.FindNextValueForName (next_match); 188 } 189 } 190 else 191 { 192 return &m_objects[match->value]; 193 } 194 } 195 return NULL; 196 } 197 198 199 ObjectContainerBSDArchive::Archive::shared_ptr 200 ObjectContainerBSDArchive::Archive::FindCachedArchive (const FileSpec &file, const ArchSpec &arch, const TimeValue &time, lldb::offset_t file_offset) 201 { 202 Mutex::Locker locker(Archive::GetArchiveCacheMutex ()); 203 shared_ptr archive_sp; 204 Archive::Map &archive_map = Archive::GetArchiveCache (); 205 Archive::Map::iterator pos = archive_map.find (file); 206 // Don't cache a value for "archive_map.end()" below since we might 207 // delete an archive entry... 208 while (pos != archive_map.end() && pos->first == file) 209 { 210 bool match = true; 211 if (arch.IsValid() && pos->second->GetArchitecture().IsCompatibleMatch(arch) == false) 212 match = false; 213 else if (file_offset != LLDB_INVALID_OFFSET && pos->second->GetFileOffset() != file_offset) 214 match = false; 215 if (match) 216 { 217 if (pos->second->GetModificationTime() == time) 218 { 219 return pos->second; 220 } 221 else 222 { 223 // We have a file at the same path with the same architecture 224 // whose modification time doesn't match. It doesn't make sense 225 // for us to continue to use this BSD archive since we cache only 226 // the object info which consists of file time info and also the 227 // file offset and file size of any contianed objects. Since 228 // this information is now out of date, we won't get the correct 229 // information if we go and extract the file data, so we should 230 // remove the old and outdated entry. 231 archive_map.erase (pos); 232 pos = archive_map.find (file); 233 continue; // Continue to next iteration so we don't increment pos below... 234 } 235 } 236 ++pos; 237 } 238 return archive_sp; 239 } 240 241 ObjectContainerBSDArchive::Archive::shared_ptr 242 ObjectContainerBSDArchive::Archive::ParseAndCacheArchiveForFile 243 ( 244 const FileSpec &file, 245 const ArchSpec &arch, 246 const TimeValue &time, 247 lldb::offset_t file_offset, 248 DataExtractor &data 249 ) 250 { 251 shared_ptr archive_sp(new Archive (arch, time, file_offset, data)); 252 if (archive_sp) 253 { 254 const size_t num_objects = archive_sp->ParseObjects (); 255 if (num_objects > 0) 256 { 257 Mutex::Locker locker(Archive::GetArchiveCacheMutex ()); 258 Archive::GetArchiveCache().insert(std::make_pair(file, archive_sp)); 259 } 260 else 261 { 262 archive_sp.reset(); 263 } 264 } 265 return archive_sp; 266 } 267 268 ObjectContainerBSDArchive::Archive::Map & 269 ObjectContainerBSDArchive::Archive::GetArchiveCache () 270 { 271 static Archive::Map g_archive_map; 272 return g_archive_map; 273 } 274 275 Mutex & 276 ObjectContainerBSDArchive::Archive::GetArchiveCacheMutex () 277 { 278 static Mutex g_archive_map_mutex (Mutex::eMutexTypeRecursive); 279 return g_archive_map_mutex; 280 } 281 282 283 void 284 ObjectContainerBSDArchive::Initialize() 285 { 286 PluginManager::RegisterPlugin (GetPluginNameStatic(), 287 GetPluginDescriptionStatic(), 288 CreateInstance, 289 GetModuleSpecifications); 290 } 291 292 void 293 ObjectContainerBSDArchive::Terminate() 294 { 295 PluginManager::UnregisterPlugin (CreateInstance); 296 } 297 298 299 lldb_private::ConstString 300 ObjectContainerBSDArchive::GetPluginNameStatic() 301 { 302 static ConstString g_name("bsd-archive"); 303 return g_name; 304 } 305 306 const char * 307 ObjectContainerBSDArchive::GetPluginDescriptionStatic() 308 { 309 return "BSD Archive object container reader."; 310 } 311 312 313 ObjectContainer * 314 ObjectContainerBSDArchive::CreateInstance 315 ( 316 const lldb::ModuleSP &module_sp, 317 DataBufferSP& data_sp, 318 lldb::offset_t data_offset, 319 const FileSpec *file, 320 lldb::offset_t file_offset, 321 lldb::offset_t length) 322 { 323 ConstString object_name (module_sp->GetObjectName()); 324 if (object_name) 325 { 326 if (data_sp) 327 { 328 // We have data, which means this is the first 512 bytes of the file 329 // Check to see if the magic bytes match and if they do, read the entire 330 // table of contents for the archive and cache it 331 DataExtractor data; 332 data.SetData (data_sp, data_offset, length); 333 if (file && data_sp && ObjectContainerBSDArchive::MagicBytesMatch(data)) 334 { 335 Timer scoped_timer (__PRETTY_FUNCTION__, 336 "ObjectContainerBSDArchive::CreateInstance (module = %s, file = %p, file_offset = 0x%8.8" PRIx64 ", file_size = 0x%8.8" PRIx64 ")", 337 module_sp->GetFileSpec().GetPath().c_str(), 338 static_cast<const void*>(file), 339 static_cast<uint64_t>(file_offset), 340 static_cast<uint64_t>(length)); 341 342 // Map the entire .a file to be sure that we don't lose any data if the file 343 // gets updated by a new build while this .a file is being used for debugging 344 DataBufferSP archive_data_sp (file->MemoryMapFileContents(file_offset, length)); 345 lldb::offset_t archive_data_offset = 0; 346 347 Archive::shared_ptr archive_sp (Archive::FindCachedArchive (*file, 348 module_sp->GetArchitecture(), 349 module_sp->GetModificationTime(), 350 file_offset)); 351 std::unique_ptr<ObjectContainerBSDArchive> container_ap(new ObjectContainerBSDArchive (module_sp, 352 archive_data_sp, 353 archive_data_offset, 354 file, 355 file_offset, 356 length)); 357 358 if (container_ap.get()) 359 { 360 if (archive_sp) 361 { 362 // We already have this archive in our cache, use it 363 container_ap->SetArchive (archive_sp); 364 return container_ap.release(); 365 } 366 else if (container_ap->ParseHeader()) 367 return container_ap.release(); 368 } 369 } 370 } 371 else 372 { 373 // No data, just check for a cached archive 374 Archive::shared_ptr archive_sp (Archive::FindCachedArchive (*file, 375 module_sp->GetArchitecture(), 376 module_sp->GetModificationTime(), 377 file_offset)); 378 if (archive_sp) 379 { 380 std::unique_ptr<ObjectContainerBSDArchive> container_ap(new ObjectContainerBSDArchive (module_sp, data_sp, data_offset, file, file_offset, length)); 381 382 if (container_ap.get()) 383 { 384 // We already have this archive in our cache, use it 385 container_ap->SetArchive (archive_sp); 386 return container_ap.release(); 387 } 388 } 389 } 390 } 391 return NULL; 392 } 393 394 395 396 bool 397 ObjectContainerBSDArchive::MagicBytesMatch (const DataExtractor &data) 398 { 399 uint32_t offset = 0; 400 const char* armag = (const char* )data.PeekData (offset, sizeof(ar_hdr)); 401 if (armag && ::strncmp(armag, ARMAG, SARMAG) == 0) 402 { 403 armag += offsetof(struct ar_hdr, ar_fmag) + SARMAG; 404 if (strncmp(armag, ARFMAG, 2) == 0) 405 return true; 406 } 407 return false; 408 } 409 410 ObjectContainerBSDArchive::ObjectContainerBSDArchive 411 ( 412 const lldb::ModuleSP &module_sp, 413 DataBufferSP& data_sp, 414 lldb::offset_t data_offset, 415 const lldb_private::FileSpec *file, 416 lldb::offset_t file_offset, 417 lldb::offset_t size 418 ) : 419 ObjectContainer (module_sp, file, file_offset, size, data_sp, data_offset), 420 m_archive_sp () 421 { 422 } 423 void 424 ObjectContainerBSDArchive::SetArchive (Archive::shared_ptr &archive_sp) 425 { 426 m_archive_sp = archive_sp; 427 } 428 429 430 431 ObjectContainerBSDArchive::~ObjectContainerBSDArchive() 432 { 433 } 434 435 bool 436 ObjectContainerBSDArchive::ParseHeader () 437 { 438 if (m_archive_sp.get() == NULL) 439 { 440 if (m_data.GetByteSize() > 0) 441 { 442 ModuleSP module_sp (GetModule()); 443 if (module_sp) 444 { 445 m_archive_sp = Archive::ParseAndCacheArchiveForFile (m_file, 446 module_sp->GetArchitecture(), 447 module_sp->GetModificationTime(), 448 m_offset, 449 m_data); 450 } 451 // Clear the m_data that contains the entire archive 452 // data and let our m_archive_sp hold onto the data. 453 m_data.Clear(); 454 } 455 } 456 return m_archive_sp.get() != NULL; 457 } 458 459 void 460 ObjectContainerBSDArchive::Dump (Stream *s) const 461 { 462 s->Printf("%p: ", static_cast<const void*>(this)); 463 s->Indent(); 464 const size_t num_archs = GetNumArchitectures(); 465 const size_t num_objects = GetNumObjects(); 466 s->Printf("ObjectContainerBSDArchive, num_archs = %" PRIu64 ", num_objects = %" PRIu64 "", (uint64_t)num_archs, (uint64_t)num_objects); 467 uint32_t i; 468 ArchSpec arch; 469 s->IndentMore(); 470 for (i=0; i<num_archs; i++) 471 { 472 s->Indent(); 473 GetArchitectureAtIndex(i, arch); 474 s->Printf("arch[%u] = %s\n", i, arch.GetArchitectureName()); 475 } 476 for (i=0; i<num_objects; i++) 477 { 478 s->Indent(); 479 s->Printf("object[%u] = %s\n", i, GetObjectNameAtIndex (i)); 480 } 481 s->IndentLess(); 482 s->EOL(); 483 } 484 485 ObjectFileSP 486 ObjectContainerBSDArchive::GetObjectFile (const FileSpec *file) 487 { 488 ModuleSP module_sp (GetModule()); 489 if (module_sp) 490 { 491 if (module_sp->GetObjectName() && m_archive_sp) 492 { 493 Object *object = m_archive_sp->FindObject (module_sp->GetObjectName(), 494 module_sp->GetObjectModificationTime()); 495 if (object) 496 { 497 lldb::offset_t data_offset = object->ar_file_offset; 498 return ObjectFile::FindPlugin (module_sp, 499 file, 500 m_offset + object->ar_file_offset, 501 object->ar_file_size, 502 m_archive_sp->GetData().GetSharedDataBuffer(), 503 data_offset); 504 } 505 } 506 } 507 return ObjectFileSP(); 508 } 509 510 511 //------------------------------------------------------------------ 512 // PluginInterface protocol 513 //------------------------------------------------------------------ 514 lldb_private::ConstString 515 ObjectContainerBSDArchive::GetPluginName() 516 { 517 return GetPluginNameStatic(); 518 } 519 520 uint32_t 521 ObjectContainerBSDArchive::GetPluginVersion() 522 { 523 return 1; 524 } 525 526 527 size_t 528 ObjectContainerBSDArchive::GetModuleSpecifications (const lldb_private::FileSpec& file, 529 lldb::DataBufferSP& data_sp, 530 lldb::offset_t data_offset, 531 lldb::offset_t file_offset, 532 lldb::offset_t file_size, 533 lldb_private::ModuleSpecList &specs) 534 { 535 536 // We have data, which means this is the first 512 bytes of the file 537 // Check to see if the magic bytes match and if they do, read the entire 538 // table of contents for the archive and cache it 539 DataExtractor data; 540 data.SetData (data_sp, data_offset, data_sp->GetByteSize()); 541 if (file && data_sp && ObjectContainerBSDArchive::MagicBytesMatch(data)) 542 { 543 const size_t initial_count = specs.GetSize(); 544 TimeValue file_mod_time = file.GetModificationTime(); 545 Archive::shared_ptr archive_sp (Archive::FindCachedArchive (file, ArchSpec(), file_mod_time, file_offset)); 546 bool set_archive_arch = false; 547 if (!archive_sp) 548 { 549 set_archive_arch = true; 550 DataBufferSP data_sp (file.MemoryMapFileContents(file_offset, file_size)); 551 data.SetData (data_sp, 0, data_sp->GetByteSize()); 552 archive_sp = Archive::ParseAndCacheArchiveForFile(file, ArchSpec(), file_mod_time, file_offset, data); 553 } 554 555 if (archive_sp) 556 { 557 const size_t num_objects = archive_sp->GetNumObjects(); 558 for (size_t idx = 0; idx < num_objects; ++idx) 559 { 560 const Object *object = archive_sp->GetObjectAtIndex (idx); 561 if (object) 562 { 563 const lldb::offset_t object_file_offset = file_offset + object->ar_file_offset; 564 if (object->ar_file_offset < file_size && file_size > object_file_offset) 565 { 566 if (ObjectFile::GetModuleSpecifications(file, 567 object_file_offset, 568 file_size - object_file_offset, 569 specs)) 570 { 571 ModuleSpec &spec = specs.GetModuleSpecRefAtIndex (specs.GetSize() - 1); 572 TimeValue object_mod_time; 573 object_mod_time.OffsetWithSeconds(object->ar_date); 574 spec.GetObjectName () = object->ar_name; 575 spec.SetObjectOffset(object_file_offset); 576 spec.GetObjectModificationTime () = object_mod_time; 577 } 578 } 579 } 580 } 581 } 582 const size_t end_count = specs.GetSize(); 583 size_t num_specs_added = end_count - initial_count; 584 if (set_archive_arch && num_specs_added > 0) 585 { 586 // The archive was created but we didn't have an architecture 587 // so we need to set it 588 for (size_t i=initial_count; i<end_count; ++ i) 589 { 590 ModuleSpec module_spec; 591 if (specs.GetModuleSpecAtIndex(i, module_spec)) 592 { 593 if (module_spec.GetArchitecture().IsValid()) 594 { 595 archive_sp->SetArchitecture (module_spec.GetArchitecture()); 596 break; 597 } 598 } 599 } 600 } 601 return num_specs_added; 602 } 603 return 0; 604 } 605