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