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