PRESIDENT810 updated this revision to Diff 434391. PRESIDENT810 added a comment. Herald added a subscriber: mgorny.
Add tests by introducing llvm-ar to produce thin archives CHANGES SINCE LAST ACTION https://reviews.llvm.org/D126464/new/ https://reviews.llvm.org/D126464 Files: lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h lldb/test/API/functionalities/archives/Makefile lldb/test/API/functionalities/archives/TestBSDArchives.py lldb/test/API/functionalities/archives/c.c lldb/test/API/lit.cfg.py lldb/test/CMakeLists.txt
Index: lldb/test/CMakeLists.txt =================================================================== --- lldb/test/CMakeLists.txt +++ lldb/test/CMakeLists.txt @@ -148,6 +148,7 @@ llvm-objcopy llvm-pdbutil llvm-readobj + llvm-ar ) if(TARGET lld) Index: lldb/test/API/lit.cfg.py =================================================================== --- lldb/test/API/lit.cfg.py +++ lldb/test/API/lit.cfg.py @@ -162,6 +162,10 @@ if is_configured('llvm_libs_dir'): dotest_cmd += ['--env', 'LLVM_LIBS_DIR=' + config.llvm_libs_dir] +# This path may be needed to locate required llvm tools +if is_configured('llvm_tools_dir'): + dotest_cmd += ['--env', 'LLVM_TOOLS_DIR=' + config.llvm_tools_dir] + # Forward ASan-specific environment variables to tests, as a test may load an # ASan-ified dylib. for env_var in ('ASAN_OPTIONS', 'DYLD_INSERT_LIBRARIES'): Index: lldb/test/API/functionalities/archives/c.c =================================================================== --- /dev/null +++ lldb/test/API/functionalities/archives/c.c @@ -0,0 +1,11 @@ +static int __c_global = 3; + +int c(int arg) { + int result = arg + __c_global; + return result; +} + +int cc(int arg1) { + int result3 = arg1 - __c_global; + return result3; +} Index: lldb/test/API/functionalities/archives/TestBSDArchives.py =================================================================== --- lldb/test/API/functionalities/archives/TestBSDArchives.py +++ lldb/test/API/functionalities/archives/TestBSDArchives.py @@ -60,3 +60,10 @@ self.expect("frame variable", VARIABLES_DISPLAYED_CORRECTLY, substrs=['(int) arg = 2']) self.expect_var_path("__b_global", type="int", value="2") + + # Test loading thin archives + archive_path = self.getBuildArtifact("libbar.a") + module_specs = lldb.SBModuleSpecList.GetModuleSpecifications(archive_path) + num_specs = module_specs.GetSize() + self.assertEqual(num_specs, 1) + self.assertEqual(module_specs.GetSpecAtIndex(0).GetObjectName(), "c.o") Index: lldb/test/API/functionalities/archives/Makefile =================================================================== --- lldb/test/API/functionalities/archives/Makefile +++ lldb/test/API/functionalities/archives/Makefile @@ -1,8 +1,8 @@ -C_SOURCES := main.c a.c b.c +C_SOURCES := main.c a.c b.c c.c EXE := # Define a.out explicitly MAKE_DSYM := NO -all: a.out +all: a.out libbar.a a.out: main.o libfoo.a $(LD) $(LDFLAGS) $^ -o $@ @@ -11,4 +11,11 @@ $(AR) $(ARFLAGS) $@ $^ $(RM) $^ +# This tests whether lldb can load a thin archive +libbar.a: c.o + $(eval LLVM_AR := $(LLVM_TOOLS_DIR)/llvm-ar) + $(eval LLVM_ARFLAGS := -rcsDT) + $(LLVM_AR) $(LLVM_ARFLAGS) $@ $^ + # Note for thin archive case, we cannot remove c.o + include Makefile.rules Index: lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h =================================================================== --- lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h +++ lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h @@ -15,19 +15,24 @@ #include "lldb/Utility/ConstString.h" #include "lldb/Utility/FileSpec.h" +#include "llvm/Object/Archive.h" #include "llvm/Support/Chrono.h" +#include "llvm/Support/Path.h" #include <map> #include <memory> #include <mutex> +enum class ArchiveType { Invalid, Archive, ThinArchive }; + class ObjectContainerBSDArchive : public lldb_private::ObjectContainer { public: ObjectContainerBSDArchive(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, lldb::offset_t data_offset, const lldb_private::FileSpec *file, - lldb::offset_t offset, lldb::offset_t length); + lldb::offset_t offset, lldb::offset_t length, + ArchiveType archive_type); ~ObjectContainerBSDArchive() override; @@ -54,7 +59,7 @@ lldb::offset_t length, lldb_private::ModuleSpecList &specs); - static bool MagicBytesMatch(const lldb_private::DataExtractor &data); + static ArchiveType MagicBytesMatch(const lldb_private::DataExtractor &data); // Member Functions bool ParseHeader() override; @@ -78,6 +83,10 @@ void Clear(); + lldb::offset_t ExtractFromThin(const lldb_private::DataExtractor &data, + lldb::offset_t offset, + llvm::StringRef stringTable); + lldb::offset_t Extract(const lldb_private::DataExtractor &data, lldb::offset_t offset); /// Object name in the archive. @@ -112,7 +121,7 @@ Archive(const lldb_private::ArchSpec &arch, const llvm::sys::TimePoint<> &mod_time, lldb::offset_t file_offset, - lldb_private::DataExtractor &data); + lldb_private::DataExtractor &data, ArchiveType archive_type); ~Archive(); @@ -127,7 +136,7 @@ static Archive::shared_ptr ParseAndCacheArchiveForFile( const lldb_private::FileSpec &file, const lldb_private::ArchSpec &arch, const llvm::sys::TimePoint<> &mod_time, lldb::offset_t file_offset, - lldb_private::DataExtractor &data); + lldb_private::DataExtractor &data, ArchiveType archive_type); size_t GetNumObjects() const { return m_objects.size(); } @@ -156,6 +165,8 @@ lldb_private::DataExtractor &GetData() { return m_data; } + ArchiveType GetArchiveType() { return m_archive_type; } + protected: typedef lldb_private::UniqueCStringMap<uint32_t> ObjectNameToIndexMap; // Member Variables @@ -167,11 +178,14 @@ lldb_private::DataExtractor m_data; ///< The data for this object container ///so we don't lose data if the .a files ///gets modified + ArchiveType m_archive_type; }; void SetArchive(Archive::shared_ptr &archive_sp); Archive::shared_ptr m_archive_sp; + + ArchiveType m_archive_type; }; #endif // LLDB_SOURCE_PLUGINS_OBJECTCONTAINER_BSD_ARCHIVE_OBJECTCONTAINERBSDARCHIVE_H Index: lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp =================================================================== --- lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp +++ lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp @@ -40,6 +40,8 @@ using namespace lldb; using namespace lldb_private; +using namespace llvm::object; + LLDB_PLUGIN_DEFINE(ObjectContainerBSDArchive) ObjectContainerBSDArchive::Object::Object() : ar_name() {} @@ -55,6 +57,74 @@ file_size = 0; } +lldb::offset_t ObjectContainerBSDArchive::Object::ExtractFromThin( + const DataExtractor &data, lldb::offset_t offset, + llvm::StringRef stringTable) { + size_t ar_name_len = 0; + std::string str; + char *err; + + // File header + // + // The common format is as follows. + // + // Offset Length Name Format + // 0 16 File name ASCII right padded with spaces (no spaces + // allowed in file name) + // 16 12 File mod Decimal as cstring right padded with + // spaces + // 28 6 Owner ID Decimal as cstring right padded with + // spaces + // 34 6 Group ID Decimal as cstring right padded with + // spaces + // 40 8 File mode Octal as cstring right padded with + // spaces + // 48 10 File byte size Decimal as cstring right padded with + // spaces + // 58 2 File magic 0x60 0x0A + + // Make sure there is enough data for the file header and bail if not + if (!data.ValidOffsetForDataOfSize(offset, 60)) + return LLDB_INVALID_OFFSET; + + str.assign((const char *)data.GetData(&offset, 16), 16); + if (!(llvm::StringRef(str).startswith("//") || stringTable.empty())) { + // Strip off any trailing spaces. + const size_t last_pos = str.find_last_not_of(' '); + if (last_pos != std::string::npos) { + if (last_pos + 1 < 16) + str.erase(last_pos + 1); + } + int start = strtoul(str.c_str() + 1, &err, 10); + int end = stringTable.find('\n', start); + str.assign(stringTable.data() + start, end - start - 1); + ar_name.SetCString(str.c_str()); + } + + str.assign((const char *)data.GetData(&offset, 12), 12); + modification_time = strtoul(str.c_str(), &err, 10); + + str.assign((const char *)data.GetData(&offset, 6), 6); + uid = strtoul(str.c_str(), &err, 10); + + str.assign((const char *)data.GetData(&offset, 6), 6); + gid = strtoul(str.c_str(), &err, 10); + + str.assign((const char *)data.GetData(&offset, 8), 8); + mode = strtoul(str.c_str(), &err, 8); + + str.assign((const char *)data.GetData(&offset, 10), 10); + size = strtoul(str.c_str(), &err, 10); + + str.assign((const char *)data.GetData(&offset, 2), 2); + if (str == ARFMAG) { + file_offset = offset; + file_size = size - ar_name_len; + return offset; + } + return LLDB_INVALID_OFFSET; +} + lldb::offset_t ObjectContainerBSDArchive::Object::Extract(const DataExtractor &data, lldb::offset_t offset) { @@ -136,9 +206,10 @@ ObjectContainerBSDArchive::Archive::Archive(const lldb_private::ArchSpec &arch, const llvm::sys::TimePoint<> &time, lldb::offset_t file_offset, - lldb_private::DataExtractor &data) + lldb_private::DataExtractor &data, + ArchiveType archive_type) : m_arch(arch), m_modification_time(time), m_file_offset(file_offset), - m_objects(), m_data(data) {} + m_objects(), m_data(data), m_archive_type(archive_type) {} ObjectContainerBSDArchive::Archive::~Archive() = default; @@ -161,6 +232,48 @@ obj.Clear(); } while (data.ValidOffset(offset)); + // Now sort all of the object name pointers + m_object_name_to_index_map.Sort(); + } else if (str == ThinArchiveMagic) { + Object obj; + size_t obj_idx; + + // Retrieve symbol table + offset = obj.ExtractFromThin(data, offset, ""); + if (offset == LLDB_INVALID_OFFSET) + return m_objects.size(); + obj_idx = m_objects.size(); + m_objects.push_back(obj); + // Insert all of the C strings out of order for now... + m_object_name_to_index_map.Append(obj.ar_name, obj_idx); + offset += obj.file_size; + obj.Clear(); + + // Retrieve string table + offset = obj.ExtractFromThin(data, offset, ""); + if (offset == LLDB_INVALID_OFFSET) + return m_objects.size(); + obj_idx = m_objects.size(); + m_objects.push_back(obj); + // Insert all of the C strings out of order for now... + m_object_name_to_index_map.Append(obj.ar_name, obj_idx); + // Extract string table + llvm::StringRef strtab((const char *)data.GetData(&offset, obj.size), + obj.size); + obj.Clear(); + + // Retrieve object files + do { + offset = obj.ExtractFromThin(data, offset, strtab); + if (offset == LLDB_INVALID_OFFSET) + break; + obj_idx = m_objects.size(); + m_objects.push_back(obj); + // Insert all of the C strings out of order for now... + m_object_name_to_index_map.Append(obj.ar_name, obj_idx); + obj.Clear(); + } while (data.ValidOffset(offset)); + // Now sort all of the object name pointers m_object_name_to_index_map.Sort(); } @@ -237,8 +350,9 @@ ObjectContainerBSDArchive::Archive::ParseAndCacheArchiveForFile( const FileSpec &file, const ArchSpec &arch, const llvm::sys::TimePoint<> &time, lldb::offset_t file_offset, - DataExtractor &data) { - shared_ptr archive_sp(new Archive(arch, time, file_offset, data)); + DataExtractor &data, ArchiveType archive_type) { + shared_ptr archive_sp( + new Archive(arch, time, file_offset, data, archive_type)); if (archive_sp) { const size_t num_objects = archive_sp->ParseObjects(); if (num_objects > 0) { @@ -288,7 +402,8 @@ // contents for the archive and cache it DataExtractor data; data.SetData(data_sp, data_offset, length); - if (file && data_sp && ObjectContainerBSDArchive::MagicBytesMatch(data)) { + ArchiveType archive_type = ObjectContainerBSDArchive::MagicBytesMatch(data); + if (file && data_sp && archive_type != ArchiveType::Invalid) { LLDB_SCOPED_TIMERF( "ObjectContainerBSDArchive::CreateInstance (module = %s, file = " "%p, file_offset = 0x%8.8" PRIx64 ", file_size = 0x%8.8" PRIx64 ")", @@ -312,7 +427,7 @@ std::unique_ptr<ObjectContainerBSDArchive> container_up( new ObjectContainerBSDArchive(module_sp, archive_data_sp, archive_data_offset, file, file_offset, - length)); + length, archive_type)); if (container_up) { if (archive_sp) { @@ -331,7 +446,8 @@ if (archive_sp) { std::unique_ptr<ObjectContainerBSDArchive> container_up( new ObjectContainerBSDArchive(module_sp, data_sp, data_offset, file, - file_offset, length)); + file_offset, length, + archive_sp->GetArchiveType())); if (container_up) { // We already have this archive in our cache, use it @@ -343,23 +459,35 @@ return nullptr; } -bool ObjectContainerBSDArchive::MagicBytesMatch(const DataExtractor &data) { +ArchiveType +ObjectContainerBSDArchive::MagicBytesMatch(const DataExtractor &data) { uint32_t offset = 0; const char *armag = (const char *)data.PeekData(offset, sizeof(ar_hdr)); - if (armag && ::strncmp(armag, ARMAG, SARMAG) == 0) { + if (armag == nullptr) + return ArchiveType::Invalid; + if (::strncmp(armag, ARMAG, SARMAG) == 0) { armag += offsetof(struct ar_hdr, ar_fmag) + SARMAG; if (strncmp(armag, ARFMAG, 2) == 0) - return true; + return ArchiveType::Archive; + } else if (::strncmp(armag, ThinArchiveMagic, strlen(ThinArchiveMagic)) == + 0) { + armag += offsetof(struct ar_hdr, ar_fmag) + strlen(ThinArchiveMagic); + if (strncmp(armag, ARFMAG, 2) == 0) { + return ArchiveType::ThinArchive; + } } - return false; + return ArchiveType::Invalid; } ObjectContainerBSDArchive::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) + lldb::offset_t file_offset, lldb::offset_t size, ArchiveType archive_type) : ObjectContainer(module_sp, file, file_offset, size, data_sp, data_offset), - m_archive_sp() {} + m_archive_sp() { + m_archive_type = archive_type; +} + void ObjectContainerBSDArchive::SetArchive(Archive::shared_ptr &archive_sp) { m_archive_sp = archive_sp; } @@ -373,7 +501,7 @@ if (module_sp) { m_archive_sp = Archive::ParseAndCacheArchiveForFile( m_file, module_sp->GetArchitecture(), - module_sp->GetModificationTime(), m_offset, m_data); + module_sp->GetModificationTime(), m_offset, m_data, m_archive_type); } // Clear the m_data that contains the entire archive data and let our // m_archive_sp hold onto the data. @@ -407,6 +535,19 @@ s->EOL(); } +FileSpec GetChildFileSpecificationsFromThin(llvm::StringRef childPath, + const FileSpec &parentFileSpec) { + llvm::SmallString<128> FullPath; + if (llvm::sys::path::is_absolute(childPath)) { + FullPath = childPath; + } else { + FullPath = parentFileSpec.GetDirectory().GetStringRef(); + llvm::sys::path::append(FullPath, childPath); + } + FileSpec child = FileSpec(FullPath.str(), llvm::sys::path::Style::posix); + return child; +} + ObjectFileSP ObjectContainerBSDArchive::GetObjectFile(const FileSpec *file) { ModuleSP module_sp(GetModule()); if (module_sp) { @@ -414,6 +555,22 @@ Object *object = m_archive_sp->FindObject( module_sp->GetObjectName(), module_sp->GetObjectModificationTime()); if (object) { + if (m_archive_type == ArchiveType::ThinArchive) { + // Set file to child object file + FileSpec child = GetChildFileSpecificationsFromThin( + object->ar_name.GetStringRef(), m_file); + lldb::offset_t file_offset = 0; + lldb::offset_t file_size = object->size; + std::shared_ptr<DataBuffer> child_data_sp = + FileSystem::Instance().CreateDataBuffer(child, file_size, + file_offset); + if (child_data_sp->GetByteSize() != object->file_size) + return ObjectFileSP(); + lldb::offset_t data_offset = 0; + return ObjectFile::FindPlugin( + module_sp, &child, m_offset + object->file_offset, + object->file_size, child_data_sp, data_offset); + } lldb::offset_t data_offset = object->file_offset; return ObjectFile::FindPlugin( module_sp, file, m_offset + object->file_offset, object->file_size, @@ -434,7 +591,8 @@ // contents for the archive and cache it DataExtractor data; data.SetData(data_sp, data_offset, data_sp->GetByteSize()); - if (!file || !data_sp || !ObjectContainerBSDArchive::MagicBytesMatch(data)) + ArchiveType archive_type = ObjectContainerBSDArchive::MagicBytesMatch(data); + if (!file || !data_sp || archive_type == ArchiveType::Invalid) return 0; const size_t initial_count = specs.GetSize(); @@ -449,7 +607,7 @@ if (data_sp) { data.SetData(data_sp, 0, data_sp->GetByteSize()); archive_sp = Archive::ParseAndCacheArchiveForFile( - file, ArchSpec(), file_mod_time, file_offset, data); + file, ArchSpec(), file_mod_time, file_offset, data, archive_type); } } @@ -458,6 +616,24 @@ for (size_t idx = 0; idx < num_objects; ++idx) { const Object *object = archive_sp->GetObjectAtIndex(idx); if (object) { + if (archive_sp->GetArchiveType() == ArchiveType::ThinArchive) { + if (object->ar_name.IsEmpty()) + continue; + FileSpec child = GetChildFileSpecificationsFromThin( + object->ar_name.GetStringRef(), file); + if (ObjectFile::GetModuleSpecifications(child, 0, object->file_size, + specs)) { + ModuleSpec &spec = + specs.GetModuleSpecRefAtIndex(specs.GetSize() - 1); + llvm::sys::TimePoint<> object_mod_time( + std::chrono::seconds(object->modification_time)); + spec.GetObjectName() = object->ar_name; + spec.SetObjectOffset(0); + spec.SetObjectSize(object->file_size); + spec.GetObjectModificationTime() = object_mod_time; + } + continue; + } const lldb::offset_t object_file_offset = file_offset + object->file_offset; if (object->file_offset < file_size && file_size > object_file_offset) {
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits