Index: lldb/trunk/source/Plugins/Process/minidump/CMakeLists.txt =================================================================== --- lldb/trunk/source/Plugins/Process/minidump/CMakeLists.txt +++ lldb/trunk/source/Plugins/Process/minidump/CMakeLists.txt @@ -3,5 +3,6 @@ add_lldb_library(lldbPluginProcessMinidump MinidumpTypes.cpp MinidumpParser.cpp + RegisterContextMinidump_x86_32.cpp RegisterContextMinidump_x86_64.cpp ) Index: lldb/trunk/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.h =================================================================== --- lldb/trunk/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.h +++ lldb/trunk/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.h @@ -0,0 +1,138 @@ +//===-- RegisterContextMinidump_x86_32.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_x86_32_h_ +#define liblldb_RegisterContextMinidump_x86_32_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" +#include "llvm/Support/Endian.h" + +// C includes +// C++ includes + +namespace lldb_private { + +namespace minidump { + +// 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 +ConvertMinidumpContext_x86_32(llvm::ArrayRef source_data, + RegisterInfoInterface *target_reg_interface); + +// Reference: see breakpad/crashpad source or WinNT.h +struct MinidumpFloatingSaveAreaX86 { + llvm::support::ulittle32_t control_word; + llvm::support::ulittle32_t status_word; + llvm::support::ulittle32_t tag_word; + llvm::support::ulittle32_t error_offset; + llvm::support::ulittle32_t error_selector; + llvm::support::ulittle32_t data_offset; + llvm::support::ulittle32_t data_selector; + + enum { + RegisterAreaSize = 80, + }; + // register_area contains eight 80-bit (x87 "long double") quantities for + // floating-point registers %st0 (%mm0) through %st7 (%mm7). + uint8_t register_area[RegisterAreaSize]; + llvm::support::ulittle32_t cr0_npx_state; +}; + +struct MinidumpContext_x86_32 { + // The context_flags field determines which parts + // of the structure are populated (have valid values) + llvm::support::ulittle32_t context_flags; + + // The next 6 registers are included with + // MinidumpContext_x86_32_Flags::DebugRegisters + llvm::support::ulittle32_t dr0; + llvm::support::ulittle32_t dr1; + llvm::support::ulittle32_t dr2; + llvm::support::ulittle32_t dr3; + llvm::support::ulittle32_t dr6; + llvm::support::ulittle32_t dr7; + + // The next field is included with + // MinidumpContext_x86_32_Flags::FloatingPoint + MinidumpFloatingSaveAreaX86 float_save; + + // The next 4 registers are included with + // MinidumpContext_x86_32_Flags::Segments + llvm::support::ulittle32_t gs; + llvm::support::ulittle32_t fs; + llvm::support::ulittle32_t es; + llvm::support::ulittle32_t ds; + + // The next 6 registers are included with + // MinidumpContext_x86_32_Flags::Integer + llvm::support::ulittle32_t edi; + llvm::support::ulittle32_t esi; + llvm::support::ulittle32_t ebx; + llvm::support::ulittle32_t edx; + llvm::support::ulittle32_t ecx; + llvm::support::ulittle32_t eax; + + // The next 6 registers are included with + // MinidumpContext_x86_32_Flags::Control + llvm::support::ulittle32_t ebp; + llvm::support::ulittle32_t eip; + llvm::support::ulittle32_t cs; // WinNT.h says "must be sanitized" + llvm::support::ulittle32_t eflags; // WinNT.h says "must be sanitized" + llvm::support::ulittle32_t esp; + llvm::support::ulittle32_t ss; + + // The next field is included with + // MinidumpContext_x86_32_Flags::ExtendedRegisters + // It contains vector (MMX/SSE) registers. It it laid out in the + // format used by the fxsave and fsrstor instructions, so it includes + // a copy of the x87 floating-point registers as well. See FXSAVE in + // "Intel Architecture Software Developer's Manual, Volume 2." + enum { + ExtendedRegistersSize = 512, + }; + uint8_t extended_registers[ExtendedRegistersSize]; +}; + +LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); + +// 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. +enum class MinidumpContext_x86_32_Flags : uint32_t { + x86_32_Flag = 0x00010000, // CONTEXT_i386, CONTEXT_i486 + Control = x86_32_Flag | 0x00000001, + Integer = x86_32_Flag | 0x00000002, + Segments = x86_32_Flag | 0x00000004, + FloatingPoint = x86_32_Flag | 0x00000008, + DebugRegisters = x86_32_Flag | 0x00000010, + ExtendedRegisters = x86_32_Flag | 0x00000020, + XState = x86_32_Flag | 0x00000040, + + Full = Control | Integer | Segments, + All = Full | FloatingPoint | DebugRegisters | ExtendedRegisters, + + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ All) +}; + +} // end namespace minidump +} // end namespace lldb_private +#endif // liblldb_RegisterContextMinidump_x86_32_h_ Index: lldb/trunk/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.cpp +++ lldb/trunk/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.cpp @@ -0,0 +1,99 @@ +//===-- RegisterContextMinidump_x86_32.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_32.h" + +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" + +// C includes +// C++ includes + +using namespace lldb_private; +using namespace minidump; + +static void writeRegister(const void *reg_src, + llvm::MutableArrayRef reg_dest) { + memcpy(reg_dest.data(), reg_src, reg_dest.size()); +} + +lldb::DataBufferSP lldb_private::minidump::ConvertMinidumpContext_x86_32( + 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(); + + if (source_data.size() < sizeof(MinidumpContext_x86_32)) + return nullptr; + + const MinidumpContext_x86_32 *context; + consumeObject(source_data, context); + + const MinidumpContext_x86_32_Flags context_flags = + static_cast( + static_cast(context->context_flags)); + auto x86_32_Flag = MinidumpContext_x86_32_Flags::x86_32_Flag; + auto ControlFlag = MinidumpContext_x86_32_Flags::Control; + auto IntegerFlag = MinidumpContext_x86_32_Flags::Integer; + auto SegmentsFlag = MinidumpContext_x86_32_Flags::Segments; + + if ((context_flags & x86_32_Flag) != x86_32_Flag) { + return nullptr; + } + + if ((context_flags & ControlFlag) == ControlFlag) { + writeRegister(&context->ebp, + reg_info[lldb_ebp_i386].mutable_data(result_base)); + writeRegister(&context->eip, + reg_info[lldb_eip_i386].mutable_data(result_base)); + writeRegister(&context->cs, + reg_info[lldb_cs_i386].mutable_data(result_base)); + writeRegister(&context->eflags, + reg_info[lldb_eflags_i386].mutable_data(result_base)); + writeRegister(&context->esp, + reg_info[lldb_esp_i386].mutable_data(result_base)); + writeRegister(&context->ss, + reg_info[lldb_ss_i386].mutable_data(result_base)); + } + + if ((context_flags & SegmentsFlag) == SegmentsFlag) { + writeRegister(&context->ds, + reg_info[lldb_ds_i386].mutable_data(result_base)); + writeRegister(&context->es, + reg_info[lldb_es_i386].mutable_data(result_base)); + writeRegister(&context->fs, + reg_info[lldb_fs_i386].mutable_data(result_base)); + writeRegister(&context->gs, + reg_info[lldb_gs_i386].mutable_data(result_base)); + } + + if ((context_flags & IntegerFlag) == IntegerFlag) { + writeRegister(&context->eax, + reg_info[lldb_eax_i386].mutable_data(result_base)); + writeRegister(&context->ecx, + reg_info[lldb_ecx_i386].mutable_data(result_base)); + writeRegister(&context->edx, + reg_info[lldb_edx_i386].mutable_data(result_base)); + writeRegister(&context->ebx, + reg_info[lldb_ebx_i386].mutable_data(result_base)); + writeRegister(&context->esi, + reg_info[lldb_esi_i386].mutable_data(result_base)); + writeRegister(&context->edi, + reg_info[lldb_edi_i386].mutable_data(result_base)); + } + + // TODO parse the floating point registers + + return result_context_buf; +} Index: lldb/trunk/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h =================================================================== --- lldb/trunk/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h +++ lldb/trunk/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h @@ -35,8 +35,8 @@ // 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); +ConvertMinidumpContext_x86_64(llvm::ArrayRef source_data, + RegisterInfoInterface *target_reg_interface); struct Uint128 { llvm::support::ulittle64_t high; @@ -72,7 +72,7 @@ llvm::support::ulittle64_t p5_home; llvm::support::ulittle64_t p6_home; - // The context_flags field determines and which parts + // The context_flags field determines which parts // of the structure are populated (have valid values) llvm::support::ulittle32_t context_flags; llvm::support::ulittle32_t mx_csr; Index: lldb/trunk/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp +++ lldb/trunk/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp @@ -19,8 +19,8 @@ using namespace lldb_private; using namespace minidump; -llvm::MutableArrayRef getDestRegister(uint8_t *context, - const RegisterInfo ®) { +static llvm::MutableArrayRef getDestRegister(uint8_t *context, + const RegisterInfo ®) { auto bytes = reg.mutable_data(context); switch (reg.kinds[lldb::eRegisterKindLLDB]) { @@ -41,13 +41,13 @@ } } -void writeRegister(const void *reg_src, uint8_t *context, - const RegisterInfo ®) { +static void writeRegister(const void *reg_src, uint8_t *context, + const RegisterInfo ®) { llvm::MutableArrayRef reg_dest = getDestRegister(context, reg); memcpy(reg_dest.data(), reg_src, reg_dest.size()); } -lldb::DataBufferSP lldb_private::minidump::ConvertMinidumpContextToRegIface( +lldb::DataBufferSP lldb_private::minidump::ConvertMinidumpContext_x86_64( llvm::ArrayRef source_data, RegisterInfoInterface *target_reg_interface) { Index: lldb/trunk/unittests/Process/minidump/CMakeLists.txt =================================================================== --- lldb/trunk/unittests/Process/minidump/CMakeLists.txt +++ lldb/trunk/unittests/Process/minidump/CMakeLists.txt @@ -3,6 +3,7 @@ ) set(test_inputs + linux-i386.dmp linux-x86_64.dmp linux-x86_64_not_crashed.dmp fizzbuzz_no_heap.dmp Index: lldb/trunk/unittests/Process/minidump/MinidumpParserTest.cpp =================================================================== --- lldb/trunk/unittests/Process/minidump/MinidumpParserTest.cpp +++ lldb/trunk/unittests/Process/minidump/MinidumpParserTest.cpp @@ -8,9 +8,11 @@ //===----------------------------------------------------------------------===// // Project includes +#include "Plugins/Process/Utility/RegisterContextLinux_i386.h" #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_32.h" #include "Plugins/Process/minidump/RegisterContextMinidump_x86_64.h" // Other libraries and framework includes @@ -61,7 +63,7 @@ std::unique_ptr parser; }; -TEST_F(MinidumpParserTest, GetThreads) { +TEST_F(MinidumpParserTest, GetThreadsAndGetThreadContext) { SetUpData("linux-x86_64.dmp"); llvm::ArrayRef thread_list; @@ -275,58 +277,97 @@ // Register stuff // TODO probably split register stuff tests into different file? -#define REG_VAL(x) *(reinterpret_cast(x)) +#define REG_VAL32(x) *(reinterpret_cast(x)) +#define REG_VAL64(x) *(reinterpret_cast(x)) -TEST_F(MinidumpParserTest, ConvertRegisterContext) { +TEST_F(MinidumpParserTest, ConvertMinidumpContext_x86_32) { + SetUpData("linux-i386.dmp"); + llvm::ArrayRef thread_list = parser->GetThreads(); + const MinidumpThread thread = thread_list[0]; + llvm::ArrayRef registers(parser->GetThreadContext(thread)); + + ArchSpec arch = parser->GetArchitecture(); + RegisterInfoInterface *reg_interface = new RegisterContextLinux_i386(arch); + lldb::DataBufferSP buf = + ConvertMinidumpContext_x86_32(registers, reg_interface); + ASSERT_EQ(reg_interface->GetGPRSize(), buf->GetByteSize()); + + const RegisterInfo *reg_info = reg_interface->GetRegisterInfo(); + + std::map reg_values; + + reg_values[lldb_eax_i386] = 0x00000000; + reg_values[lldb_ebx_i386] = 0xf7778000; + reg_values[lldb_ecx_i386] = 0x00000001; + reg_values[lldb_edx_i386] = 0xff9dd4a3; + reg_values[lldb_edi_i386] = 0x080482a8; + reg_values[lldb_esi_i386] = 0xff9dd55c; + reg_values[lldb_ebp_i386] = 0xff9dd53c; + reg_values[lldb_esp_i386] = 0xff9dd52c; + reg_values[lldb_eip_i386] = 0x080482a0; + reg_values[lldb_eflags_i386] = 0x00010282; + reg_values[lldb_cs_i386] = 0x00000023; + reg_values[lldb_fs_i386] = 0x00000000; + reg_values[lldb_gs_i386] = 0x00000063; + reg_values[lldb_ss_i386] = 0x0000002b; + reg_values[lldb_ds_i386] = 0x0000002b; + reg_values[lldb_es_i386] = 0x0000002b; + + 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_VAL32(buf->GetBytes() + reg_info[reg_index].byte_offset)); + } + } +} + +TEST_F(MinidumpParserTest, ConvertMinidumpContext_x86_64) { 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); + llvm::ArrayRef registers(parser->GetThreadContext(thread)); ArchSpec arch = parser->GetArchitecture(); RegisterInfoInterface *reg_interface = new RegisterContextLinux_x86_64(arch); lldb::DataBufferSP buf = - ConvertMinidumpContextToRegIface(registers, reg_interface); + ConvertMinidumpContext_x86_64(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 + 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; 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)); + REG_VAL64(buf->GetBytes() + reg_info[reg_index].byte_offset)); } } }