15ffd83dbSDimitry Andric //===-- ObjectContainerBSDArchive.cpp -------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "ObjectContainerBSDArchive.h"
100b57cec5SDimitry Andric 
110b57cec5SDimitry Andric #if defined(_WIN32) || defined(__ANDROID__)
120b57cec5SDimitry Andric // Defines from ar, missing on Windows
130b57cec5SDimitry Andric #define SARMAG 8
140b57cec5SDimitry Andric #define ARFMAG "`\n"
150b57cec5SDimitry Andric 
160b57cec5SDimitry Andric typedef struct ar_hdr {
170b57cec5SDimitry Andric   char ar_name[16];
180b57cec5SDimitry Andric   char ar_date[12];
190b57cec5SDimitry Andric   char ar_uid[6], ar_gid[6];
200b57cec5SDimitry Andric   char ar_mode[8];
210b57cec5SDimitry Andric   char ar_size[10];
220b57cec5SDimitry Andric   char ar_fmag[2];
230b57cec5SDimitry Andric } ar_hdr;
240b57cec5SDimitry Andric #else
250b57cec5SDimitry Andric #include <ar.h>
260b57cec5SDimitry Andric #endif
270b57cec5SDimitry Andric 
280b57cec5SDimitry Andric #include "lldb/Core/Module.h"
290b57cec5SDimitry Andric #include "lldb/Core/ModuleSpec.h"
300b57cec5SDimitry Andric #include "lldb/Core/PluginManager.h"
310b57cec5SDimitry Andric #include "lldb/Host/FileSystem.h"
320b57cec5SDimitry Andric #include "lldb/Symbol/ObjectFile.h"
330b57cec5SDimitry Andric #include "lldb/Utility/ArchSpec.h"
34*5f757f3fSDimitry Andric #include "lldb/Utility/LLDBLog.h"
350b57cec5SDimitry Andric #include "lldb/Utility/Stream.h"
360b57cec5SDimitry Andric #include "lldb/Utility/Timer.h"
370b57cec5SDimitry Andric 
38*5f757f3fSDimitry Andric #include "llvm/Object/Archive.h"
390b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
400b57cec5SDimitry Andric 
410b57cec5SDimitry Andric using namespace lldb;
420b57cec5SDimitry Andric using namespace lldb_private;
430b57cec5SDimitry Andric 
44753f127fSDimitry Andric using namespace llvm::object;
45753f127fSDimitry Andric 
LLDB_PLUGIN_DEFINE(ObjectContainerBSDArchive)465ffd83dbSDimitry Andric LLDB_PLUGIN_DEFINE(ObjectContainerBSDArchive)
475ffd83dbSDimitry Andric 
48fe6060f1SDimitry Andric ObjectContainerBSDArchive::Object::Object() : ar_name() {}
490b57cec5SDimitry Andric 
Clear()500b57cec5SDimitry Andric void ObjectContainerBSDArchive::Object::Clear() {
510b57cec5SDimitry Andric   ar_name.Clear();
520b57cec5SDimitry Andric   modification_time = 0;
530b57cec5SDimitry Andric   size = 0;
540b57cec5SDimitry Andric   file_offset = 0;
550b57cec5SDimitry Andric   file_size = 0;
560b57cec5SDimitry Andric }
570b57cec5SDimitry Andric 
Dump() const58*5f757f3fSDimitry Andric void ObjectContainerBSDArchive::Object::Dump() const {
59*5f757f3fSDimitry Andric   printf("name        = \"%s\"\n", ar_name.GetCString());
60*5f757f3fSDimitry Andric   printf("mtime       = 0x%8.8" PRIx32 "\n", modification_time);
61*5f757f3fSDimitry Andric   printf("size        = 0x%8.8" PRIx32 " (%" PRIu32 ")\n", size, size);
62*5f757f3fSDimitry Andric   printf("file_offset = 0x%16.16" PRIx64 " (%" PRIu64 ")\n", file_offset,
63*5f757f3fSDimitry Andric          file_offset);
64*5f757f3fSDimitry Andric   printf("file_size   = 0x%16.16" PRIx64 " (%" PRIu64 ")\n\n", file_size,
65*5f757f3fSDimitry Andric          file_size);
660b57cec5SDimitry Andric }
670b57cec5SDimitry Andric 
Archive(const lldb_private::ArchSpec & arch,const llvm::sys::TimePoint<> & time,lldb::offset_t file_offset,lldb_private::DataExtractor & data,ArchiveType archive_type)680b57cec5SDimitry Andric ObjectContainerBSDArchive::Archive::Archive(const lldb_private::ArchSpec &arch,
690b57cec5SDimitry Andric                                             const llvm::sys::TimePoint<> &time,
700b57cec5SDimitry Andric                                             lldb::offset_t file_offset,
71753f127fSDimitry Andric                                             lldb_private::DataExtractor &data,
72753f127fSDimitry Andric                                             ArchiveType archive_type)
730b57cec5SDimitry Andric     : m_arch(arch), m_modification_time(time), m_file_offset(file_offset),
74753f127fSDimitry Andric       m_objects(), m_data(data), m_archive_type(archive_type) {}
750b57cec5SDimitry Andric 
76*5f757f3fSDimitry Andric Log *l = GetLog(LLDBLog::Object);
77fe6060f1SDimitry Andric ObjectContainerBSDArchive::Archive::~Archive() = default;
780b57cec5SDimitry Andric 
ParseObjects()790b57cec5SDimitry Andric size_t ObjectContainerBSDArchive::Archive::ParseObjects() {
800b57cec5SDimitry Andric   DataExtractor &data = m_data;
810b57cec5SDimitry Andric 
82*5f757f3fSDimitry Andric   std::unique_ptr<llvm::MemoryBuffer> mem_buffer =
83*5f757f3fSDimitry Andric       llvm::MemoryBuffer::getMemBuffer(
84*5f757f3fSDimitry Andric             llvm::StringRef((const char *)data.GetDataStart(),
85*5f757f3fSDimitry Andric                             data.GetByteSize()),
86*5f757f3fSDimitry Andric             llvm::StringRef(),
87*5f757f3fSDimitry Andric             /*RequiresNullTerminator=*/false);
88753f127fSDimitry Andric 
89*5f757f3fSDimitry Andric   auto exp_ar = llvm::object::Archive::create(mem_buffer->getMemBufferRef());
90*5f757f3fSDimitry Andric   if (!exp_ar) {
91*5f757f3fSDimitry Andric     LLDB_LOG_ERROR(l, exp_ar.takeError(), "failed to create archive: {0}");
92*5f757f3fSDimitry Andric     return 0;
930b57cec5SDimitry Andric   }
94*5f757f3fSDimitry Andric   auto llvm_archive = std::move(exp_ar.get());
95*5f757f3fSDimitry Andric 
96*5f757f3fSDimitry Andric   llvm::Error iter_err = llvm::Error::success();
97*5f757f3fSDimitry Andric   Object obj;
98*5f757f3fSDimitry Andric   for (const auto &child: llvm_archive->children(iter_err)) {
99*5f757f3fSDimitry Andric     obj.Clear();
100*5f757f3fSDimitry Andric     auto exp_name = child.getName();
101*5f757f3fSDimitry Andric     if (exp_name) {
102*5f757f3fSDimitry Andric       obj.ar_name = ConstString(exp_name.get());
103*5f757f3fSDimitry Andric     } else {
104*5f757f3fSDimitry Andric       LLDB_LOG_ERROR(l, exp_name.takeError(),
105*5f757f3fSDimitry Andric                      "failed to get archive object name: {0}");
106*5f757f3fSDimitry Andric       continue;
107*5f757f3fSDimitry Andric     }
108*5f757f3fSDimitry Andric 
109*5f757f3fSDimitry Andric     auto exp_mtime = child.getLastModified();
110*5f757f3fSDimitry Andric     if (exp_mtime) {
111*5f757f3fSDimitry Andric       obj.modification_time =
112*5f757f3fSDimitry Andric           std::chrono::duration_cast<std::chrono::seconds>(
113*5f757f3fSDimitry Andric               std::chrono::time_point_cast<std::chrono::seconds>(
114*5f757f3fSDimitry Andric                     exp_mtime.get()).time_since_epoch()).count();
115*5f757f3fSDimitry Andric     } else {
116*5f757f3fSDimitry Andric       LLDB_LOG_ERROR(l, exp_mtime.takeError(),
117*5f757f3fSDimitry Andric                      "failed to get archive object time: {0}");
118*5f757f3fSDimitry Andric       continue;
119*5f757f3fSDimitry Andric     }
120*5f757f3fSDimitry Andric 
121*5f757f3fSDimitry Andric     auto exp_size = child.getRawSize();
122*5f757f3fSDimitry Andric     if (exp_size) {
123*5f757f3fSDimitry Andric       obj.size = exp_size.get();
124*5f757f3fSDimitry Andric     } else {
125*5f757f3fSDimitry Andric       LLDB_LOG_ERROR(l, exp_size.takeError(),
126*5f757f3fSDimitry Andric                      "failed to get archive object size: {0}");
127*5f757f3fSDimitry Andric       continue;
128*5f757f3fSDimitry Andric     }
129*5f757f3fSDimitry Andric 
130*5f757f3fSDimitry Andric     obj.file_offset = child.getDataOffset();
131*5f757f3fSDimitry Andric 
132*5f757f3fSDimitry Andric     auto exp_file_size = child.getSize();
133*5f757f3fSDimitry Andric     if (exp_file_size) {
134*5f757f3fSDimitry Andric       obj.file_size = exp_file_size.get();
135*5f757f3fSDimitry Andric     } else {
136*5f757f3fSDimitry Andric       LLDB_LOG_ERROR(l, exp_file_size.takeError(),
137*5f757f3fSDimitry Andric                      "failed to get archive object file size: {0}");
138*5f757f3fSDimitry Andric       continue;
139*5f757f3fSDimitry Andric     }
140*5f757f3fSDimitry Andric     m_object_name_to_index_map.Append(obj.ar_name, m_objects.size());
141*5f757f3fSDimitry Andric     m_objects.push_back(obj);
142*5f757f3fSDimitry Andric   }
143*5f757f3fSDimitry Andric   if (iter_err) {
144*5f757f3fSDimitry Andric     LLDB_LOG_ERROR(l, std::move(iter_err),
145*5f757f3fSDimitry Andric                    "failed to iterate over archive objects: {0}");
146*5f757f3fSDimitry Andric   }
147*5f757f3fSDimitry Andric   // Now sort all of the object name pointers
148*5f757f3fSDimitry Andric   m_object_name_to_index_map.Sort();
1490b57cec5SDimitry Andric   return m_objects.size();
1500b57cec5SDimitry Andric }
1510b57cec5SDimitry Andric 
1520b57cec5SDimitry Andric ObjectContainerBSDArchive::Object *
FindObject(ConstString object_name,const llvm::sys::TimePoint<> & object_mod_time)1530b57cec5SDimitry Andric ObjectContainerBSDArchive::Archive::FindObject(
1540b57cec5SDimitry Andric     ConstString object_name, const llvm::sys::TimePoint<> &object_mod_time) {
1550b57cec5SDimitry Andric   const ObjectNameToIndexMap::Entry *match =
1560b57cec5SDimitry Andric       m_object_name_to_index_map.FindFirstValueForName(object_name);
1570b57cec5SDimitry Andric   if (!match)
1580b57cec5SDimitry Andric     return nullptr;
1590b57cec5SDimitry Andric   if (object_mod_time == llvm::sys::TimePoint<>())
1600b57cec5SDimitry Andric     return &m_objects[match->value];
1610b57cec5SDimitry Andric 
1620b57cec5SDimitry Andric   const uint64_t object_modification_date = llvm::sys::toTimeT(object_mod_time);
1630b57cec5SDimitry Andric   if (m_objects[match->value].modification_time == object_modification_date)
1640b57cec5SDimitry Andric     return &m_objects[match->value];
1650b57cec5SDimitry Andric 
1660b57cec5SDimitry Andric   const ObjectNameToIndexMap::Entry *next_match =
1670b57cec5SDimitry Andric       m_object_name_to_index_map.FindNextValueForName(match);
1680b57cec5SDimitry Andric   while (next_match) {
1690b57cec5SDimitry Andric     if (m_objects[next_match->value].modification_time ==
1700b57cec5SDimitry Andric         object_modification_date)
1710b57cec5SDimitry Andric       return &m_objects[next_match->value];
1720b57cec5SDimitry Andric     next_match = m_object_name_to_index_map.FindNextValueForName(next_match);
1730b57cec5SDimitry Andric   }
1740b57cec5SDimitry Andric 
1750b57cec5SDimitry Andric   return nullptr;
1760b57cec5SDimitry Andric }
1770b57cec5SDimitry Andric 
1780b57cec5SDimitry Andric ObjectContainerBSDArchive::Archive::shared_ptr
FindCachedArchive(const FileSpec & file,const ArchSpec & arch,const llvm::sys::TimePoint<> & time,lldb::offset_t file_offset)1790b57cec5SDimitry Andric ObjectContainerBSDArchive::Archive::FindCachedArchive(
1800b57cec5SDimitry Andric     const FileSpec &file, const ArchSpec &arch,
1810b57cec5SDimitry Andric     const llvm::sys::TimePoint<> &time, lldb::offset_t file_offset) {
1820b57cec5SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(Archive::GetArchiveCacheMutex());
1830b57cec5SDimitry Andric   shared_ptr archive_sp;
1840b57cec5SDimitry Andric   Archive::Map &archive_map = Archive::GetArchiveCache();
1850b57cec5SDimitry Andric   Archive::Map::iterator pos = archive_map.find(file);
1860b57cec5SDimitry Andric   // Don't cache a value for "archive_map.end()" below since we might delete an
1870b57cec5SDimitry Andric   // archive entry...
1880b57cec5SDimitry Andric   while (pos != archive_map.end() && pos->first == file) {
1890b57cec5SDimitry Andric     bool match = true;
1900b57cec5SDimitry Andric     if (arch.IsValid() &&
1910b57cec5SDimitry Andric         !pos->second->GetArchitecture().IsCompatibleMatch(arch))
1920b57cec5SDimitry Andric       match = false;
1930b57cec5SDimitry Andric     else if (file_offset != LLDB_INVALID_OFFSET &&
1940b57cec5SDimitry Andric              pos->second->GetFileOffset() != file_offset)
1950b57cec5SDimitry Andric       match = false;
1960b57cec5SDimitry Andric     if (match) {
1970b57cec5SDimitry Andric       if (pos->second->GetModificationTime() == time) {
1980b57cec5SDimitry Andric         return pos->second;
1990b57cec5SDimitry Andric       } else {
2000b57cec5SDimitry Andric         // We have a file at the same path with the same architecture whose
2010b57cec5SDimitry Andric         // modification time doesn't match. It doesn't make sense for us to
2020b57cec5SDimitry Andric         // continue to use this BSD archive since we cache only the object info
2030b57cec5SDimitry Andric         // which consists of file time info and also the file offset and file
2040b57cec5SDimitry Andric         // size of any contained objects. Since this information is now out of
2050b57cec5SDimitry Andric         // date, we won't get the correct information if we go and extract the
2060b57cec5SDimitry Andric         // file data, so we should remove the old and outdated entry.
2070b57cec5SDimitry Andric         archive_map.erase(pos);
2080b57cec5SDimitry Andric         pos = archive_map.find(file);
2090b57cec5SDimitry Andric         continue; // Continue to next iteration so we don't increment pos
2100b57cec5SDimitry Andric                   // below...
2110b57cec5SDimitry Andric       }
2120b57cec5SDimitry Andric     }
2130b57cec5SDimitry Andric     ++pos;
2140b57cec5SDimitry Andric   }
2150b57cec5SDimitry Andric   return archive_sp;
2160b57cec5SDimitry Andric }
2170b57cec5SDimitry Andric 
2180b57cec5SDimitry Andric ObjectContainerBSDArchive::Archive::shared_ptr
ParseAndCacheArchiveForFile(const FileSpec & file,const ArchSpec & arch,const llvm::sys::TimePoint<> & time,lldb::offset_t file_offset,DataExtractor & data,ArchiveType archive_type)2190b57cec5SDimitry Andric ObjectContainerBSDArchive::Archive::ParseAndCacheArchiveForFile(
2200b57cec5SDimitry Andric     const FileSpec &file, const ArchSpec &arch,
2210b57cec5SDimitry Andric     const llvm::sys::TimePoint<> &time, lldb::offset_t file_offset,
222753f127fSDimitry Andric     DataExtractor &data, ArchiveType archive_type) {
223753f127fSDimitry Andric   shared_ptr archive_sp(
224753f127fSDimitry Andric       new Archive(arch, time, file_offset, data, archive_type));
2250b57cec5SDimitry Andric   if (archive_sp) {
2260b57cec5SDimitry Andric     const size_t num_objects = archive_sp->ParseObjects();
2270b57cec5SDimitry Andric     if (num_objects > 0) {
2280b57cec5SDimitry Andric       std::lock_guard<std::recursive_mutex> guard(
2290b57cec5SDimitry Andric           Archive::GetArchiveCacheMutex());
2300b57cec5SDimitry Andric       Archive::GetArchiveCache().insert(std::make_pair(file, archive_sp));
2310b57cec5SDimitry Andric     } else {
2320b57cec5SDimitry Andric       archive_sp.reset();
2330b57cec5SDimitry Andric     }
2340b57cec5SDimitry Andric   }
2350b57cec5SDimitry Andric   return archive_sp;
2360b57cec5SDimitry Andric }
2370b57cec5SDimitry Andric 
2380b57cec5SDimitry Andric ObjectContainerBSDArchive::Archive::Map &
GetArchiveCache()2390b57cec5SDimitry Andric ObjectContainerBSDArchive::Archive::GetArchiveCache() {
2400b57cec5SDimitry Andric   static Archive::Map g_archive_map;
2410b57cec5SDimitry Andric   return g_archive_map;
2420b57cec5SDimitry Andric }
2430b57cec5SDimitry Andric 
2440b57cec5SDimitry Andric std::recursive_mutex &
GetArchiveCacheMutex()2450b57cec5SDimitry Andric ObjectContainerBSDArchive::Archive::GetArchiveCacheMutex() {
2460b57cec5SDimitry Andric   static std::recursive_mutex g_archive_map_mutex;
2470b57cec5SDimitry Andric   return g_archive_map_mutex;
2480b57cec5SDimitry Andric }
2490b57cec5SDimitry Andric 
Initialize()2500b57cec5SDimitry Andric void ObjectContainerBSDArchive::Initialize() {
2510b57cec5SDimitry Andric   PluginManager::RegisterPlugin(GetPluginNameStatic(),
2520b57cec5SDimitry Andric                                 GetPluginDescriptionStatic(), CreateInstance,
2530b57cec5SDimitry Andric                                 GetModuleSpecifications);
2540b57cec5SDimitry Andric }
2550b57cec5SDimitry Andric 
Terminate()2560b57cec5SDimitry Andric void ObjectContainerBSDArchive::Terminate() {
2570b57cec5SDimitry Andric   PluginManager::UnregisterPlugin(CreateInstance);
2580b57cec5SDimitry Andric }
2590b57cec5SDimitry Andric 
CreateInstance(const lldb::ModuleSP & module_sp,DataBufferSP & data_sp,lldb::offset_t data_offset,const FileSpec * file,lldb::offset_t file_offset,lldb::offset_t length)2600b57cec5SDimitry Andric ObjectContainer *ObjectContainerBSDArchive::CreateInstance(
2610b57cec5SDimitry Andric     const lldb::ModuleSP &module_sp, DataBufferSP &data_sp,
2620b57cec5SDimitry Andric     lldb::offset_t data_offset, const FileSpec *file,
2630b57cec5SDimitry Andric     lldb::offset_t file_offset, lldb::offset_t length) {
2640b57cec5SDimitry Andric   ConstString object_name(module_sp->GetObjectName());
2650b57cec5SDimitry Andric   if (!object_name)
2660b57cec5SDimitry Andric     return nullptr;
2670b57cec5SDimitry Andric 
2680b57cec5SDimitry Andric   if (data_sp) {
2690b57cec5SDimitry Andric     // We have data, which means this is the first 512 bytes of the file Check
2700b57cec5SDimitry Andric     // to see if the magic bytes match and if they do, read the entire table of
2710b57cec5SDimitry Andric     // contents for the archive and cache it
2720b57cec5SDimitry Andric     DataExtractor data;
2730b57cec5SDimitry Andric     data.SetData(data_sp, data_offset, length);
274753f127fSDimitry Andric     ArchiveType archive_type = ObjectContainerBSDArchive::MagicBytesMatch(data);
275753f127fSDimitry Andric     if (file && data_sp && archive_type != ArchiveType::Invalid) {
276e8d8bef9SDimitry Andric       LLDB_SCOPED_TIMERF(
2770b57cec5SDimitry Andric           "ObjectContainerBSDArchive::CreateInstance (module = %s, file = "
2780b57cec5SDimitry Andric           "%p, file_offset = 0x%8.8" PRIx64 ", file_size = 0x%8.8" PRIx64 ")",
2790b57cec5SDimitry Andric           module_sp->GetFileSpec().GetPath().c_str(),
2800b57cec5SDimitry Andric           static_cast<const void *>(file), static_cast<uint64_t>(file_offset),
2810b57cec5SDimitry Andric           static_cast<uint64_t>(length));
2820b57cec5SDimitry Andric 
2830b57cec5SDimitry Andric       // Map the entire .a file to be sure that we don't lose any data if the
2840b57cec5SDimitry Andric       // file gets updated by a new build while this .a file is being used for
2850b57cec5SDimitry Andric       // debugging
2860b57cec5SDimitry Andric       DataBufferSP archive_data_sp =
2870b57cec5SDimitry Andric           FileSystem::Instance().CreateDataBuffer(*file, length, file_offset);
2880b57cec5SDimitry Andric       if (!archive_data_sp)
2890b57cec5SDimitry Andric         return nullptr;
2900b57cec5SDimitry Andric 
2910b57cec5SDimitry Andric       lldb::offset_t archive_data_offset = 0;
2920b57cec5SDimitry Andric 
2930b57cec5SDimitry Andric       Archive::shared_ptr archive_sp(Archive::FindCachedArchive(
2940b57cec5SDimitry Andric           *file, module_sp->GetArchitecture(), module_sp->GetModificationTime(),
2950b57cec5SDimitry Andric           file_offset));
2960b57cec5SDimitry Andric       std::unique_ptr<ObjectContainerBSDArchive> container_up(
2970b57cec5SDimitry Andric           new ObjectContainerBSDArchive(module_sp, archive_data_sp,
2980b57cec5SDimitry Andric                                         archive_data_offset, file, file_offset,
299753f127fSDimitry Andric                                         length, archive_type));
3000b57cec5SDimitry Andric 
3010b57cec5SDimitry Andric       if (container_up) {
3020b57cec5SDimitry Andric         if (archive_sp) {
3030b57cec5SDimitry Andric           // We already have this archive in our cache, use it
3040b57cec5SDimitry Andric           container_up->SetArchive(archive_sp);
3050b57cec5SDimitry Andric           return container_up.release();
3060b57cec5SDimitry Andric         } else if (container_up->ParseHeader())
3070b57cec5SDimitry Andric           return container_up.release();
3080b57cec5SDimitry Andric       }
3090b57cec5SDimitry Andric     }
3100b57cec5SDimitry Andric   } else {
3110b57cec5SDimitry Andric     // No data, just check for a cached archive
3120b57cec5SDimitry Andric     Archive::shared_ptr archive_sp(Archive::FindCachedArchive(
3130b57cec5SDimitry Andric         *file, module_sp->GetArchitecture(), module_sp->GetModificationTime(),
3140b57cec5SDimitry Andric         file_offset));
3150b57cec5SDimitry Andric     if (archive_sp) {
3160b57cec5SDimitry Andric       std::unique_ptr<ObjectContainerBSDArchive> container_up(
3170b57cec5SDimitry Andric           new ObjectContainerBSDArchive(module_sp, data_sp, data_offset, file,
318753f127fSDimitry Andric                                         file_offset, length,
319753f127fSDimitry Andric                                         archive_sp->GetArchiveType()));
3200b57cec5SDimitry Andric 
3210b57cec5SDimitry Andric       if (container_up) {
3220b57cec5SDimitry Andric         // We already have this archive in our cache, use it
3230b57cec5SDimitry Andric         container_up->SetArchive(archive_sp);
3240b57cec5SDimitry Andric         return container_up.release();
3250b57cec5SDimitry Andric       }
3260b57cec5SDimitry Andric     }
3270b57cec5SDimitry Andric   }
3280b57cec5SDimitry Andric   return nullptr;
3290b57cec5SDimitry Andric }
3300b57cec5SDimitry Andric 
331753f127fSDimitry Andric ArchiveType
MagicBytesMatch(const DataExtractor & data)332753f127fSDimitry Andric ObjectContainerBSDArchive::MagicBytesMatch(const DataExtractor &data) {
3330b57cec5SDimitry Andric   uint32_t offset = 0;
334*5f757f3fSDimitry Andric   const char *armag = (const char *)data.PeekData(offset,
335*5f757f3fSDimitry Andric                                                   sizeof(ar_hdr) + SARMAG);
336753f127fSDimitry Andric   if (armag == nullptr)
337753f127fSDimitry Andric     return ArchiveType::Invalid;
338*5f757f3fSDimitry Andric   ArchiveType result = ArchiveType::Invalid;
339*5f757f3fSDimitry Andric   if (strncmp(armag, ArchiveMagic, SARMAG) == 0)
340*5f757f3fSDimitry Andric       result = ArchiveType::Archive;
341*5f757f3fSDimitry Andric   else if (strncmp(armag, ThinArchiveMagic, SARMAG) == 0)
342*5f757f3fSDimitry Andric       result = ArchiveType::ThinArchive;
343*5f757f3fSDimitry Andric   else
344*5f757f3fSDimitry Andric       return ArchiveType::Invalid;
345*5f757f3fSDimitry Andric 
3460b57cec5SDimitry Andric   armag += offsetof(struct ar_hdr, ar_fmag) + SARMAG;
3470b57cec5SDimitry Andric   if (strncmp(armag, ARFMAG, 2) == 0)
348*5f757f3fSDimitry Andric       return result;
349753f127fSDimitry Andric   return ArchiveType::Invalid;
3500b57cec5SDimitry Andric }
3510b57cec5SDimitry Andric 
ObjectContainerBSDArchive(const lldb::ModuleSP & module_sp,DataBufferSP & data_sp,lldb::offset_t data_offset,const lldb_private::FileSpec * file,lldb::offset_t file_offset,lldb::offset_t size,ArchiveType archive_type)3520b57cec5SDimitry Andric ObjectContainerBSDArchive::ObjectContainerBSDArchive(
3530b57cec5SDimitry Andric     const lldb::ModuleSP &module_sp, DataBufferSP &data_sp,
3540b57cec5SDimitry Andric     lldb::offset_t data_offset, const lldb_private::FileSpec *file,
355753f127fSDimitry Andric     lldb::offset_t file_offset, lldb::offset_t size, ArchiveType archive_type)
3560b57cec5SDimitry Andric     : ObjectContainer(module_sp, file, file_offset, size, data_sp, data_offset),
357753f127fSDimitry Andric       m_archive_sp() {
358753f127fSDimitry Andric   m_archive_type = archive_type;
359753f127fSDimitry Andric }
360753f127fSDimitry Andric 
SetArchive(Archive::shared_ptr & archive_sp)3610b57cec5SDimitry Andric void ObjectContainerBSDArchive::SetArchive(Archive::shared_ptr &archive_sp) {
3620b57cec5SDimitry Andric   m_archive_sp = archive_sp;
3630b57cec5SDimitry Andric }
3640b57cec5SDimitry Andric 
365fe6060f1SDimitry Andric ObjectContainerBSDArchive::~ObjectContainerBSDArchive() = default;
3660b57cec5SDimitry Andric 
ParseHeader()3670b57cec5SDimitry Andric bool ObjectContainerBSDArchive::ParseHeader() {
3680b57cec5SDimitry Andric   if (m_archive_sp.get() == nullptr) {
3690b57cec5SDimitry Andric     if (m_data.GetByteSize() > 0) {
3700b57cec5SDimitry Andric       ModuleSP module_sp(GetModule());
3710b57cec5SDimitry Andric       if (module_sp) {
3720b57cec5SDimitry Andric         m_archive_sp = Archive::ParseAndCacheArchiveForFile(
3730b57cec5SDimitry Andric             m_file, module_sp->GetArchitecture(),
374753f127fSDimitry Andric             module_sp->GetModificationTime(), m_offset, m_data, m_archive_type);
3750b57cec5SDimitry Andric       }
3760b57cec5SDimitry Andric       // Clear the m_data that contains the entire archive data and let our
3770b57cec5SDimitry Andric       // m_archive_sp hold onto the data.
3780b57cec5SDimitry Andric       m_data.Clear();
3790b57cec5SDimitry Andric     }
3800b57cec5SDimitry Andric   }
3810b57cec5SDimitry Andric   return m_archive_sp.get() != nullptr;
3820b57cec5SDimitry Andric }
3830b57cec5SDimitry Andric 
GetChildFileSpecificationsFromThin(llvm::StringRef childPath,const FileSpec & parentFileSpec)384753f127fSDimitry Andric FileSpec GetChildFileSpecificationsFromThin(llvm::StringRef childPath,
385753f127fSDimitry Andric                                             const FileSpec &parentFileSpec) {
386753f127fSDimitry Andric   llvm::SmallString<128> FullPath;
387753f127fSDimitry Andric   if (llvm::sys::path::is_absolute(childPath)) {
388753f127fSDimitry Andric     FullPath = childPath;
389753f127fSDimitry Andric   } else {
390753f127fSDimitry Andric     FullPath = parentFileSpec.GetDirectory().GetStringRef();
391753f127fSDimitry Andric     llvm::sys::path::append(FullPath, childPath);
392753f127fSDimitry Andric   }
393753f127fSDimitry Andric   FileSpec child = FileSpec(FullPath.str(), llvm::sys::path::Style::posix);
394753f127fSDimitry Andric   return child;
395753f127fSDimitry Andric }
396753f127fSDimitry Andric 
GetObjectFile(const FileSpec * file)3970b57cec5SDimitry Andric ObjectFileSP ObjectContainerBSDArchive::GetObjectFile(const FileSpec *file) {
3980b57cec5SDimitry Andric   ModuleSP module_sp(GetModule());
3990b57cec5SDimitry Andric   if (module_sp) {
4000b57cec5SDimitry Andric     if (module_sp->GetObjectName() && m_archive_sp) {
4010b57cec5SDimitry Andric       Object *object = m_archive_sp->FindObject(
4020b57cec5SDimitry Andric           module_sp->GetObjectName(), module_sp->GetObjectModificationTime());
4030b57cec5SDimitry Andric       if (object) {
404753f127fSDimitry Andric         if (m_archive_type == ArchiveType::ThinArchive) {
405753f127fSDimitry Andric           // Set file to child object file
406753f127fSDimitry Andric           FileSpec child = GetChildFileSpecificationsFromThin(
407753f127fSDimitry Andric               object->ar_name.GetStringRef(), m_file);
408753f127fSDimitry Andric           lldb::offset_t file_offset = 0;
409753f127fSDimitry Andric           lldb::offset_t file_size = object->size;
410753f127fSDimitry Andric           std::shared_ptr<DataBuffer> child_data_sp =
411753f127fSDimitry Andric               FileSystem::Instance().CreateDataBuffer(child, file_size,
412753f127fSDimitry Andric                                                       file_offset);
413*5f757f3fSDimitry Andric           if (!child_data_sp ||
414*5f757f3fSDimitry Andric               child_data_sp->GetByteSize() != object->file_size)
415753f127fSDimitry Andric             return ObjectFileSP();
416753f127fSDimitry Andric           lldb::offset_t data_offset = 0;
417753f127fSDimitry Andric           return ObjectFile::FindPlugin(
418753f127fSDimitry Andric               module_sp, &child, m_offset + object->file_offset,
419753f127fSDimitry Andric               object->file_size, child_data_sp, data_offset);
420753f127fSDimitry Andric         }
4210b57cec5SDimitry Andric         lldb::offset_t data_offset = object->file_offset;
4220b57cec5SDimitry Andric         return ObjectFile::FindPlugin(
4230b57cec5SDimitry Andric             module_sp, file, m_offset + object->file_offset, object->file_size,
4240b57cec5SDimitry Andric             m_archive_sp->GetData().GetSharedDataBuffer(), data_offset);
4250b57cec5SDimitry Andric       }
4260b57cec5SDimitry Andric     }
4270b57cec5SDimitry Andric   }
4280b57cec5SDimitry Andric   return ObjectFileSP();
4290b57cec5SDimitry Andric }
4300b57cec5SDimitry Andric 
GetModuleSpecifications(const lldb_private::FileSpec & file,lldb::DataBufferSP & data_sp,lldb::offset_t data_offset,lldb::offset_t file_offset,lldb::offset_t file_size,lldb_private::ModuleSpecList & specs)4310b57cec5SDimitry Andric size_t ObjectContainerBSDArchive::GetModuleSpecifications(
4320b57cec5SDimitry Andric     const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp,
4330b57cec5SDimitry Andric     lldb::offset_t data_offset, lldb::offset_t file_offset,
4340b57cec5SDimitry Andric     lldb::offset_t file_size, lldb_private::ModuleSpecList &specs) {
4350b57cec5SDimitry Andric 
4360b57cec5SDimitry Andric   // We have data, which means this is the first 512 bytes of the file Check to
4370b57cec5SDimitry Andric   // see if the magic bytes match and if they do, read the entire table of
4380b57cec5SDimitry Andric   // contents for the archive and cache it
4390b57cec5SDimitry Andric   DataExtractor data;
4400b57cec5SDimitry Andric   data.SetData(data_sp, data_offset, data_sp->GetByteSize());
441753f127fSDimitry Andric   ArchiveType archive_type = ObjectContainerBSDArchive::MagicBytesMatch(data);
442753f127fSDimitry Andric   if (!file || !data_sp || archive_type == ArchiveType::Invalid)
4430b57cec5SDimitry Andric     return 0;
4440b57cec5SDimitry Andric 
4450b57cec5SDimitry Andric   const size_t initial_count = specs.GetSize();
4460b57cec5SDimitry Andric   llvm::sys::TimePoint<> file_mod_time = FileSystem::Instance().GetModificationTime(file);
4470b57cec5SDimitry Andric   Archive::shared_ptr archive_sp(
4480b57cec5SDimitry Andric       Archive::FindCachedArchive(file, ArchSpec(), file_mod_time, file_offset));
4490b57cec5SDimitry Andric   bool set_archive_arch = false;
4500b57cec5SDimitry Andric   if (!archive_sp) {
4510b57cec5SDimitry Andric     set_archive_arch = true;
4520b57cec5SDimitry Andric     data_sp =
4530b57cec5SDimitry Andric         FileSystem::Instance().CreateDataBuffer(file, file_size, file_offset);
4540b57cec5SDimitry Andric     if (data_sp) {
4550b57cec5SDimitry Andric       data.SetData(data_sp, 0, data_sp->GetByteSize());
4560b57cec5SDimitry Andric       archive_sp = Archive::ParseAndCacheArchiveForFile(
457753f127fSDimitry Andric           file, ArchSpec(), file_mod_time, file_offset, data, archive_type);
4580b57cec5SDimitry Andric     }
4590b57cec5SDimitry Andric   }
4600b57cec5SDimitry Andric 
4610b57cec5SDimitry Andric   if (archive_sp) {
4620b57cec5SDimitry Andric     const size_t num_objects = archive_sp->GetNumObjects();
4630b57cec5SDimitry Andric     for (size_t idx = 0; idx < num_objects; ++idx) {
4640b57cec5SDimitry Andric       const Object *object = archive_sp->GetObjectAtIndex(idx);
4650b57cec5SDimitry Andric       if (object) {
466753f127fSDimitry Andric         if (archive_sp->GetArchiveType() == ArchiveType::ThinArchive) {
467753f127fSDimitry Andric           if (object->ar_name.IsEmpty())
468753f127fSDimitry Andric             continue;
469753f127fSDimitry Andric           FileSpec child = GetChildFileSpecificationsFromThin(
470753f127fSDimitry Andric               object->ar_name.GetStringRef(), file);
471753f127fSDimitry Andric           if (ObjectFile::GetModuleSpecifications(child, 0, object->file_size,
472753f127fSDimitry Andric                                                   specs)) {
473753f127fSDimitry Andric             ModuleSpec &spec =
474753f127fSDimitry Andric                 specs.GetModuleSpecRefAtIndex(specs.GetSize() - 1);
475753f127fSDimitry Andric             llvm::sys::TimePoint<> object_mod_time(
476753f127fSDimitry Andric                 std::chrono::seconds(object->modification_time));
477753f127fSDimitry Andric             spec.GetObjectName() = object->ar_name;
478753f127fSDimitry Andric             spec.SetObjectOffset(0);
479753f127fSDimitry Andric             spec.SetObjectSize(object->file_size);
480753f127fSDimitry Andric             spec.GetObjectModificationTime() = object_mod_time;
481753f127fSDimitry Andric           }
482753f127fSDimitry Andric           continue;
483753f127fSDimitry Andric         }
4840b57cec5SDimitry Andric         const lldb::offset_t object_file_offset =
4850b57cec5SDimitry Andric             file_offset + object->file_offset;
4860b57cec5SDimitry Andric         if (object->file_offset < file_size && file_size > object_file_offset) {
4870b57cec5SDimitry Andric           if (ObjectFile::GetModuleSpecifications(
4880b57cec5SDimitry Andric                   file, object_file_offset, file_size - object_file_offset,
4890b57cec5SDimitry Andric                   specs)) {
4900b57cec5SDimitry Andric             ModuleSpec &spec =
4910b57cec5SDimitry Andric                 specs.GetModuleSpecRefAtIndex(specs.GetSize() - 1);
4920b57cec5SDimitry Andric             llvm::sys::TimePoint<> object_mod_time(
4930b57cec5SDimitry Andric                 std::chrono::seconds(object->modification_time));
4940b57cec5SDimitry Andric             spec.GetObjectName() = object->ar_name;
4950b57cec5SDimitry Andric             spec.SetObjectOffset(object_file_offset);
496*5f757f3fSDimitry Andric             spec.SetObjectSize(object->file_size);
4970b57cec5SDimitry Andric             spec.GetObjectModificationTime() = object_mod_time;
4980b57cec5SDimitry Andric           }
4990b57cec5SDimitry Andric         }
5000b57cec5SDimitry Andric       }
5010b57cec5SDimitry Andric     }
5020b57cec5SDimitry Andric   }
5030b57cec5SDimitry Andric   const size_t end_count = specs.GetSize();
5040b57cec5SDimitry Andric   size_t num_specs_added = end_count - initial_count;
5050b57cec5SDimitry Andric   if (set_archive_arch && num_specs_added > 0) {
5060b57cec5SDimitry Andric     // The archive was created but we didn't have an architecture so we need to
5070b57cec5SDimitry Andric     // set it
5080b57cec5SDimitry Andric     for (size_t i = initial_count; i < end_count; ++i) {
5090b57cec5SDimitry Andric       ModuleSpec module_spec;
5100b57cec5SDimitry Andric       if (specs.GetModuleSpecAtIndex(i, module_spec)) {
5110b57cec5SDimitry Andric         if (module_spec.GetArchitecture().IsValid()) {
5120b57cec5SDimitry Andric           archive_sp->SetArchitecture(module_spec.GetArchitecture());
5130b57cec5SDimitry Andric           break;
5140b57cec5SDimitry Andric         }
5150b57cec5SDimitry Andric       }
5160b57cec5SDimitry Andric     }
5170b57cec5SDimitry Andric   }
5180b57cec5SDimitry Andric   return num_specs_added;
5190b57cec5SDimitry Andric }
520