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