1 //===-- ObjectContainerBSDArchive.cpp -------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "ObjectContainerBSDArchive.h"
10 
11 #if defined(_WIN32) || defined(__ANDROID__)
12 // Defines from ar, missing on Windows
13 #define ARMAG "!<arch>\n"
14 #define SARMAG 8
15 #define ARFMAG "`\n"
16 
17 typedef struct ar_hdr {
18   char ar_name[16];
19   char ar_date[12];
20   char ar_uid[6], ar_gid[6];
21   char ar_mode[8];
22   char ar_size[10];
23   char ar_fmag[2];
24 } ar_hdr;
25 #else
26 #include <ar.h>
27 #endif
28 
29 #include "lldb/Core/Module.h"
30 #include "lldb/Core/ModuleSpec.h"
31 #include "lldb/Core/PluginManager.h"
32 #include "lldb/Host/FileSystem.h"
33 #include "lldb/Symbol/ObjectFile.h"
34 #include "lldb/Utility/ArchSpec.h"
35 #include "lldb/Utility/Stream.h"
36 #include "lldb/Utility/Timer.h"
37 
38 #include "llvm/Support/MemoryBuffer.h"
39 
40 using namespace lldb;
41 using namespace lldb_private;
42 
43 using namespace llvm::object;
44 
45 LLDB_PLUGIN_DEFINE(ObjectContainerBSDArchive)
46 
47 ObjectContainerBSDArchive::Object::Object() : ar_name() {}
48 
49 void ObjectContainerBSDArchive::Object::Clear() {
50   ar_name.Clear();
51   modification_time = 0;
52   uid = 0;
53   gid = 0;
54   mode = 0;
55   size = 0;
56   file_offset = 0;
57   file_size = 0;
58 }
59 
60 lldb::offset_t ObjectContainerBSDArchive::Object::ExtractFromThin(
61     const DataExtractor &data, lldb::offset_t offset,
62     llvm::StringRef stringTable) {
63   size_t ar_name_len = 0;
64   std::string str;
65   char *err;
66 
67   // File header
68   //
69   // The common format is as follows.
70   //
71   //  Offset  Length	Name            Format
72   //  0       16      File name       ASCII right padded with spaces (no spaces
73   //  allowed in file name)
74   //  16      12      File mod        Decimal as cstring right padded with
75   //  spaces
76   //  28      6       Owner ID        Decimal as cstring right padded with
77   //  spaces
78   //  34      6       Group ID        Decimal as cstring right padded with
79   //  spaces
80   //  40      8       File mode       Octal   as cstring right padded with
81   //  spaces
82   //  48      10      File byte size  Decimal as cstring right padded with
83   //  spaces
84   //  58      2       File magic      0x60 0x0A
85 
86   // Make sure there is enough data for the file header and bail if not
87   if (!data.ValidOffsetForDataOfSize(offset, 60))
88     return LLDB_INVALID_OFFSET;
89 
90   str.assign((const char *)data.GetData(&offset, 16), 16);
91   if (!(llvm::StringRef(str).startswith("//") || stringTable.empty())) {
92     // Strip off any trailing spaces.
93     const size_t last_pos = str.find_last_not_of(' ');
94     if (last_pos != std::string::npos) {
95       if (last_pos + 1 < 16)
96         str.erase(last_pos + 1);
97     }
98     int start = strtoul(str.c_str() + 1, &err, 10);
99     int end = stringTable.find('\n', start);
100     str.assign(stringTable.data() + start, end - start - 1);
101     ar_name.SetCString(str.c_str());
102   }
103 
104   str.assign((const char *)data.GetData(&offset, 12), 12);
105   modification_time = strtoul(str.c_str(), &err, 10);
106 
107   str.assign((const char *)data.GetData(&offset, 6), 6);
108   uid = strtoul(str.c_str(), &err, 10);
109 
110   str.assign((const char *)data.GetData(&offset, 6), 6);
111   gid = strtoul(str.c_str(), &err, 10);
112 
113   str.assign((const char *)data.GetData(&offset, 8), 8);
114   mode = strtoul(str.c_str(), &err, 8);
115 
116   str.assign((const char *)data.GetData(&offset, 10), 10);
117   size = strtoul(str.c_str(), &err, 10);
118 
119   str.assign((const char *)data.GetData(&offset, 2), 2);
120   if (str == ARFMAG) {
121     file_offset = offset;
122     file_size = size - ar_name_len;
123     return offset;
124   }
125   return LLDB_INVALID_OFFSET;
126 }
127 
128 lldb::offset_t
129 ObjectContainerBSDArchive::Object::Extract(const DataExtractor &data,
130                                            lldb::offset_t offset) {
131   size_t ar_name_len = 0;
132   std::string str;
133   char *err;
134 
135   // File header
136   //
137   // The common format is as follows.
138   //
139   //  Offset  Length	Name            Format
140   //  0       16      File name       ASCII right padded with spaces (no spaces
141   //  allowed in file name)
142   //  16      12      File mod        Decimal as cstring right padded with
143   //  spaces
144   //  28      6       Owner ID        Decimal as cstring right padded with
145   //  spaces
146   //  34      6       Group ID        Decimal as cstring right padded with
147   //  spaces
148   //  40      8       File mode       Octal   as cstring right padded with
149   //  spaces
150   //  48      10      File byte size  Decimal as cstring right padded with
151   //  spaces
152   //  58      2       File magic      0x60 0x0A
153 
154   // Make sure there is enough data for the file header and bail if not
155   if (!data.ValidOffsetForDataOfSize(offset, 60))
156     return LLDB_INVALID_OFFSET;
157 
158   str.assign((const char *)data.GetData(&offset, 16), 16);
159   if (llvm::StringRef(str).startswith("#1/")) {
160     // If the name is longer than 16 bytes, or contains an embedded space then
161     // it will use this format where the length of the name is here and the
162     // name characters are after this header.
163     ar_name_len = strtoul(str.c_str() + 3, &err, 10);
164   } else {
165     // Strip off any trailing spaces.
166     const size_t last_pos = str.find_last_not_of(' ');
167     if (last_pos != std::string::npos) {
168       if (last_pos + 1 < 16)
169         str.erase(last_pos + 1);
170     }
171     ar_name.SetCString(str.c_str());
172   }
173 
174   str.assign((const char *)data.GetData(&offset, 12), 12);
175   modification_time = strtoul(str.c_str(), &err, 10);
176 
177   str.assign((const char *)data.GetData(&offset, 6), 6);
178   uid = strtoul(str.c_str(), &err, 10);
179 
180   str.assign((const char *)data.GetData(&offset, 6), 6);
181   gid = strtoul(str.c_str(), &err, 10);
182 
183   str.assign((const char *)data.GetData(&offset, 8), 8);
184   mode = strtoul(str.c_str(), &err, 8);
185 
186   str.assign((const char *)data.GetData(&offset, 10), 10);
187   size = strtoul(str.c_str(), &err, 10);
188 
189   str.assign((const char *)data.GetData(&offset, 2), 2);
190   if (str == ARFMAG) {
191     if (ar_name_len > 0) {
192       const void *ar_name_ptr = data.GetData(&offset, ar_name_len);
193       // Make sure there was enough data for the string value and bail if not
194       if (ar_name_ptr == nullptr)
195         return LLDB_INVALID_OFFSET;
196       str.assign((const char *)ar_name_ptr, ar_name_len);
197       ar_name.SetCString(str.c_str());
198     }
199     file_offset = offset;
200     file_size = size - ar_name_len;
201     return offset;
202   }
203   return LLDB_INVALID_OFFSET;
204 }
205 
206 ObjectContainerBSDArchive::Archive::Archive(const lldb_private::ArchSpec &arch,
207                                             const llvm::sys::TimePoint<> &time,
208                                             lldb::offset_t file_offset,
209                                             lldb_private::DataExtractor &data,
210                                             ArchiveType archive_type)
211     : m_arch(arch), m_modification_time(time), m_file_offset(file_offset),
212       m_objects(), m_data(data), m_archive_type(archive_type) {}
213 
214 ObjectContainerBSDArchive::Archive::~Archive() = default;
215 
216 size_t ObjectContainerBSDArchive::Archive::ParseObjects() {
217   DataExtractor &data = m_data;
218   std::string str;
219   lldb::offset_t offset = 0;
220   str.assign((const char *)data.GetData(&offset, SARMAG), SARMAG);
221   if (str == ARMAG) {
222     Object obj;
223     do {
224       offset = obj.Extract(data, offset);
225       if (offset == LLDB_INVALID_OFFSET)
226         break;
227       size_t obj_idx = m_objects.size();
228       m_objects.push_back(obj);
229       // Insert all of the C strings out of order for now...
230       m_object_name_to_index_map.Append(obj.ar_name, obj_idx);
231       offset += obj.file_size;
232       obj.Clear();
233     } while (data.ValidOffset(offset));
234 
235     // Now sort all of the object name pointers
236     m_object_name_to_index_map.Sort();
237   } else if (str == ThinArchiveMagic) {
238     Object obj;
239     size_t obj_idx;
240 
241     // Retrieve symbol table
242     offset = obj.ExtractFromThin(data, offset, "");
243     if (offset == LLDB_INVALID_OFFSET)
244       return m_objects.size();
245     obj_idx = m_objects.size();
246     m_objects.push_back(obj);
247     // Insert all of the C strings out of order for now...
248     m_object_name_to_index_map.Append(obj.ar_name, obj_idx);
249     offset += obj.file_size;
250     obj.Clear();
251 
252     // Retrieve string table
253     offset = obj.ExtractFromThin(data, offset, "");
254     if (offset == LLDB_INVALID_OFFSET)
255       return m_objects.size();
256     obj_idx = m_objects.size();
257     m_objects.push_back(obj);
258     // Insert all of the C strings out of order for now...
259     m_object_name_to_index_map.Append(obj.ar_name, obj_idx);
260     // Extract string table
261     llvm::StringRef strtab((const char *)data.GetData(&offset, obj.size),
262                            obj.size);
263     obj.Clear();
264 
265     // Retrieve object files
266     do {
267       offset = obj.ExtractFromThin(data, offset, strtab);
268       if (offset == LLDB_INVALID_OFFSET)
269         break;
270       obj_idx = m_objects.size();
271       m_objects.push_back(obj);
272       // Insert all of the C strings out of order for now...
273       m_object_name_to_index_map.Append(obj.ar_name, obj_idx);
274       obj.Clear();
275     } while (data.ValidOffset(offset));
276 
277     // Now sort all of the object name pointers
278     m_object_name_to_index_map.Sort();
279   }
280   return m_objects.size();
281 }
282 
283 ObjectContainerBSDArchive::Object *
284 ObjectContainerBSDArchive::Archive::FindObject(
285     ConstString object_name, const llvm::sys::TimePoint<> &object_mod_time) {
286   const ObjectNameToIndexMap::Entry *match =
287       m_object_name_to_index_map.FindFirstValueForName(object_name);
288   if (!match)
289     return nullptr;
290   if (object_mod_time == llvm::sys::TimePoint<>())
291     return &m_objects[match->value];
292 
293   const uint64_t object_modification_date = llvm::sys::toTimeT(object_mod_time);
294   if (m_objects[match->value].modification_time == object_modification_date)
295     return &m_objects[match->value];
296 
297   const ObjectNameToIndexMap::Entry *next_match =
298       m_object_name_to_index_map.FindNextValueForName(match);
299   while (next_match) {
300     if (m_objects[next_match->value].modification_time ==
301         object_modification_date)
302       return &m_objects[next_match->value];
303     next_match = m_object_name_to_index_map.FindNextValueForName(next_match);
304   }
305 
306   return nullptr;
307 }
308 
309 ObjectContainerBSDArchive::Archive::shared_ptr
310 ObjectContainerBSDArchive::Archive::FindCachedArchive(
311     const FileSpec &file, const ArchSpec &arch,
312     const llvm::sys::TimePoint<> &time, lldb::offset_t file_offset) {
313   std::lock_guard<std::recursive_mutex> guard(Archive::GetArchiveCacheMutex());
314   shared_ptr archive_sp;
315   Archive::Map &archive_map = Archive::GetArchiveCache();
316   Archive::Map::iterator pos = archive_map.find(file);
317   // Don't cache a value for "archive_map.end()" below since we might delete an
318   // archive entry...
319   while (pos != archive_map.end() && pos->first == file) {
320     bool match = true;
321     if (arch.IsValid() &&
322         !pos->second->GetArchitecture().IsCompatibleMatch(arch))
323       match = false;
324     else if (file_offset != LLDB_INVALID_OFFSET &&
325              pos->second->GetFileOffset() != file_offset)
326       match = false;
327     if (match) {
328       if (pos->second->GetModificationTime() == time) {
329         return pos->second;
330       } else {
331         // We have a file at the same path with the same architecture whose
332         // modification time doesn't match. It doesn't make sense for us to
333         // continue to use this BSD archive since we cache only the object info
334         // which consists of file time info and also the file offset and file
335         // size of any contained objects. Since this information is now out of
336         // date, we won't get the correct information if we go and extract the
337         // file data, so we should remove the old and outdated entry.
338         archive_map.erase(pos);
339         pos = archive_map.find(file);
340         continue; // Continue to next iteration so we don't increment pos
341                   // below...
342       }
343     }
344     ++pos;
345   }
346   return archive_sp;
347 }
348 
349 ObjectContainerBSDArchive::Archive::shared_ptr
350 ObjectContainerBSDArchive::Archive::ParseAndCacheArchiveForFile(
351     const FileSpec &file, const ArchSpec &arch,
352     const llvm::sys::TimePoint<> &time, lldb::offset_t file_offset,
353     DataExtractor &data, ArchiveType archive_type) {
354   shared_ptr archive_sp(
355       new Archive(arch, time, file_offset, data, archive_type));
356   if (archive_sp) {
357     const size_t num_objects = archive_sp->ParseObjects();
358     if (num_objects > 0) {
359       std::lock_guard<std::recursive_mutex> guard(
360           Archive::GetArchiveCacheMutex());
361       Archive::GetArchiveCache().insert(std::make_pair(file, archive_sp));
362     } else {
363       archive_sp.reset();
364     }
365   }
366   return archive_sp;
367 }
368 
369 ObjectContainerBSDArchive::Archive::Map &
370 ObjectContainerBSDArchive::Archive::GetArchiveCache() {
371   static Archive::Map g_archive_map;
372   return g_archive_map;
373 }
374 
375 std::recursive_mutex &
376 ObjectContainerBSDArchive::Archive::GetArchiveCacheMutex() {
377   static std::recursive_mutex g_archive_map_mutex;
378   return g_archive_map_mutex;
379 }
380 
381 void ObjectContainerBSDArchive::Initialize() {
382   PluginManager::RegisterPlugin(GetPluginNameStatic(),
383                                 GetPluginDescriptionStatic(), CreateInstance,
384                                 GetModuleSpecifications);
385 }
386 
387 void ObjectContainerBSDArchive::Terminate() {
388   PluginManager::UnregisterPlugin(CreateInstance);
389 }
390 
391 ObjectContainer *ObjectContainerBSDArchive::CreateInstance(
392     const lldb::ModuleSP &module_sp, DataBufferSP &data_sp,
393     lldb::offset_t data_offset, const FileSpec *file,
394     lldb::offset_t file_offset, lldb::offset_t length) {
395   ConstString object_name(module_sp->GetObjectName());
396   if (!object_name)
397     return nullptr;
398 
399   if (data_sp) {
400     // We have data, which means this is the first 512 bytes of the file Check
401     // to see if the magic bytes match and if they do, read the entire table of
402     // contents for the archive and cache it
403     DataExtractor data;
404     data.SetData(data_sp, data_offset, length);
405     ArchiveType archive_type = ObjectContainerBSDArchive::MagicBytesMatch(data);
406     if (file && data_sp && archive_type != ArchiveType::Invalid) {
407       LLDB_SCOPED_TIMERF(
408           "ObjectContainerBSDArchive::CreateInstance (module = %s, file = "
409           "%p, file_offset = 0x%8.8" PRIx64 ", file_size = 0x%8.8" PRIx64 ")",
410           module_sp->GetFileSpec().GetPath().c_str(),
411           static_cast<const void *>(file), static_cast<uint64_t>(file_offset),
412           static_cast<uint64_t>(length));
413 
414       // Map the entire .a file to be sure that we don't lose any data if the
415       // file gets updated by a new build while this .a file is being used for
416       // debugging
417       DataBufferSP archive_data_sp =
418           FileSystem::Instance().CreateDataBuffer(*file, length, file_offset);
419       if (!archive_data_sp)
420         return nullptr;
421 
422       lldb::offset_t archive_data_offset = 0;
423 
424       Archive::shared_ptr archive_sp(Archive::FindCachedArchive(
425           *file, module_sp->GetArchitecture(), module_sp->GetModificationTime(),
426           file_offset));
427       std::unique_ptr<ObjectContainerBSDArchive> container_up(
428           new ObjectContainerBSDArchive(module_sp, archive_data_sp,
429                                         archive_data_offset, file, file_offset,
430                                         length, archive_type));
431 
432       if (container_up) {
433         if (archive_sp) {
434           // We already have this archive in our cache, use it
435           container_up->SetArchive(archive_sp);
436           return container_up.release();
437         } else if (container_up->ParseHeader())
438           return container_up.release();
439       }
440     }
441   } else {
442     // No data, just check for a cached archive
443     Archive::shared_ptr archive_sp(Archive::FindCachedArchive(
444         *file, module_sp->GetArchitecture(), module_sp->GetModificationTime(),
445         file_offset));
446     if (archive_sp) {
447       std::unique_ptr<ObjectContainerBSDArchive> container_up(
448           new ObjectContainerBSDArchive(module_sp, data_sp, data_offset, file,
449                                         file_offset, length,
450                                         archive_sp->GetArchiveType()));
451 
452       if (container_up) {
453         // We already have this archive in our cache, use it
454         container_up->SetArchive(archive_sp);
455         return container_up.release();
456       }
457     }
458   }
459   return nullptr;
460 }
461 
462 ArchiveType
463 ObjectContainerBSDArchive::MagicBytesMatch(const DataExtractor &data) {
464   uint32_t offset = 0;
465   const char *armag = (const char *)data.PeekData(offset, sizeof(ar_hdr));
466   if (armag == nullptr)
467     return ArchiveType::Invalid;
468   if (::strncmp(armag, ARMAG, SARMAG) == 0) {
469     armag += offsetof(struct ar_hdr, ar_fmag) + SARMAG;
470     if (strncmp(armag, ARFMAG, 2) == 0)
471       return ArchiveType::Archive;
472   } else if (::strncmp(armag, ThinArchiveMagic, strlen(ThinArchiveMagic)) ==
473              0) {
474     armag += offsetof(struct ar_hdr, ar_fmag) + strlen(ThinArchiveMagic);
475     if (strncmp(armag, ARFMAG, 2) == 0) {
476       return ArchiveType::ThinArchive;
477     }
478   }
479   return ArchiveType::Invalid;
480 }
481 
482 ObjectContainerBSDArchive::ObjectContainerBSDArchive(
483     const lldb::ModuleSP &module_sp, DataBufferSP &data_sp,
484     lldb::offset_t data_offset, const lldb_private::FileSpec *file,
485     lldb::offset_t file_offset, lldb::offset_t size, ArchiveType archive_type)
486     : ObjectContainer(module_sp, file, file_offset, size, data_sp, data_offset),
487       m_archive_sp() {
488   m_archive_type = archive_type;
489 }
490 
491 void ObjectContainerBSDArchive::SetArchive(Archive::shared_ptr &archive_sp) {
492   m_archive_sp = archive_sp;
493 }
494 
495 ObjectContainerBSDArchive::~ObjectContainerBSDArchive() = default;
496 
497 bool ObjectContainerBSDArchive::ParseHeader() {
498   if (m_archive_sp.get() == nullptr) {
499     if (m_data.GetByteSize() > 0) {
500       ModuleSP module_sp(GetModule());
501       if (module_sp) {
502         m_archive_sp = Archive::ParseAndCacheArchiveForFile(
503             m_file, module_sp->GetArchitecture(),
504             module_sp->GetModificationTime(), m_offset, m_data, m_archive_type);
505       }
506       // Clear the m_data that contains the entire archive data and let our
507       // m_archive_sp hold onto the data.
508       m_data.Clear();
509     }
510   }
511   return m_archive_sp.get() != nullptr;
512 }
513 
514 void ObjectContainerBSDArchive::Dump(Stream *s) const {
515   s->Printf("%p: ", static_cast<const void *>(this));
516   s->Indent();
517   const size_t num_archs = GetNumArchitectures();
518   const size_t num_objects = GetNumObjects();
519   s->Printf("ObjectContainerBSDArchive, num_archs = %" PRIu64
520             ", num_objects = %" PRIu64 "",
521             (uint64_t)num_archs, (uint64_t)num_objects);
522   uint32_t i;
523   ArchSpec arch;
524   s->IndentMore();
525   for (i = 0; i < num_archs; i++) {
526     s->Indent();
527     GetArchitectureAtIndex(i, arch);
528     s->Printf("arch[%u] = %s\n", i, arch.GetArchitectureName());
529   }
530   for (i = 0; i < num_objects; i++) {
531     s->Indent();
532     s->Printf("object[%u] = %s\n", i, GetObjectNameAtIndex(i));
533   }
534   s->IndentLess();
535   s->EOL();
536 }
537 
538 FileSpec GetChildFileSpecificationsFromThin(llvm::StringRef childPath,
539                                             const FileSpec &parentFileSpec) {
540   llvm::SmallString<128> FullPath;
541   if (llvm::sys::path::is_absolute(childPath)) {
542     FullPath = childPath;
543   } else {
544     FullPath = parentFileSpec.GetDirectory().GetStringRef();
545     llvm::sys::path::append(FullPath, childPath);
546   }
547   FileSpec child = FileSpec(FullPath.str(), llvm::sys::path::Style::posix);
548   return child;
549 }
550 
551 ObjectFileSP ObjectContainerBSDArchive::GetObjectFile(const FileSpec *file) {
552   ModuleSP module_sp(GetModule());
553   if (module_sp) {
554     if (module_sp->GetObjectName() && m_archive_sp) {
555       Object *object = m_archive_sp->FindObject(
556           module_sp->GetObjectName(), module_sp->GetObjectModificationTime());
557       if (object) {
558         if (m_archive_type == ArchiveType::ThinArchive) {
559           // Set file to child object file
560           FileSpec child = GetChildFileSpecificationsFromThin(
561               object->ar_name.GetStringRef(), m_file);
562           lldb::offset_t file_offset = 0;
563           lldb::offset_t file_size = object->size;
564           std::shared_ptr<DataBuffer> child_data_sp =
565               FileSystem::Instance().CreateDataBuffer(child, file_size,
566                                                       file_offset);
567           if (child_data_sp->GetByteSize() != object->file_size)
568             return ObjectFileSP();
569           lldb::offset_t data_offset = 0;
570           return ObjectFile::FindPlugin(
571               module_sp, &child, m_offset + object->file_offset,
572               object->file_size, child_data_sp, data_offset);
573         }
574         lldb::offset_t data_offset = object->file_offset;
575         return ObjectFile::FindPlugin(
576             module_sp, file, m_offset + object->file_offset, object->file_size,
577             m_archive_sp->GetData().GetSharedDataBuffer(), data_offset);
578       }
579     }
580   }
581   return ObjectFileSP();
582 }
583 
584 size_t ObjectContainerBSDArchive::GetModuleSpecifications(
585     const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp,
586     lldb::offset_t data_offset, lldb::offset_t file_offset,
587     lldb::offset_t file_size, lldb_private::ModuleSpecList &specs) {
588 
589   // We have data, which means this is the first 512 bytes of the file Check to
590   // see if the magic bytes match and if they do, read the entire table of
591   // contents for the archive and cache it
592   DataExtractor data;
593   data.SetData(data_sp, data_offset, data_sp->GetByteSize());
594   ArchiveType archive_type = ObjectContainerBSDArchive::MagicBytesMatch(data);
595   if (!file || !data_sp || archive_type == ArchiveType::Invalid)
596     return 0;
597 
598   const size_t initial_count = specs.GetSize();
599   llvm::sys::TimePoint<> file_mod_time = FileSystem::Instance().GetModificationTime(file);
600   Archive::shared_ptr archive_sp(
601       Archive::FindCachedArchive(file, ArchSpec(), file_mod_time, file_offset));
602   bool set_archive_arch = false;
603   if (!archive_sp) {
604     set_archive_arch = true;
605     data_sp =
606         FileSystem::Instance().CreateDataBuffer(file, file_size, file_offset);
607     if (data_sp) {
608       data.SetData(data_sp, 0, data_sp->GetByteSize());
609       archive_sp = Archive::ParseAndCacheArchiveForFile(
610           file, ArchSpec(), file_mod_time, file_offset, data, archive_type);
611     }
612   }
613 
614   if (archive_sp) {
615     const size_t num_objects = archive_sp->GetNumObjects();
616     for (size_t idx = 0; idx < num_objects; ++idx) {
617       const Object *object = archive_sp->GetObjectAtIndex(idx);
618       if (object) {
619         if (archive_sp->GetArchiveType() == ArchiveType::ThinArchive) {
620           if (object->ar_name.IsEmpty())
621             continue;
622           FileSpec child = GetChildFileSpecificationsFromThin(
623               object->ar_name.GetStringRef(), file);
624           if (ObjectFile::GetModuleSpecifications(child, 0, object->file_size,
625                                                   specs)) {
626             ModuleSpec &spec =
627                 specs.GetModuleSpecRefAtIndex(specs.GetSize() - 1);
628             llvm::sys::TimePoint<> object_mod_time(
629                 std::chrono::seconds(object->modification_time));
630             spec.GetObjectName() = object->ar_name;
631             spec.SetObjectOffset(0);
632             spec.SetObjectSize(object->file_size);
633             spec.GetObjectModificationTime() = object_mod_time;
634           }
635           continue;
636         }
637         const lldb::offset_t object_file_offset =
638             file_offset + object->file_offset;
639         if (object->file_offset < file_size && file_size > object_file_offset) {
640           if (ObjectFile::GetModuleSpecifications(
641                   file, object_file_offset, file_size - object_file_offset,
642                   specs)) {
643             ModuleSpec &spec =
644                 specs.GetModuleSpecRefAtIndex(specs.GetSize() - 1);
645             llvm::sys::TimePoint<> object_mod_time(
646                 std::chrono::seconds(object->modification_time));
647             spec.GetObjectName() = object->ar_name;
648             spec.SetObjectOffset(object_file_offset);
649             spec.SetObjectSize(file_size - object_file_offset);
650             spec.GetObjectModificationTime() = object_mod_time;
651           }
652         }
653       }
654     }
655   }
656   const size_t end_count = specs.GetSize();
657   size_t num_specs_added = end_count - initial_count;
658   if (set_archive_arch && num_specs_added > 0) {
659     // The archive was created but we didn't have an architecture so we need to
660     // set it
661     for (size_t i = initial_count; i < end_count; ++i) {
662       ModuleSpec module_spec;
663       if (specs.GetModuleSpecAtIndex(i, module_spec)) {
664         if (module_spec.GetArchitecture().IsValid()) {
665           archive_sp->SetArchitecture(module_spec.GetArchitecture());
666           break;
667         }
668       }
669     }
670   }
671   return num_specs_added;
672 }
673