Index: lldb/include/lldb/Symbol/DWARFCallFrameInfo.h =================================================================== --- lldb/include/lldb/Symbol/DWARFCallFrameInfo.h +++ lldb/include/lldb/Symbol/DWARFCallFrameInfo.h @@ -14,6 +14,7 @@ #include "lldb/Core/AddressRange.h" #include "lldb/Core/dwarf.h" +#include "lldb/Symbol/ICallFrameInfo.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/UnwindPlan.h" #include "lldb/Utility/Flags.h" @@ -30,7 +31,7 @@ // generate an UnwindPlan based on the FDE in the eh_frame / debug_frame // section. -class DWARFCallFrameInfo { +class DWARFCallFrameInfo : public virtual ICallFrameInfo { public: enum Type { EH, DWARF }; @@ -41,19 +42,17 @@ // Locate an AddressRange that includes the provided Address in this object's // eh_frame/debug_info Returns true if a range is found to cover that // address. - bool GetAddressRange(Address addr, AddressRange &range); + bool GetAddressRange(Address addr, AddressRange &range) override; /// Return an UnwindPlan based on the call frame information encoded in the /// FDE of this DWARFCallFrameInfo section. The returned plan will be valid /// (at least) for the given address. - bool GetUnwindPlan(const Address &addr, UnwindPlan &unwind_plan); + bool GetUnwindPlan(const Address &addr, UnwindPlan &unwind_plan) override; /// Return an UnwindPlan based on the call frame information encoded in the /// FDE of this DWARFCallFrameInfo section. The returned plan will be valid /// (at least) for some address in the given range. - bool GetUnwindPlan(const AddressRange &range, UnwindPlan &unwind_plan); - - typedef RangeVector FunctionAddressAndSizeVector; + bool GetUnwindPlan(const AddressRange &range, UnwindPlan &unwind_plan) override; // Build a vector of file address and size for all functions in this Module // based on the eh_frame FDE entries. @@ -70,10 +69,9 @@ // is present in this Module. void - GetFunctionAddressAndSizeVector(FunctionAddressAndSizeVector &function_info); + GetFunctionAddressAndSizeVector(FunctionAddressAndSizeVector &function_info) override; - void ForEachFDEEntries( - const std::function &callback); + void ForEachEntries(const std::function &callback) override; private: enum { CFI_AUG_MAX_SIZE = 8, CFI_HEADER_SIZE = 8 }; Index: lldb/include/lldb/Symbol/ICallFrameInfo.h =================================================================== --- /dev/null +++ lldb/include/lldb/Symbol/ICallFrameInfo.h @@ -0,0 +1,34 @@ +//===-- ICallFrameInfo.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 liblldb_ICallFrameInfo_h_ +#define liblldb_ICallFrameInfo_h_ + +#include "lldb/Core/AddressRange.h" +#include "lldb/Utility/RangeMap.h" + +namespace lldb_private { + +class ICallFrameInfo { +public: + virtual ~ICallFrameInfo() = default; + + virtual bool GetAddressRange(Address addr, AddressRange &range) = 0; + + virtual bool GetUnwindPlan(const Address &addr, UnwindPlan &unwind_plan) = 0; + virtual bool GetUnwindPlan(const AddressRange &range, UnwindPlan &unwind_plan) = 0; + + typedef RangeVector FunctionAddressAndSizeVector; + virtual void GetFunctionAddressAndSizeVector(FunctionAddressAndSizeVector &function_info) = 0; + + virtual void ForEachEntries(const std::function &callback) = 0; +}; + +} // namespace lldb_private + +#endif // liblldb_ICallFrameInfo_h_ Index: lldb/include/lldb/Symbol/ObjectFile.h =================================================================== --- lldb/include/lldb/Symbol/ObjectFile.h +++ lldb/include/lldb/Symbol/ObjectFile.h @@ -650,6 +650,11 @@ /// \return virtual std::vector GetLoadableData(Target &target); + virtual std::unique_ptr CreateEHCallFrameInfo(); + virtual std::unique_ptr CreateDebugCallFrameInfo(); + virtual std::unique_ptr CreateCompactUnwindInfo(); + virtual std::unique_ptr CreateArmUnwindInfo(); + protected: // Member variables. FileSpec m_file; Index: lldb/include/lldb/Symbol/UnwindTable.h =================================================================== --- lldb/include/lldb/Symbol/UnwindTable.h +++ lldb/include/lldb/Symbol/UnwindTable.h @@ -27,8 +27,8 @@ ~UnwindTable(); - lldb_private::DWARFCallFrameInfo *GetEHFrameInfo(); - lldb_private::DWARFCallFrameInfo *GetDebugFrameInfo(); + lldb_private::ICallFrameInfo *GetEHFrameInfo(); + lldb_private::ICallFrameInfo *GetDebugFrameInfo(); lldb_private::CompactUnwindInfo *GetCompactUnwindInfo(); @@ -71,8 +71,8 @@ bool m_initialized; // delay some initialization until ObjectFile is set up std::mutex m_mutex; - std::unique_ptr m_eh_frame_up; - std::unique_ptr m_debug_frame_up; + std::unique_ptr m_eh_frame_up; + std::unique_ptr m_debug_frame_up; std::unique_ptr m_compact_unwind_up; std::unique_ptr m_arm_unwind_up; Index: lldb/include/lldb/lldb-forward.h =================================================================== --- lldb/include/lldb/lldb-forward.h +++ lldb/include/lldb/lldb-forward.h @@ -69,7 +69,7 @@ class ConnectionFileDescriptor; class ConstString; class CXXSyntheticChildren; -class DWARFCallFrameInfo; +class ICallFrameInfo; class DWARFDataExtractor; class DWARFExpression; class DataBuffer; Index: lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h =================================================================== --- lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h +++ lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h @@ -297,7 +297,7 @@ lldb::user_id_t section_id); void ParseUnwindSymbols(lldb_private::Symtab *symbol_table, - lldb_private::DWARFCallFrameInfo *eh_frame); + lldb_private::ICallFrameInfo *eh_frame); /// Relocates debug sections unsigned RelocateDebugSections(const elf::ELFSectionHeader *rel_hdr, Index: lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp =================================================================== --- lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp +++ lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -18,7 +18,7 @@ #include "lldb/Core/PluginManager.h" #include "lldb/Core/Section.h" #include "lldb/Host/FileSystem.h" -#include "lldb/Symbol/DWARFCallFrameInfo.h" +#include "lldb/Symbol/ICallFrameInfo.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" @@ -2691,7 +2691,7 @@ } } - if (DWARFCallFrameInfo *eh_frame = + if (ICallFrameInfo *eh_frame = GetModule()->GetUnwindTable().GetEHFrameInfo()) { if (m_symtab_up == nullptr) m_symtab_up.reset(new Symtab(this)); @@ -2751,7 +2751,7 @@ } void ObjectFileELF::ParseUnwindSymbols(Symtab *symbol_table, - DWARFCallFrameInfo *eh_frame) { + ICallFrameInfo *eh_frame) { SectionList *section_list = GetSectionList(); if (!section_list) return; @@ -2763,8 +2763,8 @@ // recalculate the index first. std::vector new_symbols; - eh_frame->ForEachFDEEntries([this, symbol_table, section_list, &new_symbols]( - lldb::addr_t file_addr, uint32_t size, dw_offset_t) { + eh_frame->ForEachEntries([this, symbol_table, section_list, &new_symbols]( + lldb::addr_t file_addr, uint32_t size) { Symbol *symbol = symbol_table->FindSymbolAtFileAddress(file_addr); if (symbol) { if (!symbol->GetByteSizeIsValid()) { Index: lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp =================================================================== --- lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +++ lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -2422,12 +2422,12 @@ m_type != eTypeDebugInfo) { DWARFCallFrameInfo eh_frame(*this, eh_frame_section_sp, DWARFCallFrameInfo::EH); - DWARFCallFrameInfo::FunctionAddressAndSizeVector functions; + ICallFrameInfo::FunctionAddressAndSizeVector functions; eh_frame.GetFunctionAddressAndSizeVector(functions); addr_t text_base_addr = text_section_sp->GetFileAddress(); size_t count = functions.GetSize(); for (size_t i = 0; i < count; ++i) { - const DWARFCallFrameInfo::FunctionAddressAndSizeVector::Entry *func = + const ICallFrameInfo::FunctionAddressAndSizeVector::Entry *func = functions.GetEntryAtIndex(i); if (func) { FunctionStarts::Entry function_start_entry; Index: lldb/source/Plugins/ObjectFile/PECOFF/CMakeLists.txt =================================================================== --- lldb/source/Plugins/ObjectFile/PECOFF/CMakeLists.txt +++ lldb/source/Plugins/ObjectFile/PECOFF/CMakeLists.txt @@ -1,5 +1,6 @@ add_lldb_library(lldbPluginObjectFilePECOFF PLUGIN ObjectFilePECOFF.cpp + PECallFrameInfo.cpp WindowsMiniDump.cpp LINK_LIBS Index: lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h =================================================================== --- lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h +++ lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h @@ -135,7 +135,15 @@ bool IsWindowsSubsystem(); + uint32_t GetRVA(const lldb_private::Address &addr) const; + lldb_private::Address GetAddress(uint32_t rva); + lldb::addr_t GetFileAddress(uint32_t rva) const; + lldb_private::DataExtractor ReadImageData(uint32_t offset, size_t size); + lldb_private::DataExtractor ReadImageDataByRVA(uint32_t rva, size_t size); + + std::unique_ptr + CreateEHCallFrameInfo() override; protected: bool NeedsEndianSwap() const; @@ -216,6 +224,7 @@ enum coff_data_dir_type { coff_data_dir_export_table = 0, coff_data_dir_import_table = 1, + coff_data_dir_exception_table = 3 }; typedef struct section_header { Index: lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp =================================================================== --- lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp +++ lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "ObjectFilePECOFF.h" +#include "PECallFrameInfo.h" #include "WindowsMiniDump.h" #include "lldb/Core/FileSpecList.h" @@ -514,7 +515,26 @@ 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_file) { // A bit of a hack, but we intend to write to this buffer, so we can't // mmap it. @@ -537,6 +557,15 @@ return data; } +DataExtractor ObjectFilePECOFF::ReadImageDataByRVA(uint32_t rva, size_t size) { + if (m_file) { + Address addr = GetAddress(rva); + rva = addr.GetSection()->GetFileOffset() + addr.GetOffset(); + } + + return ReadImageData(rva, size); +} + // ParseSectionHeaders bool ObjectFilePECOFF::ParseSectionHeaders( uint32_t section_header_data_offset) { @@ -674,14 +703,8 @@ uint32_t data_start = m_coff_header_opt.data_dirs[coff_data_dir_export_table].vmaddr; - uint32_t address_rva = data_start; - if (m_file) { - Address address(m_coff_header_opt.image_base + data_start, sect_list); - address_rva = - address.GetSection()->GetFileOffset() + address.GetOffset(); - } - DataExtractor symtab_data = - ReadImageData(address_rva, m_coff_header_opt.data_dirs[0].vmsize); + DataExtractor symtab_data = ReadImageDataByRVA( + data_start, m_coff_header_opt.data_dirs[0].vmsize); lldb::offset_t offset = 0; // Read export_table header @@ -736,6 +759,19 @@ return m_symtab_up.get(); } +std::unique_ptr ObjectFilePECOFF::CreateEHCallFrameInfo() { + 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 {}; + + return std::make_unique(*this, data_dir_exception.vmaddr, + data_dir_exception.vmsize); +} + bool ObjectFilePECOFF::IsStripped() { // TODO: determine this for COFF return false; Index: lldb/source/Plugins/ObjectFile/PECOFF/PECallFrameInfo.h =================================================================== --- /dev/null +++ lldb/source/Plugins/ObjectFile/PECOFF/PECallFrameInfo.h @@ -0,0 +1,49 @@ +//===-- PECallFrameInfo.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 liblldb_PECallFrameInfo_h_ +#define liblldb_PECallFrameInfo_h_ + +#include "lldb/Symbol/ICallFrameInfo.h" +#include "lldb/Utility/DataExtractor.h" + +class ObjectFilePECOFF; + +namespace llvm { namespace Win64EH { + +struct RuntimeFunction; + +}} + + +class PECallFrameInfo : public virtual lldb_private::ICallFrameInfo { +public: + explicit PECallFrameInfo(ObjectFilePECOFF &object_file, + uint32_t exception_dir_rva, + uint32_t exception_dir_size); + + bool GetAddressRange(lldb_private::Address addr, + lldb_private::AddressRange &range) override; + + bool GetUnwindPlan(const lldb_private::Address &addr, + lldb_private::UnwindPlan &unwind_plan) override; + bool GetUnwindPlan(const lldb_private::AddressRange &range, + lldb_private::UnwindPlan &unwind_plan) override; + + void GetFunctionAddressAndSizeVector(FunctionAddressAndSizeVector &function_info) override; + + void ForEachEntries(const std::function &callback) override; + +private: + const llvm::Win64EH::RuntimeFunction *FindRuntimeFunctionItersectsWithRange(const lldb_private::AddressRange &range) const; + + ObjectFilePECOFF &m_object_file; + lldb_private::DataExtractor m_exception_data; +}; + +#endif // liblldb_PECallFrameInfo_h_ Index: lldb/source/Plugins/ObjectFile/PECOFF/PECallFrameInfo.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/ObjectFile/PECOFF/PECallFrameInfo.cpp @@ -0,0 +1,542 @@ +#include "PECallFrameInfo.h" + +#include "ObjectFilePECOFF.h" + +#include "lldb/Symbol/UnwindPlan.h" +#include "llvm/Support/Win64EH.h" +#include "Plugins/Process/Utility/lldb-x86-register-enums.h" + +using namespace lldb; +using namespace lldb_private; +using namespace llvm::Win64EH; + +template +static const T *TypedRead(const DataExtractor &data_extractor, offset_t &offset, offset_t size = sizeof(T)) { + return static_cast(data_extractor.GetData(&offset, size)); +} + +struct EHInstruction { + enum class Type { + PUSH_REGISTER, + ALLOCATE, + SET_FRAME_POINTER_REGISTER, + SAVE_REGISTER + }; + + uint8_t offset; + Type type; + uint32_t reg; + uint32_t frame_offset; +}; + +using EHProgram = std::vector; + +class UnwindCodesIterator { +public: + UnwindCodesIterator(ObjectFilePECOFF &object_file, uint32_t unwind_info_rva); + + bool GetNext(); + bool IsError() const { return m_error; } + + const UnwindInfo *GetUnwindInfo() const { return m_unwind_info; } + const UnwindCode *GetUnwindCode() const { return m_unwind_code; } + bool IsChained() const { return m_chained; } + +private: + ObjectFilePECOFF &m_object_file; + + bool m_error; + + uint32_t m_unwind_info_rva; + DataExtractor m_unwind_info_data; + const UnwindInfo *m_unwind_info; + + DataExtractor m_unwind_code_data; + offset_t m_unwind_code_offset; + const UnwindCode *m_unwind_code; + + bool m_chained; +}; + +UnwindCodesIterator::UnwindCodesIterator(ObjectFilePECOFF &object_file, uint32_t unwind_info_rva) + : m_object_file(object_file), + m_error(false), + m_unwind_info_rva(unwind_info_rva), + m_unwind_info(nullptr), + m_unwind_code_offset{}, + m_unwind_code(nullptr), + m_chained(false) +{ } + +bool UnwindCodesIterator::GetNext() { + static constexpr int UNWIND_INFO_SIZE = 4; + + m_error = false; + m_unwind_code = nullptr; + while (!m_unwind_code) { + if (!m_unwind_info) { + m_unwind_info_data = m_object_file.ReadImageDataByRVA(m_unwind_info_rva, UNWIND_INFO_SIZE); + + offset_t offset = 0; + m_unwind_info = TypedRead(m_unwind_info_data, offset, UNWIND_INFO_SIZE); + if (!m_unwind_info) { + m_error = true; + break; + } + + m_unwind_code_data = m_object_file.ReadImageDataByRVA(m_unwind_info_rva + UNWIND_INFO_SIZE, m_unwind_info->NumCodes * sizeof(UnwindCode)); + m_unwind_code_offset = 0; + } + + if (m_unwind_code_offset < m_unwind_code_data.GetByteSize()) { + m_unwind_code = TypedRead(m_unwind_code_data, m_unwind_code_offset); + m_error = !m_unwind_code; + break; + } + + if (!(m_unwind_info->getFlags() & UNW_ChainInfo)) + break; + + uint32_t runtime_function_rva = m_unwind_info_rva + UNWIND_INFO_SIZE + ((m_unwind_info->NumCodes + 1) & ~1) * sizeof(UnwindCode); + DataExtractor runtime_function_data = m_object_file.ReadImageDataByRVA(runtime_function_rva, sizeof(RuntimeFunction)); + + offset_t offset = 0; + const auto *runtime_function = TypedRead(runtime_function_data, offset); + if (!runtime_function) { + m_error = true; + break; + } + + m_unwind_info_rva = runtime_function->UnwindInfoOffset; + m_unwind_info = nullptr; + m_chained = true; + } + + return !!m_unwind_code; +} + +class EHProgramBuilder { +public: + EHProgramBuilder(ObjectFilePECOFF &object_file, uint32_t unwind_info_rva); + + bool Build(); + + const EHProgram &GetProgram() const { return m_program; } + +private: + static uint32_t ConvertMachineToLLDBRegister(uint8_t machine_reg); + static uint32_t ConvertXMMToLLDBRegister(uint8_t xmm_reg); + + bool ProcessUnwindCode(UnwindCode code); + void Finalize(); + + bool ParseBigOrScaledFrameOffset(uint32_t &result, bool big, uint32_t scale); + bool ParseBigFrameOffset(uint32_t &result); + bool ParseFrameOffset(uint32_t &result); + + UnwindCodesIterator m_iterator; + EHProgram m_program; +}; + +EHProgramBuilder::EHProgramBuilder(ObjectFilePECOFF &object_file, uint32_t unwind_info_rva) + : m_iterator(object_file, unwind_info_rva) +{ } + +bool EHProgramBuilder::Build() { + while (m_iterator.GetNext()) + if (!ProcessUnwindCode(*m_iterator.GetUnwindCode())) + return false; + + if (m_iterator.IsError()) + return false; + + Finalize(); + + return true; +} + +uint32_t EHProgramBuilder::ConvertMachineToLLDBRegister(uint8_t machine_reg) { + static uint32_t machine_to_lldb_register[] = { + lldb_rax_x86_64, + lldb_rcx_x86_64, + lldb_rdx_x86_64, + lldb_rbx_x86_64, + lldb_rsp_x86_64, + lldb_rbp_x86_64, + lldb_rsi_x86_64, + lldb_rdi_x86_64, + lldb_r8_x86_64, + lldb_r9_x86_64, + lldb_r10_x86_64, + lldb_r11_x86_64, + lldb_r12_x86_64, + lldb_r13_x86_64, + lldb_r14_x86_64, + lldb_r15_x86_64 + }; + + if (machine_reg >= sizeof machine_to_lldb_register / sizeof + machine_to_lldb_register[0]) + return LLDB_INVALID_REGNUM; + + return machine_to_lldb_register[machine_reg]; +} + +uint32_t EHProgramBuilder::ConvertXMMToLLDBRegister(uint8_t xmm_reg) { + static uint32_t xmm_to_lldb_register[] = { + lldb_xmm0_x86_64, + lldb_xmm1_x86_64, + lldb_xmm2_x86_64, + lldb_xmm3_x86_64, + lldb_xmm4_x86_64, + lldb_xmm5_x86_64, + lldb_xmm6_x86_64, + lldb_xmm7_x86_64, + lldb_xmm8_x86_64, + lldb_xmm9_x86_64, + lldb_xmm10_x86_64, + lldb_xmm11_x86_64, + lldb_xmm12_x86_64, + lldb_xmm13_x86_64, + lldb_xmm14_x86_64, + lldb_xmm15_x86_64 + }; + + if (xmm_reg >= sizeof xmm_to_lldb_register / sizeof xmm_to_lldb_register[0]) + return LLDB_INVALID_REGNUM; + + return xmm_to_lldb_register[xmm_reg]; +} + +bool EHProgramBuilder::ProcessUnwindCode(UnwindCode code) { + uint8_t o = m_iterator.IsChained() ? 0 : code.u.CodeOffset; + uint8_t unwind_operation = code.getUnwindOp(); + uint8_t operation_info = code.getOpInfo(); + + switch (unwind_operation) { + case UOP_PushNonVol: { + uint32_t r = ConvertMachineToLLDBRegister(operation_info); + if (r == LLDB_INVALID_REGNUM) + return false; + + m_program.emplace_back(EHInstruction{o, EHInstruction::Type::PUSH_REGISTER, r, 8}); + + return true; + } + case UOP_AllocLarge: { + uint32_t fo; + if (!ParseBigOrScaledFrameOffset(fo, operation_info, 8)) + return false; + + m_program.emplace_back(EHInstruction{o, EHInstruction::Type::ALLOCATE, LLDB_INVALID_REGNUM, fo}); + + return true; + } + case UOP_AllocSmall: { + m_program.emplace_back(EHInstruction{o, EHInstruction::Type::ALLOCATE, LLDB_INVALID_REGNUM, static_cast(operation_info) * 8 + 8}); + return true; + } + case UOP_SetFPReg: { + uint32_t fpr = LLDB_INVALID_REGNUM; + if (m_iterator.GetUnwindInfo()->getFrameRegister()) + fpr = ConvertMachineToLLDBRegister(m_iterator.GetUnwindInfo()->getFrameRegister()); + if (fpr == LLDB_INVALID_REGNUM) + return false; + + uint32_t fpro = static_cast(m_iterator.GetUnwindInfo()->getFrameOffset()) * 16; + + m_program.emplace_back(EHInstruction{o, EHInstruction::Type::SET_FRAME_POINTER_REGISTER, fpr, fpro}); + + return true; + } + case UOP_SaveNonVol: + case UOP_SaveNonVolBig: { + uint32_t r = ConvertMachineToLLDBRegister(operation_info); + if (r == LLDB_INVALID_REGNUM) + return false; + + uint32_t fo; + if (!ParseBigOrScaledFrameOffset(fo, unwind_operation == UOP_SaveNonVolBig, 8)) + return false; + + m_program.emplace_back(EHInstruction{o, EHInstruction::Type::SAVE_REGISTER, r, fo}); + + return true; + } + case UOP_Epilog: { + return m_iterator.GetNext(); + } + case UOP_SpareCode: { + // ReSharper disable once CppIdenticalOperandsInBinaryExpression + return m_iterator.GetNext() && m_iterator.GetNext(); + } + case UOP_SaveXMM128: + case UOP_SaveXMM128Big: { + uint32_t r = ConvertXMMToLLDBRegister(operation_info); + if (r == LLDB_INVALID_REGNUM) + return false; + + uint32_t fo; + if (!ParseBigOrScaledFrameOffset(fo, unwind_operation == UOP_SaveXMM128Big, 16)) + return false; + + m_program.emplace_back(EHInstruction{o, EHInstruction::Type::SAVE_REGISTER, r, fo}); + + return true; + } + case UOP_PushMachFrame: { + if (operation_info) + m_program.emplace_back(EHInstruction{o, EHInstruction::Type::ALLOCATE, LLDB_INVALID_REGNUM, 8}); + m_program.emplace_back(EHInstruction{o, EHInstruction::Type::PUSH_REGISTER, lldb_rip_x86_64, 8}); + m_program.emplace_back(EHInstruction{o, EHInstruction::Type::PUSH_REGISTER, lldb_cs_x86_64, 8}); + m_program.emplace_back(EHInstruction{o, EHInstruction::Type::PUSH_REGISTER, lldb_rflags_x86_64, 8}); + m_program.emplace_back(EHInstruction{o, EHInstruction::Type::PUSH_REGISTER, lldb_rsp_x86_64, 8}); + m_program.emplace_back(EHInstruction{o, EHInstruction::Type::PUSH_REGISTER, lldb_ss_x86_64, 8}); + + return true; + } + default: + return false; + } +} + +void EHProgramBuilder::Finalize() { + for (const EHInstruction &i : m_program) + if (i.reg == lldb_rip_x86_64) + return; + + m_program.emplace_back(EHInstruction{0, EHInstruction::Type::PUSH_REGISTER, lldb_rip_x86_64, 8}); +} + +bool EHProgramBuilder::ParseBigOrScaledFrameOffset(uint32_t &result, bool big, uint32_t scale) { + if (big) { + if (!ParseBigFrameOffset(result)) + return false; + } else { + if (!ParseFrameOffset(result)) + return false; + + result *= scale; + } + + return true; +} + +bool EHProgramBuilder::ParseBigFrameOffset(uint32_t &result) { + if (!m_iterator.GetNext()) + return false; + + result = m_iterator.GetUnwindCode()->FrameOffset; + + if (!m_iterator.GetNext()) + return false; + + result += static_cast(m_iterator.GetUnwindCode()->FrameOffset) << 16; + + return true; +} + +bool EHProgramBuilder::ParseFrameOffset(uint32_t &result) { + if (!m_iterator.GetNext()) + return false; + + result = m_iterator.GetUnwindCode()->FrameOffset; + + return true; +} + +class EHProgramRange { +public: + EHProgramRange(EHProgram::const_iterator begin, EHProgram::const_iterator end); + + std::unique_ptr BuildUnwindPlanRow() const; + +private: + int32_t GetCFAFrameOffset() const; + + EHProgram::const_iterator m_begin; + EHProgram::const_iterator m_end; +}; + +EHProgramRange::EHProgramRange(EHProgram::const_iterator begin, EHProgram::const_iterator end) + : m_begin(begin), + m_end(end) +{ } + +std::unique_ptr EHProgramRange::BuildUnwindPlanRow() const { + std::unique_ptr row = std::make_unique(); + + if (m_begin != m_end) + row->SetOffset(m_begin->offset); + + int32_t cfa_frame_offset = GetCFAFrameOffset(); + + bool frame_pointer_found = false; + for (EHProgram::const_iterator it = m_begin; it != m_end; ++it) { + switch (it->type) { + case EHInstruction::Type::SET_FRAME_POINTER_REGISTER: + row->GetCFAValue().SetIsRegisterPlusOffset(it->reg, cfa_frame_offset - it->frame_offset); + frame_pointer_found = true; + break; + default: + break; + } + if (frame_pointer_found) + break; + } + if (!frame_pointer_found) + row->GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, cfa_frame_offset); + + int32_t rsp_frame_offset = 0; + for (EHProgram::const_iterator it = m_begin; it != m_end; ++it) { + switch (it->type) { + case EHInstruction::Type::PUSH_REGISTER: + row->SetRegisterLocationToAtCFAPlusOffset(it->reg, rsp_frame_offset - cfa_frame_offset, false); + rsp_frame_offset += it->frame_offset; + break; + case EHInstruction::Type::ALLOCATE: + rsp_frame_offset += it->frame_offset; + break; + case EHInstruction::Type::SAVE_REGISTER: + row->SetRegisterLocationToAtCFAPlusOffset(it->reg, it->frame_offset - cfa_frame_offset, false); + break; + default: + break; + } + } + + row->SetRegisterLocationToIsCFAPlusOffset(lldb_rsp_x86_64, 0, false); + + return row; +} + +int32_t EHProgramRange::GetCFAFrameOffset() const { + int32_t result = 0; + + for (EHProgram::const_iterator it = m_begin; it != m_end; ++it) { + switch (it->type) { + case EHInstruction::Type::PUSH_REGISTER: + case EHInstruction::Type::ALLOCATE: + result += it->frame_offset; + default: + break; + } + } + + return result; +} + +PECallFrameInfo::PECallFrameInfo(ObjectFilePECOFF &object_file, + uint32_t exception_dir_rva, + uint32_t exception_dir_size) + : m_object_file(object_file), + m_exception_data(object_file.ReadImageDataByRVA(exception_dir_rva, exception_dir_size)) { +} + +bool PECallFrameInfo::GetAddressRange(Address addr, AddressRange& range) { + range.Clear(); + + const RuntimeFunction *runtime_function = FindRuntimeFunctionItersectsWithRange(AddressRange(addr, 1)); + if (!runtime_function) + return false; + + range.GetBaseAddress() = m_object_file.GetAddress(runtime_function->StartAddress); + range.SetByteSize(runtime_function->EndAddress - runtime_function->StartAddress); + + return true; +} + +bool PECallFrameInfo::GetUnwindPlan(const Address& addr, UnwindPlan& unwind_plan) { + return GetUnwindPlan(AddressRange(addr, 1), unwind_plan); +} + +bool PECallFrameInfo::GetUnwindPlan(const AddressRange& range, UnwindPlan& unwind_plan) { + unwind_plan.Clear(); + + unwind_plan.SetSourceName("PE EH info"); + unwind_plan.SetSourcedFromCompiler(eLazyBoolYes); + unwind_plan.SetRegisterKind(eRegisterKindLLDB); + + const RuntimeFunction *runtime_function = FindRuntimeFunctionItersectsWithRange(range); + if (!runtime_function) + return false; + + EHProgramBuilder builder(m_object_file, runtime_function->UnwindInfoOffset); + if (!builder.Build()) + return false; + + std::vector rows; + + uint32_t last_offset = UINT32_MAX; + for (auto it = builder.GetProgram().begin(); it != builder.GetProgram().end(); ++it) { + if (it->offset == last_offset) + continue; + + EHProgramRange program_range = EHProgramRange(it, builder.GetProgram().end()); + rows.push_back(program_range.BuildUnwindPlanRow()); + + last_offset = it->offset; + } + + for (auto it = rows.rbegin(); it != rows.rend(); ++it) + unwind_plan.AppendRow(*it); + + unwind_plan.SetPlanValidAddressRange(AddressRange(m_object_file.GetAddress(runtime_function->StartAddress), runtime_function->EndAddress - runtime_function->StartAddress)); + unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); + + return true; +} + +void PECallFrameInfo::GetFunctionAddressAndSizeVector(FunctionAddressAndSizeVector& function_info) { + function_info.Clear(); + + offset_t offset = 0; + while(offset < m_exception_data.GetByteSize()) { + const auto *runtime_function = TypedRead(m_exception_data, offset); + if (!runtime_function) + break; + + function_info.Append({m_object_file.GetFileAddress(runtime_function->StartAddress), + runtime_function->EndAddress - runtime_function->StartAddress}); + } +} + +void PECallFrameInfo::ForEachEntries(const std::function& callback) { + offset_t offset = 0; + while(offset < m_exception_data.GetByteSize()) { + const auto *runtime_function = TypedRead(m_exception_data, offset); + if (!runtime_function) + break; + + if (!callback(m_object_file.GetFileAddress(runtime_function->StartAddress), + runtime_function->EndAddress - runtime_function->StartAddress)) + break; + } +} + +const RuntimeFunction *PECallFrameInfo::FindRuntimeFunctionItersectsWithRange(const AddressRange& range) const { + uint32_t rva = m_object_file.GetRVA(range.GetBaseAddress()); + addr_t size = range.GetByteSize(); + + uint32_t begin = 0; + uint32_t end = m_exception_data.GetByteSize() / sizeof(RuntimeFunction); + while (begin < end) { + uint32_t curr = (begin + end) / 2; + + offset_t offset = curr * sizeof(RuntimeFunction); + const auto *runtime_function = TypedRead(m_exception_data, offset); + if (!runtime_function) + break; + + if (runtime_function->StartAddress < rva + size && runtime_function->EndAddress > rva) + return runtime_function; + + if (runtime_function->StartAddress >= rva + size) + end = curr; + + if (runtime_function->EndAddress <= rva) + begin = curr + 1; + } + + return nullptr; +} Index: lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp =================================================================== --- lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp +++ lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp @@ -762,7 +762,7 @@ // Even with -fomit-frame-pointer, we can try eh_frame to get back on // track. - DWARFCallFrameInfo *eh_frame = + ICallFrameInfo *eh_frame = pc_module_sp->GetUnwindTable().GetEHFrameInfo(); if (eh_frame) { unwind_plan_sp = std::make_shared(lldb::eRegisterKindGeneric); Index: lldb/source/Symbol/DWARFCallFrameInfo.cpp =================================================================== --- lldb/source/Symbol/DWARFCallFrameInfo.cpp +++ lldb/source/Symbol/DWARFCallFrameInfo.cpp @@ -18,8 +18,8 @@ #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Timer.h" -#include #include +#include using namespace lldb; using namespace lldb_private; @@ -450,9 +450,10 @@ } if (next_entry > m_cfi_data.GetByteSize() + 1) { - Host::SystemLog(Host::eSystemLogError, "error: Invalid fde/cie next " - "entry offset of 0x%x found in " - "cie/fde at 0x%x\n", + Host::SystemLog(Host::eSystemLogError, + "error: Invalid fde/cie next " + "entry offset of 0x%x found in " + "cie/fde at 0x%x\n", next_entry, current_entry); // Don't trust anything in this eh_frame section if we find blatantly // invalid data. @@ -512,9 +513,10 @@ FDEEntryMap::Entry fde(addr, length, current_entry); m_fde_index.Append(fde); } else { - Host::SystemLog(Host::eSystemLogError, "error: unable to find CIE at " - "0x%8.8x for cie_id = 0x%8.8x for " - "entry at 0x%8.8x.\n", + Host::SystemLog(Host::eSystemLogError, + "error: unable to find CIE at " + "0x%8.8x for cie_id = 0x%8.8x for " + "entry at 0x%8.8x.\n", cie_offset, cie_id, current_entry); } offset = next_entry; @@ -603,7 +605,7 @@ offset += aug_data_len; } unwind_plan.SetUnwindPlanForSignalTrap( - strchr(cie->augmentation, 'S') ? eLazyBoolYes : eLazyBoolNo); + strchr(cie->augmentation, 'S') ? eLazyBoolYes : eLazyBoolNo); Address lsda_data; Address personality_function_ptr; @@ -1011,13 +1013,13 @@ return false; } -void DWARFCallFrameInfo::ForEachFDEEntries( - const std::function &callback) { +void DWARFCallFrameInfo::ForEachEntries( + const std::function &callback) { GetFDEIndex(); for (size_t i = 0, c = m_fde_index.GetSize(); i < c; ++i) { const FDEEntryMap::Entry &entry = m_fde_index.GetEntryRef(i); - if (!callback(entry.base, entry.size, entry.data)) + if (!callback(entry.base, entry.size)) break; } } Index: lldb/source/Symbol/FuncUnwinders.cpp =================================================================== --- lldb/source/Symbol/FuncUnwinders.cpp +++ lldb/source/Symbol/FuncUnwinders.cpp @@ -104,7 +104,7 @@ m_tried_unwind_plan_eh_frame = true; if (m_range.GetBaseAddress().IsValid()) { - DWARFCallFrameInfo *eh_frame = m_unwind_table.GetEHFrameInfo(); + ICallFrameInfo *eh_frame = m_unwind_table.GetEHFrameInfo(); if (eh_frame) { m_unwind_plan_eh_frame_sp = std::make_shared(lldb::eRegisterKindGeneric); @@ -122,7 +122,7 @@ m_tried_unwind_plan_debug_frame = true; if (m_range.GetBaseAddress().IsValid()) { - DWARFCallFrameInfo *debug_frame = m_unwind_table.GetDebugFrameInfo(); + ICallFrameInfo *debug_frame = m_unwind_table.GetDebugFrameInfo(); if (debug_frame) { m_unwind_plan_debug_frame_sp = std::make_shared(lldb::eRegisterKindGeneric); Index: lldb/source/Symbol/ObjectFile.cpp =================================================================== --- lldb/source/Symbol/ObjectFile.cpp +++ lldb/source/Symbol/ObjectFile.cpp @@ -22,6 +22,9 @@ #include "lldb/Utility/RegularExpression.h" #include "lldb/Utility/Timer.h" #include "lldb/lldb-private.h" +#include "lldb/Symbol/DWARFCallFrameInfo.h" +#include "lldb/Symbol/CompactUnwindInfo.h" +#include "lldb/Symbol/ArmUnwindInfo.h" using namespace lldb; using namespace lldb_private; @@ -671,6 +674,60 @@ return loadables; } +std::unique_ptr ObjectFile::CreateEHCallFrameInfo() { + SectionList *sl = GetSectionList(); + if (!sl) + return {}; + + SectionSP sect = sl->FindSectionByType(eSectionTypeEHFrame, true); + if (!sect) + return {}; + + return std::make_unique(*this, sect, + DWARFCallFrameInfo::EH); +} + +std::unique_ptr ObjectFile::CreateDebugCallFrameInfo() { + SectionList *sl = GetSectionList(); + if (!sl) + return {}; + + SectionSP sect = sl->FindSectionByType(eSectionTypeDWARFDebugFrame, true); + if (!sect) + return {}; + + return std::make_unique(*this, sect, + DWARFCallFrameInfo::DWARF); +} + +std::unique_ptr ObjectFile::CreateCompactUnwindInfo() { + SectionList *sl = GetSectionList(); + if (!sl) + return {}; + + SectionSP sect = sl->FindSectionByType(eSectionTypeCompactUnwind, true); + if (!sect) + return {}; + + return std::make_unique(*this, sect); +} + +std::unique_ptr ObjectFile::CreateArmUnwindInfo() { + SectionList *sl = GetSectionList(); + if (!sl) + return {}; + + SectionSP sect = sl->FindSectionByType(eSectionTypeARMexidx, true); + if (!sect) + return {}; + + SectionSP sect_extab = sl->FindSectionByType(eSectionTypeARMextab, true); + if (!sect_extab) + return {}; + + return std::make_unique(*this, sect, sect_extab); +} + void ObjectFile::RelocateSection(lldb_private::Section *section) { } Index: lldb/source/Symbol/UnwindTable.cpp =================================================================== --- lldb/source/Symbol/UnwindTable.cpp +++ lldb/source/Symbol/UnwindTable.cpp @@ -47,34 +47,10 @@ if (!object_file) return; - SectionList *sl = m_module.GetSectionList(); - if (!sl) - return; - - SectionSP sect = sl->FindSectionByType(eSectionTypeEHFrame, true); - if (sect.get()) { - m_eh_frame_up.reset( - new DWARFCallFrameInfo(*object_file, sect, DWARFCallFrameInfo::EH)); - } - - sect = sl->FindSectionByType(eSectionTypeDWARFDebugFrame, true); - if (sect) { - m_debug_frame_up.reset( - new DWARFCallFrameInfo(*object_file, sect, DWARFCallFrameInfo::DWARF)); - } - - sect = sl->FindSectionByType(eSectionTypeCompactUnwind, true); - if (sect) { - m_compact_unwind_up.reset(new CompactUnwindInfo(*object_file, sect)); - } - - sect = sl->FindSectionByType(eSectionTypeARMexidx, true); - if (sect) { - SectionSP sect_extab = sl->FindSectionByType(eSectionTypeARMextab, true); - if (sect_extab.get()) { - m_arm_unwind_up.reset(new ArmUnwindInfo(*object_file, sect, sect_extab)); - } - } + m_eh_frame_up = object_file->CreateEHCallFrameInfo(); + m_debug_frame_up = object_file->CreateDebugCallFrameInfo(); + m_compact_unwind_up = object_file->CreateCompactUnwindInfo(); + m_arm_unwind_up = object_file->CreateArmUnwindInfo(); } UnwindTable::~UnwindTable() {} @@ -83,12 +59,6 @@ SymbolContext &sc) { AddressRange range; - // First check the symbol context - if (sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0, - false, range) && - range.GetBaseAddress().IsValid()) - return range; - // Does the eh_frame unwind info has a function bounds for this addr? if (m_eh_frame_up && m_eh_frame_up->GetAddressRange(addr, range)) return range; @@ -97,6 +67,12 @@ if (m_debug_frame_up && m_debug_frame_up->GetAddressRange(addr, range)) return range; + // Last check the symbol context + if (sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0, + false, range) && + range.GetBaseAddress().IsValid()) + return range; + return llvm::None; } @@ -162,12 +138,12 @@ s.EOL(); } -DWARFCallFrameInfo *UnwindTable::GetEHFrameInfo() { +ICallFrameInfo *UnwindTable::GetEHFrameInfo() { Initialize(); return m_eh_frame_up.get(); } -DWARFCallFrameInfo *UnwindTable::GetDebugFrameInfo() { +ICallFrameInfo *UnwindTable::GetDebugFrameInfo() { Initialize(); return m_debug_frame_up.get(); } Index: lldb/unittests/Symbol/CMakeLists.txt =================================================================== --- lldb/unittests/Symbol/CMakeLists.txt +++ lldb/unittests/Symbol/CMakeLists.txt @@ -3,6 +3,7 @@ PostfixExpressionTest.cpp TestClangASTContext.cpp TestDWARFCallFrameInfo.cpp + TestPECallFrameInfo.cpp TestType.cpp TestLineEntry.cpp @@ -13,6 +14,7 @@ lldbPluginObjectFileELF lldbPluginObjectFileMachO lldbPluginSymbolFileDWARF + lldbPluginObjectFilePECOFF lldbPluginSymbolFileSymtab LLVMTestingSupport ) Index: lldb/unittests/Symbol/TestPECallFrameInfo.cpp =================================================================== --- /dev/null +++ lldb/unittests/Symbol/TestPECallFrameInfo.cpp @@ -0,0 +1,344 @@ +//===-- TestPECallFrameInfo.cpp ------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h" +#include "Plugins/Process/Utility/lldb-x86-register-enums.h" +#include "Plugins/SymbolFile/Symtab/SymbolFileSymtab.h" + +#include "TestingSupport/TestUtilities.h" + +#include "lldb/Core/Module.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Symbol/ICallFrameInfo.h" +#include "lldb/Symbol/UnwindPlan.h" + +#include "llvm/Testing/Support/Error.h" + +using namespace lldb_private; +using namespace lldb; + +class PECallFrameInfoTest : public testing::Test { +public: + void SetUp() override { + FileSystem::Initialize(); + HostInfo::Initialize(); + ObjectFilePECOFF::Initialize(); + SymbolFileSymtab::Initialize(); + } + + void TearDown() override { + SymbolFileSymtab::Terminate(); + ObjectFilePECOFF::Terminate(); + HostInfo::Terminate(); + FileSystem::Terminate(); + } + +protected: + void GetUnwindPlan(addr_t file_addr, UnwindPlan &plan) const; +}; + +void PECallFrameInfoTest::GetUnwindPlan(addr_t file_addr, UnwindPlan &plan) const { + llvm::Expected ExpectedFile = TestFile::fromYaml( + R"( +--- !COFF +OptionalHeader: + AddressOfEntryPoint: 0 + ImageBase: 16777216 + SectionAlignment: 4096 + FileAlignment: 512 + MajorOperatingSystemVersion: 6 + MinorOperatingSystemVersion: 0 + MajorImageVersion: 0 + MinorImageVersion: 0 + MajorSubsystemVersion: 6 + MinorSubsystemVersion: 0 + Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI + DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ] + SizeOfStackReserve: 1048576 + SizeOfStackCommit: 4096 + SizeOfHeapReserve: 1048576 + SizeOfHeapCommit: 4096 + ExportTable: + RelativeVirtualAddress: 0 + Size: 0 + ImportTable: + RelativeVirtualAddress: 0 + Size: 0 + ResourceTable: + RelativeVirtualAddress: 0 + Size: 0 + ExceptionTable: + RelativeVirtualAddress: 12288 + Size: 60 + CertificateTable: + RelativeVirtualAddress: 0 + Size: 0 + BaseRelocationTable: + RelativeVirtualAddress: 0 + Size: 0 + Debug: + RelativeVirtualAddress: 0 + Size: 0 + Architecture: + RelativeVirtualAddress: 0 + Size: 0 + GlobalPtr: + RelativeVirtualAddress: 0 + Size: 0 + TlsTable: + RelativeVirtualAddress: 0 + Size: 0 + LoadConfigTable: + RelativeVirtualAddress: 0 + Size: 0 + BoundImport: + RelativeVirtualAddress: 0 + Size: 0 + IAT: + RelativeVirtualAddress: 0 + Size: 0 + DelayImportDescriptor: + RelativeVirtualAddress: 0 + Size: 0 + ClrRuntimeHeader: + RelativeVirtualAddress: 0 + Size: 0 +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + VirtualAddress: 4096 + VirtualSize: 4096 + - Name: .rdata + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + VirtualAddress: 8192 + VirtualSize: 68 + SectionData: 010C06000C3208F006E00470036002302105020005540D0000100000001100000020000019400E352F74670028646600213465001A3315015E000EF00CE00AD008C00650 + + +# Unwind info at 0x2000: +# 01 0C 06 00 No chained info, prolog size = 0xC, unwind codes size is 6 words, no frame register +# 0C 32 UOP_AllocSmall(2) 3 * 8 + 8 bytes, offset in prolog is 0xC +# 08 F0 UOP_PushNonVol(0) R15(0xF), offset in prolog is 8 +# 06 E0 UOP_PushNonVol(0) R14(0xE), offset in prolog is 6 +# 04 70 UOP_PushNonVol(0) RDI(7), offset in prolog is 4 +# 03 60 UOP_PushNonVol(0) RSI(6), offset in prolog is 3 +# 02 30 UOP_PushNonVol(0) RBX(3), offset in prolog is 2 +# Corresponding prolog: +# 00 push rbx +# 02 push rsi +# 03 push rdi +# 04 push r14 +# 06 push r15 +# 08 sub rsp, 20h + +# Unwind info at 0x2010: +# 21 05 02 00 Has chained info, prolog size = 5, unwind codes size is 2 words, no frame register +# 05 54 0D 00 UOP_SaveNonVol(4) RBP(5) to RSP + 0xD * 8, offset in prolog is 5 +# Chained runtime function: +# 00 10 00 00 Start address is 0x1000 +# 00 11 00 00 End address is 0x1100 +# 00 20 00 00 Unwind info RVA is 0x2000 +# Corresponding prolog: +# 00 mov [rsp+68h], rbp + +# Unwind info at 0x2024: +# 19 40 0E 35 No chained info, prolog size = 0x40, unwind codes size is 0xE words, frame register is RBP, frame register offset is RSP + 3 * 16 +# 2F 74 67 00 UOP_SaveNonVol(4) RDI(7) to RSP + 0x67 * 8, offset in prolog is 0x2F +# 28 64 66 00 UOP_SaveNonVol(4) RSI(6) to RSP + 0x66 * 8, offset in prolog is 0x28 +# 21 34 65 00 UOP_SaveNonVol(4) RBX(3) to RSP + 0x65 * 8, offset in prolog is 0x21 +# 1A 33 UOP_SetFPReg(3), offset in prolog is 0x1A +# 15 01 5E 00 UOP_AllocLarge(1) 0x5E * 8 bytes, offset in prolog is 0x15 +# 0E F0 UOP_PushNonVol(0) R15(0xF), offset in prolog is 0xE +# 0C E0 UOP_PushNonVol(0) R14(0xE), offset in prolog is 0xC +# 0A D0 UOP_PushNonVol(0) R13(0xD), offset in prolog is 0xA +# 08 C0 UOP_PushNonVol(0) R12(0xC), offset in prolog is 8 +# 06 50 UOP_PushNonVol(0) RBP(5), offset in prolog is 6 +# Corresponding prolog: +# 00 mov [rsp+8], rcx +# 05 push rbp +# 06 push r12 +# 08 push r13 +# 0A push r14 +# 0C push r15 +# 0E sub rsp, 2F0h +# 15 lea rbp, [rsp+30h] +# 1A mov [rbp+2F8h], rbx +# 21 mov [rbp+300h], rsi +# 28 mov [rbp+308h], rdi + + - Name: .pdata + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + VirtualAddress: 12288 + VirtualSize: 60 + SectionData: 000000000000000000000000000000000000000000000000001000000011000000200000001100000012000010200000001200000013000024200000 + +# 00 00 00 00 +# 00 00 00 00 Test correct processing of empty runtime functions at begin +# 00 00 00 00 + +# 00 00 00 00 +# 00 00 00 00 Test correct processing of empty runtime functions at begin +# 00 00 00 00 + +# 00 10 00 00 Start address is 0x1000 +# 00 11 00 00 End address is 0x1100 +# 00 20 00 00 Unwind info RVA is 0x2000 + +# 00 11 00 00 Start address is 0x1100 +# 00 12 00 00 End address is 0x1200 +# 10 20 00 00 Unwind info RVA is 0x2010 + +# 00 12 00 00 Start address is 0x1200 +# 00 13 00 00 End address is 0x1300 +# 24 20 00 00 Unwind info RVA is 0x2024 + +symbols: [] +... +)"); + ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded()); + + ModuleSP module_sp = std::make_shared(ModuleSpec(FileSpec(ExpectedFile->name()))); + ObjectFile *object_file = module_sp->GetObjectFile(); + ASSERT_NE(object_file, nullptr); + + std::unique_ptr cfi = object_file->CreateEHCallFrameInfo(); + ASSERT_NE(cfi.get(), nullptr); + + SectionList *sect_list = object_file->GetSectionList(); + ASSERT_NE(sect_list, nullptr); + + EXPECT_TRUE(cfi->GetUnwindPlan(Address(file_addr, sect_list), plan)); +} + +TEST_F(PECallFrameInfoTest, Basic_eh) { + UnwindPlan plan(eRegisterKindLLDB); + GetUnwindPlan(0x1001080, plan); + EXPECT_EQ(plan.GetRowCount(), 7); + + UnwindPlan::Row row; + row.SetOffset(0); + row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 8); + row.SetRegisterLocationToIsCFAPlusOffset(lldb_rsp_x86_64, 0, true); + row.SetRegisterLocationToAtCFAPlusOffset(lldb_rip_x86_64, -8, true); + EXPECT_EQ(*plan.GetRowAtIndex(0), row); + + row.SetOffset(2); + row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x10); + row.SetRegisterLocationToAtCFAPlusOffset(lldb_rbx_x86_64, -0x10, true); + EXPECT_EQ(*plan.GetRowAtIndex(1), row); + + row.SetOffset(3); + row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x18); + row.SetRegisterLocationToAtCFAPlusOffset(lldb_rsi_x86_64, -0x18, true); + EXPECT_EQ(*plan.GetRowAtIndex(2), row); + + row.SetOffset(4); + row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x20); + row.SetRegisterLocationToAtCFAPlusOffset(lldb_rdi_x86_64, -0x20, true); + EXPECT_EQ(*plan.GetRowAtIndex(3), row); + + row.SetOffset(6); + row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x28); + row.SetRegisterLocationToAtCFAPlusOffset(lldb_r14_x86_64, -0x28, true); + EXPECT_EQ(*plan.GetRowAtIndex(4), row); + + row.SetOffset(8); + row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x30); + row.SetRegisterLocationToAtCFAPlusOffset(lldb_r15_x86_64, -0x30, true); + EXPECT_EQ(*plan.GetRowAtIndex(5), row); + + row.SetOffset(0xC); + row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x50); + EXPECT_EQ(*plan.GetRowAtIndex(6), row); +} + +TEST_F(PECallFrameInfoTest, Chained_eh) { + UnwindPlan plan(eRegisterKindLLDB); + GetUnwindPlan(0x1001180, plan); + EXPECT_EQ(plan.GetRowCount(), 2); + + UnwindPlan::Row row; + row.SetOffset(0); + row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x50); + row.SetRegisterLocationToIsCFAPlusOffset(lldb_rsp_x86_64, 0, true); + row.SetRegisterLocationToAtCFAPlusOffset(lldb_rip_x86_64, -8, true); + row.SetRegisterLocationToAtCFAPlusOffset(lldb_rbx_x86_64, -0x10, true); + row.SetRegisterLocationToAtCFAPlusOffset(lldb_rsi_x86_64, -0x18, true); + row.SetRegisterLocationToAtCFAPlusOffset(lldb_rdi_x86_64, -0x20, true); + row.SetRegisterLocationToAtCFAPlusOffset(lldb_r14_x86_64, -0x28, true); + row.SetRegisterLocationToAtCFAPlusOffset(lldb_r15_x86_64, -0x30, true); + EXPECT_EQ(*plan.GetRowAtIndex(0), row); + + row.SetOffset(5); + row.SetRegisterLocationToAtCFAPlusOffset(lldb_rbp_x86_64, 0x18, true); + EXPECT_EQ(*plan.GetRowAtIndex(1), row); +} + +TEST_F(PECallFrameInfoTest, Frame_reg_eh) { + UnwindPlan plan(eRegisterKindLLDB); + GetUnwindPlan(0x1001280, plan); + EXPECT_EQ(plan.GetRowCount(), 11); + + UnwindPlan::Row row; + row.SetOffset(0); + row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 8); + row.SetRegisterLocationToIsCFAPlusOffset(lldb_rsp_x86_64, 0, true); + row.SetRegisterLocationToAtCFAPlusOffset(lldb_rip_x86_64, -8, true); + EXPECT_EQ(*plan.GetRowAtIndex(0), row); + + row.SetOffset(6); + row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x10); + row.SetRegisterLocationToAtCFAPlusOffset(lldb_rbp_x86_64, -0x10, true); + EXPECT_EQ(*plan.GetRowAtIndex(1), row); + + row.SetOffset(8); + row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x18); + row.SetRegisterLocationToAtCFAPlusOffset(lldb_r12_x86_64, -0x18, true); + EXPECT_EQ(*plan.GetRowAtIndex(2), row); + + row.SetOffset(0xA); + row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x20); + row.SetRegisterLocationToAtCFAPlusOffset(lldb_r13_x86_64, -0x20, true); + EXPECT_EQ(*plan.GetRowAtIndex(3), row); + + row.SetOffset(0xC); + row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x28); + row.SetRegisterLocationToAtCFAPlusOffset(lldb_r14_x86_64, -0x28, true); + EXPECT_EQ(*plan.GetRowAtIndex(4), row); + + row.SetOffset(0xE); + row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x30); + row.SetRegisterLocationToAtCFAPlusOffset(lldb_r15_x86_64, -0x30, true); + EXPECT_EQ(*plan.GetRowAtIndex(5), row); + + row.SetOffset(0x15); + row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64, 0x320); + EXPECT_EQ(*plan.GetRowAtIndex(6), row); + + row.SetOffset(0x1A); + row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rbp_x86_64, 0x2F0); + EXPECT_EQ(*plan.GetRowAtIndex(7), row); + + row.SetOffset(0x21); + row.SetRegisterLocationToAtCFAPlusOffset(lldb_rbx_x86_64, 8, true); + EXPECT_EQ(*plan.GetRowAtIndex(8), row); + + row.SetOffset(0x28); + row.SetRegisterLocationToAtCFAPlusOffset(lldb_rsi_x86_64, 0x10, true); + EXPECT_EQ(*plan.GetRowAtIndex(9), row); + + row.SetOffset(0x2F); + row.SetRegisterLocationToAtCFAPlusOffset(lldb_rdi_x86_64, 0x18, true); + EXPECT_EQ(*plan.GetRowAtIndex(10), row); +} Index: llvm/include/llvm/Support/Win64EH.h =================================================================== --- llvm/include/llvm/Support/Win64EH.h +++ llvm/include/llvm/Support/Win64EH.h @@ -30,7 +30,9 @@ UOP_SetFPReg, UOP_SaveNonVol, UOP_SaveNonVolBig, - UOP_SaveXMM128 = 8, + UOP_Epilog, + UOP_SpareCode, + UOP_SaveXMM128, UOP_SaveXMM128Big, UOP_PushMachFrame, // The following set of unwind opcodes is for ARM64. They are documented at