summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lldb/include/lldb/Symbol/CallFrameInfo.h28
-rw-r--r--lldb/include/lldb/Symbol/FuncUnwinders.h9
-rw-r--r--lldb/include/lldb/Symbol/ObjectFile.h3
-rw-r--r--lldb/include/lldb/Symbol/UnwindTable.h3
-rw-r--r--lldb/include/lldb/lldb-forward.h1
-rw-r--r--lldb/source/Commands/CommandObjectTarget.cpp19
-rw-r--r--lldb/source/Plugins/ObjectFile/PECOFF/CMakeLists.txt1
-rw-r--r--lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp52
-rw-r--r--lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h8
-rw-r--r--lldb/source/Plugins/ObjectFile/PECOFF/PECallFrameInfo.cpp535
-rw-r--r--lldb/source/Plugins/ObjectFile/PECOFF/PECallFrameInfo.h47
-rw-r--r--lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp17
-rw-r--r--lldb/source/Symbol/FuncUnwinders.cpp65
-rw-r--r--lldb/source/Symbol/ObjectFile.cpp5
-rw-r--r--lldb/source/Symbol/UnwindTable.cpp18
-rw-r--r--lldb/unittests/ObjectFile/CMakeLists.txt1
-rw-r--r--lldb/unittests/ObjectFile/PECOFF/CMakeLists.txt8
-rw-r--r--lldb/unittests/ObjectFile/PECOFF/TestPECallFrameInfo.cpp336
-rw-r--r--llvm/include/llvm/Support/Win64EH.h4
19 files changed, 1149 insertions, 11 deletions
diff --git a/lldb/include/lldb/Symbol/CallFrameInfo.h b/lldb/include/lldb/Symbol/CallFrameInfo.h
new file mode 100644
index 00000000000..765ddb41ab0
--- /dev/null
+++ b/lldb/include/lldb/Symbol/CallFrameInfo.h
@@ -0,0 +1,28 @@
+//===-- CallFrameInfo.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_CallFrameInfo_h_
+#define liblldb_CallFrameInfo_h_
+
+#include "lldb/Core/Address.h"
+
+namespace lldb_private {
+
+class CallFrameInfo {
+public:
+ virtual ~CallFrameInfo() = 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;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CallFrameInfo_h_
diff --git a/lldb/include/lldb/Symbol/FuncUnwinders.h b/lldb/include/lldb/Symbol/FuncUnwinders.h
index cc767d4e1e8..c49f6b0fa94 100644
--- a/lldb/include/lldb/Symbol/FuncUnwinders.h
+++ b/lldb/include/lldb/Symbol/FuncUnwinders.h
@@ -76,6 +76,11 @@ public:
lldb::UnwindPlanSP GetAssemblyUnwindPlan(Target &target, Thread &thread);
+ lldb::UnwindPlanSP GetObjectFileUnwindPlan(Target &target);
+
+ lldb::UnwindPlanSP GetObjectFileAugmentedUnwindPlan(Target &target,
+ Thread &thread);
+
lldb::UnwindPlanSP GetEHFrameUnwindPlan(Target &target);
lldb::UnwindPlanSP GetEHFrameAugmentedUnwindPlan(Target &target,
@@ -113,10 +118,12 @@ private:
std::recursive_mutex m_mutex;
lldb::UnwindPlanSP m_unwind_plan_assembly_sp;
+ lldb::UnwindPlanSP m_unwind_plan_object_file_sp;
lldb::UnwindPlanSP m_unwind_plan_eh_frame_sp;
lldb::UnwindPlanSP m_unwind_plan_debug_frame_sp;
// augmented by assembly inspection so it's valid everywhere
+ lldb::UnwindPlanSP m_unwind_plan_object_file_augmented_sp;
lldb::UnwindPlanSP m_unwind_plan_eh_frame_augmented_sp;
lldb::UnwindPlanSP m_unwind_plan_debug_frame_augmented_sp;
@@ -130,7 +137,9 @@ private:
// Fetching the UnwindPlans can be expensive - if we've already attempted to
// get one & failed, don't try again.
bool m_tried_unwind_plan_assembly : 1, m_tried_unwind_plan_eh_frame : 1,
+ m_tried_unwind_plan_object_file : 1,
m_tried_unwind_plan_debug_frame : 1,
+ m_tried_unwind_plan_object_file_augmented : 1,
m_tried_unwind_plan_eh_frame_augmented : 1,
m_tried_unwind_plan_debug_frame_augmented : 1,
m_tried_unwind_plan_compact_unwind : 1,
diff --git a/lldb/include/lldb/Symbol/ObjectFile.h b/lldb/include/lldb/Symbol/ObjectFile.h
index 7a0acd8e304..4d04f23a828 100644
--- a/lldb/include/lldb/Symbol/ObjectFile.h
+++ b/lldb/include/lldb/Symbol/ObjectFile.h
@@ -656,6 +656,9 @@ public:
/// \return
virtual std::vector<LoadableData> GetLoadableData(Target &target);
+ /// Creates a plugin-specific call frame info
+ virtual std::unique_ptr<CallFrameInfo> CreateCallFrameInfo();
+
protected:
// Member variables.
FileSpec m_file;
diff --git a/lldb/include/lldb/Symbol/UnwindTable.h b/lldb/include/lldb/Symbol/UnwindTable.h
index b4d7f0661d5..c1dc519c4b2 100644
--- a/lldb/include/lldb/Symbol/UnwindTable.h
+++ b/lldb/include/lldb/Symbol/UnwindTable.h
@@ -27,6 +27,8 @@ public:
~UnwindTable();
+ lldb_private::CallFrameInfo *GetObjectFileUnwindInfo();
+
lldb_private::DWARFCallFrameInfo *GetEHFrameInfo();
lldb_private::DWARFCallFrameInfo *GetDebugFrameInfo();
@@ -71,6 +73,7 @@ private:
bool m_initialized; // delay some initialization until ObjectFile is set up
std::mutex m_mutex;
+ std::unique_ptr<CallFrameInfo> m_object_file_unwind_up;
std::unique_ptr<DWARFCallFrameInfo> m_eh_frame_up;
std::unique_ptr<DWARFCallFrameInfo> m_debug_frame_up;
std::unique_ptr<CompactUnwindInfo> m_compact_unwind_up;
diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h
index 35f8928debe..8315fbb2734 100644
--- a/lldb/include/lldb/lldb-forward.h
+++ b/lldb/include/lldb/lldb-forward.h
@@ -44,6 +44,7 @@ class BreakpointSiteList;
class BroadcastEventSpec;
class Broadcaster;
class BroadcasterManager;
+class CallFrameInfo;
class ClangASTContext;
class ClangASTImporter;
class ClangASTMetadata;
diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp
index 566de9ba071..8a768e513eb 100644
--- a/lldb/source/Commands/CommandObjectTarget.cpp
+++ b/lldb/source/Commands/CommandObjectTarget.cpp
@@ -3445,6 +3445,25 @@ protected:
result.GetOutputStream().Printf("\n");
}
+ UnwindPlanSP of_unwind_sp =
+ func_unwinders_sp->GetObjectFileUnwindPlan(*target);
+ if (of_unwind_sp) {
+ result.GetOutputStream().Printf("object file UnwindPlan:\n");
+ of_unwind_sp->Dump(result.GetOutputStream(), thread.get(),
+ LLDB_INVALID_ADDRESS);
+ result.GetOutputStream().Printf("\n");
+ }
+
+ UnwindPlanSP of_unwind_augmented_sp =
+ func_unwinders_sp->GetObjectFileAugmentedUnwindPlan(*target,
+ *thread);
+ if (of_unwind_augmented_sp) {
+ result.GetOutputStream().Printf("object file augmented UnwindPlan:\n");
+ of_unwind_augmented_sp->Dump(result.GetOutputStream(), thread.get(),
+ LLDB_INVALID_ADDRESS);
+ result.GetOutputStream().Printf("\n");
+ }
+
UnwindPlanSP ehframe_sp =
func_unwinders_sp->GetEHFrameUnwindPlan(*target);
if (ehframe_sp) {
diff --git a/lldb/source/Plugins/ObjectFile/PECOFF/CMakeLists.txt b/lldb/source/Plugins/ObjectFile/PECOFF/CMakeLists.txt
index 6981f7ecb71..ad768feca30 100644
--- a/lldb/source/Plugins/ObjectFile/PECOFF/CMakeLists.txt
+++ b/lldb/source/Plugins/ObjectFile/PECOFF/CMakeLists.txt
@@ -7,6 +7,7 @@ endif()
add_lldb_library(lldbPluginObjectFilePECOFF PLUGIN
ObjectFilePECOFF.cpp
+ PECallFrameInfo.cpp
WindowsMiniDump.cpp
LINK_LIBS
diff --git a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp
index 16c131fa469..eee98491a88 100644
--- a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp
+++ b/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"
@@ -518,7 +519,26 @@ bool ObjectFilePECOFF::ParseCOFFOptionalHeader(lldb::offset_t *offset_ptr) {
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.
@@ -541,6 +561,15 @@ DataExtractor ObjectFilePECOFF::ReadImageData(uint32_t offset, size_t size) {
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) {
@@ -678,14 +707,8 @@ Symtab *ObjectFilePECOFF::GetSymtab() {
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
@@ -740,6 +763,19 @@ Symtab *ObjectFilePECOFF::GetSymtab() {
return m_symtab_up.get();
}
+std::unique_ptr<CallFrameInfo> 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 {};
+
+ return std::make_unique<PECallFrameInfo>(*this, data_dir_exception.vmaddr,
+ data_dir_exception.vmsize);
+}
+
bool ObjectFilePECOFF::IsStripped() {
// TODO: determine this for COFF
return false;
diff --git a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h
index 68ea7a7270c..78088ecc437 100644
--- a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h
+++ b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h
@@ -135,7 +135,14 @@ public:
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<lldb_private::CallFrameInfo> CreateCallFrameInfo() override;
protected:
bool NeedsEndianSwap() const;
@@ -216,6 +223,7 @@ protected:
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 {
diff --git a/lldb/source/Plugins/ObjectFile/PECOFF/PECallFrameInfo.cpp b/lldb/source/Plugins/ObjectFile/PECOFF/PECallFrameInfo.cpp
new file mode 100644
index 00000000000..fe67e87f09f
--- /dev/null
+++ b/lldb/source/Plugins/ObjectFile/PECOFF/PECallFrameInfo.cpp
@@ -0,0 +1,535 @@
+#include "PECallFrameInfo.h"
+
+#include "ObjectFilePECOFF.h"
+
+#include "Plugins/Process/Utility/lldb-x86-register-enums.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "llvm/Support/Win64EH.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm::Win64EH;
+
+template <typename T>
+static const T *TypedRead(const DataExtractor &data_extractor, offset_t &offset,
+ offset_t size = sizeof(T)) {
+ return static_cast<const T *>(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<EHInstruction>;
+
+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<UnwindInfo>(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<UnwindCode>(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<RuntimeFunction>(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 >= llvm::array_lengthof(machine_to_lldb_register))
+ 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 >= llvm::array_lengthof(xmm_to_lldb_register))
+ 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<uint32_t>(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<uint32_t>(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<uint32_t>(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<UnwindPlan::Row> 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<UnwindPlan::Row> EHProgramRange::BuildUnwindPlanRow() const {
+ std::unique_ptr<UnwindPlan::Row> row = std::make_unique<UnwindPlan::Row>();
+
+ 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_dir(object_file.ReadImageDataByRVA(exception_dir_rva,
+ exception_dir_size)) {}
+
+bool PECallFrameInfo::GetAddressRange(Address addr, AddressRange &range) {
+ range.Clear();
+
+ const RuntimeFunction *runtime_function =
+ FindRuntimeFunctionIntersectsWithRange(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 =
+ FindRuntimeFunctionIntersectsWithRange(range);
+ if (!runtime_function)
+ return false;
+
+ EHProgramBuilder builder(m_object_file, runtime_function->UnwindInfoOffset);
+ if (!builder.Build())
+ return false;
+
+ std::vector<UnwindPlan::RowSP> 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;
+}
+
+const RuntimeFunction *PECallFrameInfo::FindRuntimeFunctionIntersectsWithRange(
+ 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_dir.GetByteSize() / sizeof(RuntimeFunction);
+ while (begin < end) {
+ uint32_t curr = (begin + end) / 2;
+
+ offset_t offset = curr * sizeof(RuntimeFunction);
+ const auto *runtime_function =
+ TypedRead<RuntimeFunction>(m_exception_dir, 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;
+}
diff --git a/lldb/source/Plugins/ObjectFile/PECOFF/PECallFrameInfo.h b/lldb/source/Plugins/ObjectFile/PECOFF/PECallFrameInfo.h
new file mode 100644
index 00000000000..b5932dc726a
--- /dev/null
+++ b/lldb/source/Plugins/ObjectFile/PECOFF/PECallFrameInfo.h
@@ -0,0 +1,47 @@
+//===-- 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/CallFrameInfo.h"
+#include "lldb/Utility/DataExtractor.h"
+
+class ObjectFilePECOFF;
+
+namespace llvm {
+namespace Win64EH {
+
+struct RuntimeFunction;
+
+}
+} // namespace llvm
+
+class PECallFrameInfo : public virtual lldb_private::CallFrameInfo {
+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;
+
+private:
+ const llvm::Win64EH::RuntimeFunction *FindRuntimeFunctionIntersectsWithRange(
+ const lldb_private::AddressRange &range) const;
+
+ ObjectFilePECOFF &m_object_file;
+ lldb_private::DataExtractor m_exception_dir;
+};
+
+#endif // liblldb_PECallFrameInfo_h_
diff --git a/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp b/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp
index 50795c09f28..49a589f1498 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp
+++ b/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp
@@ -12,6 +12,7 @@
#include "lldb/Core/Value.h"
#include "lldb/Expression/DWARFExpression.h"
#include "lldb/Symbol/ArmUnwindInfo.h"
+#include "lldb/Symbol/CallFrameInfo.h"
#include "lldb/Symbol/DWARFCallFrameInfo.h"
#include "lldb/Symbol/FuncUnwinders.h"
#include "lldb/Symbol/Function.h"
@@ -784,6 +785,16 @@ UnwindPlanSP RegisterContextLLDB::GetFullUnwindPlanForFrame() {
unwind_plan_sp.reset();
}
+ CallFrameInfo *object_file_unwind =
+ pc_module_sp->GetUnwindTable().GetObjectFileUnwindInfo();
+ if (object_file_unwind) {
+ unwind_plan_sp = std::make_shared<UnwindPlan>(lldb::eRegisterKindGeneric);
+ if (object_file_unwind->GetUnwindPlan(m_current_pc, *unwind_plan_sp))
+ return unwind_plan_sp;
+ else
+ unwind_plan_sp.reset();
+ }
+
return arch_default_unwind_plan_sp;
}
@@ -796,6 +807,9 @@ UnwindPlanSP RegisterContextLLDB::GetFullUnwindPlanForFrame() {
m_fast_unwind_plan_sp.reset();
unwind_plan_sp =
func_unwinders_sp->GetEHFrameUnwindPlan(process->GetTarget());
+ if (!unwind_plan_sp)
+ unwind_plan_sp =
+ func_unwinders_sp->GetObjectFileUnwindPlan(process->GetTarget());
if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress(m_current_pc) &&
unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolYes) {
return unwind_plan_sp;
@@ -818,6 +832,9 @@ UnwindPlanSP RegisterContextLLDB::GetFullUnwindPlanForFrame() {
// intend) or compact unwind (this won't work)
unwind_plan_sp =
func_unwinders_sp->GetEHFrameUnwindPlan(process->GetTarget());
+ if (!unwind_plan_sp)
+ unwind_plan_sp =
+ func_unwinders_sp->GetObjectFileUnwindPlan(process->GetTarget());
if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress(m_current_pc)) {
UnwindLogMsgVerbose("frame uses %s for full UnwindPlan because the "
"DynamicLoader suggested we prefer it",
diff --git a/lldb/source/Symbol/FuncUnwinders.cpp b/lldb/source/Symbol/FuncUnwinders.cpp
index 09cb9b00aaf..f609bf7821e 100644
--- a/lldb/source/Symbol/FuncUnwinders.cpp
+++ b/lldb/source/Symbol/FuncUnwinders.cpp
@@ -10,6 +10,7 @@
#include "lldb/Core/Address.h"
#include "lldb/Core/AddressRange.h"
#include "lldb/Symbol/ArmUnwindInfo.h"
+#include "lldb/Symbol/CallFrameInfo.h"
#include "lldb/Symbol/CompactUnwindInfo.h"
#include "lldb/Symbol/DWARFCallFrameInfo.h"
#include "lldb/Symbol/ObjectFile.h"
@@ -58,6 +59,8 @@ UnwindPlanSP FuncUnwinders::GetUnwindPlanAtCallSite(Target &target,
Thread &thread) {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (UnwindPlanSP plan_sp = GetObjectFileUnwindPlan(target))
+ return plan_sp;
if (UnwindPlanSP plan_sp = GetSymbolFileUnwindPlan(thread))
return plan_sp;
if (UnwindPlanSP plan_sp = GetDebugFrameUnwindPlan(target))
@@ -97,6 +100,26 @@ UnwindPlanSP FuncUnwinders::GetCompactUnwindUnwindPlan(Target &target) {
return UnwindPlanSP();
}
+lldb::UnwindPlanSP FuncUnwinders::GetObjectFileUnwindPlan(Target &target) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (m_unwind_plan_object_file_sp.get() ||
+ m_tried_unwind_plan_object_file)
+ return m_unwind_plan_object_file_sp;
+
+ m_tried_unwind_plan_object_file = true;
+ if (m_range.GetBaseAddress().IsValid()) {
+ CallFrameInfo *object_file_frame = m_unwind_table.GetObjectFileUnwindInfo();
+ if (object_file_frame) {
+ m_unwind_plan_object_file_sp =
+ std::make_shared<UnwindPlan>(lldb::eRegisterKindGeneric);
+ if (!object_file_frame->GetUnwindPlan(m_range,
+ *m_unwind_plan_object_file_sp))
+ m_unwind_plan_object_file_sp.reset();
+ }
+ }
+ return m_unwind_plan_object_file_sp;
+}
+
UnwindPlanSP FuncUnwinders::GetEHFrameUnwindPlan(Target &target) {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
if (m_unwind_plan_eh_frame_sp.get() || m_tried_unwind_plan_eh_frame)
@@ -185,6 +208,38 @@ UnwindPlanSP FuncUnwinders::GetSymbolFileUnwindPlan(Thread &thread) {
return m_unwind_plan_symbol_file_sp;
}
+UnwindPlanSP
+FuncUnwinders::GetObjectFileAugmentedUnwindPlan(Target &target,
+ Thread &thread) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (m_unwind_plan_object_file_augmented_sp.get() ||
+ m_tried_unwind_plan_object_file_augmented)
+ return m_unwind_plan_object_file_augmented_sp;
+
+ m_tried_unwind_plan_object_file_augmented = true;
+
+ UnwindPlanSP object_file_unwind_plan = GetObjectFileUnwindPlan(target);
+ if (!object_file_unwind_plan)
+ return m_unwind_plan_object_file_augmented_sp;
+
+ m_unwind_plan_object_file_augmented_sp =
+ std::make_shared<UnwindPlan>(*object_file_unwind_plan);
+
+ // Augment the instructions with epilogue descriptions if necessary
+ // so the UnwindPlan can be used at any instruction in the function.
+
+ UnwindAssemblySP assembly_profiler_sp(GetUnwindAssemblyProfiler(target));
+ if (assembly_profiler_sp) {
+ if (!assembly_profiler_sp->AugmentUnwindPlanFromCallSite(
+ m_range, thread, *m_unwind_plan_object_file_augmented_sp)) {
+ m_unwind_plan_object_file_augmented_sp.reset();
+ }
+ } else {
+ m_unwind_plan_object_file_augmented_sp.reset();
+ }
+ return m_unwind_plan_object_file_augmented_sp;
+}
+
UnwindPlanSP FuncUnwinders::GetEHFrameAugmentedUnwindPlan(Target &target,
Thread &thread) {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
@@ -328,6 +383,8 @@ UnwindPlanSP FuncUnwinders::GetUnwindPlanAtNonCallSite(Target &target,
UnwindPlanSP eh_frame_sp = GetEHFrameUnwindPlan(target);
if (!eh_frame_sp)
eh_frame_sp = GetDebugFrameUnwindPlan(target);
+ if (!eh_frame_sp)
+ eh_frame_sp = GetObjectFileUnwindPlan(target);
UnwindPlanSP arch_default_at_entry_sp =
GetUnwindPlanArchitectureDefaultAtFunctionEntry(thread);
UnwindPlanSP arch_default_sp = GetUnwindPlanArchitectureDefault(thread);
@@ -366,6 +423,8 @@ UnwindPlanSP FuncUnwinders::GetUnwindPlanAtNonCallSite(Target &target,
return plan_sp;
if (UnwindPlanSP plan_sp = GetEHFrameAugmentedUnwindPlan(target, thread))
return plan_sp;
+ if (UnwindPlanSP plan_sp = GetObjectFileAugmentedUnwindPlan(target, thread))
+ return plan_sp;
return assembly_sp;
}
@@ -473,6 +532,9 @@ Address FuncUnwinders::GetLSDAAddress(Target &target) {
if (unwind_plan_sp.get() == nullptr) {
unwind_plan_sp = GetCompactUnwindUnwindPlan(target);
}
+ if (unwind_plan_sp.get() == nullptr) {
+ unwind_plan_sp = GetObjectFileUnwindPlan(target);
+ }
if (unwind_plan_sp.get() && unwind_plan_sp->GetLSDAAddress().IsValid()) {
lsda_addr = unwind_plan_sp->GetLSDAAddress();
}
@@ -486,6 +548,9 @@ Address FuncUnwinders::GetPersonalityRoutinePtrAddress(Target &target) {
if (unwind_plan_sp.get() == nullptr) {
unwind_plan_sp = GetCompactUnwindUnwindPlan(target);
}
+ if (unwind_plan_sp.get() == nullptr) {
+ unwind_plan_sp = GetObjectFileUnwindPlan(target);
+ }
if (unwind_plan_sp.get() &&
unwind_plan_sp->GetPersonalityFunctionPtr().IsValid()) {
personality_addr = unwind_plan_sp->GetPersonalityFunctionPtr();
diff --git a/lldb/source/Symbol/ObjectFile.cpp b/lldb/source/Symbol/ObjectFile.cpp
index 8bfce5ce7a4..38bc7722d0d 100644
--- a/lldb/source/Symbol/ObjectFile.cpp
+++ b/lldb/source/Symbol/ObjectFile.cpp
@@ -11,6 +11,7 @@
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/Section.h"
+#include "lldb/Symbol/CallFrameInfo.h"
#include "lldb/Symbol/ObjectContainer.h"
#include "lldb/Symbol/SymbolFile.h"
#include "lldb/Target/Process.h"
@@ -670,6 +671,10 @@ ObjectFile::GetLoadableData(Target &target) {
return loadables;
}
+std::unique_ptr<CallFrameInfo> ObjectFile::CreateCallFrameInfo() {
+ return {};
+}
+
void ObjectFile::RelocateSection(lldb_private::Section *section)
{
}
diff --git a/lldb/source/Symbol/UnwindTable.cpp b/lldb/source/Symbol/UnwindTable.cpp
index 7566d8027f0..045957a67b3 100644
--- a/lldb/source/Symbol/UnwindTable.cpp
+++ b/lldb/source/Symbol/UnwindTable.cpp
@@ -13,6 +13,7 @@
#include "lldb/Core/Module.h"
#include "lldb/Core/Section.h"
#include "lldb/Symbol/ArmUnwindInfo.h"
+#include "lldb/Symbol/CallFrameInfo.h"
#include "lldb/Symbol/CompactUnwindInfo.h"
#include "lldb/Symbol/DWARFCallFrameInfo.h"
#include "lldb/Symbol/FuncUnwinders.h"
@@ -29,7 +30,8 @@ using namespace lldb_private;
UnwindTable::UnwindTable(Module &module)
: m_module(module), m_unwinds(), m_initialized(false), m_mutex(),
- m_eh_frame_up(), m_compact_unwind_up(), m_arm_unwind_up() {}
+ m_object_file_unwind_up(), m_eh_frame_up(), m_compact_unwind_up(),
+ m_arm_unwind_up() {}
// We can't do some of this initialization when the ObjectFile is running its
// ctor; delay doing it until needed for something.
@@ -47,6 +49,8 @@ void UnwindTable::Initialize() {
if (!object_file)
return;
+ m_object_file_unwind_up = object_file->CreateCallFrameInfo();
+
SectionList *sl = m_module.GetSectionList();
if (!sl)
return;
@@ -83,7 +87,12 @@ llvm::Optional<AddressRange> UnwindTable::GetAddressRange(const Address &addr,
SymbolContext &sc) {
AddressRange range;
- // First check the symbol context
+ // First check the unwind info from the object file plugin
+ if (m_object_file_unwind_up &&
+ m_object_file_unwind_up->GetAddressRange(addr, range))
+ return range;
+
+ // Check the symbol context
if (sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0,
false, range) &&
range.GetBaseAddress().IsValid())
@@ -162,6 +171,11 @@ void UnwindTable::Dump(Stream &s) {
s.EOL();
}
+lldb_private::CallFrameInfo *UnwindTable::GetObjectFileUnwindInfo() {
+ Initialize();
+ return m_object_file_unwind_up.get();
+}
+
DWARFCallFrameInfo *UnwindTable::GetEHFrameInfo() {
Initialize();
return m_eh_frame_up.get();
diff --git a/lldb/unittests/ObjectFile/CMakeLists.txt b/lldb/unittests/ObjectFile/CMakeLists.txt
index 62c003e3f2d..a9b42ea3199 100644
--- a/lldb/unittests/ObjectFile/CMakeLists.txt
+++ b/lldb/unittests/ObjectFile/CMakeLists.txt
@@ -1,2 +1,3 @@
add_subdirectory(Breakpad)
add_subdirectory(ELF)
+add_subdirectory(PECOFF)
diff --git a/lldb/unittests/ObjectFile/PECOFF/CMakeLists.txt b/lldb/unittests/ObjectFile/PECOFF/CMakeLists.txt
new file mode 100644
index 00000000000..3ce5a7b9739
--- /dev/null
+++ b/lldb/unittests/ObjectFile/PECOFF/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_lldb_unittest(ObjectFilePECOFFTests
+ TestPECallFrameInfo.cpp
+
+ LINK_LIBS
+ lldbUtilityHelpers
+ lldbPluginObjectFilePECOFF
+ LLVMTestingSupport
+ )
diff --git a/lldb/unittests/ObjectFile/PECOFF/TestPECallFrameInfo.cpp b/lldb/unittests/ObjectFile/PECOFF/TestPECallFrameInfo.cpp
new file mode 100644
index 00000000000..cb0a2afea67
--- /dev/null
+++ b/lldb/unittests/ObjectFile/PECOFF/TestPECallFrameInfo.cpp
@@ -0,0 +1,336 @@
+//===-- 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 "TestingSupport/TestUtilities.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/CallFrameInfo.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();
+ ObjectFilePECOFF::Initialize();
+ }
+
+ void TearDown() override {
+ ObjectFilePECOFF::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<TestFile> 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<Module>(ModuleSpec(FileSpec(ExpectedFile->name())));
+ ObjectFile *object_file = module_sp->GetObjectFile();
+ ASSERT_NE(object_file, nullptr);
+
+ std::unique_ptr<CallFrameInfo> cfi = object_file->CreateCallFrameInfo();
+ 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);
+}
diff --git a/llvm/include/llvm/Support/Win64EH.h b/llvm/include/llvm/Support/Win64EH.h
index bdd23b41594..8220131e5be 100644
--- a/llvm/include/llvm/Support/Win64EH.h
+++ b/llvm/include/llvm/Support/Win64EH.h
@@ -30,7 +30,9 @@ enum UnwindOpcodes {
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
OpenPOWER on IntegriCloud