1 //===-- ObjectContainerBSDArchive.cpp -------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "ObjectContainerBSDArchive.h"
11 #if defined(_WIN32) || defined(__ANDROID__)
12 // Defines from ar, missing on Windows
16 typedef struct ar_hdr
{
19 char ar_uid
[6], ar_gid
[6];
28 #include "lldb/Core/Module.h"
29 #include "lldb/Core/ModuleSpec.h"
30 #include "lldb/Core/PluginManager.h"
31 #include "lldb/Host/FileSystem.h"
32 #include "lldb/Symbol/ObjectFile.h"
33 #include "lldb/Utility/ArchSpec.h"
34 #include "lldb/Utility/LLDBLog.h"
35 #include "lldb/Utility/Stream.h"
36 #include "lldb/Utility/Timer.h"
38 #include "llvm/Object/Archive.h"
39 #include "llvm/Support/MemoryBuffer.h"
42 using namespace lldb_private
;
44 using namespace llvm::object
;
46 LLDB_PLUGIN_DEFINE(ObjectContainerBSDArchive
)
48 ObjectContainerBSDArchive::Object::Object() : ar_name() {}
50 void ObjectContainerBSDArchive::Object::Clear() {
52 modification_time
= 0;
58 void ObjectContainerBSDArchive::Object::Dump() const {
59 printf("name = \"%s\"\n", ar_name
.GetCString());
60 printf("mtime = 0x%8.8" PRIx32
"\n", modification_time
);
61 printf("size = 0x%8.8" PRIx32
" (%" PRIu32
")\n", size
, size
);
62 printf("file_offset = 0x%16.16" PRIx64
" (%" PRIu64
")\n", file_offset
,
64 printf("file_size = 0x%16.16" PRIx64
" (%" PRIu64
")\n\n", file_size
,
68 ObjectContainerBSDArchive::Archive::Archive(const lldb_private::ArchSpec
&arch
,
69 const llvm::sys::TimePoint
<> &time
,
70 lldb::offset_t file_offset
,
71 lldb_private::DataExtractor
&data
,
72 ArchiveType archive_type
)
73 : m_arch(arch
), m_modification_time(time
), m_file_offset(file_offset
),
74 m_objects(), m_data(data
), m_archive_type(archive_type
) {}
76 Log
*l
= GetLog(LLDBLog::Object
);
77 ObjectContainerBSDArchive::Archive::~Archive() = default;
79 size_t ObjectContainerBSDArchive::Archive::ParseObjects() {
80 DataExtractor
&data
= m_data
;
82 std::unique_ptr
<llvm::MemoryBuffer
> mem_buffer
=
83 llvm::MemoryBuffer::getMemBuffer(
84 llvm::StringRef((const char *)data
.GetDataStart(),
87 /*RequiresNullTerminator=*/false);
89 auto exp_ar
= llvm::object::Archive::create(mem_buffer
->getMemBufferRef());
91 LLDB_LOG_ERROR(l
, exp_ar
.takeError(), "failed to create archive: {0}");
94 auto llvm_archive
= std::move(exp_ar
.get());
96 llvm::Error iter_err
= llvm::Error::success();
98 for (const auto &child
: llvm_archive
->children(iter_err
)) {
100 auto exp_name
= child
.getName();
102 obj
.ar_name
= ConstString(exp_name
.get());
104 LLDB_LOG_ERROR(l
, exp_name
.takeError(),
105 "failed to get archive object name: {0}");
109 auto exp_mtime
= child
.getLastModified();
111 obj
.modification_time
=
112 std::chrono::duration_cast
<std::chrono::seconds
>(
113 std::chrono::time_point_cast
<std::chrono::seconds
>(
114 exp_mtime
.get()).time_since_epoch()).count();
116 LLDB_LOG_ERROR(l
, exp_mtime
.takeError(),
117 "failed to get archive object time: {0}");
121 auto exp_size
= child
.getRawSize();
123 obj
.size
= exp_size
.get();
125 LLDB_LOG_ERROR(l
, exp_size
.takeError(),
126 "failed to get archive object size: {0}");
130 obj
.file_offset
= child
.getDataOffset();
132 auto exp_file_size
= child
.getSize();
134 obj
.file_size
= exp_file_size
.get();
136 LLDB_LOG_ERROR(l
, exp_file_size
.takeError(),
137 "failed to get archive object file size: {0}");
140 m_object_name_to_index_map
.Append(obj
.ar_name
, m_objects
.size());
141 m_objects
.push_back(obj
);
144 LLDB_LOG_ERROR(l
, std::move(iter_err
),
145 "failed to iterate over archive objects: {0}");
147 // Now sort all of the object name pointers
148 m_object_name_to_index_map
.Sort();
149 return m_objects
.size();
152 ObjectContainerBSDArchive::Object
*
153 ObjectContainerBSDArchive::Archive::FindObject(
154 ConstString object_name
, const llvm::sys::TimePoint
<> &object_mod_time
) {
155 const ObjectNameToIndexMap::Entry
*match
=
156 m_object_name_to_index_map
.FindFirstValueForName(object_name
);
159 if (object_mod_time
== llvm::sys::TimePoint
<>())
160 return &m_objects
[match
->value
];
162 const uint64_t object_modification_date
= llvm::sys::toTimeT(object_mod_time
);
163 if (m_objects
[match
->value
].modification_time
== object_modification_date
)
164 return &m_objects
[match
->value
];
166 const ObjectNameToIndexMap::Entry
*next_match
=
167 m_object_name_to_index_map
.FindNextValueForName(match
);
169 if (m_objects
[next_match
->value
].modification_time
==
170 object_modification_date
)
171 return &m_objects
[next_match
->value
];
172 next_match
= m_object_name_to_index_map
.FindNextValueForName(next_match
);
178 ObjectContainerBSDArchive::Archive::shared_ptr
179 ObjectContainerBSDArchive::Archive::FindCachedArchive(
180 const FileSpec
&file
, const ArchSpec
&arch
,
181 const llvm::sys::TimePoint
<> &time
, lldb::offset_t file_offset
) {
182 std::lock_guard
<std::recursive_mutex
> guard(Archive::GetArchiveCacheMutex());
183 shared_ptr archive_sp
;
184 Archive::Map
&archive_map
= Archive::GetArchiveCache();
185 Archive::Map::iterator pos
= archive_map
.find(file
);
186 // Don't cache a value for "archive_map.end()" below since we might delete an
188 while (pos
!= archive_map
.end() && pos
->first
== file
) {
190 if (arch
.IsValid() &&
191 !pos
->second
->GetArchitecture().IsCompatibleMatch(arch
))
193 else if (file_offset
!= LLDB_INVALID_OFFSET
&&
194 pos
->second
->GetFileOffset() != file_offset
)
197 if (pos
->second
->GetModificationTime() == time
) {
200 // We have a file at the same path with the same architecture whose
201 // modification time doesn't match. It doesn't make sense for us to
202 // continue to use this BSD archive since we cache only the object info
203 // which consists of file time info and also the file offset and file
204 // size of any contained objects. Since this information is now out of
205 // date, we won't get the correct information if we go and extract the
206 // file data, so we should remove the old and outdated entry.
207 archive_map
.erase(pos
);
208 pos
= archive_map
.find(file
);
209 continue; // Continue to next iteration so we don't increment pos
218 ObjectContainerBSDArchive::Archive::shared_ptr
219 ObjectContainerBSDArchive::Archive::ParseAndCacheArchiveForFile(
220 const FileSpec
&file
, const ArchSpec
&arch
,
221 const llvm::sys::TimePoint
<> &time
, lldb::offset_t file_offset
,
222 DataExtractor
&data
, ArchiveType archive_type
) {
223 shared_ptr
archive_sp(
224 new Archive(arch
, time
, file_offset
, data
, archive_type
));
226 const size_t num_objects
= archive_sp
->ParseObjects();
227 if (num_objects
> 0) {
228 std::lock_guard
<std::recursive_mutex
> guard(
229 Archive::GetArchiveCacheMutex());
230 Archive::GetArchiveCache().insert(std::make_pair(file
, archive_sp
));
238 ObjectContainerBSDArchive::Archive::Map
&
239 ObjectContainerBSDArchive::Archive::GetArchiveCache() {
240 static Archive::Map g_archive_map
;
241 return g_archive_map
;
244 std::recursive_mutex
&
245 ObjectContainerBSDArchive::Archive::GetArchiveCacheMutex() {
246 static std::recursive_mutex g_archive_map_mutex
;
247 return g_archive_map_mutex
;
250 void ObjectContainerBSDArchive::Initialize() {
251 PluginManager::RegisterPlugin(GetPluginNameStatic(),
252 GetPluginDescriptionStatic(), CreateInstance
,
253 GetModuleSpecifications
);
256 void ObjectContainerBSDArchive::Terminate() {
257 PluginManager::UnregisterPlugin(CreateInstance
);
260 ObjectContainer
*ObjectContainerBSDArchive::CreateInstance(
261 const lldb::ModuleSP
&module_sp
, DataBufferSP
&data_sp
,
262 lldb::offset_t data_offset
, const FileSpec
*file
,
263 lldb::offset_t file_offset
, lldb::offset_t length
) {
264 ConstString
object_name(module_sp
->GetObjectName());
269 // We have data, which means this is the first 512 bytes of the file Check
270 // to see if the magic bytes match and if they do, read the entire table of
271 // contents for the archive and cache it
273 data
.SetData(data_sp
, data_offset
, length
);
274 ArchiveType archive_type
= ObjectContainerBSDArchive::MagicBytesMatch(data
);
275 if (file
&& data_sp
&& archive_type
!= ArchiveType::Invalid
) {
277 "ObjectContainerBSDArchive::CreateInstance (module = %s, file = "
278 "%p, file_offset = 0x%8.8" PRIx64
", file_size = 0x%8.8" PRIx64
")",
279 module_sp
->GetFileSpec().GetPath().c_str(),
280 static_cast<const void *>(file
), static_cast<uint64_t>(file_offset
),
281 static_cast<uint64_t>(length
));
283 // Map the entire .a file to be sure that we don't lose any data if the
284 // file gets updated by a new build while this .a file is being used for
286 DataBufferSP archive_data_sp
=
287 FileSystem::Instance().CreateDataBuffer(*file
, length
, file_offset
);
288 if (!archive_data_sp
)
291 lldb::offset_t archive_data_offset
= 0;
293 Archive::shared_ptr
archive_sp(Archive::FindCachedArchive(
294 *file
, module_sp
->GetArchitecture(), module_sp
->GetModificationTime(),
296 std::unique_ptr
<ObjectContainerBSDArchive
> container_up(
297 new ObjectContainerBSDArchive(module_sp
, archive_data_sp
,
298 archive_data_offset
, file
, file_offset
,
299 length
, archive_type
));
303 // We already have this archive in our cache, use it
304 container_up
->SetArchive(archive_sp
);
305 return container_up
.release();
306 } else if (container_up
->ParseHeader())
307 return container_up
.release();
311 // No data, just check for a cached archive
312 Archive::shared_ptr
archive_sp(Archive::FindCachedArchive(
313 *file
, module_sp
->GetArchitecture(), module_sp
->GetModificationTime(),
316 std::unique_ptr
<ObjectContainerBSDArchive
> container_up(
317 new ObjectContainerBSDArchive(module_sp
, data_sp
, data_offset
, file
,
319 archive_sp
->GetArchiveType()));
322 // We already have this archive in our cache, use it
323 container_up
->SetArchive(archive_sp
);
324 return container_up
.release();
332 ObjectContainerBSDArchive::MagicBytesMatch(const DataExtractor
&data
) {
334 const char *armag
= (const char *)data
.PeekData(offset
,
335 sizeof(ar_hdr
) + SARMAG
);
336 if (armag
== nullptr)
337 return ArchiveType::Invalid
;
338 ArchiveType result
= ArchiveType::Invalid
;
339 if (strncmp(armag
, ArchiveMagic
, SARMAG
) == 0)
340 result
= ArchiveType::Archive
;
341 else if (strncmp(armag
, ThinArchiveMagic
, SARMAG
) == 0)
342 result
= ArchiveType::ThinArchive
;
344 return ArchiveType::Invalid
;
346 armag
+= offsetof(struct ar_hdr
, ar_fmag
) + SARMAG
;
347 if (strncmp(armag
, ARFMAG
, 2) == 0)
349 return ArchiveType::Invalid
;
352 ObjectContainerBSDArchive::ObjectContainerBSDArchive(
353 const lldb::ModuleSP
&module_sp
, DataBufferSP
&data_sp
,
354 lldb::offset_t data_offset
, const lldb_private::FileSpec
*file
,
355 lldb::offset_t file_offset
, lldb::offset_t size
, ArchiveType archive_type
)
356 : ObjectContainer(module_sp
, file
, file_offset
, size
, data_sp
, data_offset
),
358 m_archive_type
= archive_type
;
361 void ObjectContainerBSDArchive::SetArchive(Archive::shared_ptr
&archive_sp
) {
362 m_archive_sp
= archive_sp
;
365 ObjectContainerBSDArchive::~ObjectContainerBSDArchive() = default;
367 bool ObjectContainerBSDArchive::ParseHeader() {
368 if (m_archive_sp
.get() == nullptr) {
369 if (m_data
.GetByteSize() > 0) {
370 ModuleSP
module_sp(GetModule());
372 m_archive_sp
= Archive::ParseAndCacheArchiveForFile(
373 m_file
, module_sp
->GetArchitecture(),
374 module_sp
->GetModificationTime(), m_offset
, m_data
, m_archive_type
);
376 // Clear the m_data that contains the entire archive data and let our
377 // m_archive_sp hold onto the data.
381 return m_archive_sp
.get() != nullptr;
384 FileSpec
GetChildFileSpecificationsFromThin(llvm::StringRef childPath
,
385 const FileSpec
&parentFileSpec
) {
386 llvm::SmallString
<128> FullPath
;
387 if (llvm::sys::path::is_absolute(childPath
)) {
388 FullPath
= childPath
;
390 FullPath
= parentFileSpec
.GetDirectory().GetStringRef();
391 llvm::sys::path::append(FullPath
, childPath
);
393 FileSpec child
= FileSpec(FullPath
.str(), llvm::sys::path::Style::posix
);
397 ObjectFileSP
ObjectContainerBSDArchive::GetObjectFile(const FileSpec
*file
) {
398 ModuleSP
module_sp(GetModule());
400 if (module_sp
->GetObjectName() && m_archive_sp
) {
401 Object
*object
= m_archive_sp
->FindObject(
402 module_sp
->GetObjectName(), module_sp
->GetObjectModificationTime());
404 if (m_archive_type
== ArchiveType::ThinArchive
) {
405 // Set file to child object file
406 FileSpec child
= GetChildFileSpecificationsFromThin(
407 object
->ar_name
.GetStringRef(), m_file
);
408 lldb::offset_t file_offset
= 0;
409 lldb::offset_t file_size
= object
->size
;
410 std::shared_ptr
<DataBuffer
> child_data_sp
=
411 FileSystem::Instance().CreateDataBuffer(child
, file_size
,
413 if (!child_data_sp
||
414 child_data_sp
->GetByteSize() != object
->file_size
)
415 return ObjectFileSP();
416 lldb::offset_t data_offset
= 0;
417 return ObjectFile::FindPlugin(
418 module_sp
, &child
, m_offset
+ object
->file_offset
,
419 object
->file_size
, child_data_sp
, data_offset
);
421 lldb::offset_t data_offset
= object
->file_offset
;
422 return ObjectFile::FindPlugin(
423 module_sp
, file
, m_offset
+ object
->file_offset
, object
->file_size
,
424 m_archive_sp
->GetData().GetSharedDataBuffer(), data_offset
);
428 return ObjectFileSP();
431 size_t ObjectContainerBSDArchive::GetModuleSpecifications(
432 const lldb_private::FileSpec
&file
, lldb::DataBufferSP
&data_sp
,
433 lldb::offset_t data_offset
, lldb::offset_t file_offset
,
434 lldb::offset_t file_size
, lldb_private::ModuleSpecList
&specs
) {
436 // We have data, which means this is the first 512 bytes of the file Check to
437 // see if the magic bytes match and if they do, read the entire table of
438 // contents for the archive and cache it
440 data
.SetData(data_sp
, data_offset
, data_sp
->GetByteSize());
441 ArchiveType archive_type
= ObjectContainerBSDArchive::MagicBytesMatch(data
);
442 if (!file
|| !data_sp
|| archive_type
== ArchiveType::Invalid
)
445 const size_t initial_count
= specs
.GetSize();
446 llvm::sys::TimePoint
<> file_mod_time
= FileSystem::Instance().GetModificationTime(file
);
447 Archive::shared_ptr
archive_sp(
448 Archive::FindCachedArchive(file
, ArchSpec(), file_mod_time
, file_offset
));
449 bool set_archive_arch
= false;
451 set_archive_arch
= true;
453 FileSystem::Instance().CreateDataBuffer(file
, file_size
, file_offset
);
455 data
.SetData(data_sp
, 0, data_sp
->GetByteSize());
456 archive_sp
= Archive::ParseAndCacheArchiveForFile(
457 file
, ArchSpec(), file_mod_time
, file_offset
, data
, archive_type
);
462 const size_t num_objects
= archive_sp
->GetNumObjects();
463 for (size_t idx
= 0; idx
< num_objects
; ++idx
) {
464 const Object
*object
= archive_sp
->GetObjectAtIndex(idx
);
466 if (archive_sp
->GetArchiveType() == ArchiveType::ThinArchive
) {
467 if (object
->ar_name
.IsEmpty())
469 FileSpec child
= GetChildFileSpecificationsFromThin(
470 object
->ar_name
.GetStringRef(), file
);
471 if (ObjectFile::GetModuleSpecifications(child
, 0, object
->file_size
,
474 specs
.GetModuleSpecRefAtIndex(specs
.GetSize() - 1);
475 llvm::sys::TimePoint
<> object_mod_time(
476 std::chrono::seconds(object
->modification_time
));
477 spec
.GetObjectName() = object
->ar_name
;
478 spec
.SetObjectOffset(0);
479 spec
.SetObjectSize(object
->file_size
);
480 spec
.GetObjectModificationTime() = object_mod_time
;
484 const lldb::offset_t object_file_offset
=
485 file_offset
+ object
->file_offset
;
486 if (object
->file_offset
< file_size
&& file_size
> object_file_offset
) {
487 if (ObjectFile::GetModuleSpecifications(
488 file
, object_file_offset
, file_size
- object_file_offset
,
491 specs
.GetModuleSpecRefAtIndex(specs
.GetSize() - 1);
492 llvm::sys::TimePoint
<> object_mod_time(
493 std::chrono::seconds(object
->modification_time
));
494 spec
.GetObjectName() = object
->ar_name
;
495 spec
.SetObjectOffset(object_file_offset
);
496 spec
.SetObjectSize(object
->file_size
);
497 spec
.GetObjectModificationTime() = object_mod_time
;
503 const size_t end_count
= specs
.GetSize();
504 size_t num_specs_added
= end_count
- initial_count
;
505 if (set_archive_arch
&& num_specs_added
> 0) {
506 // The archive was created but we didn't have an architecture so we need to
508 for (size_t i
= initial_count
; i
< end_count
; ++i
) {
509 ModuleSpec module_spec
;
510 if (specs
.GetModuleSpecAtIndex(i
, module_spec
)) {
511 if (module_spec
.GetArchitecture().IsValid()) {
512 archive_sp
->SetArchitecture(module_spec
.GetArchitecture());
518 return num_specs_added
;