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 FileSpec GetChildFileSpecificationsFromThin(llvm::StringRef childPath,
515                                             const FileSpec &parentFileSpec) {
516   llvm::SmallString<128> FullPath;
517   if (llvm::sys::path::is_absolute(childPath)) {
518     FullPath = childPath;
519   } else {
520     FullPath = parentFileSpec.GetDirectory().GetStringRef();
521     llvm::sys::path::append(FullPath, childPath);
522   }
523   FileSpec child = FileSpec(FullPath.str(), llvm::sys::path::Style::posix);
524   return child;
525 }
526 
527 ObjectFileSP ObjectContainerBSDArchive::GetObjectFile(const FileSpec *file) {
528   ModuleSP module_sp(GetModule());
529   if (module_sp) {
530     if (module_sp->GetObjectName() && m_archive_sp) {
531       Object *object = m_archive_sp->FindObject(
532           module_sp->GetObjectName(), module_sp->GetObjectModificationTime());
533       if (object) {
534         if (m_archive_type == ArchiveType::ThinArchive) {
535           // Set file to child object file
536           FileSpec child = GetChildFileSpecificationsFromThin(
537               object->ar_name.GetStringRef(), m_file);
538           lldb::offset_t file_offset = 0;
539           lldb::offset_t file_size = object->size;
540           std::shared_ptr<DataBuffer> child_data_sp =
541               FileSystem::Instance().CreateDataBuffer(child, file_size,
542                                                       file_offset);
543           if (child_data_sp->GetByteSize() != object->file_size)
544             return ObjectFileSP();
545           lldb::offset_t data_offset = 0;
546           return ObjectFile::FindPlugin(
547               module_sp, &child, m_offset + object->file_offset,
548               object->file_size, child_data_sp, data_offset);
549         }
550         lldb::offset_t data_offset = object->file_offset;
551         return ObjectFile::FindPlugin(
552             module_sp, file, m_offset + object->file_offset, object->file_size,
553             m_archive_sp->GetData().GetSharedDataBuffer(), data_offset);
554       }
555     }
556   }
557   return ObjectFileSP();
558 }
559 
560 size_t ObjectContainerBSDArchive::GetModuleSpecifications(
561     const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp,
562     lldb::offset_t data_offset, lldb::offset_t file_offset,
563     lldb::offset_t file_size, lldb_private::ModuleSpecList &specs) {
564 
565   // We have data, which means this is the first 512 bytes of the file Check to
566   // see if the magic bytes match and if they do, read the entire table of
567   // contents for the archive and cache it
568   DataExtractor data;
569   data.SetData(data_sp, data_offset, data_sp->GetByteSize());
570   ArchiveType archive_type = ObjectContainerBSDArchive::MagicBytesMatch(data);
571   if (!file || !data_sp || archive_type == ArchiveType::Invalid)
572     return 0;
573 
574   const size_t initial_count = specs.GetSize();
575   llvm::sys::TimePoint<> file_mod_time = FileSystem::Instance().GetModificationTime(file);
576   Archive::shared_ptr archive_sp(
577       Archive::FindCachedArchive(file, ArchSpec(), file_mod_time, file_offset));
578   bool set_archive_arch = false;
579   if (!archive_sp) {
580     set_archive_arch = true;
581     data_sp =
582         FileSystem::Instance().CreateDataBuffer(file, file_size, file_offset);
583     if (data_sp) {
584       data.SetData(data_sp, 0, data_sp->GetByteSize());
585       archive_sp = Archive::ParseAndCacheArchiveForFile(
586           file, ArchSpec(), file_mod_time, file_offset, data, archive_type);
587     }
588   }
589 
590   if (archive_sp) {
591     const size_t num_objects = archive_sp->GetNumObjects();
592     for (size_t idx = 0; idx < num_objects; ++idx) {
593       const Object *object = archive_sp->GetObjectAtIndex(idx);
594       if (object) {
595         if (archive_sp->GetArchiveType() == ArchiveType::ThinArchive) {
596           if (object->ar_name.IsEmpty())
597             continue;
598           FileSpec child = GetChildFileSpecificationsFromThin(
599               object->ar_name.GetStringRef(), file);
600           if (ObjectFile::GetModuleSpecifications(child, 0, object->file_size,
601                                                   specs)) {
602             ModuleSpec &spec =
603                 specs.GetModuleSpecRefAtIndex(specs.GetSize() - 1);
604             llvm::sys::TimePoint<> object_mod_time(
605                 std::chrono::seconds(object->modification_time));
606             spec.GetObjectName() = object->ar_name;
607             spec.SetObjectOffset(0);
608             spec.SetObjectSize(object->file_size);
609             spec.GetObjectModificationTime() = object_mod_time;
610           }
611           continue;
612         }
613         const lldb::offset_t object_file_offset =
614             file_offset + object->file_offset;
615         if (object->file_offset < file_size && file_size > object_file_offset) {
616           if (ObjectFile::GetModuleSpecifications(
617                   file, object_file_offset, file_size - object_file_offset,
618                   specs)) {
619             ModuleSpec &spec =
620                 specs.GetModuleSpecRefAtIndex(specs.GetSize() - 1);
621             llvm::sys::TimePoint<> object_mod_time(
622                 std::chrono::seconds(object->modification_time));
623             spec.GetObjectName() = object->ar_name;
624             spec.SetObjectOffset(object_file_offset);
625             spec.SetObjectSize(file_size - object_file_offset);
626             spec.GetObjectModificationTime() = object_mod_time;
627           }
628         }
629       }
630     }
631   }
632   const size_t end_count = specs.GetSize();
633   size_t num_specs_added = end_count - initial_count;
634   if (set_archive_arch && num_specs_added > 0) {
635     // The archive was created but we didn't have an architecture so we need to
636     // set it
637     for (size_t i = initial_count; i < end_count; ++i) {
638       ModuleSpec module_spec;
639       if (specs.GetModuleSpecAtIndex(i, module_spec)) {
640         if (module_spec.GetArchitecture().IsValid()) {
641           archive_sp->SetArchitecture(module_spec.GetArchitecture());
642           break;
643         }
644       }
645     }
646   }
647   return num_specs_added;
648 }
649