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 uint32_t 53 ObjectContainerBSDArchive::Object::Extract (const DataExtractor& data, uint32_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_INDEX32; 102 } 103 104 ObjectContainerBSDArchive::Archive::Archive 105 ( 106 const lldb_private::ArchSpec &arch, 107 const lldb_private::TimeValue &time 108 ) : 109 m_arch (arch), 110 m_time (time), 111 m_objects() 112 { 113 } 114 115 ObjectContainerBSDArchive::Archive::~Archive () 116 { 117 } 118 119 size_t 120 ObjectContainerBSDArchive::Archive::ParseObjects (DataExtractor &data) 121 { 122 std::string str; 123 uint32_t offset = 0; 124 str.assign((const char *)data.GetData(&offset, SARMAG), SARMAG); 125 if (str == ARMAG) 126 { 127 Object obj; 128 do 129 { 130 offset = obj.Extract (data, offset); 131 if (offset == LLDB_INVALID_INDEX32) 132 break; 133 uint32_t obj_idx = m_objects.size(); 134 m_objects.push_back(obj); 135 // Insert all of the C strings out of order for now... 136 m_object_name_to_index_map.Append (obj.ar_name.GetCString(), obj_idx); 137 offset += obj.ar_file_size; 138 obj.Clear(); 139 } while (data.ValidOffset(offset)); 140 141 // Now sort all of the object name pointers 142 m_object_name_to_index_map.Sort (); 143 } 144 return m_objects.size(); 145 } 146 147 ObjectContainerBSDArchive::Object * 148 ObjectContainerBSDArchive::Archive::FindObject (const ConstString &object_name) 149 { 150 const UniqueCStringMap<uint32_t>::Entry *match = m_object_name_to_index_map.FindFirstValueForName (object_name.GetCString()); 151 if (match) 152 return &m_objects[match->value]; 153 return NULL; 154 } 155 156 157 ObjectContainerBSDArchive::Archive::shared_ptr 158 ObjectContainerBSDArchive::Archive::FindCachedArchive (const FileSpec &file, const ArchSpec &arch, const TimeValue &time) 159 { 160 Mutex::Locker locker(Archive::GetArchiveCacheMutex ()); 161 shared_ptr archive_sp; 162 Archive::Map &archive_map = Archive::GetArchiveCache (); 163 Archive::Map::iterator pos = archive_map.find (file); 164 // Don't cache a value for "archive_map.end()" below since we might 165 // delete an archive entry... 166 while (pos != archive_map.end() && pos->first == file) 167 { 168 if (pos->second->GetArchitecture().IsCompatibleMatch(arch)) 169 { 170 if (pos->second->GetModificationTime() == time) 171 { 172 return pos->second; 173 } 174 else 175 { 176 // We have a file at the same path with the same architecture 177 // whose modification time doesn't match. It doesn't make sense 178 // for us to continue to use this BSD archive since we cache only 179 // the object info which consists of file time info and also the 180 // file offset and file size of any contianed objects. Since 181 // this information is now out of date, we won't get the correct 182 // information if we go and extract the file data, so we should 183 // remove the old and outdated entry. 184 archive_map.erase (pos); 185 pos = archive_map.find (file); 186 continue; 187 } 188 } 189 ++pos; 190 } 191 return archive_sp; 192 } 193 194 ObjectContainerBSDArchive::Archive::shared_ptr 195 ObjectContainerBSDArchive::Archive::ParseAndCacheArchiveForFile 196 ( 197 const FileSpec &file, 198 const ArchSpec &arch, 199 const TimeValue &time, 200 DataExtractor &data 201 ) 202 { 203 shared_ptr archive_sp(new Archive (arch, time)); 204 if (archive_sp) 205 { 206 if (archive_sp->ParseObjects (data) > 0) 207 { 208 Mutex::Locker locker(Archive::GetArchiveCacheMutex ()); 209 Archive::GetArchiveCache().insert(std::make_pair(file, archive_sp)); 210 } 211 else 212 { 213 archive_sp.reset(); 214 } 215 } 216 return archive_sp; 217 } 218 219 ObjectContainerBSDArchive::Archive::Map & 220 ObjectContainerBSDArchive::Archive::GetArchiveCache () 221 { 222 static Archive::Map g_archive_map; 223 return g_archive_map; 224 } 225 226 Mutex & 227 ObjectContainerBSDArchive::Archive::GetArchiveCacheMutex () 228 { 229 static Mutex g_archive_map_mutex (Mutex::eMutexTypeRecursive); 230 return g_archive_map_mutex; 231 } 232 233 234 void 235 ObjectContainerBSDArchive::Initialize() 236 { 237 PluginManager::RegisterPlugin (GetPluginNameStatic(), 238 GetPluginDescriptionStatic(), 239 CreateInstance); 240 } 241 242 void 243 ObjectContainerBSDArchive::Terminate() 244 { 245 PluginManager::UnregisterPlugin (CreateInstance); 246 } 247 248 249 const char * 250 ObjectContainerBSDArchive::GetPluginNameStatic() 251 { 252 return "object-container.bsd-archive"; 253 } 254 255 const char * 256 ObjectContainerBSDArchive::GetPluginDescriptionStatic() 257 { 258 return "BSD Archive object container reader."; 259 } 260 261 262 ObjectContainer * 263 ObjectContainerBSDArchive::CreateInstance 264 ( 265 const lldb::ModuleSP &module_sp, 266 DataBufferSP& data_sp, 267 const FileSpec *file, 268 addr_t offset, 269 addr_t length) 270 { 271 DataExtractor data; 272 data.SetData (data_sp, offset, length); 273 if (file && data_sp && ObjectContainerBSDArchive::MagicBytesMatch(data)) 274 { 275 Timer scoped_timer (__PRETTY_FUNCTION__, 276 "ObjectContainerBSDArchive::CreateInstance (module = %s/%s, file = %p, file_offset = 0x%8.8" PRIx64 ", file_size = 0x%8.8" PRIx64 ")", 277 module_sp->GetFileSpec().GetDirectory().AsCString(), 278 module_sp->GetFileSpec().GetFilename().AsCString(), 279 file, (uint64_t) offset, (uint64_t) length); 280 281 Archive::shared_ptr archive_sp (Archive::FindCachedArchive (*file, module_sp->GetArchitecture(), module_sp->GetModificationTime())); 282 283 std::auto_ptr<ObjectContainerBSDArchive> container_ap(new ObjectContainerBSDArchive (module_sp, data_sp, file, offset, length)); 284 285 if (container_ap.get()) 286 { 287 if (archive_sp) 288 { 289 // We already have this archive in our cache, use it 290 container_ap->SetArchive (archive_sp); 291 return container_ap.release(); 292 } 293 else if (container_ap->ParseHeader()) 294 return container_ap.release(); 295 } 296 } 297 return NULL; 298 } 299 300 301 302 bool 303 ObjectContainerBSDArchive::MagicBytesMatch (const DataExtractor &data) 304 { 305 uint32_t offset = 0; 306 const char* armag = (const char* )data.PeekData (offset, sizeof(ar_hdr)); 307 if (armag && ::strncmp(armag, ARMAG, SARMAG) == 0) 308 { 309 armag += offsetof(struct ar_hdr, ar_fmag) + SARMAG; 310 if (strncmp(armag, ARFMAG, 2) == 0) 311 return true; 312 } 313 return false; 314 } 315 316 ObjectContainerBSDArchive::ObjectContainerBSDArchive 317 ( 318 const lldb::ModuleSP &module_sp, 319 DataBufferSP& dataSP, 320 const lldb_private::FileSpec *file, 321 lldb::addr_t offset, 322 lldb::addr_t size 323 ) : 324 ObjectContainer (module_sp, file, offset, size, dataSP), 325 m_archive_sp () 326 { 327 } 328 void 329 ObjectContainerBSDArchive::SetArchive (Archive::shared_ptr &archive_sp) 330 { 331 m_archive_sp = archive_sp; 332 } 333 334 335 336 ObjectContainerBSDArchive::~ObjectContainerBSDArchive() 337 { 338 } 339 340 bool 341 ObjectContainerBSDArchive::ParseHeader () 342 { 343 if (m_archive_sp.get() == NULL) 344 { 345 if (m_data.GetByteSize() > 0) 346 { 347 ModuleSP module_sp (GetModule()); 348 if (module_sp) 349 { 350 m_archive_sp = Archive::ParseAndCacheArchiveForFile (m_file, 351 module_sp->GetArchitecture(), 352 module_sp->GetModificationTime(), 353 m_data); 354 } 355 } 356 } 357 return m_archive_sp.get() != NULL; 358 } 359 360 void 361 ObjectContainerBSDArchive::Dump (Stream *s) const 362 { 363 s->Printf("%p: ", this); 364 s->Indent(); 365 const size_t num_archs = GetNumArchitectures(); 366 const size_t num_objects = GetNumObjects(); 367 s->Printf("ObjectContainerBSDArchive, num_archs = %lu, num_objects = %lu", num_archs, num_objects); 368 uint32_t i; 369 ArchSpec arch; 370 s->IndentMore(); 371 for (i=0; i<num_archs; i++) 372 { 373 s->Indent(); 374 GetArchitectureAtIndex(i, arch); 375 s->Printf("arch[%u] = %s\n", i, arch.GetArchitectureName()); 376 } 377 for (i=0; i<num_objects; i++) 378 { 379 s->Indent(); 380 s->Printf("object[%u] = %s\n", i, GetObjectNameAtIndex (i)); 381 } 382 s->IndentLess(); 383 s->EOL(); 384 } 385 386 ObjectFileSP 387 ObjectContainerBSDArchive::GetObjectFile (const FileSpec *file) 388 { 389 ModuleSP module_sp (GetModule()); 390 if (module_sp) 391 { 392 if (module_sp->GetObjectName() && m_archive_sp) 393 { 394 Object *object = m_archive_sp->FindObject (module_sp->GetObjectName()); 395 if (object) 396 return ObjectFile::FindPlugin (module_sp, 397 file, 398 m_offset + object->ar_file_offset, 399 object->ar_file_size, 400 m_data.GetSharedDataBuffer()); 401 } 402 } 403 return ObjectFileSP(); 404 } 405 406 407 //------------------------------------------------------------------ 408 // PluginInterface protocol 409 //------------------------------------------------------------------ 410 const char * 411 ObjectContainerBSDArchive::GetPluginName() 412 { 413 return "object-container.bsd-archive"; 414 } 415 416 const char * 417 ObjectContainerBSDArchive::GetShortPluginName() 418 { 419 return GetPluginNameStatic(); 420 } 421 422 uint32_t 423 ObjectContainerBSDArchive::GetPluginVersion() 424 { 425 return 1; 426 } 427 428