diff options
Diffstat (limited to 'source/Plugins/Process/minidump')
-rw-r--r-- | source/Plugins/Process/minidump/CMakeLists.txt | 10 | ||||
-rw-r--r-- | source/Plugins/Process/minidump/MinidumpParser.cpp | 458 | ||||
-rw-r--r-- | source/Plugins/Process/minidump/MinidumpParser.h | 102 | ||||
-rw-r--r-- | source/Plugins/Process/minidump/MinidumpTypes.cpp | 235 | ||||
-rw-r--r-- | source/Plugins/Process/minidump/MinidumpTypes.h | 470 | ||||
-rw-r--r-- | source/Plugins/Process/minidump/NtStructures.h | 37 | ||||
-rw-r--r-- | source/Plugins/Process/minidump/ProcessMinidump.cpp | 301 | ||||
-rw-r--r-- | source/Plugins/Process/minidump/ProcessMinidump.h | 105 | ||||
-rw-r--r-- | source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.cpp | 99 | ||||
-rw-r--r-- | source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.h | 138 | ||||
-rw-r--r-- | source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp | 113 | ||||
-rw-r--r-- | source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h | 183 | ||||
-rw-r--r-- | source/Plugins/Process/minidump/ThreadMinidump.cpp | 114 | ||||
-rw-r--r-- | source/Plugins/Process/minidump/ThreadMinidump.h | 52 |
14 files changed, 2417 insertions, 0 deletions
diff --git a/source/Plugins/Process/minidump/CMakeLists.txt b/source/Plugins/Process/minidump/CMakeLists.txt new file mode 100644 index 0000000000000..ddc89cbd92c15 --- /dev/null +++ b/source/Plugins/Process/minidump/CMakeLists.txt @@ -0,0 +1,10 @@ +include_directories(../Utility) + +add_lldb_library(lldbPluginProcessMinidump + MinidumpTypes.cpp + MinidumpParser.cpp + RegisterContextMinidump_x86_32.cpp + RegisterContextMinidump_x86_64.cpp + ProcessMinidump.cpp + ThreadMinidump.cpp + ) diff --git a/source/Plugins/Process/minidump/MinidumpParser.cpp b/source/Plugins/Process/minidump/MinidumpParser.cpp new file mode 100644 index 0000000000000..37b3709c09c16 --- /dev/null +++ b/source/Plugins/Process/minidump/MinidumpParser.cpp @@ -0,0 +1,458 @@ +//===-- MinidumpParser.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 "MinidumpParser.h" +#include "NtStructures.h" +#include "RegisterContextMinidump_x86_32.h" + +// Other libraries and framework includes +#include "lldb/Target/MemoryRegionInfo.h" + +// C includes +// C++ includes +#include <map> + +using namespace lldb_private; +using namespace minidump; + +llvm::Optional<MinidumpParser> +MinidumpParser::Create(const lldb::DataBufferSP &data_buf_sp) { + if (data_buf_sp->GetByteSize() < sizeof(MinidumpHeader)) { + return llvm::None; + } + + llvm::ArrayRef<uint8_t> header_data(data_buf_sp->GetBytes(), + sizeof(MinidumpHeader)); + const MinidumpHeader *header = MinidumpHeader::Parse(header_data); + + if (header == nullptr) { + return llvm::None; + } + + lldb::offset_t directory_list_offset = header->stream_directory_rva; + // check if there is enough data for the parsing of the directory list + if ((directory_list_offset + + sizeof(MinidumpDirectory) * header->streams_count) > + data_buf_sp->GetByteSize()) { + return llvm::None; + } + + const MinidumpDirectory *directory = nullptr; + Error error; + llvm::ArrayRef<uint8_t> directory_data( + data_buf_sp->GetBytes() + directory_list_offset, + sizeof(MinidumpDirectory) * header->streams_count); + llvm::DenseMap<uint32_t, MinidumpLocationDescriptor> directory_map; + + for (uint32_t i = 0; i < header->streams_count; ++i) { + error = consumeObject(directory_data, directory); + if (error.Fail()) { + return llvm::None; + } + directory_map[static_cast<const uint32_t>(directory->stream_type)] = + directory->location; + } + + return MinidumpParser(data_buf_sp, header, std::move(directory_map)); +} + +MinidumpParser::MinidumpParser( + const lldb::DataBufferSP &data_buf_sp, const MinidumpHeader *header, + llvm::DenseMap<uint32_t, MinidumpLocationDescriptor> &&directory_map) + : m_data_sp(data_buf_sp), m_header(header), m_directory_map(directory_map) { +} + +llvm::ArrayRef<uint8_t> MinidumpParser::GetData() { + return llvm::ArrayRef<uint8_t>(m_data_sp->GetBytes(), + m_data_sp->GetByteSize()); +} + +llvm::ArrayRef<uint8_t> +MinidumpParser::GetStream(MinidumpStreamType stream_type) { + auto iter = m_directory_map.find(static_cast<uint32_t>(stream_type)); + if (iter == m_directory_map.end()) + return {}; + + // check if there is enough data + if (iter->second.rva + iter->second.data_size > m_data_sp->GetByteSize()) + return {}; + + return llvm::ArrayRef<uint8_t>(m_data_sp->GetBytes() + iter->second.rva, + iter->second.data_size); +} + +llvm::Optional<std::string> MinidumpParser::GetMinidumpString(uint32_t rva) { + auto arr_ref = m_data_sp->GetData(); + if (rva > arr_ref.size()) + return llvm::None; + arr_ref = arr_ref.drop_front(rva); + return parseMinidumpString(arr_ref); +} + +llvm::ArrayRef<MinidumpThread> MinidumpParser::GetThreads() { + llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::ThreadList); + + if (data.size() == 0) + return llvm::None; + + return MinidumpThread::ParseThreadList(data); +} + +llvm::ArrayRef<uint8_t> +MinidumpParser::GetThreadContext(const MinidumpThread &td) { + if (td.thread_context.rva + td.thread_context.data_size > GetData().size()) + return {}; + + return GetData().slice(td.thread_context.rva, td.thread_context.data_size); +} + +llvm::ArrayRef<uint8_t> +MinidumpParser::GetThreadContextWow64(const MinidumpThread &td) { + // On Windows, a 32-bit process can run on a 64-bit machine under + // WOW64. If the minidump was captured with a 64-bit debugger, then + // the CONTEXT we just grabbed from the mini_dump_thread is the one + // for the 64-bit "native" process rather than the 32-bit "guest" + // process we care about. In this case, we can get the 32-bit CONTEXT + // from the TEB (Thread Environment Block) of the 64-bit process. + auto teb_mem = GetMemory(td.teb, sizeof(TEB64)); + if (teb_mem.empty()) + return {}; + + const TEB64 *wow64teb; + Error error = consumeObject(teb_mem, wow64teb); + if (error.Fail()) + return {}; + + // Slot 1 of the thread-local storage in the 64-bit TEB points to a + // structure that includes the 32-bit CONTEXT (after a ULONG). + // See: https://msdn.microsoft.com/en-us/library/ms681670.aspx + auto context = + GetMemory(wow64teb->tls_slots[1] + 4, sizeof(MinidumpContext_x86_32)); + if (context.size() < sizeof(MinidumpContext_x86_32)) + return {}; + + return context; + // NOTE: We don't currently use the TEB for anything else. If we + // need it in the future, the 32-bit TEB is located according to the address + // stored in the first slot of the 64-bit TEB (wow64teb.Reserved1[0]). +} + +const MinidumpSystemInfo *MinidumpParser::GetSystemInfo() { + llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::SystemInfo); + + if (data.size() == 0) + return nullptr; + + return MinidumpSystemInfo::Parse(data); +} + +ArchSpec MinidumpParser::GetArchitecture() { + ArchSpec arch_spec; + const MinidumpSystemInfo *system_info = GetSystemInfo(); + + if (!system_info) + return arch_spec; + + // TODO what to do about big endiand flavors of arm ? + // TODO set the arm subarch stuff if the minidump has info about it + + llvm::Triple triple; + triple.setVendor(llvm::Triple::VendorType::UnknownVendor); + + const MinidumpCPUArchitecture arch = + static_cast<const MinidumpCPUArchitecture>( + static_cast<const uint32_t>(system_info->processor_arch)); + + switch (arch) { + case MinidumpCPUArchitecture::X86: + triple.setArch(llvm::Triple::ArchType::x86); + break; + case MinidumpCPUArchitecture::AMD64: + triple.setArch(llvm::Triple::ArchType::x86_64); + break; + case MinidumpCPUArchitecture::ARM: + triple.setArch(llvm::Triple::ArchType::arm); + break; + case MinidumpCPUArchitecture::ARM64: + triple.setArch(llvm::Triple::ArchType::aarch64); + break; + default: + triple.setArch(llvm::Triple::ArchType::UnknownArch); + break; + } + + const MinidumpOSPlatform os = static_cast<const MinidumpOSPlatform>( + static_cast<const uint32_t>(system_info->platform_id)); + + // TODO add all of the OSes that Minidump/breakpad distinguishes? + switch (os) { + case MinidumpOSPlatform::Win32S: + case MinidumpOSPlatform::Win32Windows: + case MinidumpOSPlatform::Win32NT: + case MinidumpOSPlatform::Win32CE: + triple.setOS(llvm::Triple::OSType::Win32); + break; + case MinidumpOSPlatform::Linux: + triple.setOS(llvm::Triple::OSType::Linux); + break; + case MinidumpOSPlatform::MacOSX: + triple.setOS(llvm::Triple::OSType::MacOSX); + break; + case MinidumpOSPlatform::Android: + triple.setOS(llvm::Triple::OSType::Linux); + triple.setEnvironment(llvm::Triple::EnvironmentType::Android); + break; + default: + triple.setOS(llvm::Triple::OSType::UnknownOS); + break; + } + + arch_spec.SetTriple(triple); + + return arch_spec; +} + +const MinidumpMiscInfo *MinidumpParser::GetMiscInfo() { + llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::MiscInfo); + + if (data.size() == 0) + return nullptr; + + return MinidumpMiscInfo::Parse(data); +} + +llvm::Optional<LinuxProcStatus> MinidumpParser::GetLinuxProcStatus() { + llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::LinuxProcStatus); + + if (data.size() == 0) + return llvm::None; + + return LinuxProcStatus::Parse(data); +} + +llvm::Optional<lldb::pid_t> MinidumpParser::GetPid() { + const MinidumpMiscInfo *misc_info = GetMiscInfo(); + if (misc_info != nullptr) { + return misc_info->GetPid(); + } + + llvm::Optional<LinuxProcStatus> proc_status = GetLinuxProcStatus(); + if (proc_status.hasValue()) { + return proc_status->GetPid(); + } + + return llvm::None; +} + +llvm::ArrayRef<MinidumpModule> MinidumpParser::GetModuleList() { + llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::ModuleList); + + if (data.size() == 0) + return {}; + + return MinidumpModule::ParseModuleList(data); +} + +std::vector<const MinidumpModule *> MinidumpParser::GetFilteredModuleList() { + llvm::ArrayRef<MinidumpModule> modules = GetModuleList(); + // map module_name -> pair(load_address, pointer to module struct in memory) + llvm::StringMap<std::pair<uint64_t, const MinidumpModule *>> lowest_addr; + + std::vector<const MinidumpModule *> filtered_modules; + + llvm::Optional<std::string> name; + std::string module_name; + + for (const auto &module : modules) { + name = GetMinidumpString(module.module_name_rva); + + if (!name) + continue; + + module_name = name.getValue(); + + auto iter = lowest_addr.end(); + bool exists; + std::tie(iter, exists) = lowest_addr.try_emplace( + module_name, std::make_pair(module.base_of_image, &module)); + + if (exists && module.base_of_image < iter->second.first) + iter->second = std::make_pair(module.base_of_image, &module); + } + + filtered_modules.reserve(lowest_addr.size()); + for (const auto &module : lowest_addr) { + filtered_modules.push_back(module.second.second); + } + + return filtered_modules; +} + +const MinidumpExceptionStream *MinidumpParser::GetExceptionStream() { + llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::Exception); + + if (data.size() == 0) + return nullptr; + + return MinidumpExceptionStream::Parse(data); +} + +llvm::Optional<minidump::Range> +MinidumpParser::FindMemoryRange(lldb::addr_t addr) { + llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::MemoryList); + llvm::ArrayRef<uint8_t> data64 = GetStream(MinidumpStreamType::Memory64List); + + if (data.empty() && data64.empty()) + return llvm::None; + + if (!data.empty()) { + llvm::ArrayRef<MinidumpMemoryDescriptor> memory_list = + MinidumpMemoryDescriptor::ParseMemoryList(data); + + if (memory_list.empty()) + return llvm::None; + + for (const auto &memory_desc : memory_list) { + const MinidumpLocationDescriptor &loc_desc = memory_desc.memory; + const lldb::addr_t range_start = memory_desc.start_of_memory_range; + const size_t range_size = loc_desc.data_size; + + if (loc_desc.rva + loc_desc.data_size > GetData().size()) + return llvm::None; + + if (range_start <= addr && addr < range_start + range_size) { + return minidump::Range(range_start, + GetData().slice(loc_desc.rva, range_size)); + } + } + } + + // Some Minidumps have a Memory64ListStream that captures all the heap + // memory (full-memory Minidumps). We can't exactly use the same loop as + // above, because the Minidump uses slightly different data structures to + // describe those + + if (!data64.empty()) { + llvm::ArrayRef<MinidumpMemoryDescriptor64> memory64_list; + uint64_t base_rva; + std::tie(memory64_list, base_rva) = + MinidumpMemoryDescriptor64::ParseMemory64List(data64); + + if (memory64_list.empty()) + return llvm::None; + + for (const auto &memory_desc64 : memory64_list) { + const lldb::addr_t range_start = memory_desc64.start_of_memory_range; + const size_t range_size = memory_desc64.data_size; + + if (base_rva + range_size > GetData().size()) + return llvm::None; + + if (range_start <= addr && addr < range_start + range_size) { + return minidump::Range(range_start, + GetData().slice(base_rva, range_size)); + } + base_rva += range_size; + } + } + + return llvm::None; +} + +llvm::ArrayRef<uint8_t> MinidumpParser::GetMemory(lldb::addr_t addr, + size_t size) { + // I don't have a sense of how frequently this is called or how many memory + // ranges a Minidump typically has, so I'm not sure if searching for the + // appropriate range linearly each time is stupid. Perhaps we should build + // an index for faster lookups. + llvm::Optional<minidump::Range> range = FindMemoryRange(addr); + if (!range) + return {}; + + // There's at least some overlap between the beginning of the desired range + // (addr) and the current range. Figure out where the overlap begins and + // how much overlap there is. + + const size_t offset = addr - range->start; + + if (addr < range->start || offset >= range->range_ref.size()) + return {}; + + const size_t overlap = std::min(size, range->range_ref.size() - offset); + return range->range_ref.slice(offset, overlap); +} + +llvm::Optional<MemoryRegionInfo> +MinidumpParser::GetMemoryRegionInfo(lldb::addr_t load_addr) { + MemoryRegionInfo info; + llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::MemoryInfoList); + if (data.empty()) + return llvm::None; + + std::vector<const MinidumpMemoryInfo *> mem_info_list = + MinidumpMemoryInfo::ParseMemoryInfoList(data); + if (mem_info_list.empty()) + return llvm::None; + + const auto yes = MemoryRegionInfo::eYes; + const auto no = MemoryRegionInfo::eNo; + + const MinidumpMemoryInfo *next_entry = nullptr; + for (const auto &entry : mem_info_list) { + const auto head = entry->base_address; + const auto tail = head + entry->region_size; + + if (head <= load_addr && load_addr < tail) { + info.GetRange().SetRangeBase( + (entry->state != uint32_t(MinidumpMemoryInfoState::MemFree)) + ? head + : load_addr); + info.GetRange().SetRangeEnd(tail); + + const uint32_t PageNoAccess = + static_cast<uint32_t>(MinidumpMemoryProtectionContants::PageNoAccess); + info.SetReadable((entry->protect & PageNoAccess) == 0 ? yes : no); + + const uint32_t PageWritable = + static_cast<uint32_t>(MinidumpMemoryProtectionContants::PageWritable); + info.SetWritable((entry->protect & PageWritable) != 0 ? yes : no); + + const uint32_t PageExecutable = static_cast<uint32_t>( + MinidumpMemoryProtectionContants::PageExecutable); + info.SetExecutable((entry->protect & PageExecutable) != 0 ? yes : no); + + const uint32_t MemFree = + static_cast<uint32_t>(MinidumpMemoryInfoState::MemFree); + info.SetMapped((entry->state != MemFree) ? yes : no); + + return info; + } else if (head > load_addr && + (next_entry == nullptr || head < next_entry->base_address)) { + // In case there is no region containing load_addr keep track of the + // nearest region after load_addr so we can return the distance to it. + next_entry = entry; + } + } + + // No containing region found. Create an unmapped region that extends to the + // next region or LLDB_INVALID_ADDRESS + info.GetRange().SetRangeBase(load_addr); + info.GetRange().SetRangeEnd((next_entry != nullptr) ? next_entry->base_address + : LLDB_INVALID_ADDRESS); + info.SetReadable(no); + info.SetWritable(no); + info.SetExecutable(no); + info.SetMapped(no); + + // Note that the memory info list doesn't seem to contain ranges in kernel + // space, so if you're walking a stack that has kernel frames, the stack may + // appear truncated. + return info; +} diff --git a/source/Plugins/Process/minidump/MinidumpParser.h b/source/Plugins/Process/minidump/MinidumpParser.h new file mode 100644 index 0000000000000..67523a72ad115 --- /dev/null +++ b/source/Plugins/Process/minidump/MinidumpParser.h @@ -0,0 +1,102 @@ +//===-- MinidumpParser.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_MinidumpParser_h_ +#define liblldb_MinidumpParser_h_ + +// Project includes +#include "MinidumpTypes.h" + +// Other libraries and framework includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/Error.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" + +// C includes + +// C++ includes +#include <cstring> +#include <unordered_map> + +namespace lldb_private { + +namespace minidump { + +// Describes a range of memory captured in the Minidump +struct Range { + lldb::addr_t start; // virtual address of the beginning of the range + // range_ref - absolute pointer to the first byte of the range and size + llvm::ArrayRef<uint8_t> range_ref; + + Range(lldb::addr_t start, llvm::ArrayRef<uint8_t> range_ref) + : start(start), range_ref(range_ref) {} +}; + +class MinidumpParser { +public: + static llvm::Optional<MinidumpParser> + Create(const lldb::DataBufferSP &data_buf_sp); + + llvm::ArrayRef<uint8_t> GetData(); + + llvm::ArrayRef<uint8_t> GetStream(MinidumpStreamType stream_type); + + llvm::Optional<std::string> GetMinidumpString(uint32_t rva); + + llvm::ArrayRef<MinidumpThread> GetThreads(); + + llvm::ArrayRef<uint8_t> GetThreadContext(const MinidumpThread &td); + + llvm::ArrayRef<uint8_t> GetThreadContextWow64(const MinidumpThread &td); + + const MinidumpSystemInfo *GetSystemInfo(); + + ArchSpec GetArchitecture(); + + const MinidumpMiscInfo *GetMiscInfo(); + + llvm::Optional<LinuxProcStatus> GetLinuxProcStatus(); + + llvm::Optional<lldb::pid_t> GetPid(); + + llvm::ArrayRef<MinidumpModule> GetModuleList(); + + // There are cases in which there is more than one record in the ModuleList + // for the same module name.(e.g. when the binary has non contiguous segments) + // So this function returns a filtered module list - if it finds records that + // have the same name, it keeps the copy with the lowest load address. + std::vector<const MinidumpModule *> GetFilteredModuleList(); + + const MinidumpExceptionStream *GetExceptionStream(); + + llvm::Optional<Range> FindMemoryRange(lldb::addr_t addr); + + llvm::ArrayRef<uint8_t> GetMemory(lldb::addr_t addr, size_t size); + + llvm::Optional<MemoryRegionInfo> GetMemoryRegionInfo(lldb::addr_t); + +private: + lldb::DataBufferSP m_data_sp; + const MinidumpHeader *m_header; + llvm::DenseMap<uint32_t, MinidumpLocationDescriptor> m_directory_map; + + MinidumpParser( + const lldb::DataBufferSP &data_buf_sp, const MinidumpHeader *header, + llvm::DenseMap<uint32_t, MinidumpLocationDescriptor> &&directory_map); +}; + +} // end namespace minidump +} // end namespace lldb_private +#endif // liblldb_MinidumpParser_h_ diff --git a/source/Plugins/Process/minidump/MinidumpTypes.cpp b/source/Plugins/Process/minidump/MinidumpTypes.cpp new file mode 100644 index 0000000000000..863d124a7ccce --- /dev/null +++ b/source/Plugins/Process/minidump/MinidumpTypes.cpp @@ -0,0 +1,235 @@ +//===-- MinidumpTypes.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 "MinidumpTypes.h" + +// Other libraries and framework includes +// C includes +// C++ includes + +using namespace lldb_private; +using namespace minidump; + +const MinidumpHeader *MinidumpHeader::Parse(llvm::ArrayRef<uint8_t> &data) { + const MinidumpHeader *header = nullptr; + Error error = consumeObject(data, header); + + const MinidumpHeaderConstants signature = + static_cast<const MinidumpHeaderConstants>( + static_cast<const uint32_t>(header->signature)); + const MinidumpHeaderConstants version = + static_cast<const MinidumpHeaderConstants>( + static_cast<const uint32_t>(header->version) & 0x0000ffff); + // the high 16 bits of the version field are implementation specific + + if (error.Fail() || signature != MinidumpHeaderConstants::Signature || + version != MinidumpHeaderConstants::Version) + return nullptr; + + // TODO check for max number of streams ? + // TODO more sanity checks ? + + return header; +} + +// Minidump string +llvm::Optional<std::string> +lldb_private::minidump::parseMinidumpString(llvm::ArrayRef<uint8_t> &data) { + std::string result; + + const uint32_t *source_length; + Error error = consumeObject(data, source_length); + if (error.Fail() || *source_length > data.size() || *source_length % 2 != 0) + return llvm::None; + + auto source_start = reinterpret_cast<const llvm::UTF16 *>(data.data()); + // source_length is the length of the string in bytes + // we need the length of the string in UTF-16 characters/code points (16 bits + // per char) + // that's why it's divided by 2 + const auto source_end = source_start + (*source_length) / 2; + // resize to worst case length + result.resize(UNI_MAX_UTF8_BYTES_PER_CODE_POINT * (*source_length) / 2); + auto result_start = reinterpret_cast<llvm::UTF8 *>(&result[0]); + const auto result_end = result_start + result.size(); + llvm::ConvertUTF16toUTF8(&source_start, source_end, &result_start, result_end, + llvm::strictConversion); + const auto result_size = + std::distance(reinterpret_cast<llvm::UTF8 *>(&result[0]), result_start); + result.resize(result_size); // shrink to actual length + + return result; +} + +// MinidumpThread +const MinidumpThread *MinidumpThread::Parse(llvm::ArrayRef<uint8_t> &data) { + const MinidumpThread *thread = nullptr; + Error error = consumeObject(data, thread); + if (error.Fail()) + return nullptr; + + return thread; +} + +llvm::ArrayRef<MinidumpThread> +MinidumpThread::ParseThreadList(llvm::ArrayRef<uint8_t> &data) { + const llvm::support::ulittle32_t *thread_count; + Error error = consumeObject(data, thread_count); + if (error.Fail() || *thread_count * sizeof(MinidumpThread) > data.size()) + return {}; + + return llvm::ArrayRef<MinidumpThread>( + reinterpret_cast<const MinidumpThread *>(data.data()), *thread_count); +} + +// MinidumpSystemInfo +const MinidumpSystemInfo * +MinidumpSystemInfo::Parse(llvm::ArrayRef<uint8_t> &data) { + const MinidumpSystemInfo *system_info; + Error error = consumeObject(data, system_info); + if (error.Fail()) + return nullptr; + + return system_info; +} + +// MinidumpMiscInfo +const MinidumpMiscInfo *MinidumpMiscInfo::Parse(llvm::ArrayRef<uint8_t> &data) { + const MinidumpMiscInfo *misc_info; + Error error = consumeObject(data, misc_info); + if (error.Fail()) + return nullptr; + + return misc_info; +} + +llvm::Optional<lldb::pid_t> MinidumpMiscInfo::GetPid() const { + uint32_t pid_flag = + static_cast<const uint32_t>(MinidumpMiscInfoFlags::ProcessID); + if (flags1 & pid_flag) + return llvm::Optional<lldb::pid_t>(process_id); + + return llvm::None; +} + +// Linux Proc Status +// it's stored as an ascii string in the file +llvm::Optional<LinuxProcStatus> +LinuxProcStatus::Parse(llvm::ArrayRef<uint8_t> &data) { + LinuxProcStatus result; + result.proc_status = + llvm::StringRef(reinterpret_cast<const char *>(data.data()), data.size()); + data = data.drop_front(data.size()); + + llvm::SmallVector<llvm::StringRef, 0> lines; + result.proc_status.split(lines, '\n', 42); + // /proc/$pid/status has 41 lines, but why not use 42? + for (auto line : lines) { + if (line.consume_front("Pid:")) { + line = line.trim(); + if (!line.getAsInteger(10, result.pid)) + return result; + } + } + + return llvm::None; +} + +lldb::pid_t LinuxProcStatus::GetPid() const { return pid; } + +// Module stuff +const MinidumpModule *MinidumpModule::Parse(llvm::ArrayRef<uint8_t> &data) { + const MinidumpModule *module = nullptr; + Error error = consumeObject(data, module); + if (error.Fail()) + return nullptr; + + return module; +} + +llvm::ArrayRef<MinidumpModule> +MinidumpModule::ParseModuleList(llvm::ArrayRef<uint8_t> &data) { + + const llvm::support::ulittle32_t *modules_count; + Error error = consumeObject(data, modules_count); + if (error.Fail() || *modules_count * sizeof(MinidumpModule) > data.size()) + return {}; + + return llvm::ArrayRef<MinidumpModule>( + reinterpret_cast<const MinidumpModule *>(data.data()), *modules_count); +} + +// Exception stuff +const MinidumpExceptionStream * +MinidumpExceptionStream::Parse(llvm::ArrayRef<uint8_t> &data) { + const MinidumpExceptionStream *exception_stream = nullptr; + Error error = consumeObject(data, exception_stream); + if (error.Fail()) + return nullptr; + + return exception_stream; +} + +llvm::ArrayRef<MinidumpMemoryDescriptor> +MinidumpMemoryDescriptor::ParseMemoryList(llvm::ArrayRef<uint8_t> &data) { + const llvm::support::ulittle32_t *mem_ranges_count; + Error error = consumeObject(data, mem_ranges_count); + if (error.Fail() || + *mem_ranges_count * sizeof(MinidumpMemoryDescriptor) > data.size()) + return {}; + + return llvm::makeArrayRef( + reinterpret_cast<const MinidumpMemoryDescriptor *>(data.data()), + *mem_ranges_count); +} + +std::pair<llvm::ArrayRef<MinidumpMemoryDescriptor64>, uint64_t> +MinidumpMemoryDescriptor64::ParseMemory64List(llvm::ArrayRef<uint8_t> &data) { + const llvm::support::ulittle64_t *mem_ranges_count; + Error error = consumeObject(data, mem_ranges_count); + if (error.Fail() || + *mem_ranges_count * sizeof(MinidumpMemoryDescriptor64) > data.size()) + return {}; + + const llvm::support::ulittle64_t *base_rva; + error = consumeObject(data, base_rva); + if (error.Fail()) + return {}; + + return std::make_pair( + llvm::makeArrayRef( + reinterpret_cast<const MinidumpMemoryDescriptor64 *>(data.data()), + *mem_ranges_count), + *base_rva); +} + +std::vector<const MinidumpMemoryInfo *> +MinidumpMemoryInfo::ParseMemoryInfoList(llvm::ArrayRef<uint8_t> &data) { + const MinidumpMemoryInfoListHeader *header; + Error error = consumeObject(data, header); + if (error.Fail() || + header->size_of_header < sizeof(MinidumpMemoryInfoListHeader) || + header->size_of_entry < sizeof(MinidumpMemoryInfo)) + return {}; + + data = data.drop_front(header->size_of_header - + sizeof(MinidumpMemoryInfoListHeader)); + + if (header->size_of_entry * header->num_of_entries > data.size()) + return {}; + + std::vector<const MinidumpMemoryInfo *> result; + for (uint64_t i = 0; i < header->num_of_entries; ++i) { + result.push_back(reinterpret_cast<const MinidumpMemoryInfo *>( + data.data() + i * header->size_of_entry)); + } + + return result; +} diff --git a/source/Plugins/Process/minidump/MinidumpTypes.h b/source/Plugins/Process/minidump/MinidumpTypes.h new file mode 100644 index 0000000000000..46871a1b84d88 --- /dev/null +++ b/source/Plugins/Process/minidump/MinidumpTypes.h @@ -0,0 +1,470 @@ +//===-- MinidumpTypes.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_MinidumpTypes_h_ +#define liblldb_MinidumpTypes_h_ + +// Project includes + +// Other libraries and framework includes +#include "lldb/Core/Error.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitmaskEnum.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/Endian.h" + +// C includes +// C++ includes + +// Reference: +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms679293(v=vs.85).aspx +// https://chromium.googlesource.com/breakpad/breakpad/ + +namespace lldb_private { + +namespace minidump { + +LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); + +enum class MinidumpHeaderConstants : uint32_t { + Signature = 0x504d444d, // 'PMDM' + Version = 0x0000a793, // 42899 + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ Signature) + +}; + +// Reference: +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680394.aspx +enum class MinidumpStreamType : uint32_t { + Unused = 0, + Reserved0 = 1, + Reserved1 = 2, + ThreadList = 3, + ModuleList = 4, + MemoryList = 5, + Exception = 6, + SystemInfo = 7, + ThreadExList = 8, + Memory64List = 9, + CommentA = 10, + CommentW = 11, + HandleData = 12, + FunctionTable = 13, + UnloadedModuleList = 14, + MiscInfo = 15, + MemoryInfoList = 16, + ThreadInfoList = 17, + HandleOperationList = 18, + Token = 19, + JavascriptData = 20, + SystemMemoryInfo = 21, + ProcessVMCounters = 22, + LastReserved = 0x0000ffff, + + /* Breakpad extension types. 0x4767 = "Gg" */ + BreakpadInfo = 0x47670001, + AssertionInfo = 0x47670002, + /* These are additional minidump stream values which are specific to + * the linux breakpad implementation. */ + LinuxCPUInfo = 0x47670003, /* /proc/cpuinfo */ + LinuxProcStatus = 0x47670004, /* /proc/$x/status */ + LinuxLSBRelease = 0x47670005, /* /etc/lsb-release */ + LinuxCMDLine = 0x47670006, /* /proc/$x/cmdline */ + LinuxEnviron = 0x47670007, /* /proc/$x/environ */ + LinuxAuxv = 0x47670008, /* /proc/$x/auxv */ + LinuxMaps = 0x47670009, /* /proc/$x/maps */ + LinuxDSODebug = 0x4767000A +}; + +// for MinidumpSystemInfo.processor_arch +enum class MinidumpCPUArchitecture : uint16_t { + X86 = 0, /* PROCESSOR_ARCHITECTURE_INTEL */ + MIPS = 1, /* PROCESSOR_ARCHITECTURE_MIPS */ + Alpha = 2, /* PROCESSOR_ARCHITECTURE_ALPHA */ + PPC = 3, /* PROCESSOR_ARCHITECTURE_PPC */ + SHX = 4, /* PROCESSOR_ARCHITECTURE_SHX (Super-H) */ + ARM = 5, /* PROCESSOR_ARCHITECTURE_ARM */ + IA64 = 6, /* PROCESSOR_ARCHITECTURE_IA64 */ + Alpha64 = 7, /* PROCESSOR_ARCHITECTURE_ALPHA64 */ + MSIL = 8, /* PROCESSOR_ARCHITECTURE_MSIL + * (Microsoft Intermediate Language) */ + AMD64 = 9, /* PROCESSOR_ARCHITECTURE_AMD64 */ + X86Win64 = 10, /* PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 (WoW64) */ + SPARC = 0x8001, /* Breakpad-defined value for SPARC */ + PPC64 = 0x8002, /* Breakpad-defined value for PPC64 */ + ARM64 = 0x8003, /* Breakpad-defined value for ARM64 */ + MIPS64 = 0x8004, /* Breakpad-defined value for MIPS64 */ + Unknown = 0xffff /* PROCESSOR_ARCHITECTURE_UNKNOWN */ +}; + +// for MinidumpSystemInfo.platform_id +enum class MinidumpOSPlatform : uint32_t { + Win32S = 0, /* VER_PLATFORM_WIN32s (Windows 3.1) */ + Win32Windows = 1, /* VER_PLATFORM_WIN32_WINDOWS (Windows 95-98-Me) */ + Win32NT = 2, /* VER_PLATFORM_WIN32_NT (Windows NT, 2000+) */ + Win32CE = 3, /* VER_PLATFORM_WIN32_CE, VER_PLATFORM_WIN32_HH + * (Windows CE, Windows Mobile, "Handheld") */ + + /* The following values are Breakpad-defined. */ + Unix = 0x8000, /* Generic Unix-ish */ + MacOSX = 0x8101, /* Mac OS X/Darwin */ + IOS = 0x8102, /* iOS */ + Linux = 0x8201, /* Linux */ + Solaris = 0x8202, /* Solaris */ + Android = 0x8203, /* Android */ + PS3 = 0x8204, /* PS3 */ + NaCl = 0x8205 /* Native Client (NaCl) */ +}; + +// For MinidumpCPUInfo.arm_cpu_info.elf_hwcaps. +// This matches the Linux kernel definitions from <asm/hwcaps.h> +enum class MinidumpPCPUInformationARMElfHwCaps : uint32_t { + SWP = (1 << 0), + Half = (1 << 1), + Thumb = (1 << 2), + _26BIT = (1 << 3), + FastMult = (1 << 4), + FPA = (1 << 5), + VFP = (1 << 6), + EDSP = (1 << 7), + Java = (1 << 8), + IWMMXT = (1 << 9), + Crunch = (1 << 10), + ThumbEE = (1 << 11), + Neon = (1 << 12), + VFPv3 = (1 << 13), + VFPv3D16 = (1 << 14), + TLS = (1 << 15), + VFPv4 = (1 << 16), + IDIVA = (1 << 17), + IDIVT = (1 << 18), + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ IDIVT) +}; + +enum class MinidumpMiscInfoFlags : uint32_t { + ProcessID = (1 << 0), + ProcessTimes = (1 << 1), + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ ProcessTimes) +}; + +template <typename T> +Error consumeObject(llvm::ArrayRef<uint8_t> &Buffer, const T *&Object) { + Error error; + if (Buffer.size() < sizeof(T)) { + error.SetErrorString("Insufficient buffer!"); + return error; + } + + Object = reinterpret_cast<const T *>(Buffer.data()); + Buffer = Buffer.drop_front(sizeof(T)); + return error; +} + +// parse a MinidumpString which is with UTF-16 +// Reference: +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680395(v=vs.85).aspx +llvm::Optional<std::string> parseMinidumpString(llvm::ArrayRef<uint8_t> &data); + +// Reference: +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680378(v=vs.85).aspx +struct MinidumpHeader { + llvm::support::ulittle32_t signature; + llvm::support::ulittle32_t + version; // The high 16 bits of version field are implementation specific + llvm::support::ulittle32_t streams_count; + llvm::support::ulittle32_t + stream_directory_rva; // offset of the stream directory + llvm::support::ulittle32_t checksum; + llvm::support::ulittle32_t time_date_stamp; // time_t format + llvm::support::ulittle64_t flags; + + static const MinidumpHeader *Parse(llvm::ArrayRef<uint8_t> &data); +}; +static_assert(sizeof(MinidumpHeader) == 32, + "sizeof MinidumpHeader is not correct!"); + +// Reference: +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680383.aspx +struct MinidumpLocationDescriptor { + llvm::support::ulittle32_t data_size; + llvm::support::ulittle32_t rva; +}; +static_assert(sizeof(MinidumpLocationDescriptor) == 8, + "sizeof MinidumpLocationDescriptor is not correct!"); + +// Reference: +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680384(v=vs.85).aspx +struct MinidumpMemoryDescriptor { + llvm::support::ulittle64_t start_of_memory_range; + MinidumpLocationDescriptor memory; + + static llvm::ArrayRef<MinidumpMemoryDescriptor> + ParseMemoryList(llvm::ArrayRef<uint8_t> &data); +}; +static_assert(sizeof(MinidumpMemoryDescriptor) == 16, + "sizeof MinidumpMemoryDescriptor is not correct!"); + +struct MinidumpMemoryDescriptor64 { + llvm::support::ulittle64_t start_of_memory_range; + llvm::support::ulittle64_t data_size; + + static std::pair<llvm::ArrayRef<MinidumpMemoryDescriptor64>, uint64_t> + ParseMemory64List(llvm::ArrayRef<uint8_t> &data); +}; +static_assert(sizeof(MinidumpMemoryDescriptor64) == 16, + "sizeof MinidumpMemoryDescriptor64 is not correct!"); + +// Reference: +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680365.aspx +struct MinidumpDirectory { + llvm::support::ulittle32_t stream_type; + MinidumpLocationDescriptor location; +}; +static_assert(sizeof(MinidumpDirectory) == 12, + "sizeof MinidumpDirectory is not correct!"); + +// Reference: +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680385(v=vs.85).aspx +struct MinidumpMemoryInfoListHeader { + llvm::support::ulittle32_t size_of_header; + llvm::support::ulittle32_t size_of_entry; + llvm::support::ulittle64_t num_of_entries; +}; +static_assert(sizeof(MinidumpMemoryInfoListHeader) == 16, + "sizeof MinidumpMemoryInfoListHeader is not correct!"); + +// Reference: +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680386(v=vs.85).aspx +struct MinidumpMemoryInfo { + llvm::support::ulittle64_t base_address; + llvm::support::ulittle64_t allocation_base; + llvm::support::ulittle32_t allocation_protect; + llvm::support::ulittle32_t alignment1; + llvm::support::ulittle64_t region_size; + llvm::support::ulittle32_t state; + llvm::support::ulittle32_t protect; + llvm::support::ulittle32_t type; + llvm::support::ulittle32_t alignment2; + + static std::vector<const MinidumpMemoryInfo *> + ParseMemoryInfoList(llvm::ArrayRef<uint8_t> &data); +}; +static_assert(sizeof(MinidumpMemoryInfo) == 48, + "sizeof MinidumpMemoryInfo is not correct!"); + +enum class MinidumpMemoryInfoState : uint32_t { + MemCommit = 0x1000, + MemFree = 0x10000, + MemReserve = 0x2000, + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ MemFree) +}; + +enum class MinidumpMemoryInfoType : uint32_t { + MemImage = 0x1000000, + MemMapped = 0x40000, + MemPrivate = 0x20000, + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ MemImage) +}; + +// Reference: +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa366786(v=vs.85).aspx +enum class MinidumpMemoryProtectionContants : uint32_t { + PageExecute = 0x10, + PageExecuteRead = 0x20, + PageExecuteReadWrite = 0x40, + PageExecuteWriteCopy = 0x80, + PageNoAccess = 0x01, + PageReadOnly = 0x02, + PageReadWrite = 0x04, + PageWriteCopy = 0x08, + PageTargetsInvalid = 0x40000000, + PageTargetsNoUpdate = 0x40000000, + + PageWritable = PageExecuteReadWrite | PageExecuteWriteCopy | PageReadWrite | + PageWriteCopy, + PageExecutable = PageExecute | PageExecuteRead | PageExecuteReadWrite | + PageExecuteWriteCopy, + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ PageTargetsInvalid) +}; + +// Reference: +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680517(v=vs.85).aspx +struct MinidumpThread { + llvm::support::ulittle32_t thread_id; + llvm::support::ulittle32_t suspend_count; + llvm::support::ulittle32_t priority_class; + llvm::support::ulittle32_t priority; + llvm::support::ulittle64_t teb; + MinidumpMemoryDescriptor stack; + MinidumpLocationDescriptor thread_context; + + static const MinidumpThread *Parse(llvm::ArrayRef<uint8_t> &data); + + static llvm::ArrayRef<MinidumpThread> + ParseThreadList(llvm::ArrayRef<uint8_t> &data); +}; +static_assert(sizeof(MinidumpThread) == 48, + "sizeof MinidumpThread is not correct!"); + +// Reference: +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680396(v=vs.85).aspx +union MinidumpCPUInfo { + struct { + llvm::support::ulittle32_t vendor_id[3]; /* cpuid 0: ebx, edx, ecx */ + llvm::support::ulittle32_t version_information; /* cpuid 1: eax */ + llvm::support::ulittle32_t feature_information; /* cpuid 1: edx */ + llvm::support::ulittle32_t + amd_extended_cpu_features; /* cpuid 0x80000001, ebx */ + } x86_cpu_info; + struct { + llvm::support::ulittle32_t cpuid; + llvm::support::ulittle32_t elf_hwcaps; /* linux specific, 0 otherwise */ + } arm_cpu_info; + struct { + llvm::support::ulittle64_t processor_features[2]; + } other_cpu_info; +}; +static_assert(sizeof(MinidumpCPUInfo) == 24, + "sizeof MinidumpCPUInfo is not correct!"); + +// Reference: +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680396(v=vs.85).aspx +struct MinidumpSystemInfo { + llvm::support::ulittle16_t processor_arch; + llvm::support::ulittle16_t processor_level; + llvm::support::ulittle16_t processor_revision; + + uint8_t number_of_processors; + uint8_t product_type; + + llvm::support::ulittle32_t major_version; + llvm::support::ulittle32_t minor_version; + llvm::support::ulittle32_t build_number; + llvm::support::ulittle32_t platform_id; + llvm::support::ulittle32_t csd_version_rva; + + llvm::support::ulittle16_t suit_mask; + llvm::support::ulittle16_t reserved2; + + MinidumpCPUInfo cpu; + + static const MinidumpSystemInfo *Parse(llvm::ArrayRef<uint8_t> &data); +}; +static_assert(sizeof(MinidumpSystemInfo) == 56, + "sizeof MinidumpSystemInfo is not correct!"); + +// TODO misc2, misc3 ? +// Reference: +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680389(v=vs.85).aspx +struct MinidumpMiscInfo { + llvm::support::ulittle32_t size; + // flags1 represents what info in the struct is valid + llvm::support::ulittle32_t flags1; + llvm::support::ulittle32_t process_id; + llvm::support::ulittle32_t process_create_time; + llvm::support::ulittle32_t process_user_time; + llvm::support::ulittle32_t process_kernel_time; + + static const MinidumpMiscInfo *Parse(llvm::ArrayRef<uint8_t> &data); + + llvm::Optional<lldb::pid_t> GetPid() const; +}; +static_assert(sizeof(MinidumpMiscInfo) == 24, + "sizeof MinidumpMiscInfo is not correct!"); + +// The /proc/pid/status is saved as an ascii string in the file +class LinuxProcStatus { +public: + llvm::StringRef proc_status; + lldb::pid_t pid; + + static llvm::Optional<LinuxProcStatus> Parse(llvm::ArrayRef<uint8_t> &data); + + lldb::pid_t GetPid() const; + +private: + LinuxProcStatus() = default; +}; + +// MinidumpModule stuff +struct MinidumpVSFixedFileInfo { + llvm::support::ulittle32_t signature; + llvm::support::ulittle32_t struct_version; + llvm::support::ulittle32_t file_version_hi; + llvm::support::ulittle32_t file_version_lo; + llvm::support::ulittle32_t product_version_hi; + llvm::support::ulittle32_t product_version_lo; + // file_flags_mask - identifies valid bits in fileFlags + llvm::support::ulittle32_t file_flags_mask; + llvm::support::ulittle32_t file_flags; + llvm::support::ulittle32_t file_os; + llvm::support::ulittle32_t file_type; + llvm::support::ulittle32_t file_subtype; + llvm::support::ulittle32_t file_date_hi; + llvm::support::ulittle32_t file_date_lo; +}; +static_assert(sizeof(MinidumpVSFixedFileInfo) == 52, + "sizeof MinidumpVSFixedFileInfo is not correct!"); + +struct MinidumpModule { + llvm::support::ulittle64_t base_of_image; + llvm::support::ulittle32_t size_of_image; + llvm::support::ulittle32_t checksum; + llvm::support::ulittle32_t time_date_stamp; + llvm::support::ulittle32_t module_name_rva; + MinidumpVSFixedFileInfo version_info; + MinidumpLocationDescriptor CV_record; + MinidumpLocationDescriptor misc_record; + llvm::support::ulittle32_t reserved0[2]; + llvm::support::ulittle32_t reserved1[2]; + + static const MinidumpModule *Parse(llvm::ArrayRef<uint8_t> &data); + + static llvm::ArrayRef<MinidumpModule> + ParseModuleList(llvm::ArrayRef<uint8_t> &data); +}; +static_assert(sizeof(MinidumpModule) == 108, + "sizeof MinidumpVSFixedFileInfo is not correct!"); + +// Exception stuff +struct MinidumpException { + enum : unsigned { + ExceptonInfoMaxParams = 15, + DumpRequested = 0xFFFFFFFF, + }; + + llvm::support::ulittle32_t exception_code; + llvm::support::ulittle32_t exception_flags; + llvm::support::ulittle64_t exception_record; + llvm::support::ulittle64_t exception_address; + llvm::support::ulittle32_t number_parameters; + llvm::support::ulittle32_t unused_alignment; + llvm::support::ulittle64_t exception_information[ExceptonInfoMaxParams]; +}; +static_assert(sizeof(MinidumpException) == 152, + "sizeof MinidumpException is not correct!"); + +struct MinidumpExceptionStream { + llvm::support::ulittle32_t thread_id; + llvm::support::ulittle32_t alignment; + MinidumpException exception_record; + MinidumpLocationDescriptor thread_context; + + static const MinidumpExceptionStream *Parse(llvm::ArrayRef<uint8_t> &data); +}; +static_assert(sizeof(MinidumpExceptionStream) == 168, + "sizeof MinidumpExceptionStream is not correct!"); + +} // namespace minidump +} // namespace lldb_private +#endif // liblldb_MinidumpTypes_h_ diff --git a/source/Plugins/Process/minidump/NtStructures.h b/source/Plugins/Process/minidump/NtStructures.h new file mode 100644 index 0000000000000..c0afd77358cd6 --- /dev/null +++ b/source/Plugins/Process/minidump/NtStructures.h @@ -0,0 +1,37 @@ +//===-- NtStructures.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_Plugins_Process_Minidump_NtStructures_h_ +#define liblldb_Plugins_Process_Minidump_NtStructures_h_ + +#include "llvm/Support/Endian.h" + +namespace lldb_private { + +namespace minidump { + +// This describes the layout of a TEB (Thread Environment Block) for a 64-bit +// process. It's adapted from the 32-bit TEB in winternl.h. Currently, we care +// only about the position of the tls_slots. +struct TEB64 { + llvm::support::ulittle64_t reserved1[12]; + llvm::support::ulittle64_t process_environment_block; + llvm::support::ulittle64_t reserved2[399]; + uint8_t reserved3[1952]; + llvm::support::ulittle64_t tls_slots[64]; + uint8_t reserved4[8]; + llvm::support::ulittle64_t reserved5[26]; + llvm::support::ulittle64_t reserved_for_ole; // Windows 2000 only + llvm::support::ulittle64_t reserved6[4]; + llvm::support::ulittle64_t tls_expansion_slots; +}; + +#endif // liblldb_Plugins_Process_Minidump_NtStructures_h_ +} // namespace minidump +} // namespace lldb_private diff --git a/source/Plugins/Process/minidump/ProcessMinidump.cpp b/source/Plugins/Process/minidump/ProcessMinidump.cpp new file mode 100644 index 0000000000000..46d8df8b16f12 --- /dev/null +++ b/source/Plugins/Process/minidump/ProcessMinidump.cpp @@ -0,0 +1,301 @@ +//===-- ProcessMinidump.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 "ProcessMinidump.h" +#include "ThreadMinidump.h" + +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/State.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/LLDBAssert.h" + +// C includes +// C++ includes + +using namespace lldb_private; +using namespace minidump; + +ConstString ProcessMinidump::GetPluginNameStatic() { + static ConstString g_name("minidump"); + return g_name; +} + +const char *ProcessMinidump::GetPluginDescriptionStatic() { + return "Minidump plug-in."; +} + +lldb::ProcessSP ProcessMinidump::CreateInstance(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec *crash_file) { + if (!crash_file) + return nullptr; + + lldb::ProcessSP process_sp; + // Read enough data for the Minidump header + const size_t header_size = sizeof(MinidumpHeader); + lldb::DataBufferSP data_sp(crash_file->MemoryMapFileContents(0, header_size)); + if (!data_sp) + return nullptr; + + // first, only try to parse the header, beacuse we need to be fast + llvm::ArrayRef<uint8_t> header_data(data_sp->GetBytes(), header_size); + const MinidumpHeader *header = MinidumpHeader::Parse(header_data); + + if (data_sp->GetByteSize() != header_size || header == nullptr) + return nullptr; + + lldb::DataBufferSP all_data_sp(crash_file->MemoryMapFileContents()); + auto minidump_parser = MinidumpParser::Create(all_data_sp); + // check if the parser object is valid + if (!minidump_parser) + return nullptr; + + return std::make_shared<ProcessMinidump>(target_sp, listener_sp, *crash_file, + minidump_parser.getValue()); +} + +bool ProcessMinidump::CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) { + return true; +} + +ProcessMinidump::ProcessMinidump(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec &core_file, + MinidumpParser minidump_parser) + : Process(target_sp, listener_sp), m_minidump_parser(minidump_parser), + m_core_file(core_file), m_is_wow64(false) {} + +ProcessMinidump::~ProcessMinidump() { + Clear(); + // We need to call finalize on the process before destroying ourselves + // to make sure all of the broadcaster cleanup goes as planned. If we + // destruct this class, then Process::~Process() might have problems + // trying to fully destroy the broadcaster. + Finalize(); +} + +void ProcessMinidump::Initialize() { + static std::once_flag g_once_flag; + + std::call_once(g_once_flag, []() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), + ProcessMinidump::CreateInstance); + }); +} + +void ProcessMinidump::Terminate() { + PluginManager::UnregisterPlugin(ProcessMinidump::CreateInstance); +} + +Error ProcessMinidump::DoLoadCore() { + Error error; + + m_thread_list = m_minidump_parser.GetThreads(); + m_active_exception = m_minidump_parser.GetExceptionStream(); + ReadModuleList(); + GetTarget().SetArchitecture(GetArchitecture()); + + llvm::Optional<lldb::pid_t> pid = m_minidump_parser.GetPid(); + if (!pid) { + error.SetErrorString("failed to parse PID"); + return error; + } + SetID(pid.getValue()); + + return error; +} + +DynamicLoader *ProcessMinidump::GetDynamicLoader() { + if (m_dyld_ap.get() == nullptr) + m_dyld_ap.reset(DynamicLoader::FindPlugin(this, nullptr)); + return m_dyld_ap.get(); +} + +ConstString ProcessMinidump::GetPluginName() { return GetPluginNameStatic(); } + +uint32_t ProcessMinidump::GetPluginVersion() { return 1; } + +Error ProcessMinidump::DoDestroy() { return Error(); } + +void ProcessMinidump::RefreshStateAfterStop() { + if (!m_active_exception) + return; + + if (m_active_exception->exception_record.exception_code == + MinidumpException::DumpRequested) { + return; + } + + lldb::StopInfoSP stop_info; + lldb::ThreadSP stop_thread; + + Process::m_thread_list.SetSelectedThreadByID(m_active_exception->thread_id); + stop_thread = Process::m_thread_list.GetSelectedThread(); + ArchSpec arch = GetArchitecture(); + + if (arch.GetTriple().getOS() == llvm::Triple::Linux) { + stop_info = StopInfo::CreateStopReasonWithSignal( + *stop_thread, m_active_exception->exception_record.exception_code); + } else { + std::string desc; + llvm::raw_string_ostream desc_stream(desc); + desc_stream << "Exception " + << llvm::format_hex( + m_active_exception->exception_record.exception_code, 8) + << " encountered at address " + << llvm::format_hex( + m_active_exception->exception_record.exception_address, + 8); + stop_info = StopInfo::CreateStopReasonWithException( + *stop_thread, desc_stream.str().c_str()); + } + + stop_thread->SetStopInfo(stop_info); +} + +bool ProcessMinidump::IsAlive() { return true; } + +bool ProcessMinidump::WarnBeforeDetach() const { return false; } + +size_t ProcessMinidump::ReadMemory(lldb::addr_t addr, void *buf, size_t size, + Error &error) { + // Don't allow the caching that lldb_private::Process::ReadMemory does + // since we have it all cached in our dump file anyway. + return DoReadMemory(addr, buf, size, error); +} + +size_t ProcessMinidump::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + Error &error) { + + llvm::ArrayRef<uint8_t> mem = m_minidump_parser.GetMemory(addr, size); + if (mem.empty()) { + error.SetErrorString("could not parse memory info"); + return 0; + } + + std::memcpy(buf, mem.data(), mem.size()); + return mem.size(); +} + +ArchSpec ProcessMinidump::GetArchitecture() { + if (!m_is_wow64) { + return m_minidump_parser.GetArchitecture(); + } + + llvm::Triple triple; + triple.setVendor(llvm::Triple::VendorType::UnknownVendor); + triple.setArch(llvm::Triple::ArchType::x86); + triple.setOS(llvm::Triple::OSType::Win32); + return ArchSpec(triple); +} + +Error ProcessMinidump::GetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info) { + Error error; + auto info = m_minidump_parser.GetMemoryRegionInfo(load_addr); + if (!info) { + error.SetErrorString("No valid MemoryRegionInfo found!"); + return error; + } + range_info = info.getValue(); + return error; +} + +void ProcessMinidump::Clear() { Process::m_thread_list.Clear(); } + +bool ProcessMinidump::UpdateThreadList(ThreadList &old_thread_list, + ThreadList &new_thread_list) { + uint32_t num_threads = 0; + if (m_thread_list.size() > 0) + num_threads = m_thread_list.size(); + + for (lldb::tid_t tid = 0; tid < num_threads; ++tid) { + llvm::ArrayRef<uint8_t> context; + if (!m_is_wow64) + context = m_minidump_parser.GetThreadContext(m_thread_list[tid]); + else + context = m_minidump_parser.GetThreadContextWow64(m_thread_list[tid]); + + lldb::ThreadSP thread_sp( + new ThreadMinidump(*this, m_thread_list[tid], context)); + new_thread_list.AddThread(thread_sp); + } + return new_thread_list.GetSize(false) > 0; +} + +void ProcessMinidump::ReadModuleList() { + std::vector<const MinidumpModule *> filtered_modules = + m_minidump_parser.GetFilteredModuleList(); + + for (auto module : filtered_modules) { + llvm::Optional<std::string> name = + m_minidump_parser.GetMinidumpString(module->module_name_rva); + + if (!name) + continue; + + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES)); + if (log) { + log->Printf("ProcessMinidump::%s found module: name: %s %#010" PRIx64 + "-%#010" PRIx64 " size: %" PRIu32, + __FUNCTION__, name.getValue().c_str(), + uint64_t(module->base_of_image), + module->base_of_image + module->size_of_image, + uint32_t(module->size_of_image)); + } + + // check if the process is wow64 - a 32 bit windows process running on a + // 64 bit windows + if (llvm::StringRef(name.getValue()).endswith_lower("wow64.dll")) { + m_is_wow64 = true; + } + + const auto file_spec = FileSpec(name.getValue(), true); + ModuleSpec module_spec = file_spec; + Error error; + lldb::ModuleSP module_sp = GetTarget().GetSharedModule(module_spec, &error); + if (!module_sp || error.Fail()) { + continue; + } + + if (log) { + log->Printf("ProcessMinidump::%s load module: name: %s", __FUNCTION__, + name.getValue().c_str()); + } + + bool load_addr_changed = false; + module_sp->SetLoadAddress(GetTarget(), module->base_of_image, false, + load_addr_changed); + } +} + +bool ProcessMinidump::GetProcessInfo(ProcessInstanceInfo &info) { + info.Clear(); + info.SetProcessID(GetID()); + info.SetArchitecture(GetArchitecture()); + lldb::ModuleSP module_sp = GetTarget().GetExecutableModule(); + if (module_sp) { + const bool add_exe_file_as_first_arg = false; + info.SetExecutableFile(GetTarget().GetExecutableModule()->GetFileSpec(), + add_exe_file_as_first_arg); + } + return true; +} diff --git a/source/Plugins/Process/minidump/ProcessMinidump.h b/source/Plugins/Process/minidump/ProcessMinidump.h new file mode 100644 index 0000000000000..78eadc809a4de --- /dev/null +++ b/source/Plugins/Process/minidump/ProcessMinidump.h @@ -0,0 +1,105 @@ +//===-- ProcessMinidump.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_ProcessMinidump_h_ +#define liblldb_ProcessMinidump_h_ + +// Project includes +#include "MinidumpParser.h" +#include "MinidumpTypes.h" + +// Other libraries and framework includes +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" + +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +// C Includes +// C++ Includes + +namespace lldb_private { + +namespace minidump { + +class ProcessMinidump : public Process { +public: + static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec *crash_file_path); + + static void Initialize(); + + static void Terminate(); + + static ConstString GetPluginNameStatic(); + + static const char *GetPluginDescriptionStatic(); + + ProcessMinidump(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, + const FileSpec &core_file, MinidumpParser minidump_parser); + + ~ProcessMinidump() override; + + bool CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) override; + + Error DoLoadCore() override; + + DynamicLoader *GetDynamicLoader() override; + + ConstString GetPluginName() override; + + uint32_t GetPluginVersion() override; + + Error DoDestroy() override; + + void RefreshStateAfterStop() override; + + bool IsAlive() override; + + bool WarnBeforeDetach() const override; + + size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size, + Error &error) override; + + size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + Error &error) override; + + ArchSpec GetArchitecture(); + + Error GetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info) override; + + bool GetProcessInfo(ProcessInstanceInfo &info) override; + + MinidumpParser m_minidump_parser; + +protected: + void Clear(); + + bool UpdateThreadList(ThreadList &old_thread_list, + ThreadList &new_thread_list) override; + + void ReadModuleList(); + +private: + FileSpec m_core_file; + llvm::ArrayRef<MinidumpThread> m_thread_list; + const MinidumpExceptionStream *m_active_exception; + bool m_is_wow64; +}; + +} // namespace minidump +} // namespace lldb_private + +#endif // liblldb_ProcessMinidump_h_ diff --git a/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.cpp b/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.cpp new file mode 100644 index 0000000000000..7f3768216f3a9 --- /dev/null +++ b/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<uint8_t> reg_dest) { + memcpy(reg_dest.data(), reg_src, reg_dest.size()); +} + +lldb::DataBufferSP lldb_private::minidump::ConvertMinidumpContext_x86_32( + llvm::ArrayRef<uint8_t> 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<MinidumpContext_x86_32_Flags>( + static_cast<uint32_t>(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; +} diff --git a/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.h b/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.h new file mode 100644 index 0000000000000..e18bb3b4f5d98 --- /dev/null +++ b/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<uint8_t> 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_ diff --git a/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp b/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp new file mode 100644 index 0000000000000..881c26a5774a6 --- /dev/null +++ b/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp @@ -0,0 +1,113 @@ +//===-- 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; + +static llvm::MutableArrayRef<uint8_t> getDestRegister(uint8_t *context, + const RegisterInfo ®) { + auto bytes = reg.mutable_data(context); + + switch (reg.kinds[lldb::eRegisterKindLLDB]) { + 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; + } +} + +static void writeRegister(const void *reg_src, uint8_t *context, + const RegisterInfo ®) { + llvm::MutableArrayRef<uint8_t> reg_dest = getDestRegister(context, reg); + memcpy(reg_dest.data(), reg_src, reg_dest.size()); +} + +lldb::DataBufferSP lldb_private::minidump::ConvertMinidumpContext_x86_64( + llvm::ArrayRef<uint8_t> 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_64)) + return nullptr; + + const MinidumpContext_x86_64 *context; + consumeObject(source_data, context); + + const MinidumpContext_x86_64_Flags context_flags = + static_cast<MinidumpContext_x86_64_Flags>( + static_cast<uint32_t>(context->context_flags)); + auto x86_64_Flag = MinidumpContext_x86_64_Flags::x86_64_Flag; + auto ControlFlag = MinidumpContext_x86_64_Flags::Control; + auto IntegerFlag = MinidumpContext_x86_64_Flags::Integer; + auto SegmentsFlag = MinidumpContext_x86_64_Flags::Segments; + + if ((context_flags & x86_64_Flag) != x86_64_Flag) + return nullptr; + + if ((context_flags & ControlFlag) == ControlFlag) { + writeRegister(&context->cs, result_base, reg_info[lldb_cs_x86_64]); + writeRegister(&context->ss, result_base, reg_info[lldb_ss_x86_64]); + writeRegister(&context->eflags, result_base, reg_info[lldb_rflags_x86_64]); + writeRegister(&context->rsp, result_base, reg_info[lldb_rsp_x86_64]); + writeRegister(&context->rip, result_base, reg_info[lldb_rip_x86_64]); + } + + if ((context_flags & SegmentsFlag) == SegmentsFlag) { + writeRegister(&context->ds, result_base, reg_info[lldb_ds_x86_64]); + writeRegister(&context->es, result_base, reg_info[lldb_es_x86_64]); + writeRegister(&context->fs, result_base, reg_info[lldb_fs_x86_64]); + writeRegister(&context->gs, result_base, reg_info[lldb_gs_x86_64]); + } + + if ((context_flags & IntegerFlag) == IntegerFlag) { + writeRegister(&context->rax, result_base, reg_info[lldb_rax_x86_64]); + writeRegister(&context->rcx, result_base, reg_info[lldb_rcx_x86_64]); + writeRegister(&context->rdx, result_base, reg_info[lldb_rdx_x86_64]); + writeRegister(&context->rbx, result_base, reg_info[lldb_rbx_x86_64]); + writeRegister(&context->rbp, result_base, reg_info[lldb_rbp_x86_64]); + writeRegister(&context->rsi, result_base, reg_info[lldb_rsi_x86_64]); + writeRegister(&context->rdi, result_base, reg_info[lldb_rdi_x86_64]); + writeRegister(&context->r8, result_base, reg_info[lldb_r8_x86_64]); + writeRegister(&context->r9, result_base, reg_info[lldb_r9_x86_64]); + writeRegister(&context->r10, result_base, reg_info[lldb_r10_x86_64]); + writeRegister(&context->r11, result_base, reg_info[lldb_r11_x86_64]); + writeRegister(&context->r12, result_base, reg_info[lldb_r12_x86_64]); + writeRegister(&context->r13, result_base, reg_info[lldb_r13_x86_64]); + writeRegister(&context->r14, result_base, reg_info[lldb_r14_x86_64]); + writeRegister(&context->r15, result_base, reg_info[lldb_r15_x86_64]); + } + + // TODO parse the floating point registers + + return result_context_buf; +} diff --git a/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h b/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h new file mode 100644 index 0000000000000..9ba2ee9f29ad1 --- /dev/null +++ b/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h @@ -0,0 +1,183 @@ +//===-- 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" +#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_64(llvm::ArrayRef<uint8_t> source_data, + RegisterInfoInterface *target_reg_interface); + +struct Uint128 { + llvm::support::ulittle64_t high; + llvm::support::ulittle64_t low; +}; + +// Reference: see breakpad/crashpad source or WinNT.h +struct MinidumpXMMSaveArea32AMD64 { + llvm::support::ulittle16_t control_word; + llvm::support::ulittle16_t status_word; + uint8_t tag_word; + uint8_t reserved1; + llvm::support::ulittle16_t error_opcode; + llvm::support::ulittle32_t error_offset; + llvm::support::ulittle16_t error_selector; + llvm::support::ulittle16_t reserved2; + llvm::support::ulittle32_t data_offset; + llvm::support::ulittle16_t data_selector; + llvm::support::ulittle16_t reserved3; + llvm::support::ulittle32_t mx_csr; + llvm::support::ulittle32_t mx_csr_mask; + Uint128 float_registers[8]; + Uint128 xmm_registers[16]; + uint8_t reserved4[96]; +}; + +struct MinidumpContext_x86_64 { + // Register parameter home addresses. + llvm::support::ulittle64_t p1_home; + llvm::support::ulittle64_t p2_home; + llvm::support::ulittle64_t p3_home; + llvm::support::ulittle64_t p4_home; + llvm::support::ulittle64_t p5_home; + llvm::support::ulittle64_t p6_home; + + // 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; + + // The next register is included with + // MinidumpContext_x86_64_Flags::Control + llvm::support::ulittle16_t cs; + + // The next 4 registers are included with + // MinidumpContext_x86_64_Flags::Segments + llvm::support::ulittle16_t ds; + llvm::support::ulittle16_t es; + llvm::support::ulittle16_t fs; + llvm::support::ulittle16_t gs; + + // The next 2 registers are included with + // MinidumpContext_x86_64_Flags::Control + llvm::support::ulittle16_t ss; + llvm::support::ulittle32_t eflags; + + // The next 6 registers are included with + // MinidumpContext_x86_64_Flags::DebugRegisters + llvm::support::ulittle64_t dr0; + llvm::support::ulittle64_t dr1; + llvm::support::ulittle64_t dr2; + llvm::support::ulittle64_t dr3; + llvm::support::ulittle64_t dr6; + llvm::support::ulittle64_t dr7; + + // The next 4 registers are included with + // MinidumpContext_x86_64_Flags::Integer + llvm::support::ulittle64_t rax; + llvm::support::ulittle64_t rcx; + llvm::support::ulittle64_t rdx; + llvm::support::ulittle64_t rbx; + + // The next register is included with + // MinidumpContext_x86_64_Flags::Control + llvm::support::ulittle64_t rsp; + + // The next 11 registers are included with + // MinidumpContext_x86_64_Flags::Integer + llvm::support::ulittle64_t rbp; + llvm::support::ulittle64_t rsi; + llvm::support::ulittle64_t rdi; + llvm::support::ulittle64_t r8; + llvm::support::ulittle64_t r9; + llvm::support::ulittle64_t r10; + llvm::support::ulittle64_t r11; + llvm::support::ulittle64_t r12; + llvm::support::ulittle64_t r13; + llvm::support::ulittle64_t r14; + llvm::support::ulittle64_t r15; + + // The next register is included with + // MinidumpContext_x86_64_Flags::Control + llvm::support::ulittle64_t rip; + + // The next set of registers are included with + // MinidumpContext_x86_64_Flags:FloatingPoint + union FPR { + MinidumpXMMSaveArea32AMD64 flt_save; + struct { + Uint128 header[2]; + Uint128 legacy[8]; + Uint128 xmm[16]; + } sse_registers; + }; + + enum { + VRCount = 26, + }; + + Uint128 vector_register[VRCount]; + llvm::support::ulittle64_t vector_control; + + // The next 5 registers are included with + // MinidumpContext_x86_64_Flags::DebugRegisters + llvm::support::ulittle64_t debug_control; + llvm::support::ulittle64_t last_branch_to_rip; + llvm::support::ulittle64_t last_branch_from_rip; + llvm::support::ulittle64_t last_exception_to_rip; + llvm::support::ulittle64_t last_exception_from_rip; +}; + +// 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/source/Plugins/Process/minidump/ThreadMinidump.cpp b/source/Plugins/Process/minidump/ThreadMinidump.cpp new file mode 100644 index 0000000000000..e42108b9261a8 --- /dev/null +++ b/source/Plugins/Process/minidump/ThreadMinidump.cpp @@ -0,0 +1,114 @@ +//===-- ThreadMinidump.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 "ThreadMinidump.h" +#include "ProcessMinidump.h" + +#include "RegisterContextMinidump_x86_32.h" +#include "RegisterContextMinidump_x86_64.h" + +// Other libraries and framework includes +#include "Plugins/Process/Utility/RegisterContextLinux_i386.h" +#include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h" + +#include "Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.h" + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Unwind.h" + +// C Includes +// C++ Includes + +using namespace lldb; +using namespace lldb_private; +using namespace minidump; + +ThreadMinidump::ThreadMinidump(Process &process, const MinidumpThread &td, + llvm::ArrayRef<uint8_t> gpregset_data) + : Thread(process, td.thread_id), m_thread_reg_ctx_sp(), + m_gpregset_data(gpregset_data) {} + +ThreadMinidump::~ThreadMinidump() {} + +void ThreadMinidump::RefreshStateAfterStop() {} + +void ThreadMinidump::ClearStackFrames() {} + +RegisterContextSP ThreadMinidump::GetRegisterContext() { + if (!m_reg_context_sp) { + m_reg_context_sp = CreateRegisterContextForFrame(nullptr); + } + return m_reg_context_sp; +} + +RegisterContextSP +ThreadMinidump::CreateRegisterContextForFrame(StackFrame *frame) { + RegisterContextSP reg_ctx_sp; + uint32_t concrete_frame_idx = 0; + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + + if (frame) + concrete_frame_idx = frame->GetConcreteFrameIndex(); + + if (concrete_frame_idx == 0) { + if (m_thread_reg_ctx_sp) + return m_thread_reg_ctx_sp; + + ProcessMinidump *process = + static_cast<ProcessMinidump *>(GetProcess().get()); + ArchSpec arch = process->GetArchitecture(); + RegisterInfoInterface *reg_interface = nullptr; + + // TODO write other register contexts and add them here + switch (arch.GetMachine()) { + case llvm::Triple::x86: { + reg_interface = new RegisterContextLinux_i386(arch); + lldb::DataBufferSP buf = + ConvertMinidumpContext_x86_32(m_gpregset_data, reg_interface); + DataExtractor gpregs(buf, lldb::eByteOrderLittle, 4); + DataExtractor fpregs; + m_thread_reg_ctx_sp.reset(new RegisterContextCorePOSIX_x86_64( + *this, reg_interface, gpregs, fpregs)); + break; + } + case llvm::Triple::x86_64: { + reg_interface = new RegisterContextLinux_x86_64(arch); + lldb::DataBufferSP buf = + ConvertMinidumpContext_x86_64(m_gpregset_data, reg_interface); + DataExtractor gpregs(buf, lldb::eByteOrderLittle, 8); + DataExtractor fpregs; + m_thread_reg_ctx_sp.reset(new RegisterContextCorePOSIX_x86_64( + *this, reg_interface, gpregs, fpregs)); + break; + } + default: + break; + } + + if (!reg_interface) { + if (log) + log->Printf("elf-core::%s:: Architecture(%d) not supported", + __FUNCTION__, arch.GetMachine()); + assert(false && "Architecture not supported"); + } + + reg_ctx_sp = m_thread_reg_ctx_sp; + } else if (m_unwinder_ap) { + reg_ctx_sp = m_unwinder_ap->CreateRegisterContextForFrame(frame); + } + + return reg_ctx_sp; +} + +bool ThreadMinidump::CalculateStopInfo() { return false; } diff --git a/source/Plugins/Process/minidump/ThreadMinidump.h b/source/Plugins/Process/minidump/ThreadMinidump.h new file mode 100644 index 0000000000000..97db452edfffa --- /dev/null +++ b/source/Plugins/Process/minidump/ThreadMinidump.h @@ -0,0 +1,52 @@ +//===-- ThreadMinidump.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_ThreadMinidump_h_ +#define liblldb_ThreadMinidump_h_ + +// Project includes +#include "MinidumpTypes.h" + +// Other libraries and framework includes +#include "lldb/Target/Thread.h" + +// C Includes +// C++ Includes + +namespace lldb_private { + +namespace minidump { + +class ThreadMinidump : public Thread { +public: + ThreadMinidump(Process &process, const MinidumpThread &td, + llvm::ArrayRef<uint8_t> gpregset_data); + + ~ThreadMinidump() override; + + void RefreshStateAfterStop() override; + + lldb::RegisterContextSP GetRegisterContext() override; + + lldb::RegisterContextSP + CreateRegisterContextForFrame(StackFrame *frame) override; + + void ClearStackFrames() override; + +protected: + lldb::RegisterContextSP m_thread_reg_ctx_sp; + llvm::ArrayRef<uint8_t> m_gpregset_data; + + bool CalculateStopInfo() override; +}; + +} // namespace minidump +} // namespace lldb_private + +#endif // liblldb_ThreadMinidump_h_ |