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