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) 152 { 153 const ObjectNameToIndexMap::Entry *match = m_object_name_to_index_map.FindFirstValueForName (object_name.GetCString()); 154 if (match) 155 return &m_objects[match->value]; 156 return NULL; 157 } 158 159 160 ObjectContainerBSDArchive::Archive::shared_ptr 161 ObjectContainerBSDArchive::Archive::FindCachedArchive (const FileSpec &file, const ArchSpec &arch, const TimeValue &time) 162 { 163 Mutex::Locker locker(Archive::GetArchiveCacheMutex ()); 164 shared_ptr archive_sp; 165 Archive::Map &archive_map = Archive::GetArchiveCache (); 166 Archive::Map::iterator pos = archive_map.find (file); 167 // Don't cache a value for "archive_map.end()" below since we might 168 // delete an archive entry... 169 while (pos != archive_map.end() && pos->first == file) 170 { 171 if (pos->second->GetArchitecture().IsCompatibleMatch(arch)) 172 { 173 if (pos->second->GetModificationTime() == time) 174 { 175 return pos->second; 176 } 177 else 178 { 179 // We have a file at the same path with the same architecture 180 // whose modification time doesn't match. It doesn't make sense 181 // for us to continue to use this BSD archive since we cache only 182 // the object info which consists of file time info and also the 183 // file offset and file size of any contianed objects. Since 184 // this information is now out of date, we won't get the correct 185 // information if we go and extract the file data, so we should 186 // remove the old and outdated entry. 187 archive_map.erase (pos); 188 pos = archive_map.find (file); 189 continue; 190 } 191 } 192 ++pos; 193 } 194 return archive_sp; 195 } 196 197 ObjectContainerBSDArchive::Archive::shared_ptr 198 ObjectContainerBSDArchive::Archive::ParseAndCacheArchiveForFile 199 ( 200 const FileSpec &file, 201 const ArchSpec &arch, 202 const TimeValue &time, 203 DataExtractor &data 204 ) 205 { 206 shared_ptr archive_sp(new Archive (arch, time, data)); 207 if (archive_sp) 208 { 209 if (archive_sp->ParseObjects () > 0) 210 { 211 Mutex::Locker locker(Archive::GetArchiveCacheMutex ()); 212 Archive::GetArchiveCache().insert(std::make_pair(file, archive_sp)); 213 } 214 else 215 { 216 archive_sp.reset(); 217 } 218 } 219 return archive_sp; 220 } 221 222 ObjectContainerBSDArchive::Archive::Map & 223 ObjectContainerBSDArchive::Archive::GetArchiveCache () 224 { 225 static Archive::Map g_archive_map; 226 return g_archive_map; 227 } 228 229 Mutex & 230 ObjectContainerBSDArchive::Archive::GetArchiveCacheMutex () 231 { 232 static Mutex g_archive_map_mutex (Mutex::eMutexTypeRecursive); 233 return g_archive_map_mutex; 234 } 235 236 237 void 238 ObjectContainerBSDArchive::Initialize() 239 { 240 PluginManager::RegisterPlugin (GetPluginNameStatic(), 241 GetPluginDescriptionStatic(), 242 CreateInstance); 243 } 244 245 void 246 ObjectContainerBSDArchive::Terminate() 247 { 248 PluginManager::UnregisterPlugin (CreateInstance); 249 } 250 251 252 const char * 253 ObjectContainerBSDArchive::GetPluginNameStatic() 254 { 255 return "object-container.bsd-archive"; 256 } 257 258 const char * 259 ObjectContainerBSDArchive::GetPluginDescriptionStatic() 260 { 261 return "BSD Archive object container reader."; 262 } 263 264 265 ObjectContainer * 266 ObjectContainerBSDArchive::CreateInstance 267 ( 268 const lldb::ModuleSP &module_sp, 269 DataBufferSP& data_sp, 270 lldb::offset_t data_offset, 271 const FileSpec *file, 272 lldb::offset_t file_offset, 273 lldb::offset_t length) 274 { 275 ConstString object_name (module_sp->GetObjectName()); 276 if (object_name) 277 { 278 if (data_sp) 279 { 280 // We have data, which means this is the first 512 bytes of the file 281 // Check to see if the magic bytes match and if they do, read the entire 282 // table of contents for the archive and cache it 283 DataExtractor data; 284 data.SetData (data_sp, data_offset, length); 285 if (file && data_sp && ObjectContainerBSDArchive::MagicBytesMatch(data)) 286 { 287 Timer scoped_timer (__PRETTY_FUNCTION__, 288 "ObjectContainerBSDArchive::CreateInstance (module = %s/%s, file = %p, file_offset = 0x%8.8" PRIx64 ", file_size = 0x%8.8" PRIx64 ")", 289 module_sp->GetFileSpec().GetDirectory().AsCString(), 290 module_sp->GetFileSpec().GetFilename().AsCString(), 291 file, (uint64_t) file_offset, (uint64_t) length); 292 293 // Map the entire .a file to be sure that we don't lose any data if the file 294 // gets updated by a new build while this .a file is being used for debugging 295 DataBufferSP archive_data_sp (file->MemoryMapFileContents(file_offset, length)); 296 lldb::offset_t archive_data_offset = 0; 297 298 Archive::shared_ptr archive_sp (Archive::FindCachedArchive (*file, module_sp->GetArchitecture(), module_sp->GetModificationTime())); 299 std::auto_ptr<ObjectContainerBSDArchive> container_ap(new ObjectContainerBSDArchive (module_sp, 300 archive_data_sp, 301 archive_data_offset, 302 file, 303 file_offset, 304 length)); 305 306 if (container_ap.get()) 307 { 308 if (archive_sp) 309 { 310 // We already have this archive in our cache, use it 311 container_ap->SetArchive (archive_sp); 312 return container_ap.release(); 313 } 314 else if (container_ap->ParseHeader()) 315 return container_ap.release(); 316 } 317 } 318 } 319 else 320 { 321 // No data, just check for a cached archive 322 Archive::shared_ptr archive_sp (Archive::FindCachedArchive (*file, module_sp->GetArchitecture(), module_sp->GetModificationTime())); 323 if (archive_sp) 324 { 325 std::auto_ptr<ObjectContainerBSDArchive> container_ap(new ObjectContainerBSDArchive (module_sp, data_sp, data_offset, file, file_offset, length)); 326 327 if (container_ap.get()) 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 } 334 } 335 } 336 return NULL; 337 } 338 339 340 341 bool 342 ObjectContainerBSDArchive::MagicBytesMatch (const DataExtractor &data) 343 { 344 uint32_t offset = 0; 345 const char* armag = (const char* )data.PeekData (offset, sizeof(ar_hdr)); 346 if (armag && ::strncmp(armag, ARMAG, SARMAG) == 0) 347 { 348 armag += offsetof(struct ar_hdr, ar_fmag) + SARMAG; 349 if (strncmp(armag, ARFMAG, 2) == 0) 350 return true; 351 } 352 return false; 353 } 354 355 ObjectContainerBSDArchive::ObjectContainerBSDArchive 356 ( 357 const lldb::ModuleSP &module_sp, 358 DataBufferSP& data_sp, 359 lldb::offset_t data_offset, 360 const lldb_private::FileSpec *file, 361 lldb::offset_t file_offset, 362 lldb::offset_t size 363 ) : 364 ObjectContainer (module_sp, file, file_offset, size, data_sp, data_offset), 365 m_archive_sp () 366 { 367 } 368 void 369 ObjectContainerBSDArchive::SetArchive (Archive::shared_ptr &archive_sp) 370 { 371 m_archive_sp = archive_sp; 372 } 373 374 375 376 ObjectContainerBSDArchive::~ObjectContainerBSDArchive() 377 { 378 } 379 380 bool 381 ObjectContainerBSDArchive::ParseHeader () 382 { 383 if (m_archive_sp.get() == NULL) 384 { 385 if (m_data.GetByteSize() > 0) 386 { 387 ModuleSP module_sp (GetModule()); 388 if (module_sp) 389 { 390 m_archive_sp = Archive::ParseAndCacheArchiveForFile (m_file, 391 module_sp->GetArchitecture(), 392 module_sp->GetModificationTime(), 393 m_data); 394 } 395 // Clear the m_data that contains the entire archive 396 // data and let our m_archive_sp hold onto the data. 397 m_data.Clear(); 398 } 399 } 400 return m_archive_sp.get() != NULL; 401 } 402 403 void 404 ObjectContainerBSDArchive::Dump (Stream *s) const 405 { 406 s->Printf("%p: ", this); 407 s->Indent(); 408 const size_t num_archs = GetNumArchitectures(); 409 const size_t num_objects = GetNumObjects(); 410 s->Printf("ObjectContainerBSDArchive, num_archs = %lu, num_objects = %lu", num_archs, num_objects); 411 uint32_t i; 412 ArchSpec arch; 413 s->IndentMore(); 414 for (i=0; i<num_archs; i++) 415 { 416 s->Indent(); 417 GetArchitectureAtIndex(i, arch); 418 s->Printf("arch[%u] = %s\n", i, arch.GetArchitectureName()); 419 } 420 for (i=0; i<num_objects; i++) 421 { 422 s->Indent(); 423 s->Printf("object[%u] = %s\n", i, GetObjectNameAtIndex (i)); 424 } 425 s->IndentLess(); 426 s->EOL(); 427 } 428 429 ObjectFileSP 430 ObjectContainerBSDArchive::GetObjectFile (const FileSpec *file) 431 { 432 ModuleSP module_sp (GetModule()); 433 if (module_sp) 434 { 435 if (module_sp->GetObjectName() && m_archive_sp) 436 { 437 Object *object = m_archive_sp->FindObject (module_sp->GetObjectName()); 438 if (object) 439 { 440 lldb::offset_t data_offset = m_offset + object->ar_file_offset; 441 return ObjectFile::FindPlugin (module_sp, 442 file, 443 m_offset + object->ar_file_offset, 444 object->ar_file_size, 445 m_archive_sp->GetData().GetSharedDataBuffer(), 446 data_offset); 447 } 448 } 449 } 450 return ObjectFileSP(); 451 } 452 453 454 //------------------------------------------------------------------ 455 // PluginInterface protocol 456 //------------------------------------------------------------------ 457 const char * 458 ObjectContainerBSDArchive::GetPluginName() 459 { 460 return "object-container.bsd-archive"; 461 } 462 463 const char * 464 ObjectContainerBSDArchive::GetShortPluginName() 465 { 466 return GetPluginNameStatic(); 467 } 468 469 uint32_t 470 ObjectContainerBSDArchive::GetPluginVersion() 471 { 472 return 1; 473 } 474 475