diff --git a/lldb/include/lldb/Utility/UUID.h b/lldb/include/lldb/Utility/UUID.h index 5327719094c0..f2107d9b135b 100644 --- a/lldb/include/lldb/Utility/UUID.h +++ b/lldb/include/lldb/Utility/UUID.h @@ -1,114 +1,131 @@ //===-- UUID.h --------------------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLDB_UTILITY_UUID_H #define LLDB_UTILITY_UUID_H #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/Endian.h" #include #include #include namespace lldb_private { class Stream; class UUID { public: UUID() = default; + // Reference: + // https://crashpad.chromium.org/doxygen/structcrashpad_1_1CodeViewRecordPDB70.html + struct CvRecordPdb70 { + struct { + llvm::support::ulittle32_t Data1; + llvm::support::ulittle16_t Data2; + llvm::support::ulittle16_t Data3; + uint8_t Data4[8]; + } Uuid; + llvm::support::ulittle32_t Age; + // char PDBFileName[]; + }; + + /// Create a UUID from CvRecordPdb70. + static UUID fromCvRecord(CvRecordPdb70 debug_info); + /// Creates a UUID from the data pointed to by the bytes argument. No special /// significance is attached to any of the values. static UUID fromData(const void *bytes, uint32_t num_bytes) { if (bytes) return fromData({reinterpret_cast(bytes), num_bytes}); return UUID(); } /// Creates a uuid from the data pointed to by the bytes argument. No special /// significance is attached to any of the values. static UUID fromData(llvm::ArrayRef bytes) { return UUID(bytes); } /// Creates a UUID from the data pointed to by the bytes argument. Data /// consisting purely of zero bytes is treated as an invalid UUID. static UUID fromOptionalData(const void *bytes, uint32_t num_bytes) { if (bytes) return fromOptionalData( {reinterpret_cast(bytes), num_bytes}); return UUID(); } /// Creates a UUID from the data pointed to by the bytes argument. Data /// consisting purely of zero bytes is treated as an invalid UUID. static UUID fromOptionalData(llvm::ArrayRef bytes) { if (llvm::all_of(bytes, [](uint8_t b) { return b == 0; })) return UUID(); return UUID(bytes); } void Clear() { m_bytes.clear(); } void Dump(Stream *s) const; llvm::ArrayRef GetBytes() const { return m_bytes; } explicit operator bool() const { return IsValid(); } bool IsValid() const { return !m_bytes.empty(); } std::string GetAsString(llvm::StringRef separator = "-") const; bool SetFromStringRef(llvm::StringRef str); // Same as SetFromStringRef, but if the resultant UUID is all 0 bytes, set the // UUID to invalid. bool SetFromOptionalStringRef(llvm::StringRef str); /// Decode as many UUID bytes as possible from the C string \a cstr. /// /// \param[in] str /// An llvm::StringRef that points at a UUID string value (no leading /// spaces). The string must contain only hex characters and optionally /// can contain the '-' sepearators. /// /// \param[in] uuid_bytes /// A buffer of bytes that will contain a full or partially decoded UUID. /// /// \return /// The original string, with all decoded bytes removed. static llvm::StringRef DecodeUUIDBytesFromString(llvm::StringRef str, llvm::SmallVectorImpl &uuid_bytes); private: UUID(llvm::ArrayRef bytes) : m_bytes(bytes.begin(), bytes.end()) {} // GNU ld generates 20-byte build-ids. Size chosen to avoid heap allocations // for this case. llvm::SmallVector m_bytes; friend bool operator==(const UUID &LHS, const UUID &RHS) { return LHS.m_bytes == RHS.m_bytes; } friend bool operator!=(const UUID &LHS, const UUID &RHS) { return !(LHS == RHS); } friend bool operator<(const UUID &LHS, const UUID &RHS) { return LHS.m_bytes < RHS.m_bytes; } friend bool operator<=(const UUID &LHS, const UUID &RHS) { return !(RHS < LHS); } friend bool operator>(const UUID &LHS, const UUID &RHS) { return RHS < LHS; } friend bool operator>=(const UUID &LHS, const UUID &RHS) { return !(LHS < RHS); } }; } // namespace lldb_private #endif // LLDB_UTILITY_UUID_H diff --git a/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp b/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp index f467f926128a..35a823e9a28f 100644 --- a/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp +++ b/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp @@ -1,229 +1,197 @@ //===-- ObjectFilePDB.cpp -------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "ObjectFilePDB.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/Section.h" #include "lldb/Utility/StreamString.h" #include "llvm/BinaryFormat/Magic.h" #include "llvm/DebugInfo/PDB/Native/DbiStream.h" #include "llvm/DebugInfo/PDB/Native/InfoStream.h" #include "llvm/DebugInfo/PDB/Native/NativeSession.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h" #include "llvm/DebugInfo/PDB/PDB.h" #include "llvm/Support/BinaryByteStream.h" using namespace lldb; using namespace lldb_private; using namespace llvm::pdb; using namespace llvm::codeview; LLDB_PLUGIN_DEFINE(ObjectFilePDB) -struct CVInfoPdb70 { - // 16-byte GUID - struct _Guid { - llvm::support::ulittle32_t Data1; - llvm::support::ulittle16_t Data2; - llvm::support::ulittle16_t Data3; - uint8_t Data4[8]; - } Guid; - - llvm::support::ulittle32_t Age; -}; - static UUID GetPDBUUID(InfoStream &IS) { - // This part is similar with what has done in ObjectFilePECOFF. - using llvm::support::endian::read16be; - using llvm::support::endian::read32; - using llvm::support::endian::read32be; - - GUID guid = IS.getGuid(); - const uint8_t *guid_p = guid.Guid; - struct CVInfoPdb70 info; - info.Guid.Data1 = read32be(guid_p); - guid_p += 4; - info.Guid.Data2 = read16be(guid_p); - guid_p += 2; - info.Guid.Data3 = read16be(guid_p); - guid_p += 2; - memcpy(info.Guid.Data4, guid_p, 8); - - // Return 20-byte UUID if the Age is not zero - uint32_t age = IS.getAge(); - if (age) { - info.Age = read32(&age, llvm::support::big); - return UUID::fromOptionalData(&info, sizeof(info)); - } - // Otherwise return 16-byte GUID - return UUID::fromOptionalData(&info.Guid, sizeof(info.Guid)); + UUID::CvRecordPdb70 debug_info; + memcpy(&debug_info.Uuid, IS.getGuid().Guid, sizeof(debug_info.Uuid)); + debug_info.Age = IS.getAge(); + return UUID::fromCvRecord(debug_info); } char ObjectFilePDB::ID; void ObjectFilePDB::Initialize() { PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance, CreateMemoryInstance, GetModuleSpecifications); } void ObjectFilePDB::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } ConstString ObjectFilePDB::GetPluginNameStatic() { static ConstString g_name("pdb"); return g_name; } ArchSpec ObjectFilePDB::GetArchitecture() { auto dbi_stream = m_file_up->getPDBDbiStream(); if (!dbi_stream) { llvm::consumeError(dbi_stream.takeError()); return ArchSpec(); } PDB_Machine machine = dbi_stream->getMachineType(); switch (machine) { default: break; case PDB_Machine::Amd64: case PDB_Machine::x86: case PDB_Machine::PowerPC: case PDB_Machine::PowerPCFP: case PDB_Machine::Arm: case PDB_Machine::ArmNT: case PDB_Machine::Thumb: case PDB_Machine::Arm64: ArchSpec arch; arch.SetArchitecture(eArchTypeCOFF, static_cast(machine), LLDB_INVALID_CPUTYPE); return arch; } return ArchSpec(); } bool ObjectFilePDB::initPDBFile() { m_file_up = loadPDBFile(m_file.GetPath(), m_allocator); if (!m_file_up) return false; auto info_stream = m_file_up->getPDBInfoStream(); if (!info_stream) { llvm::consumeError(info_stream.takeError()); return false; } m_uuid = GetPDBUUID(*info_stream); return true; } ObjectFile * ObjectFilePDB::CreateInstance(const ModuleSP &module_sp, DataBufferSP &data_sp, offset_t data_offset, const FileSpec *file, offset_t file_offset, offset_t length) { auto objfile_up = std::make_unique( module_sp, data_sp, data_offset, file, file_offset, length); if (!objfile_up->initPDBFile()) return nullptr; return objfile_up.release(); } ObjectFile *ObjectFilePDB::CreateMemoryInstance(const ModuleSP &module_sp, DataBufferSP &data_sp, const ProcessSP &process_sp, addr_t header_addr) { return nullptr; } size_t ObjectFilePDB::GetModuleSpecifications( const FileSpec &file, DataBufferSP &data_sp, offset_t data_offset, offset_t file_offset, offset_t length, ModuleSpecList &specs) { const size_t initial_count = specs.GetSize(); ModuleSpec module_spec(file); llvm::BumpPtrAllocator allocator; std::unique_ptr pdb_file = loadPDBFile(file.GetPath(), allocator); if (!pdb_file) return initial_count; auto info_stream = pdb_file->getPDBInfoStream(); if (!info_stream) { llvm::consumeError(info_stream.takeError()); return initial_count; } auto dbi_stream = pdb_file->getPDBDbiStream(); if (!dbi_stream) { llvm::consumeError(dbi_stream.takeError()); return initial_count; } lldb_private::UUID &uuid = module_spec.GetUUID(); uuid = GetPDBUUID(*info_stream); ArchSpec &module_arch = module_spec.GetArchitecture(); switch (dbi_stream->getMachineType()) { case PDB_Machine::Amd64: module_arch.SetTriple("x86_64-pc-windows"); specs.Append(module_spec); break; case PDB_Machine::x86: module_arch.SetTriple("i386-pc-windows"); specs.Append(module_spec); module_arch.SetTriple("i686-pc-windows"); specs.Append(module_spec); break; case PDB_Machine::ArmNT: module_arch.SetTriple("armv7-pc-windows"); specs.Append(module_spec); break; case PDB_Machine::Arm64: module_arch.SetTriple("aarch64-pc-windows"); specs.Append(module_spec); break; default: break; } return specs.GetSize() - initial_count; } ObjectFilePDB::ObjectFilePDB(const ModuleSP &module_sp, DataBufferSP &data_sp, offset_t data_offset, const FileSpec *file, offset_t offset, offset_t length) : ObjectFile(module_sp, file, offset, length, data_sp, data_offset) {} std::unique_ptr ObjectFilePDB::loadPDBFile(std::string PdbPath, llvm::BumpPtrAllocator &Allocator) { llvm::file_magic magic; auto ec = llvm::identify_magic(PdbPath, magic); if (ec || magic != llvm::file_magic::pdb) return nullptr; llvm::ErrorOr> ErrorOrBuffer = llvm::MemoryBuffer::getFile(PdbPath, /*FileSize=*/-1, /*RequiresNullTerminator=*/false); if (!ErrorOrBuffer) return nullptr; std::unique_ptr Buffer = std::move(*ErrorOrBuffer); llvm::StringRef Path = Buffer->getBufferIdentifier(); auto Stream = std::make_unique( std::move(Buffer), llvm::support::little); auto File = std::make_unique(Path, std::move(Stream), Allocator); if (auto EC = File->parseFileHeaders()) { llvm::consumeError(std::move(EC)); return nullptr; } if (auto EC = File->parseStreamData()) { llvm::consumeError(std::move(EC)); return nullptr; } return File; } diff --git a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp index 2646ce35d85c..4e2598c82440 100644 --- a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp @@ -1,1239 +1,1210 @@ //===-- ObjectFilePECOFF.cpp ----------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "ObjectFilePECOFF.h" #include "PECallFrameInfo.h" #include "WindowsMiniDump.h" #include "lldb/Core/FileSpecList.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/Section.h" #include "lldb/Core/StreamFile.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/Process.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/Timer.h" #include "lldb/Utility/UUID.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/Object/COFFImportFile.h" #include "llvm/Support/Error.h" #include "llvm/Support/MemoryBuffer.h" #define IMAGE_DOS_SIGNATURE 0x5A4D // MZ #define IMAGE_NT_SIGNATURE 0x00004550 // PE00 #define OPT_HEADER_MAGIC_PE32 0x010b #define OPT_HEADER_MAGIC_PE32_PLUS 0x020b using namespace lldb; using namespace lldb_private; LLDB_PLUGIN_DEFINE(ObjectFilePECOFF) -struct CVInfoPdb70 { - // 16-byte GUID - struct _Guid { - llvm::support::ulittle32_t Data1; - llvm::support::ulittle16_t Data2; - llvm::support::ulittle16_t Data3; - uint8_t Data4[8]; - } Guid; - - llvm::support::ulittle32_t Age; -}; - static UUID GetCoffUUID(llvm::object::COFFObjectFile &coff_obj) { const llvm::codeview::DebugInfo *pdb_info = nullptr; llvm::StringRef pdb_file; - // This part is similar with what has done in minidump parser. if (!coff_obj.getDebugPDBInfo(pdb_info, pdb_file) && pdb_info) { if (pdb_info->PDB70.CVSignature == llvm::OMF::Signature::PDB70) { - using llvm::support::endian::read16be; - using llvm::support::endian::read32be; - - const uint8_t *sig = pdb_info->PDB70.Signature; - struct CVInfoPdb70 info; - info.Guid.Data1 = read32be(sig); - sig += 4; - info.Guid.Data2 = read16be(sig); - sig += 2; - info.Guid.Data3 = read16be(sig); - sig += 2; - memcpy(info.Guid.Data4, sig, 8); - - // Return 20-byte UUID if the Age is not zero - if (pdb_info->PDB70.Age) { - info.Age = read32be(&pdb_info->PDB70.Age); - return UUID::fromOptionalData(&info, sizeof(info)); - } - // Otherwise return 16-byte GUID - return UUID::fromOptionalData(&info.Guid, sizeof(info.Guid)); + UUID::CvRecordPdb70 info; + memcpy(&info.Uuid, pdb_info->PDB70.Signature, sizeof(info.Uuid)); + info.Age = pdb_info->PDB70.Age; + return UUID::fromCvRecord(info); } } return UUID(); } char ObjectFilePECOFF::ID; void ObjectFilePECOFF::Initialize() { PluginManager::RegisterPlugin( GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance, CreateMemoryInstance, GetModuleSpecifications, SaveCore); } void ObjectFilePECOFF::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } lldb_private::ConstString ObjectFilePECOFF::GetPluginNameStatic() { static ConstString g_name("pe-coff"); return g_name; } const char *ObjectFilePECOFF::GetPluginDescriptionStatic() { return "Portable Executable and Common Object File Format object file reader " "(32 and 64 bit)"; } ObjectFile *ObjectFilePECOFF::CreateInstance(const lldb::ModuleSP &module_sp, DataBufferSP &data_sp, lldb::offset_t data_offset, const lldb_private::FileSpec *file_p, lldb::offset_t file_offset, lldb::offset_t length) { FileSpec file = file_p ? *file_p : FileSpec(); if (!data_sp) { data_sp = MapFileData(file, length, file_offset); if (!data_sp) return nullptr; data_offset = 0; } if (!ObjectFilePECOFF::MagicBytesMatch(data_sp)) return nullptr; // Update the data to contain the entire file if it doesn't already if (data_sp->GetByteSize() < length) { data_sp = MapFileData(file, length, file_offset); if (!data_sp) return nullptr; } auto objfile_up = std::make_unique( module_sp, data_sp, data_offset, file_p, file_offset, length); if (!objfile_up || !objfile_up->ParseHeader()) return nullptr; // Cache coff binary. if (!objfile_up->CreateBinary()) return nullptr; return objfile_up.release(); } ObjectFile *ObjectFilePECOFF::CreateMemoryInstance( const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) { if (!data_sp || !ObjectFilePECOFF::MagicBytesMatch(data_sp)) return nullptr; auto objfile_up = std::make_unique( module_sp, data_sp, process_sp, header_addr); if (objfile_up.get() && objfile_up->ParseHeader()) { return objfile_up.release(); } return nullptr; } size_t ObjectFilePECOFF::GetModuleSpecifications( const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp, lldb::offset_t data_offset, lldb::offset_t file_offset, lldb::offset_t length, lldb_private::ModuleSpecList &specs) { const size_t initial_count = specs.GetSize(); if (!data_sp || !ObjectFilePECOFF::MagicBytesMatch(data_sp)) return initial_count; Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); if (data_sp->GetByteSize() < length) if (DataBufferSP full_sp = MapFileData(file, -1, file_offset)) data_sp = std::move(full_sp); auto binary = llvm::object::createBinary(llvm::MemoryBufferRef( toStringRef(data_sp->GetData()), file.GetFilename().GetStringRef())); if (!binary) { LLDB_LOG_ERROR(log, binary.takeError(), "Failed to create binary for file ({1}): {0}", file); return initial_count; } auto *COFFObj = llvm::dyn_cast(binary->get()); if (!COFFObj) return initial_count; ModuleSpec module_spec(file); ArchSpec &spec = module_spec.GetArchitecture(); lldb_private::UUID &uuid = module_spec.GetUUID(); if (!uuid.IsValid()) uuid = GetCoffUUID(*COFFObj); switch (COFFObj->getMachine()) { case MachineAmd64: spec.SetTriple("x86_64-pc-windows"); specs.Append(module_spec); break; case MachineX86: spec.SetTriple("i386-pc-windows"); specs.Append(module_spec); spec.SetTriple("i686-pc-windows"); specs.Append(module_spec); break; case MachineArmNt: spec.SetTriple("armv7-pc-windows"); specs.Append(module_spec); break; case MachineArm64: spec.SetTriple("aarch64-pc-windows"); specs.Append(module_spec); break; default: break; } return specs.GetSize() - initial_count; } bool ObjectFilePECOFF::SaveCore(const lldb::ProcessSP &process_sp, const lldb_private::FileSpec &outfile, lldb_private::Status &error) { return SaveMiniDump(process_sp, outfile, error); } bool ObjectFilePECOFF::MagicBytesMatch(DataBufferSP &data_sp) { DataExtractor data(data_sp, eByteOrderLittle, 4); lldb::offset_t offset = 0; uint16_t magic = data.GetU16(&offset); return magic == IMAGE_DOS_SIGNATURE; } lldb::SymbolType ObjectFilePECOFF::MapSymbolType(uint16_t coff_symbol_type) { // TODO: We need to complete this mapping of COFF symbol types to LLDB ones. // For now, here's a hack to make sure our function have types. const auto complex_type = coff_symbol_type >> llvm::COFF::SCT_COMPLEX_TYPE_SHIFT; if (complex_type == llvm::COFF::IMAGE_SYM_DTYPE_FUNCTION) { return lldb::eSymbolTypeCode; } return lldb::eSymbolTypeInvalid; } bool ObjectFilePECOFF::CreateBinary() { if (m_binary) return true; Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); auto binary = llvm::object::createBinary(llvm::MemoryBufferRef( toStringRef(m_data.GetData()), m_file.GetFilename().GetStringRef())); if (!binary) { LLDB_LOG_ERROR(log, binary.takeError(), "Failed to create binary for file ({1}): {0}", m_file); return false; } // Make sure we only handle COFF format. m_binary = llvm::unique_dyn_cast(std::move(*binary)); if (!m_binary) return false; LLDB_LOG(log, "this = {0}, module = {1} ({2}), file = {3}, binary = {4}", this, GetModule().get(), GetModule()->GetSpecificationDescription(), m_file.GetPath(), m_binary.get()); return true; } ObjectFilePECOFF::ObjectFilePECOFF(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) : ObjectFile(module_sp, file, file_offset, length, data_sp, data_offset), m_dos_header(), m_coff_header(), m_sect_headers(), m_entry_point_address(), m_deps_filespec() { ::memset(&m_dos_header, 0, sizeof(m_dos_header)); ::memset(&m_coff_header, 0, sizeof(m_coff_header)); } ObjectFilePECOFF::ObjectFilePECOFF(const lldb::ModuleSP &module_sp, DataBufferSP &header_data_sp, const lldb::ProcessSP &process_sp, addr_t header_addr) : ObjectFile(module_sp, process_sp, header_addr, header_data_sp), m_dos_header(), m_coff_header(), m_sect_headers(), m_entry_point_address(), m_deps_filespec() { ::memset(&m_dos_header, 0, sizeof(m_dos_header)); ::memset(&m_coff_header, 0, sizeof(m_coff_header)); } ObjectFilePECOFF::~ObjectFilePECOFF() {} bool ObjectFilePECOFF::ParseHeader() { ModuleSP module_sp(GetModule()); if (module_sp) { std::lock_guard guard(module_sp->GetMutex()); m_sect_headers.clear(); m_data.SetByteOrder(eByteOrderLittle); lldb::offset_t offset = 0; if (ParseDOSHeader(m_data, m_dos_header)) { offset = m_dos_header.e_lfanew; uint32_t pe_signature = m_data.GetU32(&offset); if (pe_signature != IMAGE_NT_SIGNATURE) return false; if (ParseCOFFHeader(m_data, &offset, m_coff_header)) { if (m_coff_header.hdrsize > 0) ParseCOFFOptionalHeader(&offset); ParseSectionHeaders(offset); } m_data.SetAddressByteSize(GetAddressByteSize()); return true; } } return false; } bool ObjectFilePECOFF::SetLoadAddress(Target &target, addr_t value, bool value_is_offset) { bool changed = false; ModuleSP module_sp = GetModule(); if (module_sp) { size_t num_loaded_sections = 0; SectionList *section_list = GetSectionList(); if (section_list) { if (!value_is_offset) { value -= m_image_base; } const size_t num_sections = section_list->GetSize(); size_t sect_idx = 0; for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) { // Iterate through the object file sections to find all of the sections // that have SHF_ALLOC in their flag bits. SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx)); if (section_sp && !section_sp->IsThreadSpecific()) { if (target.GetSectionLoadList().SetSectionLoadAddress( section_sp, section_sp->GetFileAddress() + value)) ++num_loaded_sections; } } changed = num_loaded_sections > 0; } } return changed; } ByteOrder ObjectFilePECOFF::GetByteOrder() const { return eByteOrderLittle; } bool ObjectFilePECOFF::IsExecutable() const { return (m_coff_header.flags & llvm::COFF::IMAGE_FILE_DLL) == 0; } uint32_t ObjectFilePECOFF::GetAddressByteSize() const { if (m_coff_header_opt.magic == OPT_HEADER_MAGIC_PE32_PLUS) return 8; else if (m_coff_header_opt.magic == OPT_HEADER_MAGIC_PE32) return 4; return 4; } // NeedsEndianSwap // // Return true if an endian swap needs to occur when extracting data from this // file. bool ObjectFilePECOFF::NeedsEndianSwap() const { #if defined(__LITTLE_ENDIAN__) return false; #else return true; #endif } // ParseDOSHeader bool ObjectFilePECOFF::ParseDOSHeader(DataExtractor &data, dos_header_t &dos_header) { bool success = false; lldb::offset_t offset = 0; success = data.ValidOffsetForDataOfSize(0, sizeof(dos_header)); if (success) { dos_header.e_magic = data.GetU16(&offset); // Magic number success = dos_header.e_magic == IMAGE_DOS_SIGNATURE; if (success) { dos_header.e_cblp = data.GetU16(&offset); // Bytes on last page of file dos_header.e_cp = data.GetU16(&offset); // Pages in file dos_header.e_crlc = data.GetU16(&offset); // Relocations dos_header.e_cparhdr = data.GetU16(&offset); // Size of header in paragraphs dos_header.e_minalloc = data.GetU16(&offset); // Minimum extra paragraphs needed dos_header.e_maxalloc = data.GetU16(&offset); // Maximum extra paragraphs needed dos_header.e_ss = data.GetU16(&offset); // Initial (relative) SS value dos_header.e_sp = data.GetU16(&offset); // Initial SP value dos_header.e_csum = data.GetU16(&offset); // Checksum dos_header.e_ip = data.GetU16(&offset); // Initial IP value dos_header.e_cs = data.GetU16(&offset); // Initial (relative) CS value dos_header.e_lfarlc = data.GetU16(&offset); // File address of relocation table dos_header.e_ovno = data.GetU16(&offset); // Overlay number dos_header.e_res[0] = data.GetU16(&offset); // Reserved words dos_header.e_res[1] = data.GetU16(&offset); // Reserved words dos_header.e_res[2] = data.GetU16(&offset); // Reserved words dos_header.e_res[3] = data.GetU16(&offset); // Reserved words dos_header.e_oemid = data.GetU16(&offset); // OEM identifier (for e_oeminfo) dos_header.e_oeminfo = data.GetU16(&offset); // OEM information; e_oemid specific dos_header.e_res2[0] = data.GetU16(&offset); // Reserved words dos_header.e_res2[1] = data.GetU16(&offset); // Reserved words dos_header.e_res2[2] = data.GetU16(&offset); // Reserved words dos_header.e_res2[3] = data.GetU16(&offset); // Reserved words dos_header.e_res2[4] = data.GetU16(&offset); // Reserved words dos_header.e_res2[5] = data.GetU16(&offset); // Reserved words dos_header.e_res2[6] = data.GetU16(&offset); // Reserved words dos_header.e_res2[7] = data.GetU16(&offset); // Reserved words dos_header.e_res2[8] = data.GetU16(&offset); // Reserved words dos_header.e_res2[9] = data.GetU16(&offset); // Reserved words dos_header.e_lfanew = data.GetU32(&offset); // File address of new exe header } } if (!success) memset(&dos_header, 0, sizeof(dos_header)); return success; } // ParserCOFFHeader bool ObjectFilePECOFF::ParseCOFFHeader(DataExtractor &data, lldb::offset_t *offset_ptr, coff_header_t &coff_header) { bool success = data.ValidOffsetForDataOfSize(*offset_ptr, sizeof(coff_header)); if (success) { coff_header.machine = data.GetU16(offset_ptr); coff_header.nsects = data.GetU16(offset_ptr); coff_header.modtime = data.GetU32(offset_ptr); coff_header.symoff = data.GetU32(offset_ptr); coff_header.nsyms = data.GetU32(offset_ptr); coff_header.hdrsize = data.GetU16(offset_ptr); coff_header.flags = data.GetU16(offset_ptr); } if (!success) memset(&coff_header, 0, sizeof(coff_header)); return success; } bool ObjectFilePECOFF::ParseCOFFOptionalHeader(lldb::offset_t *offset_ptr) { bool success = false; const lldb::offset_t end_offset = *offset_ptr + m_coff_header.hdrsize; if (*offset_ptr < end_offset) { success = true; m_coff_header_opt.magic = m_data.GetU16(offset_ptr); m_coff_header_opt.major_linker_version = m_data.GetU8(offset_ptr); m_coff_header_opt.minor_linker_version = m_data.GetU8(offset_ptr); m_coff_header_opt.code_size = m_data.GetU32(offset_ptr); m_coff_header_opt.data_size = m_data.GetU32(offset_ptr); m_coff_header_opt.bss_size = m_data.GetU32(offset_ptr); m_coff_header_opt.entry = m_data.GetU32(offset_ptr); m_coff_header_opt.code_offset = m_data.GetU32(offset_ptr); const uint32_t addr_byte_size = GetAddressByteSize(); if (*offset_ptr < end_offset) { if (m_coff_header_opt.magic == OPT_HEADER_MAGIC_PE32) { // PE32 only m_coff_header_opt.data_offset = m_data.GetU32(offset_ptr); } else m_coff_header_opt.data_offset = 0; if (*offset_ptr < end_offset) { m_coff_header_opt.image_base = m_data.GetMaxU64(offset_ptr, addr_byte_size); m_coff_header_opt.sect_alignment = m_data.GetU32(offset_ptr); m_coff_header_opt.file_alignment = m_data.GetU32(offset_ptr); m_coff_header_opt.major_os_system_version = m_data.GetU16(offset_ptr); m_coff_header_opt.minor_os_system_version = m_data.GetU16(offset_ptr); m_coff_header_opt.major_image_version = m_data.GetU16(offset_ptr); m_coff_header_opt.minor_image_version = m_data.GetU16(offset_ptr); m_coff_header_opt.major_subsystem_version = m_data.GetU16(offset_ptr); m_coff_header_opt.minor_subsystem_version = m_data.GetU16(offset_ptr); m_coff_header_opt.reserved1 = m_data.GetU32(offset_ptr); m_coff_header_opt.image_size = m_data.GetU32(offset_ptr); m_coff_header_opt.header_size = m_data.GetU32(offset_ptr); m_coff_header_opt.checksum = m_data.GetU32(offset_ptr); m_coff_header_opt.subsystem = m_data.GetU16(offset_ptr); m_coff_header_opt.dll_flags = m_data.GetU16(offset_ptr); m_coff_header_opt.stack_reserve_size = m_data.GetMaxU64(offset_ptr, addr_byte_size); m_coff_header_opt.stack_commit_size = m_data.GetMaxU64(offset_ptr, addr_byte_size); m_coff_header_opt.heap_reserve_size = m_data.GetMaxU64(offset_ptr, addr_byte_size); m_coff_header_opt.heap_commit_size = m_data.GetMaxU64(offset_ptr, addr_byte_size); m_coff_header_opt.loader_flags = m_data.GetU32(offset_ptr); uint32_t num_data_dir_entries = m_data.GetU32(offset_ptr); m_coff_header_opt.data_dirs.clear(); m_coff_header_opt.data_dirs.resize(num_data_dir_entries); uint32_t i; for (i = 0; i < num_data_dir_entries; i++) { m_coff_header_opt.data_dirs[i].vmaddr = m_data.GetU32(offset_ptr); m_coff_header_opt.data_dirs[i].vmsize = m_data.GetU32(offset_ptr); } m_image_base = m_coff_header_opt.image_base; } } } // Make sure we are on track for section data which follows *offset_ptr = end_offset; return success; } uint32_t ObjectFilePECOFF::GetRVA(const Address &addr) const { return addr.GetFileAddress() - m_image_base; } Address ObjectFilePECOFF::GetAddress(uint32_t rva) { SectionList *sect_list = GetSectionList(); if (!sect_list) return Address(GetFileAddress(rva)); return Address(GetFileAddress(rva), sect_list); } lldb::addr_t ObjectFilePECOFF::GetFileAddress(uint32_t rva) const { return m_image_base + rva; } DataExtractor ObjectFilePECOFF::ReadImageData(uint32_t offset, size_t size) { if (!size) return {}; if (m_data.ValidOffsetForDataOfSize(offset, size)) return DataExtractor(m_data, offset, size); ProcessSP process_sp(m_process_wp.lock()); DataExtractor data; if (process_sp) { auto data_up = std::make_unique(size, 0); Status readmem_error; size_t bytes_read = process_sp->ReadMemory(m_image_base + offset, data_up->GetBytes(), data_up->GetByteSize(), readmem_error); if (bytes_read == size) { DataBufferSP buffer_sp(data_up.release()); data.SetData(buffer_sp, 0, buffer_sp->GetByteSize()); } } return data; } DataExtractor ObjectFilePECOFF::ReadImageDataByRVA(uint32_t rva, size_t size) { Address addr = GetAddress(rva); SectionSP sect = addr.GetSection(); if (!sect) return {}; rva = sect->GetFileOffset() + addr.GetOffset(); return ReadImageData(rva, size); } // ParseSectionHeaders bool ObjectFilePECOFF::ParseSectionHeaders( uint32_t section_header_data_offset) { const uint32_t nsects = m_coff_header.nsects; m_sect_headers.clear(); if (nsects > 0) { const size_t section_header_byte_size = nsects * sizeof(section_header_t); DataExtractor section_header_data = ReadImageData(section_header_data_offset, section_header_byte_size); lldb::offset_t offset = 0; if (section_header_data.ValidOffsetForDataOfSize( offset, section_header_byte_size)) { m_sect_headers.resize(nsects); for (uint32_t idx = 0; idx < nsects; ++idx) { const void *name_data = section_header_data.GetData(&offset, 8); if (name_data) { memcpy(m_sect_headers[idx].name, name_data, 8); m_sect_headers[idx].vmsize = section_header_data.GetU32(&offset); m_sect_headers[idx].vmaddr = section_header_data.GetU32(&offset); m_sect_headers[idx].size = section_header_data.GetU32(&offset); m_sect_headers[idx].offset = section_header_data.GetU32(&offset); m_sect_headers[idx].reloff = section_header_data.GetU32(&offset); m_sect_headers[idx].lineoff = section_header_data.GetU32(&offset); m_sect_headers[idx].nreloc = section_header_data.GetU16(&offset); m_sect_headers[idx].nline = section_header_data.GetU16(&offset); m_sect_headers[idx].flags = section_header_data.GetU32(&offset); } } } } return !m_sect_headers.empty(); } llvm::StringRef ObjectFilePECOFF::GetSectionName(const section_header_t §) { llvm::StringRef hdr_name(sect.name, llvm::array_lengthof(sect.name)); hdr_name = hdr_name.split('\0').first; if (hdr_name.consume_front("/")) { lldb::offset_t stroff; if (!to_integer(hdr_name, stroff, 10)) return ""; lldb::offset_t string_file_offset = m_coff_header.symoff + (m_coff_header.nsyms * 18) + stroff; if (const char *name = m_data.GetCStr(&string_file_offset)) return name; return ""; } return hdr_name; } // GetNListSymtab Symtab *ObjectFilePECOFF::GetSymtab() { ModuleSP module_sp(GetModule()); if (module_sp) { std::lock_guard guard(module_sp->GetMutex()); if (m_symtab_up == nullptr) { SectionList *sect_list = GetSectionList(); m_symtab_up = std::make_unique(this); std::lock_guard guard(m_symtab_up->GetMutex()); const uint32_t num_syms = m_coff_header.nsyms; if (m_file && num_syms > 0 && m_coff_header.symoff > 0) { const uint32_t symbol_size = 18; const size_t symbol_data_size = num_syms * symbol_size; // Include the 4-byte string table size at the end of the symbols DataExtractor symtab_data = ReadImageData(m_coff_header.symoff, symbol_data_size + 4); lldb::offset_t offset = symbol_data_size; const uint32_t strtab_size = symtab_data.GetU32(&offset); if (strtab_size > 0) { DataExtractor strtab_data = ReadImageData( m_coff_header.symoff + symbol_data_size, strtab_size); offset = 0; std::string symbol_name; Symbol *symbols = m_symtab_up->Resize(num_syms); for (uint32_t i = 0; i < num_syms; ++i) { coff_symbol_t symbol; const uint32_t symbol_offset = offset; const char *symbol_name_cstr = nullptr; // If the first 4 bytes of the symbol string are zero, then they // are followed by a 4-byte string table offset. Else these // 8 bytes contain the symbol name if (symtab_data.GetU32(&offset) == 0) { // Long string that doesn't fit into the symbol table name, so // now we must read the 4 byte string table offset uint32_t strtab_offset = symtab_data.GetU32(&offset); symbol_name_cstr = strtab_data.PeekCStr(strtab_offset); symbol_name.assign(symbol_name_cstr); } else { // Short string that fits into the symbol table name which is 8 // bytes offset += sizeof(symbol.name) - 4; // Skip remaining symbol_name_cstr = symtab_data.PeekCStr(symbol_offset); if (symbol_name_cstr == nullptr) break; symbol_name.assign(symbol_name_cstr, sizeof(symbol.name)); } symbol.value = symtab_data.GetU32(&offset); symbol.sect = symtab_data.GetU16(&offset); symbol.type = symtab_data.GetU16(&offset); symbol.storage = symtab_data.GetU8(&offset); symbol.naux = symtab_data.GetU8(&offset); symbols[i].GetMangled().SetValue(ConstString(symbol_name.c_str())); if ((int16_t)symbol.sect >= 1) { Address symbol_addr(sect_list->FindSectionByID(symbol.sect), symbol.value); symbols[i].GetAddressRef() = symbol_addr; symbols[i].SetType(MapSymbolType(symbol.type)); } if (symbol.naux > 0) { i += symbol.naux; offset += symbol.naux * symbol_size; } } } } // Read export header if (coff_data_dir_export_table < m_coff_header_opt.data_dirs.size() && m_coff_header_opt.data_dirs[coff_data_dir_export_table].vmsize > 0 && m_coff_header_opt.data_dirs[coff_data_dir_export_table].vmaddr > 0) { export_directory_entry export_table; uint32_t data_start = m_coff_header_opt.data_dirs[coff_data_dir_export_table].vmaddr; DataExtractor symtab_data = ReadImageDataByRVA( data_start, m_coff_header_opt.data_dirs[0].vmsize); lldb::offset_t offset = 0; // Read export_table header export_table.characteristics = symtab_data.GetU32(&offset); export_table.time_date_stamp = symtab_data.GetU32(&offset); export_table.major_version = symtab_data.GetU16(&offset); export_table.minor_version = symtab_data.GetU16(&offset); export_table.name = symtab_data.GetU32(&offset); export_table.base = symtab_data.GetU32(&offset); export_table.number_of_functions = symtab_data.GetU32(&offset); export_table.number_of_names = symtab_data.GetU32(&offset); export_table.address_of_functions = symtab_data.GetU32(&offset); export_table.address_of_names = symtab_data.GetU32(&offset); export_table.address_of_name_ordinals = symtab_data.GetU32(&offset); bool has_ordinal = export_table.address_of_name_ordinals != 0; lldb::offset_t name_offset = export_table.address_of_names - data_start; lldb::offset_t name_ordinal_offset = export_table.address_of_name_ordinals - data_start; Symbol *symbols = m_symtab_up->Resize(export_table.number_of_names); std::string symbol_name; // Read each export table entry for (size_t i = 0; i < export_table.number_of_names; ++i) { uint32_t name_ordinal = has_ordinal ? symtab_data.GetU16(&name_ordinal_offset) : i; uint32_t name_address = symtab_data.GetU32(&name_offset); const char *symbol_name_cstr = symtab_data.PeekCStr(name_address - data_start); symbol_name.assign(symbol_name_cstr); lldb::offset_t function_offset = export_table.address_of_functions - data_start + sizeof(uint32_t) * name_ordinal; uint32_t function_rva = symtab_data.GetU32(&function_offset); Address symbol_addr(m_coff_header_opt.image_base + function_rva, sect_list); symbols[i].GetMangled().SetValue(ConstString(symbol_name.c_str())); symbols[i].GetAddressRef() = symbol_addr; symbols[i].SetType(lldb::eSymbolTypeCode); symbols[i].SetDebug(true); } } m_symtab_up->CalculateSymbolSizes(); } } return m_symtab_up.get(); } std::unique_ptr ObjectFilePECOFF::CreateCallFrameInfo() { if (coff_data_dir_exception_table >= m_coff_header_opt.data_dirs.size()) return {}; data_directory data_dir_exception = m_coff_header_opt.data_dirs[coff_data_dir_exception_table]; if (!data_dir_exception.vmaddr) return {}; if (m_coff_header.machine != llvm::COFF::IMAGE_FILE_MACHINE_AMD64) return {}; return std::make_unique(*this, data_dir_exception.vmaddr, data_dir_exception.vmsize); } bool ObjectFilePECOFF::IsStripped() { // TODO: determine this for COFF return false; } SectionType ObjectFilePECOFF::GetSectionType(llvm::StringRef sect_name, const section_header_t §) { ConstString const_sect_name(sect_name); static ConstString g_code_sect_name(".code"); static ConstString g_CODE_sect_name("CODE"); static ConstString g_data_sect_name(".data"); static ConstString g_DATA_sect_name("DATA"); static ConstString g_bss_sect_name(".bss"); static ConstString g_BSS_sect_name("BSS"); if (sect.flags & llvm::COFF::IMAGE_SCN_CNT_CODE && ((const_sect_name == g_code_sect_name) || (const_sect_name == g_CODE_sect_name))) { return eSectionTypeCode; } if (sect.flags & llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA && ((const_sect_name == g_data_sect_name) || (const_sect_name == g_DATA_sect_name))) { if (sect.size == 0 && sect.offset == 0) return eSectionTypeZeroFill; else return eSectionTypeData; } if (sect.flags & llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA && ((const_sect_name == g_bss_sect_name) || (const_sect_name == g_BSS_sect_name))) { if (sect.size == 0) return eSectionTypeZeroFill; else return eSectionTypeData; } SectionType section_type = llvm::StringSwitch(sect_name) .Case(".debug", eSectionTypeDebug) .Case(".stabstr", eSectionTypeDataCString) .Case(".reloc", eSectionTypeOther) .Case(".debug_abbrev", eSectionTypeDWARFDebugAbbrev) .Case(".debug_aranges", eSectionTypeDWARFDebugAranges) .Case(".debug_frame", eSectionTypeDWARFDebugFrame) .Case(".debug_info", eSectionTypeDWARFDebugInfo) .Case(".debug_line", eSectionTypeDWARFDebugLine) .Case(".debug_loc", eSectionTypeDWARFDebugLoc) .Case(".debug_loclists", eSectionTypeDWARFDebugLocLists) .Case(".debug_macinfo", eSectionTypeDWARFDebugMacInfo) .Case(".debug_names", eSectionTypeDWARFDebugNames) .Case(".debug_pubnames", eSectionTypeDWARFDebugPubNames) .Case(".debug_pubtypes", eSectionTypeDWARFDebugPubTypes) .Case(".debug_ranges", eSectionTypeDWARFDebugRanges) .Case(".debug_str", eSectionTypeDWARFDebugStr) .Case(".debug_types", eSectionTypeDWARFDebugTypes) // .eh_frame can be truncated to 8 chars. .Cases(".eh_frame", ".eh_fram", eSectionTypeEHFrame) .Case(".gosymtab", eSectionTypeGoSymtab) .Default(eSectionTypeInvalid); if (section_type != eSectionTypeInvalid) return section_type; if (sect.flags & llvm::COFF::IMAGE_SCN_CNT_CODE) return eSectionTypeCode; if (sect.flags & llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA) return eSectionTypeData; if (sect.flags & llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) { if (sect.size == 0) return eSectionTypeZeroFill; else return eSectionTypeData; } return eSectionTypeOther; } void ObjectFilePECOFF::CreateSections(SectionList &unified_section_list) { if (m_sections_up) return; m_sections_up = std::make_unique(); ModuleSP module_sp(GetModule()); if (module_sp) { std::lock_guard guard(module_sp->GetMutex()); SectionSP header_sp = std::make_shared
( module_sp, this, ~user_id_t(0), ConstString("PECOFF header"), eSectionTypeOther, m_coff_header_opt.image_base, m_coff_header_opt.header_size, /*file_offset*/ 0, m_coff_header_opt.header_size, m_coff_header_opt.sect_alignment, /*flags*/ 0); header_sp->SetPermissions(ePermissionsReadable); m_sections_up->AddSection(header_sp); unified_section_list.AddSection(header_sp); const uint32_t nsects = m_sect_headers.size(); ModuleSP module_sp(GetModule()); for (uint32_t idx = 0; idx < nsects; ++idx) { llvm::StringRef sect_name = GetSectionName(m_sect_headers[idx]); ConstString const_sect_name(sect_name); SectionType section_type = GetSectionType(sect_name, m_sect_headers[idx]); SectionSP section_sp(new Section( module_sp, // Module to which this section belongs this, // Object file to which this section belongs idx + 1, // Section ID is the 1 based section index. const_sect_name, // Name of this section section_type, m_coff_header_opt.image_base + m_sect_headers[idx].vmaddr, // File VM address == addresses as // they are found in the object file m_sect_headers[idx].vmsize, // VM size in bytes of this section m_sect_headers[idx] .offset, // Offset to the data for this section in the file m_sect_headers[idx] .size, // Size in bytes of this section as found in the file m_coff_header_opt.sect_alignment, // Section alignment m_sect_headers[idx].flags)); // Flags for this section uint32_t permissions = 0; if (m_sect_headers[idx].flags & llvm::COFF::IMAGE_SCN_MEM_EXECUTE) permissions |= ePermissionsExecutable; if (m_sect_headers[idx].flags & llvm::COFF::IMAGE_SCN_MEM_READ) permissions |= ePermissionsReadable; if (m_sect_headers[idx].flags & llvm::COFF::IMAGE_SCN_MEM_WRITE) permissions |= ePermissionsWritable; section_sp->SetPermissions(permissions); m_sections_up->AddSection(section_sp); unified_section_list.AddSection(section_sp); } } } UUID ObjectFilePECOFF::GetUUID() { if (m_uuid.IsValid()) return m_uuid; if (!CreateBinary()) return UUID(); m_uuid = GetCoffUUID(*m_binary); return m_uuid; } uint32_t ObjectFilePECOFF::ParseDependentModules() { ModuleSP module_sp(GetModule()); if (!module_sp) return 0; std::lock_guard guard(module_sp->GetMutex()); if (m_deps_filespec) return m_deps_filespec->GetSize(); // Cache coff binary if it is not done yet. if (!CreateBinary()) return 0; Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); LLDB_LOG(log, "this = {0}, module = {1} ({2}), file = {3}, binary = {4}", this, GetModule().get(), GetModule()->GetSpecificationDescription(), m_file.GetPath(), m_binary.get()); m_deps_filespec = FileSpecList(); for (const auto &entry : m_binary->import_directories()) { llvm::StringRef dll_name; // Report a bogus entry. if (llvm::Error e = entry.getName(dll_name)) { LLDB_LOGF(log, "ObjectFilePECOFF::ParseDependentModules() - failed to get " "import directory entry name: %s", llvm::toString(std::move(e)).c_str()); continue; } // At this moment we only have the base name of the DLL. The full path can // only be seen after the dynamic loading. Our best guess is Try to get it // with the help of the object file's directory. llvm::SmallString<128> dll_fullpath; FileSpec dll_specs(dll_name); dll_specs.GetDirectory().SetString(m_file.GetDirectory().GetCString()); if (!llvm::sys::fs::real_path(dll_specs.GetPath(), dll_fullpath)) m_deps_filespec->EmplaceBack(dll_fullpath); else { // Known DLLs or DLL not found in the object file directory. m_deps_filespec->EmplaceBack(dll_name); } } return m_deps_filespec->GetSize(); } uint32_t ObjectFilePECOFF::GetDependentModules(FileSpecList &files) { auto num_modules = ParseDependentModules(); auto original_size = files.GetSize(); for (unsigned i = 0; i < num_modules; ++i) files.AppendIfUnique(m_deps_filespec->GetFileSpecAtIndex(i)); return files.GetSize() - original_size; } lldb_private::Address ObjectFilePECOFF::GetEntryPointAddress() { if (m_entry_point_address.IsValid()) return m_entry_point_address; if (!ParseHeader() || !IsExecutable()) return m_entry_point_address; SectionList *section_list = GetSectionList(); addr_t file_addr = m_coff_header_opt.entry + m_coff_header_opt.image_base; if (!section_list) m_entry_point_address.SetOffset(file_addr); else m_entry_point_address.ResolveAddressUsingFileSections(file_addr, section_list); return m_entry_point_address; } Address ObjectFilePECOFF::GetBaseAddress() { return Address(GetSectionList()->GetSectionAtIndex(0), 0); } // Dump // // Dump the specifics of the runtime file container (such as any headers // segments, sections, etc). void ObjectFilePECOFF::Dump(Stream *s) { ModuleSP module_sp(GetModule()); if (module_sp) { std::lock_guard guard(module_sp->GetMutex()); s->Printf("%p: ", static_cast(this)); s->Indent(); s->PutCString("ObjectFilePECOFF"); ArchSpec header_arch = GetArchitecture(); *s << ", file = '" << m_file << "', arch = " << header_arch.GetArchitectureName() << "\n"; SectionList *sections = GetSectionList(); if (sections) sections->Dump(s->AsRawOstream(), s->GetIndentLevel(), nullptr, true, UINT32_MAX); if (m_symtab_up) m_symtab_up->Dump(s, nullptr, eSortOrderNone); if (m_dos_header.e_magic) DumpDOSHeader(s, m_dos_header); if (m_coff_header.machine) { DumpCOFFHeader(s, m_coff_header); if (m_coff_header.hdrsize) DumpOptCOFFHeader(s, m_coff_header_opt); } s->EOL(); DumpSectionHeaders(s); s->EOL(); DumpDependentModules(s); s->EOL(); } } // DumpDOSHeader // // Dump the MS-DOS header to the specified output stream void ObjectFilePECOFF::DumpDOSHeader(Stream *s, const dos_header_t &header) { s->PutCString("MSDOS Header\n"); s->Printf(" e_magic = 0x%4.4x\n", header.e_magic); s->Printf(" e_cblp = 0x%4.4x\n", header.e_cblp); s->Printf(" e_cp = 0x%4.4x\n", header.e_cp); s->Printf(" e_crlc = 0x%4.4x\n", header.e_crlc); s->Printf(" e_cparhdr = 0x%4.4x\n", header.e_cparhdr); s->Printf(" e_minalloc = 0x%4.4x\n", header.e_minalloc); s->Printf(" e_maxalloc = 0x%4.4x\n", header.e_maxalloc); s->Printf(" e_ss = 0x%4.4x\n", header.e_ss); s->Printf(" e_sp = 0x%4.4x\n", header.e_sp); s->Printf(" e_csum = 0x%4.4x\n", header.e_csum); s->Printf(" e_ip = 0x%4.4x\n", header.e_ip); s->Printf(" e_cs = 0x%4.4x\n", header.e_cs); s->Printf(" e_lfarlc = 0x%4.4x\n", header.e_lfarlc); s->Printf(" e_ovno = 0x%4.4x\n", header.e_ovno); s->Printf(" e_res[4] = { 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x }\n", header.e_res[0], header.e_res[1], header.e_res[2], header.e_res[3]); s->Printf(" e_oemid = 0x%4.4x\n", header.e_oemid); s->Printf(" e_oeminfo = 0x%4.4x\n", header.e_oeminfo); s->Printf(" e_res2[10] = { 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x, " "0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x }\n", header.e_res2[0], header.e_res2[1], header.e_res2[2], header.e_res2[3], header.e_res2[4], header.e_res2[5], header.e_res2[6], header.e_res2[7], header.e_res2[8], header.e_res2[9]); s->Printf(" e_lfanew = 0x%8.8x\n", header.e_lfanew); } // DumpCOFFHeader // // Dump the COFF header to the specified output stream void ObjectFilePECOFF::DumpCOFFHeader(Stream *s, const coff_header_t &header) { s->PutCString("COFF Header\n"); s->Printf(" machine = 0x%4.4x\n", header.machine); s->Printf(" nsects = 0x%4.4x\n", header.nsects); s->Printf(" modtime = 0x%8.8x\n", header.modtime); s->Printf(" symoff = 0x%8.8x\n", header.symoff); s->Printf(" nsyms = 0x%8.8x\n", header.nsyms); s->Printf(" hdrsize = 0x%4.4x\n", header.hdrsize); } // DumpOptCOFFHeader // // Dump the optional COFF header to the specified output stream void ObjectFilePECOFF::DumpOptCOFFHeader(Stream *s, const coff_opt_header_t &header) { s->PutCString("Optional COFF Header\n"); s->Printf(" magic = 0x%4.4x\n", header.magic); s->Printf(" major_linker_version = 0x%2.2x\n", header.major_linker_version); s->Printf(" minor_linker_version = 0x%2.2x\n", header.minor_linker_version); s->Printf(" code_size = 0x%8.8x\n", header.code_size); s->Printf(" data_size = 0x%8.8x\n", header.data_size); s->Printf(" bss_size = 0x%8.8x\n", header.bss_size); s->Printf(" entry = 0x%8.8x\n", header.entry); s->Printf(" code_offset = 0x%8.8x\n", header.code_offset); s->Printf(" data_offset = 0x%8.8x\n", header.data_offset); s->Printf(" image_base = 0x%16.16" PRIx64 "\n", header.image_base); s->Printf(" sect_alignment = 0x%8.8x\n", header.sect_alignment); s->Printf(" file_alignment = 0x%8.8x\n", header.file_alignment); s->Printf(" major_os_system_version = 0x%4.4x\n", header.major_os_system_version); s->Printf(" minor_os_system_version = 0x%4.4x\n", header.minor_os_system_version); s->Printf(" major_image_version = 0x%4.4x\n", header.major_image_version); s->Printf(" minor_image_version = 0x%4.4x\n", header.minor_image_version); s->Printf(" major_subsystem_version = 0x%4.4x\n", header.major_subsystem_version); s->Printf(" minor_subsystem_version = 0x%4.4x\n", header.minor_subsystem_version); s->Printf(" reserved1 = 0x%8.8x\n", header.reserved1); s->Printf(" image_size = 0x%8.8x\n", header.image_size); s->Printf(" header_size = 0x%8.8x\n", header.header_size); s->Printf(" checksum = 0x%8.8x\n", header.checksum); s->Printf(" subsystem = 0x%4.4x\n", header.subsystem); s->Printf(" dll_flags = 0x%4.4x\n", header.dll_flags); s->Printf(" stack_reserve_size = 0x%16.16" PRIx64 "\n", header.stack_reserve_size); s->Printf(" stack_commit_size = 0x%16.16" PRIx64 "\n", header.stack_commit_size); s->Printf(" heap_reserve_size = 0x%16.16" PRIx64 "\n", header.heap_reserve_size); s->Printf(" heap_commit_size = 0x%16.16" PRIx64 "\n", header.heap_commit_size); s->Printf(" loader_flags = 0x%8.8x\n", header.loader_flags); s->Printf(" num_data_dir_entries = 0x%8.8x\n", (uint32_t)header.data_dirs.size()); uint32_t i; for (i = 0; i < header.data_dirs.size(); i++) { s->Printf(" data_dirs[%2u] vmaddr = 0x%8.8x, vmsize = 0x%8.8x\n", i, header.data_dirs[i].vmaddr, header.data_dirs[i].vmsize); } } // DumpSectionHeader // // Dump a single ELF section header to the specified output stream void ObjectFilePECOFF::DumpSectionHeader(Stream *s, const section_header_t &sh) { std::string name = std::string(GetSectionName(sh)); s->Printf("%-16s 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%4.4x " "0x%4.4x 0x%8.8x\n", name.c_str(), sh.vmaddr, sh.vmsize, sh.offset, sh.size, sh.reloff, sh.lineoff, sh.nreloc, sh.nline, sh.flags); } // DumpSectionHeaders // // Dump all of the ELF section header to the specified output stream void ObjectFilePECOFF::DumpSectionHeaders(Stream *s) { s->PutCString("Section Headers\n"); s->PutCString("IDX name vm addr vm size file off file " "size reloc off line off nreloc nline flags\n"); s->PutCString("==== ---------------- ---------- ---------- ---------- " "---------- ---------- ---------- ------ ------ ----------\n"); uint32_t idx = 0; SectionHeaderCollIter pos, end = m_sect_headers.end(); for (pos = m_sect_headers.begin(); pos != end; ++pos, ++idx) { s->Printf("[%2u] ", idx); ObjectFilePECOFF::DumpSectionHeader(s, *pos); } } // DumpDependentModules // // Dump all of the dependent modules to the specified output stream void ObjectFilePECOFF::DumpDependentModules(lldb_private::Stream *s) { auto num_modules = ParseDependentModules(); if (num_modules > 0) { s->PutCString("Dependent Modules\n"); for (unsigned i = 0; i < num_modules; ++i) { auto spec = m_deps_filespec->GetFileSpecAtIndex(i); s->Printf(" %s\n", spec.GetFilename().GetCString()); } } } bool ObjectFilePECOFF::IsWindowsSubsystem() { switch (m_coff_header_opt.subsystem) { case llvm::COFF::IMAGE_SUBSYSTEM_NATIVE: case llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_GUI: case llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_CUI: case llvm::COFF::IMAGE_SUBSYSTEM_NATIVE_WINDOWS: case llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_CE_GUI: case llvm::COFF::IMAGE_SUBSYSTEM_XBOX: case llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION: return true; default: return false; } } ArchSpec ObjectFilePECOFF::GetArchitecture() { uint16_t machine = m_coff_header.machine; switch (machine) { default: break; case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: case llvm::COFF::IMAGE_FILE_MACHINE_I386: case llvm::COFF::IMAGE_FILE_MACHINE_POWERPC: case llvm::COFF::IMAGE_FILE_MACHINE_POWERPCFP: case llvm::COFF::IMAGE_FILE_MACHINE_ARM: case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT: case llvm::COFF::IMAGE_FILE_MACHINE_THUMB: case llvm::COFF::IMAGE_FILE_MACHINE_ARM64: ArchSpec arch; arch.SetArchitecture(eArchTypeCOFF, machine, LLDB_INVALID_CPUTYPE, IsWindowsSubsystem() ? llvm::Triple::Win32 : llvm::Triple::UnknownOS); return arch; } return ArchSpec(); } ObjectFile::Type ObjectFilePECOFF::CalculateType() { if (m_coff_header.machine != 0) { if ((m_coff_header.flags & llvm::COFF::IMAGE_FILE_DLL) == 0) return eTypeExecutable; else return eTypeSharedLibrary; } return eTypeExecutable; } ObjectFile::Strata ObjectFilePECOFF::CalculateStrata() { return eStrataUser; } // PluginInterface protocol ConstString ObjectFilePECOFF::GetPluginName() { return GetPluginNameStatic(); } uint32_t ObjectFilePECOFF::GetPluginVersion() { return 1; } diff --git a/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp b/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp index 5f2982b3c09c..9108d4d18aa5 100644 --- a/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp +++ b/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp @@ -1,716 +1,706 @@ //===-- MinidumpParser.cpp ------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "MinidumpParser.h" #include "NtStructures.h" #include "RegisterContextMinidump_x86_32.h" #include "Plugins/Process/Utility/LinuxProcMaps.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/Log.h" // C includes // C++ includes #include #include #include #include using namespace lldb_private; using namespace minidump; llvm::Expected MinidumpParser::Create(const lldb::DataBufferSP &data_sp) { auto ExpectedFile = llvm::object::MinidumpFile::create( llvm::MemoryBufferRef(toStringRef(data_sp->GetData()), "minidump")); if (!ExpectedFile) return ExpectedFile.takeError(); return MinidumpParser(data_sp, std::move(*ExpectedFile)); } MinidumpParser::MinidumpParser(lldb::DataBufferSP data_sp, std::unique_ptr file) : m_data_sp(std::move(data_sp)), m_file(std::move(file)) {} llvm::ArrayRef MinidumpParser::GetData() { return llvm::ArrayRef(m_data_sp->GetBytes(), m_data_sp->GetByteSize()); } llvm::ArrayRef MinidumpParser::GetStream(StreamType stream_type) { return m_file->getRawStream(stream_type) .getValueOr(llvm::ArrayRef()); } UUID MinidumpParser::GetModuleUUID(const minidump::Module *module) { auto cv_record = GetData().slice(module->CvRecord.RVA, module->CvRecord.DataSize); // Read the CV record signature const llvm::support::ulittle32_t *signature = nullptr; Status error = consumeObject(cv_record, signature); if (error.Fail()) return UUID(); const CvSignature cv_signature = static_cast(static_cast(*signature)); if (cv_signature == CvSignature::Pdb70) { - const CvRecordPdb70 *pdb70_uuid = nullptr; + const UUID::CvRecordPdb70 *pdb70_uuid = nullptr; Status error = consumeObject(cv_record, pdb70_uuid); if (error.Fail()) return UUID(); - - CvRecordPdb70 swapped; - if (!GetArchitecture().GetTriple().isOSBinFormatELF()) { - // LLDB's UUID class treats the data as a sequence of bytes, but breakpad - // interprets it as a sequence of little-endian fields, which it converts - // to big-endian when converting to text. Swap the bytes to big endian so - // that the string representation comes out right. - swapped = *pdb70_uuid; - llvm::sys::swapByteOrder(swapped.Uuid.Data1); - llvm::sys::swapByteOrder(swapped.Uuid.Data2); - llvm::sys::swapByteOrder(swapped.Uuid.Data3); - llvm::sys::swapByteOrder(swapped.Age); - pdb70_uuid = &swapped; + if (GetArchitecture().GetTriple().isOSBinFormatELF()) { + if (pdb70_uuid->Age != 0) + return UUID::fromOptionalData(pdb70_uuid, sizeof(*pdb70_uuid)); + return UUID::fromOptionalData(&pdb70_uuid->Uuid, + sizeof(pdb70_uuid->Uuid)); } - if (pdb70_uuid->Age != 0) - return UUID::fromOptionalData(pdb70_uuid, sizeof(*pdb70_uuid)); - return UUID::fromOptionalData(&pdb70_uuid->Uuid, sizeof(pdb70_uuid->Uuid)); + return UUID::fromCvRecord(*pdb70_uuid); } else if (cv_signature == CvSignature::ElfBuildId) return UUID::fromOptionalData(cv_record); return UUID(); } llvm::ArrayRef MinidumpParser::GetThreads() { auto ExpectedThreads = GetMinidumpFile().getThreadList(); if (ExpectedThreads) return *ExpectedThreads; LLDB_LOG_ERROR(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD), ExpectedThreads.takeError(), "Failed to read thread list: {0}"); return {}; } llvm::ArrayRef MinidumpParser::GetThreadContext(const LocationDescriptor &location) { if (location.RVA + location.DataSize > GetData().size()) return {}; return GetData().slice(location.RVA, location.DataSize); } llvm::ArrayRef MinidumpParser::GetThreadContext(const minidump::Thread &td) { return GetThreadContext(td.Context); } llvm::ArrayRef MinidumpParser::GetThreadContextWow64(const minidump::Thread &td) { // On Windows, a 32-bit process can run on a 64-bit machine under WOW64. If // the minidump was captured with a 64-bit debugger, then the CONTEXT we just // grabbed from the mini_dump_thread is the one for the 64-bit "native" // process rather than the 32-bit "guest" process we care about. In this // case, we can get the 32-bit CONTEXT from the TEB (Thread Environment // Block) of the 64-bit process. auto teb_mem = GetMemory(td.EnvironmentBlock, sizeof(TEB64)); if (teb_mem.empty()) return {}; const TEB64 *wow64teb; Status error = consumeObject(teb_mem, wow64teb); if (error.Fail()) return {}; // Slot 1 of the thread-local storage in the 64-bit TEB points to a structure // that includes the 32-bit CONTEXT (after a ULONG). See: // https://msdn.microsoft.com/en-us/library/ms681670.aspx auto context = GetMemory(wow64teb->tls_slots[1] + 4, sizeof(MinidumpContext_x86_32)); if (context.size() < sizeof(MinidumpContext_x86_32)) return {}; return context; // NOTE: We don't currently use the TEB for anything else. If we // need it in the future, the 32-bit TEB is located according to the address // stored in the first slot of the 64-bit TEB (wow64teb.Reserved1[0]). } ArchSpec MinidumpParser::GetArchitecture() { if (m_arch.IsValid()) return m_arch; // Set the architecture in m_arch llvm::Expected system_info = m_file->getSystemInfo(); if (!system_info) { LLDB_LOG_ERROR(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS), system_info.takeError(), "Failed to read SystemInfo stream: {0}"); return m_arch; } // TODO what to do about big endiand flavors of arm ? // TODO set the arm subarch stuff if the minidump has info about it llvm::Triple triple; triple.setVendor(llvm::Triple::VendorType::UnknownVendor); switch (system_info->ProcessorArch) { case ProcessorArchitecture::X86: triple.setArch(llvm::Triple::ArchType::x86); break; case ProcessorArchitecture::AMD64: triple.setArch(llvm::Triple::ArchType::x86_64); break; case ProcessorArchitecture::ARM: triple.setArch(llvm::Triple::ArchType::arm); break; case ProcessorArchitecture::ARM64: case ProcessorArchitecture::BP_ARM64: triple.setArch(llvm::Triple::ArchType::aarch64); break; default: triple.setArch(llvm::Triple::ArchType::UnknownArch); break; } // TODO add all of the OSes that Minidump/breakpad distinguishes? switch (system_info->PlatformId) { case OSPlatform::Win32S: case OSPlatform::Win32Windows: case OSPlatform::Win32NT: case OSPlatform::Win32CE: triple.setOS(llvm::Triple::OSType::Win32); triple.setVendor(llvm::Triple::VendorType::PC); break; case OSPlatform::Linux: triple.setOS(llvm::Triple::OSType::Linux); break; case OSPlatform::MacOSX: triple.setOS(llvm::Triple::OSType::MacOSX); triple.setVendor(llvm::Triple::Apple); break; case OSPlatform::IOS: triple.setOS(llvm::Triple::OSType::IOS); triple.setVendor(llvm::Triple::Apple); break; case OSPlatform::Android: triple.setOS(llvm::Triple::OSType::Linux); triple.setEnvironment(llvm::Triple::EnvironmentType::Android); break; default: { triple.setOS(llvm::Triple::OSType::UnknownOS); auto ExpectedCSD = m_file->getString(system_info->CSDVersionRVA); if (!ExpectedCSD) { LLDB_LOG_ERROR(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS), ExpectedCSD.takeError(), "Failed to CSD Version string: {0}"); } else { if (ExpectedCSD->find("Linux") != std::string::npos) triple.setOS(llvm::Triple::OSType::Linux); } break; } } m_arch.SetTriple(triple); return m_arch; } const MinidumpMiscInfo *MinidumpParser::GetMiscInfo() { llvm::ArrayRef data = GetStream(StreamType::MiscInfo); if (data.size() == 0) return nullptr; return MinidumpMiscInfo::Parse(data); } llvm::Optional MinidumpParser::GetLinuxProcStatus() { llvm::ArrayRef data = GetStream(StreamType::LinuxProcStatus); if (data.size() == 0) return llvm::None; return LinuxProcStatus::Parse(data); } llvm::Optional MinidumpParser::GetPid() { const MinidumpMiscInfo *misc_info = GetMiscInfo(); if (misc_info != nullptr) { return misc_info->GetPid(); } llvm::Optional proc_status = GetLinuxProcStatus(); if (proc_status.hasValue()) { return proc_status->GetPid(); } return llvm::None; } llvm::ArrayRef MinidumpParser::GetModuleList() { auto ExpectedModules = GetMinidumpFile().getModuleList(); if (ExpectedModules) return *ExpectedModules; LLDB_LOG_ERROR(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_MODULES), ExpectedModules.takeError(), "Failed to read module list: {0}"); return {}; } static bool CreateRegionsCacheFromLinuxMaps(MinidumpParser &parser, std::vector ®ions) { auto data = parser.GetStream(StreamType::LinuxMaps); if (data.empty()) return false; ParseLinuxMapRegions(llvm::toStringRef(data), [&](const lldb_private::MemoryRegionInfo ®ion, const lldb_private::Status &status) -> bool { if (status.Success()) regions.push_back(region); return true; }); return !regions.empty(); } /// Check for the memory regions starting at \a load_addr for a contiguous /// section that has execute permissions that matches the module path. /// /// When we load a breakpad generated minidump file, we might have the /// /proc//maps text for a process that details the memory map of the /// process that the minidump is describing. This checks the sorted memory /// regions for a section that has execute permissions. A sample maps files /// might look like: /// /// 00400000-00401000 r--p 00000000 fd:01 2838574 /tmp/a.out /// 00401000-00402000 r-xp 00001000 fd:01 2838574 /tmp/a.out /// 00402000-00403000 r--p 00002000 fd:01 2838574 /tmp/a.out /// 00403000-00404000 r--p 00002000 fd:01 2838574 /tmp/a.out /// 00404000-00405000 rw-p 00003000 fd:01 2838574 /tmp/a.out /// ... /// /// This function should return true when given 0x00400000 and "/tmp/a.out" /// is passed in as the path since it has a consecutive memory region for /// "/tmp/a.out" that has execute permissions at 0x00401000. This will help us /// differentiate if a file has been memory mapped into a process for reading /// and breakpad ends up saving a minidump file that has two module entries for /// a given file: one that is read only for the entire file, and then one that /// is the real executable that is loaded into memory for execution. For memory /// mapped files they will typically show up and r--p permissions and a range /// matcning the entire range of the file on disk: /// /// 00800000-00805000 r--p 00000000 fd:01 2838574 /tmp/a.out /// 00805000-00806000 r-xp 00001000 fd:01 1234567 /usr/lib/libc.so /// /// This function should return false when asked about 0x00800000 with /// "/tmp/a.out" as the path. /// /// \param[in] path /// The path to the module to check for in the memory regions. Only sequential /// memory regions whose paths match this path will be considered when looking /// for execute permissions. /// /// \param[in] regions /// A sorted list of memory regions obtained from a call to /// CreateRegionsCacheFromLinuxMaps. /// /// \param[in] base_of_image /// The load address of this module from BaseOfImage in the modules list. /// /// \return /// True if a contiguous region of memory belonging to the module with a /// matching path exists that has executable permissions. Returns false if /// \a regions is empty or if there are no regions with execute permissions /// that match \a path. static bool CheckForLinuxExecutable(ConstString path, const MemoryRegionInfos ®ions, lldb::addr_t base_of_image) { if (regions.empty()) return false; lldb::addr_t addr = base_of_image; MemoryRegionInfo region = MinidumpParser::GetMemoryRegionInfo(regions, addr); while (region.GetName() == path) { if (region.GetExecutable() == MemoryRegionInfo::eYes) return true; addr += region.GetRange().GetByteSize(); region = MinidumpParser::GetMemoryRegionInfo(regions, addr); } return false; } std::vector MinidumpParser::GetFilteredModuleList() { Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_MODULES); auto ExpectedModules = GetMinidumpFile().getModuleList(); if (!ExpectedModules) { LLDB_LOG_ERROR(log, ExpectedModules.takeError(), "Failed to read module list: {0}"); return {}; } // Create memory regions from the linux maps only. We do this to avoid issues // with breakpad generated minidumps where if someone has mmap'ed a shared // library into memory to accesss its data in the object file, we can get a // minidump with two mappings for a binary: one whose base image points to a // memory region that is read + execute and one that is read only. MemoryRegionInfos linux_regions; if (CreateRegionsCacheFromLinuxMaps(*this, linux_regions)) llvm::sort(linux_regions); // map module_name -> filtered_modules index typedef llvm::StringMap MapType; MapType module_name_to_filtered_index; std::vector filtered_modules; for (const auto &module : *ExpectedModules) { auto ExpectedName = m_file->getString(module.ModuleNameRVA); if (!ExpectedName) { LLDB_LOG_ERROR(log, ExpectedName.takeError(), "Failed to get module name: {0}"); continue; } MapType::iterator iter; bool inserted; // See if we have inserted this module aready into filtered_modules. If we // haven't insert an entry into module_name_to_filtered_index with the // index where we will insert it if it isn't in the vector already. std::tie(iter, inserted) = module_name_to_filtered_index.try_emplace( *ExpectedName, filtered_modules.size()); if (inserted) { // This module has not been seen yet, insert it into filtered_modules at // the index that was inserted into module_name_to_filtered_index using // "filtered_modules.size()" above. filtered_modules.push_back(&module); } else { // We have a duplicate module entry. Check the linux regions to see if // the module we already have is not really a mapped executable. If it // isn't check to see if the current duplicate module entry is a real // mapped executable, and if so, replace it. This can happen when a // process mmap's in the file for an executable in order to read bytes // from the executable file. A memory region mapping will exist for the // mmap'ed version and for the loaded executable, but only one will have // a consecutive region that is executable in the memory regions. auto dup_module = filtered_modules[iter->second]; ConstString name(*ExpectedName); if (!CheckForLinuxExecutable(name, linux_regions, dup_module->BaseOfImage) && CheckForLinuxExecutable(name, linux_regions, module.BaseOfImage)) { filtered_modules[iter->second] = &module; continue; } // This module has been seen. Modules are sometimes mentioned multiple // times when they are mapped discontiguously, so find the module with // the lowest "base_of_image" and use that as the filtered module. if (module.BaseOfImage < dup_module->BaseOfImage) filtered_modules[iter->second] = &module; } } return filtered_modules; } const minidump::ExceptionStream *MinidumpParser::GetExceptionStream() { auto ExpectedStream = GetMinidumpFile().getExceptionStream(); if (ExpectedStream) return &*ExpectedStream; LLDB_LOG_ERROR(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS), ExpectedStream.takeError(), "Failed to read minidump exception stream: {0}"); return nullptr; } llvm::Optional MinidumpParser::FindMemoryRange(lldb::addr_t addr) { llvm::ArrayRef data64 = GetStream(StreamType::Memory64List); Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_MODULES); auto ExpectedMemory = GetMinidumpFile().getMemoryList(); if (!ExpectedMemory) { LLDB_LOG_ERROR(log, ExpectedMemory.takeError(), "Failed to read memory list: {0}"); } else { for (const auto &memory_desc : *ExpectedMemory) { const LocationDescriptor &loc_desc = memory_desc.Memory; const lldb::addr_t range_start = memory_desc.StartOfMemoryRange; const size_t range_size = loc_desc.DataSize; if (loc_desc.RVA + loc_desc.DataSize > GetData().size()) return llvm::None; if (range_start <= addr && addr < range_start + range_size) { auto ExpectedSlice = GetMinidumpFile().getRawData(loc_desc); if (!ExpectedSlice) { LLDB_LOG_ERROR(log, ExpectedSlice.takeError(), "Failed to get memory slice: {0}"); return llvm::None; } return minidump::Range(range_start, *ExpectedSlice); } } } // Some Minidumps have a Memory64ListStream that captures all the heap memory // (full-memory Minidumps). We can't exactly use the same loop as above, // because the Minidump uses slightly different data structures to describe // those if (!data64.empty()) { llvm::ArrayRef memory64_list; uint64_t base_rva; std::tie(memory64_list, base_rva) = MinidumpMemoryDescriptor64::ParseMemory64List(data64); if (memory64_list.empty()) return llvm::None; for (const auto &memory_desc64 : memory64_list) { const lldb::addr_t range_start = memory_desc64.start_of_memory_range; const size_t range_size = memory_desc64.data_size; if (base_rva + range_size > GetData().size()) return llvm::None; if (range_start <= addr && addr < range_start + range_size) { return minidump::Range(range_start, GetData().slice(base_rva, range_size)); } base_rva += range_size; } } return llvm::None; } llvm::ArrayRef MinidumpParser::GetMemory(lldb::addr_t addr, size_t size) { // I don't have a sense of how frequently this is called or how many memory // ranges a Minidump typically has, so I'm not sure if searching for the // appropriate range linearly each time is stupid. Perhaps we should build // an index for faster lookups. llvm::Optional range = FindMemoryRange(addr); if (!range) return {}; // There's at least some overlap between the beginning of the desired range // (addr) and the current range. Figure out where the overlap begins and how // much overlap there is. const size_t offset = addr - range->start; if (addr < range->start || offset >= range->range_ref.size()) return {}; const size_t overlap = std::min(size, range->range_ref.size() - offset); return range->range_ref.slice(offset, overlap); } static bool CreateRegionsCacheFromMemoryInfoList(MinidumpParser &parser, std::vector ®ions) { Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_MODULES); auto ExpectedInfo = parser.GetMinidumpFile().getMemoryInfoList(); if (!ExpectedInfo) { LLDB_LOG_ERROR(log, ExpectedInfo.takeError(), "Failed to read memory info list: {0}"); return false; } constexpr auto yes = MemoryRegionInfo::eYes; constexpr auto no = MemoryRegionInfo::eNo; for (const MemoryInfo &entry : *ExpectedInfo) { MemoryRegionInfo region; region.GetRange().SetRangeBase(entry.BaseAddress); region.GetRange().SetByteSize(entry.RegionSize); MemoryProtection prot = entry.Protect; region.SetReadable(bool(prot & MemoryProtection::NoAccess) ? no : yes); region.SetWritable( bool(prot & (MemoryProtection::ReadWrite | MemoryProtection::WriteCopy | MemoryProtection::ExecuteReadWrite | MemoryProtection::ExeciteWriteCopy)) ? yes : no); region.SetExecutable( bool(prot & (MemoryProtection::Execute | MemoryProtection::ExecuteRead | MemoryProtection::ExecuteReadWrite | MemoryProtection::ExeciteWriteCopy)) ? yes : no); region.SetMapped(entry.State != MemoryState::Free ? yes : no); regions.push_back(region); } return !regions.empty(); } static bool CreateRegionsCacheFromMemoryList(MinidumpParser &parser, std::vector ®ions) { Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_MODULES); auto ExpectedMemory = parser.GetMinidumpFile().getMemoryList(); if (!ExpectedMemory) { LLDB_LOG_ERROR(log, ExpectedMemory.takeError(), "Failed to read memory list: {0}"); return false; } regions.reserve(ExpectedMemory->size()); for (const MemoryDescriptor &memory_desc : *ExpectedMemory) { if (memory_desc.Memory.DataSize == 0) continue; MemoryRegionInfo region; region.GetRange().SetRangeBase(memory_desc.StartOfMemoryRange); region.GetRange().SetByteSize(memory_desc.Memory.DataSize); region.SetReadable(MemoryRegionInfo::eYes); region.SetMapped(MemoryRegionInfo::eYes); regions.push_back(region); } regions.shrink_to_fit(); return !regions.empty(); } static bool CreateRegionsCacheFromMemory64List(MinidumpParser &parser, std::vector ®ions) { llvm::ArrayRef data = parser.GetStream(StreamType::Memory64List); if (data.empty()) return false; llvm::ArrayRef memory64_list; uint64_t base_rva; std::tie(memory64_list, base_rva) = MinidumpMemoryDescriptor64::ParseMemory64List(data); if (memory64_list.empty()) return false; regions.reserve(memory64_list.size()); for (const auto &memory_desc : memory64_list) { if (memory_desc.data_size == 0) continue; MemoryRegionInfo region; region.GetRange().SetRangeBase(memory_desc.start_of_memory_range); region.GetRange().SetByteSize(memory_desc.data_size); region.SetReadable(MemoryRegionInfo::eYes); region.SetMapped(MemoryRegionInfo::eYes); regions.push_back(region); } regions.shrink_to_fit(); return !regions.empty(); } std::pair MinidumpParser::BuildMemoryRegions() { // We create the region cache using the best source. We start with // the linux maps since they are the most complete and have names for the // regions. Next we try the MemoryInfoList since it has // read/write/execute/map data, and then fall back to the MemoryList and // Memory64List to just get a list of the memory that is mapped in this // core file MemoryRegionInfos result; const auto &return_sorted = [&](bool is_complete) { llvm::sort(result); return std::make_pair(std::move(result), is_complete); }; if (CreateRegionsCacheFromLinuxMaps(*this, result)) return return_sorted(true); if (CreateRegionsCacheFromMemoryInfoList(*this, result)) return return_sorted(true); if (CreateRegionsCacheFromMemoryList(*this, result)) return return_sorted(false); CreateRegionsCacheFromMemory64List(*this, result); return return_sorted(false); } #define ENUM_TO_CSTR(ST) \ case StreamType::ST: \ return #ST llvm::StringRef MinidumpParser::GetStreamTypeAsString(StreamType stream_type) { switch (stream_type) { ENUM_TO_CSTR(Unused); ENUM_TO_CSTR(ThreadList); ENUM_TO_CSTR(ModuleList); ENUM_TO_CSTR(MemoryList); ENUM_TO_CSTR(Exception); ENUM_TO_CSTR(SystemInfo); ENUM_TO_CSTR(ThreadExList); ENUM_TO_CSTR(Memory64List); ENUM_TO_CSTR(CommentA); ENUM_TO_CSTR(CommentW); ENUM_TO_CSTR(HandleData); ENUM_TO_CSTR(FunctionTable); ENUM_TO_CSTR(UnloadedModuleList); ENUM_TO_CSTR(MiscInfo); ENUM_TO_CSTR(MemoryInfoList); ENUM_TO_CSTR(ThreadInfoList); ENUM_TO_CSTR(HandleOperationList); ENUM_TO_CSTR(Token); ENUM_TO_CSTR(JavascriptData); ENUM_TO_CSTR(SystemMemoryInfo); ENUM_TO_CSTR(ProcessVMCounters); ENUM_TO_CSTR(LastReserved); ENUM_TO_CSTR(BreakpadInfo); ENUM_TO_CSTR(AssertionInfo); ENUM_TO_CSTR(LinuxCPUInfo); ENUM_TO_CSTR(LinuxProcStatus); ENUM_TO_CSTR(LinuxLSBRelease); ENUM_TO_CSTR(LinuxCMDLine); ENUM_TO_CSTR(LinuxEnviron); ENUM_TO_CSTR(LinuxAuxv); ENUM_TO_CSTR(LinuxMaps); ENUM_TO_CSTR(LinuxDSODebug); ENUM_TO_CSTR(LinuxProcStat); ENUM_TO_CSTR(LinuxProcUptime); ENUM_TO_CSTR(LinuxProcFD); ENUM_TO_CSTR(FacebookAppCustomData); ENUM_TO_CSTR(FacebookBuildID); ENUM_TO_CSTR(FacebookAppVersionName); ENUM_TO_CSTR(FacebookJavaStack); ENUM_TO_CSTR(FacebookDalvikInfo); ENUM_TO_CSTR(FacebookUnwindSymbols); ENUM_TO_CSTR(FacebookDumpErrorLog); ENUM_TO_CSTR(FacebookAppStateLog); ENUM_TO_CSTR(FacebookAbortReason); ENUM_TO_CSTR(FacebookThreadName); ENUM_TO_CSTR(FacebookLogcat); } return "unknown stream type"; } MemoryRegionInfo MinidumpParser::GetMemoryRegionInfo(const MemoryRegionInfos ®ions, lldb::addr_t load_addr) { MemoryRegionInfo region; auto pos = llvm::upper_bound(regions, load_addr); if (pos != regions.begin() && std::prev(pos)->GetRange().Contains(load_addr)) { return *std::prev(pos); } if (pos == regions.begin()) region.GetRange().SetRangeBase(0); else region.GetRange().SetRangeBase(std::prev(pos)->GetRange().GetRangeEnd()); if (pos == regions.end()) region.GetRange().SetRangeEnd(UINT64_MAX); else region.GetRange().SetRangeEnd(pos->GetRange().GetRangeBase()); region.SetReadable(MemoryRegionInfo::eNo); region.SetWritable(MemoryRegionInfo::eNo); region.SetExecutable(MemoryRegionInfo::eNo); region.SetMapped(MemoryRegionInfo::eNo); return region; } diff --git a/lldb/source/Plugins/Process/minidump/MinidumpTypes.h b/lldb/source/Plugins/Process/minidump/MinidumpTypes.h index a7ac65120e2b..c05fcfef05a6 100644 --- a/lldb/source/Plugins/Process/minidump/MinidumpTypes.h +++ b/lldb/source/Plugins/Process/minidump/MinidumpTypes.h @@ -1,122 +1,107 @@ //===-- MinidumpTypes.h -----------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_MINIDUMPTYPES_H #define LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_MINIDUMPTYPES_H #include "lldb/Utility/Status.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/BitmaskEnum.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Minidump.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/Endian.h" // C includes // C++ includes // Reference: // https://msdn.microsoft.com/en-us/library/windows/desktop/ms679293(v=vs.85).aspx // https://chromium.googlesource.com/breakpad/breakpad/ namespace lldb_private { namespace minidump { using namespace llvm::minidump; LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); enum class CvSignature : uint32_t { Pdb70 = 0x53445352, // RSDS ElfBuildId = 0x4270454c, // BpEL (Breakpad/Crashpad minidumps) }; -// Reference: -// https://crashpad.chromium.org/doxygen/structcrashpad_1_1CodeViewRecordPDB70.html -struct CvRecordPdb70 { - struct { - llvm::support::ulittle32_t Data1; - llvm::support::ulittle16_t Data2; - llvm::support::ulittle16_t Data3; - uint8_t Data4[8]; - } Uuid; - llvm::support::ulittle32_t Age; - // char PDBFileName[]; -}; -static_assert(sizeof(CvRecordPdb70) == 20, - "sizeof CvRecordPdb70 is not correct!"); - enum class MinidumpMiscInfoFlags : uint32_t { ProcessID = (1 << 0), ProcessTimes = (1 << 1), LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ ProcessTimes) }; template Status consumeObject(llvm::ArrayRef &Buffer, const T *&Object) { Status error; if (Buffer.size() < sizeof(T)) { error.SetErrorString("Insufficient buffer!"); return error; } Object = reinterpret_cast(Buffer.data()); Buffer = Buffer.drop_front(sizeof(T)); return error; } struct MinidumpMemoryDescriptor64 { llvm::support::ulittle64_t start_of_memory_range; llvm::support::ulittle64_t data_size; static std::pair, uint64_t> ParseMemory64List(llvm::ArrayRef &data); }; static_assert(sizeof(MinidumpMemoryDescriptor64) == 16, "sizeof MinidumpMemoryDescriptor64 is not correct!"); // TODO misc2, misc3 ? // Reference: // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680389(v=vs.85).aspx struct MinidumpMiscInfo { llvm::support::ulittle32_t size; // flags1 represents what info in the struct is valid llvm::support::ulittle32_t flags1; llvm::support::ulittle32_t process_id; llvm::support::ulittle32_t process_create_time; llvm::support::ulittle32_t process_user_time; llvm::support::ulittle32_t process_kernel_time; static const MinidumpMiscInfo *Parse(llvm::ArrayRef &data); llvm::Optional GetPid() const; }; static_assert(sizeof(MinidumpMiscInfo) == 24, "sizeof MinidumpMiscInfo is not correct!"); // The /proc/pid/status is saved as an ascii string in the file class LinuxProcStatus { public: llvm::StringRef proc_status; lldb::pid_t pid; static llvm::Optional Parse(llvm::ArrayRef &data); lldb::pid_t GetPid() const; private: LinuxProcStatus() = default; }; } // namespace minidump } // namespace lldb_private #endif // LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_MINIDUMPTYPES_H diff --git a/lldb/source/Utility/UUID.cpp b/lldb/source/Utility/UUID.cpp index 4177b43de818..73088fb22db8 100644 --- a/lldb/source/Utility/UUID.cpp +++ b/lldb/source/Utility/UUID.cpp @@ -1,112 +1,122 @@ //===-- UUID.cpp ----------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "lldb/Utility/UUID.h" #include "lldb/Utility/Stream.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Format.h" #include #include #include using namespace lldb_private; // Whether to put a separator after count uuid bytes. // For the first 16 bytes we follow the traditional UUID format. After that, we // simply put a dash after every 6 bytes. static inline bool separate(size_t count) { if (count >= 10) return (count - 10) % 6 == 0; switch (count) { case 4: case 6: case 8: return true; default: return false; } } +UUID UUID::fromCvRecord(UUID::CvRecordPdb70 debug_info) { + llvm::sys::swapByteOrder(debug_info.Uuid.Data1); + llvm::sys::swapByteOrder(debug_info.Uuid.Data2); + llvm::sys::swapByteOrder(debug_info.Uuid.Data3); + llvm::sys::swapByteOrder(debug_info.Age); + if (debug_info.Age) + return UUID::fromOptionalData(&debug_info, sizeof(debug_info)); + return UUID::fromOptionalData(&debug_info.Uuid, sizeof(debug_info.Uuid)); +} + std::string UUID::GetAsString(llvm::StringRef separator) const { std::string result; llvm::raw_string_ostream os(result); for (auto B : llvm::enumerate(GetBytes())) { if (separate(B.index())) os << separator; os << llvm::format_hex_no_prefix(B.value(), 2, true); } os.flush(); return result; } void UUID::Dump(Stream *s) const { s->PutCString(GetAsString()); } static inline int xdigit_to_int(char ch) { ch = tolower(ch); if (ch >= 'a' && ch <= 'f') return 10 + ch - 'a'; return ch - '0'; } llvm::StringRef UUID::DecodeUUIDBytesFromString(llvm::StringRef p, llvm::SmallVectorImpl &uuid_bytes) { uuid_bytes.clear(); while (p.size() >= 2) { if (isxdigit(p[0]) && isxdigit(p[1])) { int hi_nibble = xdigit_to_int(p[0]); int lo_nibble = xdigit_to_int(p[1]); // Translate the two hex nibble characters into a byte uuid_bytes.push_back((hi_nibble << 4) + lo_nibble); // Skip both hex digits p = p.drop_front(2); } else if (p.front() == '-') { // Skip dashes p = p.drop_front(); } else { // UUID values can only consist of hex characters and '-' chars break; } } return p; } bool UUID::SetFromStringRef(llvm::StringRef str) { llvm::StringRef p = str; // Skip leading whitespace characters p = p.ltrim(); llvm::SmallVector bytes; llvm::StringRef rest = UUID::DecodeUUIDBytesFromString(p, bytes); // Return false if we could not consume the entire string or if the parsed // UUID is empty. if (!rest.empty() || bytes.empty()) return false; *this = fromData(bytes); return true; } bool UUID::SetFromOptionalStringRef(llvm::StringRef str) { bool result = SetFromStringRef(str); if (result) { if (llvm::all_of(m_bytes, [](uint8_t b) { return b == 0; })) Clear(); } return result; }