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