From 4c31907dbfd40dacde0a8252283391a4f6d0049a Mon Sep 17 00:00:00 2001 From: Dimitar Vlahovski Date: Tue, 27 Sep 2016 19:05:55 +0000 Subject: [PATCH] Adding a RegisterContextMinidump_x86_64 converter Summary: This is a register context converter from Minidump to Linux reg context. This knows the layout of the register context in the Minidump file (which is the same as in Windows FYI) and as a result emits a binary data buffer that matches the Linux register context binary layout. This way we can reuse the existing RegisterContextLinux_x86_64 and RegisterContextCorePOSIX_x86_64 classes. Reviewers: labath, zturner Subscribers: beanz, mgorny, lldb-commits, amccarth Differential Revision: https://reviews.llvm.org/D24919 llvm-svn: 282529 --- lldb/include/lldb/lldb-private-types.h | 11 ++ .../Plugins/Process/minidump/CMakeLists.txt | 1 + .../Process/minidump/MinidumpParser.cpp | 5 +- .../Plugins/Process/minidump/MinidumpParser.h | 6 +- .../RegisterContextMinidump_x86_64.cpp | 158 ++++++++++++++++++ .../minidump/RegisterContextMinidump_x86_64.h | 119 +++++++++++++ .../Process/minidump/MinidumpParserTest.cpp | 63 ++++++- 7 files changed, 357 insertions(+), 6 deletions(-) create mode 100644 lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp create mode 100644 lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h diff --git a/lldb/include/lldb/lldb-private-types.h b/lldb/include/lldb/lldb-private-types.h index 8fd599d0a25af..4f192b66369fd 100644 --- a/lldb/include/lldb/lldb-private-types.h +++ b/lldb/include/lldb/lldb-private-types.h @@ -14,6 +14,8 @@ #include "lldb/lldb-private.h" +#include "llvm/ADT/ArrayRef.h" + namespace llvm { namespace sys { class DynamicLibrary; @@ -61,6 +63,15 @@ struct RegisterInfo { // the byte size of this register. size_t dynamic_size_dwarf_len; // The length of the DWARF expression in bytes // in the dynamic_size_dwarf_expr_bytes member. + + llvm::ArrayRef data(const uint8_t *context_base) const { + return llvm::ArrayRef(context_base + byte_offset, byte_size); + } + + llvm::MutableArrayRef mutable_data(uint8_t *context_base) const { + return llvm::MutableArrayRef(context_base + byte_offset, + byte_size); + } }; //---------------------------------------------------------------------- diff --git a/lldb/source/Plugins/Process/minidump/CMakeLists.txt b/lldb/source/Plugins/Process/minidump/CMakeLists.txt index eadbaa23b8564..ab3cfbb58f0b7 100644 --- a/lldb/source/Plugins/Process/minidump/CMakeLists.txt +++ b/lldb/source/Plugins/Process/minidump/CMakeLists.txt @@ -3,4 +3,5 @@ include_directories(../Utility) add_lldb_library(lldbPluginProcessMinidump MinidumpTypes.cpp MinidumpParser.cpp + RegisterContextMinidump_x86_64.cpp ) diff --git a/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp b/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp index 637f945ef8147..2ccea6839cbc7 100644 --- a/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp +++ b/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp @@ -64,8 +64,9 @@ MinidumpParser::MinidumpParser( : m_data_sp(data_buf_sp), m_header(header), m_directory_map(directory_map) { } -lldb::offset_t MinidumpParser::GetByteSize() { - return m_data_sp->GetByteSize(); +llvm::ArrayRef MinidumpParser::GetData() { + return llvm::ArrayRef(m_data_sp->GetBytes(), + m_data_sp->GetByteSize()); } llvm::ArrayRef diff --git a/lldb/source/Plugins/Process/minidump/MinidumpParser.h b/lldb/source/Plugins/Process/minidump/MinidumpParser.h index 76356127f4625..76a8ece00b270 100644 --- a/lldb/source/Plugins/Process/minidump/MinidumpParser.h +++ b/lldb/source/Plugins/Process/minidump/MinidumpParser.h @@ -39,7 +39,7 @@ class MinidumpParser { static llvm::Optional Create(const lldb::DataBufferSP &data_buf_sp); - lldb::offset_t GetByteSize(); + llvm::ArrayRef GetData(); llvm::ArrayRef GetStream(MinidumpStreamType stream_type); @@ -71,6 +71,6 @@ class MinidumpParser { llvm::DenseMap &&directory_map); }; -} // namespace minidump -} // namespace lldb_private +} // end namespace minidump +} // end namespace lldb_private #endif // liblldb_MinidumpParser_h_ diff --git a/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp b/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp new file mode 100644 index 0000000000000..481779df94808 --- /dev/null +++ b/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp @@ -0,0 +1,158 @@ +//===-- RegisterContextMinidump_x86_64.cpp ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Project includes +#include "RegisterContextMinidump_x86_64.h" + +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" + +// C includes +// C++ includes + +using namespace lldb_private; +using namespace minidump; + +void writeRegister(llvm::ArrayRef ®_src, + llvm::MutableArrayRef reg_dest) { + memcpy(reg_dest.data(), reg_src.data(), reg_dest.size()); + reg_src = reg_src.drop_front(reg_dest.size()); +} + +llvm::MutableArrayRef getDestRegister(uint8_t *context, + uint32_t lldb_reg_num, + const RegisterInfo ®) { + auto bytes = reg.mutable_data(context); + + switch (lldb_reg_num) { + case lldb_cs_x86_64: + case lldb_ds_x86_64: + case lldb_es_x86_64: + case lldb_fs_x86_64: + case lldb_gs_x86_64: + case lldb_ss_x86_64: + return bytes.take_front(2); + break; + case lldb_rflags_x86_64: + return bytes.take_front(4); + break; + default: + return bytes.take_front(8); + break; + } +} + +lldb::DataBufferSP lldb_private::minidump::ConvertMinidumpContextToRegIface( + llvm::ArrayRef source_data, + RegisterInfoInterface *target_reg_interface) { + + const RegisterInfo *reg_info = target_reg_interface->GetRegisterInfo(); + + lldb::DataBufferSP result_context_buf( + new DataBufferHeap(target_reg_interface->GetGPRSize(), 0)); + uint8_t *result_base = result_context_buf->GetBytes(); + + source_data = source_data.drop_front(6 * 8); // p[1-6] home registers + const uint32_t *context_flags; + consumeObject(source_data, context_flags); + const uint32_t x86_64_Flag = + static_cast(MinidumpContext_x86_64_Flags::x86_64_Flag); + const uint32_t ControlFlag = + static_cast(MinidumpContext_x86_64_Flags::Control); + const uint32_t IntegerFlag = + static_cast(MinidumpContext_x86_64_Flags::Integer); + const uint32_t SegmentsFlag = + static_cast(MinidumpContext_x86_64_Flags::Segments); + const uint32_t DebugRegistersFlag = + static_cast(MinidumpContext_x86_64_Flags::DebugRegisters); + + if (!(*context_flags & x86_64_Flag)) { + return result_context_buf; // error + } + + source_data = source_data.drop_front(4); // mx_csr + + if (*context_flags & ControlFlag) { + writeRegister(source_data, getDestRegister(result_base, lldb_cs_x86_64, + reg_info[lldb_cs_x86_64])); + } + + if (*context_flags & SegmentsFlag) { + writeRegister(source_data, getDestRegister(result_base, lldb_ds_x86_64, + reg_info[lldb_ds_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_es_x86_64, + reg_info[lldb_es_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_fs_x86_64, + reg_info[lldb_fs_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_gs_x86_64, + reg_info[lldb_gs_x86_64])); + } + + if (*context_flags & ControlFlag) { + writeRegister(source_data, getDestRegister(result_base, lldb_ss_x86_64, + reg_info[lldb_ss_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_rflags_x86_64, + reg_info[lldb_rflags_x86_64])); + } + + if (*context_flags & DebugRegistersFlag) { + source_data = + source_data.drop_front(6 * 8); // 6 debug registers 64 bit each + } + + if (*context_flags & IntegerFlag) { + writeRegister(source_data, getDestRegister(result_base, lldb_rax_x86_64, + reg_info[lldb_rax_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_rcx_x86_64, + reg_info[lldb_rcx_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_rdx_x86_64, + reg_info[lldb_rdx_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_rbx_x86_64, + reg_info[lldb_rbx_x86_64])); + } + + if (*context_flags & ControlFlag) { + writeRegister(source_data, getDestRegister(result_base, lldb_rsp_x86_64, + reg_info[lldb_rsp_x86_64])); + } + + if (*context_flags & IntegerFlag) { + writeRegister(source_data, getDestRegister(result_base, lldb_rbp_x86_64, + reg_info[lldb_rbp_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_rsi_x86_64, + reg_info[lldb_rsi_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_rdi_x86_64, + reg_info[lldb_rdi_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_r8_x86_64, + reg_info[lldb_r8_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_r9_x86_64, + reg_info[lldb_r9_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_r10_x86_64, + reg_info[lldb_r10_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_r11_x86_64, + reg_info[lldb_r11_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_r12_x86_64, + reg_info[lldb_r12_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_r13_x86_64, + reg_info[lldb_r13_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_r14_x86_64, + reg_info[lldb_r14_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_r15_x86_64, + reg_info[lldb_r15_x86_64])); + } + + if (*context_flags & ControlFlag) { + writeRegister(source_data, getDestRegister(result_base, lldb_rip_x86_64, + reg_info[lldb_rip_x86_64])); + } + + // TODO parse the floating point registers + + return result_context_buf; +} diff --git a/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h b/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h new file mode 100644 index 0000000000000..a25a8148d19cc --- /dev/null +++ b/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h @@ -0,0 +1,119 @@ +//===-- RegisterContextMinidump_x86_64.h ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextMinidump_h_ +#define liblldb_RegisterContextMinidump_h_ + +// Project includes +#include "MinidumpTypes.h" + +// Other libraries and framework includes +#include "Plugins/Process/Utility/RegisterInfoInterface.h" +#include "Plugins/Process/Utility/lldb-x86-register-enums.h" + +#include "lldb/Target/RegisterContext.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitmaskEnum.h" + +// C includes +// C++ includes + +namespace lldb_private { + +namespace minidump { + +// The content of the Minidump register context is as follows: +// (for reference see breakpad's source or WinNT.h) +// Register parameter home addresses: (p1_home .. p6_home) +// - uint64_t p1_home +// - uint64_t p2_home +// - uint64_t p3_home +// - uint64_t p4_home +// - uint64_t p5_home +// - uint64_t p6_home +// +// - uint32_t context_flags - field that determines the layout of the structure +// and which parts of it are populated +// - uint32_t mx_csr +// +// - uint16_t cs - included if MinidumpContext_x86_64_Flags::Control +// +// - uint16_t ds - included if MinidumpContext_x86_64_Flags::Segments +// - uint16_t es - included if MinidumpContext_x86_64_Flags::Segments +// - uint16_t fs - included if MinidumpContext_x86_64_Flags::Segments +// - uint16_t gs - included if MinidumpContext_x86_64_Flags::Segments +// +// - uint16_t ss - included if MinidumpContext_x86_64_Flags::Control +// - uint32_t rflags - included if MinidumpContext_x86_64_Flags::Control +// +// Debug registers: (included if MinidumpContext_x86_64_Flags::DebugRegisters) +// - uint64_t dr0 +// - uint64_t dr1 +// - uint64_t dr2 +// - uint64_t dr3 +// - uint64_t dr6 +// - uint64_t dr7 +// +// The next 4 registers are included if MinidumpContext_x86_64_Flags::Integer +// - uint64_t rax +// - uint64_t rcx +// - uint64_t rdx +// - uint64_t rbx +// +// - uint64_t rsp - included if MinidumpContext_x86_64_Flags::Control +// +// The next 11 registers are included if MinidumpContext_x86_64_Flags::Integer +// - uint64_t rbp +// - uint64_t rsi +// - uint64_t rdi +// - uint64_t r8 +// - uint64_t r9 +// - uint64_t r10 +// - uint64_t r11 +// - uint64_t r12 +// - uint64_t r13 +// - uint64_t r14 +// - uint64_t r15 +// +// - uint64_t rip - included if MinidumpContext_x86_64_Flags::Control +// +// TODO: add floating point registers here + +// This function receives an ArrayRef pointing to the bytes of the Minidump +// register context and returns a DataBuffer that's ordered by the offsets +// specified in the RegisterInfoInterface argument +// This way we can reuse the already existing register contexts +lldb::DataBufferSP +ConvertMinidumpContextToRegIface(llvm::ArrayRef source_data, + RegisterInfoInterface *target_reg_interface); + +// For context_flags. These values indicate the type of +// context stored in the structure. The high 24 bits identify the CPU, the +// low 8 bits identify the type of context saved. +LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); + +enum class MinidumpContext_x86_64_Flags : uint32_t { + x86_64_Flag = 0x00100000, + Control = x86_64_Flag | 0x00000001, + Integer = x86_64_Flag | 0x00000002, + Segments = x86_64_Flag | 0x00000004, + FloatingPoint = x86_64_Flag | 0x00000008, + DebugRegisters = x86_64_Flag | 0x00000010, + XState = x86_64_Flag | 0x00000040, + + Full = Control | Integer | FloatingPoint, + All = Full | Segments | DebugRegisters, + + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ All) +}; + +} // end namespace minidump +} // end namespace lldb_private +#endif // liblldb_RegisterContextMinidump_h_ diff --git a/lldb/unittests/Process/minidump/MinidumpParserTest.cpp b/lldb/unittests/Process/minidump/MinidumpParserTest.cpp index 961b1b908cd77..7597fab36c26a 100644 --- a/lldb/unittests/Process/minidump/MinidumpParserTest.cpp +++ b/lldb/unittests/Process/minidump/MinidumpParserTest.cpp @@ -8,8 +8,10 @@ //===----------------------------------------------------------------------===// // Project includes +#include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h" #include "Plugins/Process/minidump/MinidumpParser.h" #include "Plugins/Process/minidump/MinidumpTypes.h" +#include "Plugins/Process/minidump/RegisterContextMinidump_x86_64.h" // Other libraries and framework includes #include "gtest/gtest.h" @@ -18,6 +20,7 @@ #include "lldb/Core/DataExtractor.h" #include "lldb/Host/FileSpec.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" @@ -50,7 +53,7 @@ class MinidumpParserTest : public testing::Test { MinidumpParser::Create(data_sp); ASSERT_TRUE(optional_parser.hasValue()); parser.reset(new MinidumpParser(optional_parser.getValue())); - ASSERT_GT(parser->GetByteSize(), 0UL); + ASSERT_GT(parser->GetData().size(), 0UL); } llvm::SmallString<128> inputs_folder; @@ -167,3 +170,61 @@ TEST_F(MinidumpParserTest, GetPidWindows) { ASSERT_TRUE(pid.hasValue()); ASSERT_EQ(4440UL, pid.getValue()); } + +// Register stuff +// TODO probably split register stuff tests into different file? +#define REG_VAL(x) *(reinterpret_cast(x)) + +TEST_F(MinidumpParserTest, ConvertRegisterContext) { + SetUpData("linux-x86_64.dmp"); + llvm::ArrayRef thread_list = parser->GetThreads(); + const MinidumpThread thread = thread_list[0]; + llvm::ArrayRef registers(parser->GetData().data() + + thread.thread_context.rva, + thread.thread_context.data_size); + + ArchSpec arch = parser->GetArchitecture(); + RegisterInfoInterface *reg_interface = new RegisterContextLinux_x86_64(arch); + lldb::DataBufferSP buf = + ConvertMinidumpContextToRegIface(registers, reg_interface); + ASSERT_EQ(reg_interface->GetGPRSize(), buf->GetByteSize()); + + const RegisterInfo *reg_info = reg_interface->GetRegisterInfo(); + + std::map reg_values; + + // clang-format off + reg_values[lldb_rax_x86_64] = 0x0000000000000000; + reg_values[lldb_rbx_x86_64] = 0x0000000000000000; + reg_values[lldb_rcx_x86_64] = 0x0000000000000010; + reg_values[lldb_rdx_x86_64] = 0x0000000000000000; + reg_values[lldb_rdi_x86_64] = 0x00007ffceb349cf0; + reg_values[lldb_rsi_x86_64] = 0x0000000000000000; + reg_values[lldb_rbp_x86_64] = 0x00007ffceb34a210; + reg_values[lldb_rsp_x86_64] = 0x00007ffceb34a210; + reg_values[lldb_r8_x86_64] = 0x00007fe9bc1aa9c0; + reg_values[lldb_r9_x86_64] = 0x0000000000000000; + reg_values[lldb_r10_x86_64] = 0x00007fe9bc3f16a0; + reg_values[lldb_r11_x86_64] = 0x0000000000000246; + reg_values[lldb_r12_x86_64] = 0x0000000000401c92; + reg_values[lldb_r13_x86_64] = 0x00007ffceb34a430; + reg_values[lldb_r14_x86_64] = 0x0000000000000000; + reg_values[lldb_r15_x86_64] = 0x0000000000000000; + reg_values[lldb_rip_x86_64] = 0x0000000000401dc6; + reg_values[lldb_rflags_x86_64] = 0x0000000000010206; + reg_values[lldb_cs_x86_64] = 0x0000000000000033; + reg_values[lldb_fs_x86_64] = 0x0000000000000000; + reg_values[lldb_gs_x86_64] = 0x0000000000000000; + reg_values[lldb_ss_x86_64] = 0x0000000000000000; + reg_values[lldb_ds_x86_64] = 0x0000000000000000; + reg_values[lldb_es_x86_64] = 0x0000000000000000; + // clang-format on + + for (uint32_t reg_index = 0; reg_index < reg_interface->GetRegisterCount(); + ++reg_index) { + if (reg_values.find(reg_index) != reg_values.end()) { + EXPECT_EQ(reg_values[reg_index], + REG_VAL(buf->GetBytes() + reg_info[reg_index].byte_offset)); + } + } +}