ObjectContainerBSDArchive.cpp revision 263363
1185743Ssam//===-- ObjectContainerBSDArchive.cpp ---------------------------*- C++ -*-===// 2185743Ssam// 3185743Ssam// The LLVM Compiler Infrastructure 4185743Ssam// 5185743Ssam// This file is distributed under the University of Illinois Open Source 6185743Ssam// License. See LICENSE.TXT for details. 7185743Ssam// 8185743Ssam//===----------------------------------------------------------------------===// 9185743Ssam 10185743Ssam#include "ObjectContainerBSDArchive.h" 11185743Ssam 12185743Ssam#ifdef _WIN32 13185743Ssam// Defines from ar, missing on Windows 14185743Ssam#define ARMAG "!<arch>\n" 15185743Ssam#define SARMAG 8 16185743Ssam#define ARFMAG "`\n" 17185743Ssam 18185743Ssamtypedef struct ar_hdr 19185743Ssam{ 20185743Ssam char ar_name[16]; 21185743Ssam char ar_date[12]; 22185743Ssam char ar_uid[6], ar_gid[6]; 23185743Ssam char ar_mode[8]; 24185743Ssam char ar_size[10]; 25185743Ssam char ar_fmag[2]; 26185743Ssam} ar_hdr; 27185743Ssam#else 28185743Ssam#include <ar.h> 29185743Ssam#endif 30185743Ssam 31185743Ssam#include "lldb/Core/ArchSpec.h" 32185743Ssam#include "lldb/Core/DataBuffer.h" 33185743Ssam#include "lldb/Core/Module.h" 34185743Ssam#include "lldb/Core/ModuleSpec.h" 35185743Ssam#include "lldb/Core/PluginManager.h" 36185743Ssam#include "lldb/Core/Stream.h" 37185743Ssam#include "lldb/Core/Timer.h" 38185743Ssam#include "lldb/Host/Mutex.h" 39189701Ssam#include "lldb/Symbol/ObjectFile.h" 40185743Ssam 41185743Ssamusing namespace lldb; 42185743Ssamusing namespace lldb_private; 43185743Ssam 44185743Ssam 45185743Ssam 46185743SsamObjectContainerBSDArchive::Object::Object() : 47185743Ssam ar_name(), 48185743Ssam ar_date(0), 49185743Ssam ar_uid(0), 50185743Ssam ar_gid(0), 51185743Ssam ar_mode(0), 52185743Ssam ar_size(0), 53185743Ssam ar_file_offset(0), 54185743Ssam ar_file_size(0) 55185743Ssam{ 56185743Ssam} 57185743Ssam 58185743Ssamvoid 59185743SsamObjectContainerBSDArchive::Object::Clear() 60185743Ssam{ 61185743Ssam ar_name.Clear(); 62185743Ssam ar_date = 0; 63185743Ssam ar_uid = 0; 64185743Ssam ar_gid = 0; 65189701Ssam ar_mode = 0; 66189701Ssam ar_size = 0; 67189701Ssam ar_file_offset = 0; 68189701Ssam ar_file_size = 0; 69189701Ssam} 70189701Ssam 71189701Ssamlldb::offset_t 72189701SsamObjectContainerBSDArchive::Object::Extract (const DataExtractor& data, lldb::offset_t offset) 73189701Ssam{ 74189701Ssam size_t ar_name_len = 0; 75189701Ssam std::string str; 76189701Ssam char *err; 77189701Ssam str.assign ((const char *)data.GetData(&offset, 16), 16); 78189701Ssam if (str.find("#1/") == 0) 79189701Ssam { 80189701Ssam // If the name is longer than 16 bytes, or contains an embedded space 81189701Ssam // then it will use this format where the length of the name is 82189701Ssam // here and the name characters are after this header. 83189701Ssam ar_name_len = strtoul(str.c_str() + 3, &err, 10); 84189701Ssam } 85189701Ssam else 86189701Ssam { 87189701Ssam // Strip off any spaces (if the object file name contains spaces it 88189701Ssam // will use the extended format above). 89185743Ssam str.erase (str.find(' ')); 90185743Ssam ar_name.SetCString(str.c_str()); 91185743Ssam } 92185743Ssam 93185743Ssam str.assign ((const char *)data.GetData(&offset, 12), 12); 94185743Ssam ar_date = strtoul(str.c_str(), &err, 10); 95185743Ssam 96185743Ssam str.assign ((const char *)data.GetData(&offset, 6), 6); 97185743Ssam ar_uid = strtoul(str.c_str(), &err, 10); 98185743Ssam 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 123ObjectContainerBSDArchive::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 138ObjectContainerBSDArchive::Archive::~Archive () 139{ 140} 141 142size_t 143ObjectContainerBSDArchive::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 171ObjectContainerBSDArchive::Object * 172ObjectContainerBSDArchive::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 199ObjectContainerBSDArchive::Archive::shared_ptr 200ObjectContainerBSDArchive::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 241ObjectContainerBSDArchive::Archive::shared_ptr 242ObjectContainerBSDArchive::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 268ObjectContainerBSDArchive::Archive::Map & 269ObjectContainerBSDArchive::Archive::GetArchiveCache () 270{ 271 static Archive::Map g_archive_map; 272 return g_archive_map; 273} 274 275Mutex & 276ObjectContainerBSDArchive::Archive::GetArchiveCacheMutex () 277{ 278 static Mutex g_archive_map_mutex (Mutex::eMutexTypeRecursive); 279 return g_archive_map_mutex; 280} 281 282 283void 284ObjectContainerBSDArchive::Initialize() 285{ 286 PluginManager::RegisterPlugin (GetPluginNameStatic(), 287 GetPluginDescriptionStatic(), 288 CreateInstance, 289 GetModuleSpecifications); 290} 291 292void 293ObjectContainerBSDArchive::Terminate() 294{ 295 PluginManager::UnregisterPlugin (CreateInstance); 296} 297 298 299lldb_private::ConstString 300ObjectContainerBSDArchive::GetPluginNameStatic() 301{ 302 static ConstString g_name("bsd-archive"); 303 return g_name; 304} 305 306const char * 307ObjectContainerBSDArchive::GetPluginDescriptionStatic() 308{ 309 return "BSD Archive object container reader."; 310} 311 312 313ObjectContainer * 314ObjectContainerBSDArchive::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 file, (uint64_t) file_offset, (uint64_t) length); 339 340 // Map the entire .a file to be sure that we don't lose any data if the file 341 // gets updated by a new build while this .a file is being used for debugging 342 DataBufferSP archive_data_sp (file->MemoryMapFileContents(file_offset, length)); 343 lldb::offset_t archive_data_offset = 0; 344 345 Archive::shared_ptr archive_sp (Archive::FindCachedArchive (*file, 346 module_sp->GetArchitecture(), 347 module_sp->GetModificationTime(), 348 file_offset)); 349 std::unique_ptr<ObjectContainerBSDArchive> container_ap(new ObjectContainerBSDArchive (module_sp, 350 archive_data_sp, 351 archive_data_offset, 352 file, 353 file_offset, 354 length)); 355 356 if (container_ap.get()) 357 { 358 if (archive_sp) 359 { 360 // We already have this archive in our cache, use it 361 container_ap->SetArchive (archive_sp); 362 return container_ap.release(); 363 } 364 else if (container_ap->ParseHeader()) 365 return container_ap.release(); 366 } 367 } 368 } 369 else 370 { 371 // No data, just check for a cached archive 372 Archive::shared_ptr archive_sp (Archive::FindCachedArchive (*file, 373 module_sp->GetArchitecture(), 374 module_sp->GetModificationTime(), 375 file_offset)); 376 if (archive_sp) 377 { 378 std::unique_ptr<ObjectContainerBSDArchive> container_ap(new ObjectContainerBSDArchive (module_sp, data_sp, data_offset, file, file_offset, length)); 379 380 if (container_ap.get()) 381 { 382 // We already have this archive in our cache, use it 383 container_ap->SetArchive (archive_sp); 384 return container_ap.release(); 385 } 386 } 387 } 388 } 389 return NULL; 390} 391 392 393 394bool 395ObjectContainerBSDArchive::MagicBytesMatch (const DataExtractor &data) 396{ 397 uint32_t offset = 0; 398 const char* armag = (const char* )data.PeekData (offset, sizeof(ar_hdr)); 399 if (armag && ::strncmp(armag, ARMAG, SARMAG) == 0) 400 { 401 armag += offsetof(struct ar_hdr, ar_fmag) + SARMAG; 402 if (strncmp(armag, ARFMAG, 2) == 0) 403 return true; 404 } 405 return false; 406} 407 408ObjectContainerBSDArchive::ObjectContainerBSDArchive 409( 410 const lldb::ModuleSP &module_sp, 411 DataBufferSP& data_sp, 412 lldb::offset_t data_offset, 413 const lldb_private::FileSpec *file, 414 lldb::offset_t file_offset, 415 lldb::offset_t size 416) : 417 ObjectContainer (module_sp, file, file_offset, size, data_sp, data_offset), 418 m_archive_sp () 419{ 420} 421void 422ObjectContainerBSDArchive::SetArchive (Archive::shared_ptr &archive_sp) 423{ 424 m_archive_sp = archive_sp; 425} 426 427 428 429ObjectContainerBSDArchive::~ObjectContainerBSDArchive() 430{ 431} 432 433bool 434ObjectContainerBSDArchive::ParseHeader () 435{ 436 if (m_archive_sp.get() == NULL) 437 { 438 if (m_data.GetByteSize() > 0) 439 { 440 ModuleSP module_sp (GetModule()); 441 if (module_sp) 442 { 443 m_archive_sp = Archive::ParseAndCacheArchiveForFile (m_file, 444 module_sp->GetArchitecture(), 445 module_sp->GetModificationTime(), 446 m_offset, 447 m_data); 448 } 449 // Clear the m_data that contains the entire archive 450 // data and let our m_archive_sp hold onto the data. 451 m_data.Clear(); 452 } 453 } 454 return m_archive_sp.get() != NULL; 455} 456 457void 458ObjectContainerBSDArchive::Dump (Stream *s) const 459{ 460 s->Printf("%p: ", this); 461 s->Indent(); 462 const size_t num_archs = GetNumArchitectures(); 463 const size_t num_objects = GetNumObjects(); 464 s->Printf("ObjectContainerBSDArchive, num_archs = %zu, num_objects = %zu", num_archs, num_objects); 465 uint32_t i; 466 ArchSpec arch; 467 s->IndentMore(); 468 for (i=0; i<num_archs; i++) 469 { 470 s->Indent(); 471 GetArchitectureAtIndex(i, arch); 472 s->Printf("arch[%u] = %s\n", i, arch.GetArchitectureName()); 473 } 474 for (i=0; i<num_objects; i++) 475 { 476 s->Indent(); 477 s->Printf("object[%u] = %s\n", i, GetObjectNameAtIndex (i)); 478 } 479 s->IndentLess(); 480 s->EOL(); 481} 482 483ObjectFileSP 484ObjectContainerBSDArchive::GetObjectFile (const FileSpec *file) 485{ 486 ModuleSP module_sp (GetModule()); 487 if (module_sp) 488 { 489 if (module_sp->GetObjectName() && m_archive_sp) 490 { 491 Object *object = m_archive_sp->FindObject (module_sp->GetObjectName(), 492 module_sp->GetObjectModificationTime()); 493 if (object) 494 { 495 lldb::offset_t data_offset = object->ar_file_offset; 496 return ObjectFile::FindPlugin (module_sp, 497 file, 498 m_offset + object->ar_file_offset, 499 object->ar_file_size, 500 m_archive_sp->GetData().GetSharedDataBuffer(), 501 data_offset); 502 } 503 } 504 } 505 return ObjectFileSP(); 506} 507 508 509//------------------------------------------------------------------ 510// PluginInterface protocol 511//------------------------------------------------------------------ 512lldb_private::ConstString 513ObjectContainerBSDArchive::GetPluginName() 514{ 515 return GetPluginNameStatic(); 516} 517 518uint32_t 519ObjectContainerBSDArchive::GetPluginVersion() 520{ 521 return 1; 522} 523 524 525size_t 526ObjectContainerBSDArchive::GetModuleSpecifications (const lldb_private::FileSpec& file, 527 lldb::DataBufferSP& data_sp, 528 lldb::offset_t data_offset, 529 lldb::offset_t file_offset, 530 lldb::offset_t file_size, 531 lldb_private::ModuleSpecList &specs) 532{ 533 534 // We have data, which means this is the first 512 bytes of the file 535 // Check to see if the magic bytes match and if they do, read the entire 536 // table of contents for the archive and cache it 537 DataExtractor data; 538 data.SetData (data_sp, data_offset, data_sp->GetByteSize()); 539 if (file && data_sp && ObjectContainerBSDArchive::MagicBytesMatch(data)) 540 { 541 const size_t initial_count = specs.GetSize(); 542 TimeValue file_mod_time = file.GetModificationTime(); 543 Archive::shared_ptr archive_sp (Archive::FindCachedArchive (file, ArchSpec(), file_mod_time, file_offset)); 544 bool set_archive_arch = false; 545 if (!archive_sp) 546 { 547 set_archive_arch = true; 548 DataBufferSP data_sp (file.MemoryMapFileContents(file_offset, file_size)); 549 data.SetData (data_sp, 0, data_sp->GetByteSize()); 550 archive_sp = Archive::ParseAndCacheArchiveForFile(file, ArchSpec(), file_mod_time, file_offset, data); 551 } 552 553 if (archive_sp) 554 { 555 const size_t num_objects = archive_sp->GetNumObjects(); 556 for (size_t idx = 0; idx < num_objects; ++idx) 557 { 558 const Object *object = archive_sp->GetObjectAtIndex (idx); 559 if (object) 560 { 561 const lldb::offset_t object_file_offset = file_offset + object->ar_file_offset; 562 if (object->ar_file_offset < file_size && file_size > object_file_offset) 563 { 564 if (ObjectFile::GetModuleSpecifications(file, 565 object_file_offset, 566 file_size - object_file_offset, 567 specs)) 568 { 569 ModuleSpec &spec = specs.GetModuleSpecRefAtIndex (specs.GetSize() - 1); 570 TimeValue object_mod_time; 571 object_mod_time.OffsetWithSeconds(object->ar_date); 572 spec.GetObjectName () = object->ar_name; 573 spec.SetObjectOffset(object_file_offset); 574 spec.GetObjectModificationTime () = object_mod_time; 575 } 576 } 577 } 578 } 579 } 580 const size_t end_count = specs.GetSize(); 581 size_t num_specs_added = end_count - initial_count; 582 if (set_archive_arch && num_specs_added > 0) 583 { 584 // The archive was created but we didn't have an architecture 585 // so we need to set it 586 for (size_t i=initial_count; i<end_count; ++ i) 587 { 588 ModuleSpec module_spec; 589 if (specs.GetModuleSpecAtIndex(i, module_spec)) 590 { 591 if (module_spec.GetArchitecture().IsValid()) 592 { 593 archive_sp->SetArchitecture (module_spec.GetArchitecture()); 594 break; 595 } 596 } 597 } 598 } 599 return num_specs_added; 600 } 601 return 0; 602} 603