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