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