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