diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/Process/minidump')
17 files changed, 4352 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp new file mode 100644 index 000000000000..be9fae938e22 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp @@ -0,0 +1,711 @@ +//===-- MinidumpParser.cpp ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "MinidumpParser.h" +#include "NtStructures.h" +#include "RegisterContextMinidump_x86_32.h" + +#include "Plugins/Process/Utility/LinuxProcMaps.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" + +// C includes +// C++ includes +#include <algorithm> +#include <map> +#include <optional> +#include <vector> +#include <utility> + +using namespace lldb_private; +using namespace minidump; + +llvm::Expected<MinidumpParser> +MinidumpParser::Create(const lldb::DataBufferSP &data_sp) { + auto ExpectedFile = llvm::object::MinidumpFile::create( + llvm::MemoryBufferRef(toStringRef(data_sp->GetData()), "minidump")); + if (!ExpectedFile) + return ExpectedFile.takeError(); + + return MinidumpParser(data_sp, std::move(*ExpectedFile)); +} + +MinidumpParser::MinidumpParser(lldb::DataBufferSP data_sp, + std::unique_ptr<llvm::object::MinidumpFile> file) + : m_data_sp(std::move(data_sp)), m_file(std::move(file)) {} + +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(StreamType stream_type) { + return m_file->getRawStream(stream_type).value_or(llvm::ArrayRef<uint8_t>()); +} + +UUID MinidumpParser::GetModuleUUID(const minidump::Module *module) { + auto cv_record = + GetData().slice(module->CvRecord.RVA, module->CvRecord.DataSize); + + // Read the CV record signature + const llvm::support::ulittle32_t *signature = nullptr; + Status error = consumeObject(cv_record, signature); + if (error.Fail()) + return UUID(); + + const CvSignature cv_signature = + static_cast<CvSignature>(static_cast<uint32_t>(*signature)); + + if (cv_signature == CvSignature::Pdb70) { + const UUID::CvRecordPdb70 *pdb70_uuid = nullptr; + Status error = consumeObject(cv_record, pdb70_uuid); + if (error.Fail()) + return UUID(); + if (GetArchitecture().GetTriple().isOSBinFormatELF()) { + if (pdb70_uuid->Age != 0) + return UUID(pdb70_uuid, sizeof(*pdb70_uuid)); + return UUID(&pdb70_uuid->Uuid, + sizeof(pdb70_uuid->Uuid)); + } + return UUID(*pdb70_uuid); + } else if (cv_signature == CvSignature::ElfBuildId) + return UUID(cv_record); + + return UUID(); +} + +llvm::ArrayRef<minidump::Thread> MinidumpParser::GetThreads() { + auto ExpectedThreads = GetMinidumpFile().getThreadList(); + if (ExpectedThreads) + return *ExpectedThreads; + + LLDB_LOG_ERROR(GetLog(LLDBLog::Thread), ExpectedThreads.takeError(), + "Failed to read thread list: {0}"); + return {}; +} + +llvm::ArrayRef<uint8_t> +MinidumpParser::GetThreadContext(const LocationDescriptor &location) { + if (location.RVA + location.DataSize > GetData().size()) + return {}; + return GetData().slice(location.RVA, location.DataSize); +} + +llvm::ArrayRef<uint8_t> +MinidumpParser::GetThreadContext(const minidump::Thread &td) { + return GetThreadContext(td.Context); +} + +llvm::ArrayRef<uint8_t> +MinidumpParser::GetThreadContextWow64(const minidump::Thread &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.EnvironmentBlock, sizeof(TEB64)); + if (teb_mem.empty()) + return {}; + + const TEB64 *wow64teb; + Status 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]). +} + +ArchSpec MinidumpParser::GetArchitecture() { + if (m_arch.IsValid()) + return m_arch; + + // Set the architecture in m_arch + llvm::Expected<const SystemInfo &> system_info = m_file->getSystemInfo(); + + if (!system_info) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Process), system_info.takeError(), + "Failed to read SystemInfo stream: {0}"); + return m_arch; + } + + // 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); + + switch (system_info->ProcessorArch) { + case ProcessorArchitecture::X86: + triple.setArch(llvm::Triple::ArchType::x86); + break; + case ProcessorArchitecture::AMD64: + triple.setArch(llvm::Triple::ArchType::x86_64); + break; + case ProcessorArchitecture::ARM: + triple.setArch(llvm::Triple::ArchType::arm); + break; + case ProcessorArchitecture::ARM64: + case ProcessorArchitecture::BP_ARM64: + triple.setArch(llvm::Triple::ArchType::aarch64); + break; + default: + triple.setArch(llvm::Triple::ArchType::UnknownArch); + break; + } + + // TODO add all of the OSes that Minidump/breakpad distinguishes? + switch (system_info->PlatformId) { + case OSPlatform::Win32S: + case OSPlatform::Win32Windows: + case OSPlatform::Win32NT: + case OSPlatform::Win32CE: + triple.setOS(llvm::Triple::OSType::Win32); + triple.setVendor(llvm::Triple::VendorType::PC); + break; + case OSPlatform::Linux: + triple.setOS(llvm::Triple::OSType::Linux); + break; + case OSPlatform::MacOSX: + triple.setOS(llvm::Triple::OSType::MacOSX); + triple.setVendor(llvm::Triple::Apple); + break; + case OSPlatform::IOS: + triple.setOS(llvm::Triple::OSType::IOS); + triple.setVendor(llvm::Triple::Apple); + break; + case OSPlatform::Android: + triple.setOS(llvm::Triple::OSType::Linux); + triple.setEnvironment(llvm::Triple::EnvironmentType::Android); + break; + default: { + triple.setOS(llvm::Triple::OSType::UnknownOS); + auto ExpectedCSD = m_file->getString(system_info->CSDVersionRVA); + if (!ExpectedCSD) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Process), ExpectedCSD.takeError(), + "Failed to CSD Version string: {0}"); + } else { + if (ExpectedCSD->find("Linux") != std::string::npos) + triple.setOS(llvm::Triple::OSType::Linux); + } + break; + } + } + m_arch.SetTriple(triple); + return m_arch; +} + +const MinidumpMiscInfo *MinidumpParser::GetMiscInfo() { + llvm::ArrayRef<uint8_t> data = GetStream(StreamType::MiscInfo); + + if (data.size() == 0) + return nullptr; + + return MinidumpMiscInfo::Parse(data); +} + +std::optional<LinuxProcStatus> MinidumpParser::GetLinuxProcStatus() { + llvm::ArrayRef<uint8_t> data = GetStream(StreamType::LinuxProcStatus); + + if (data.size() == 0) + return std::nullopt; + + return LinuxProcStatus::Parse(data); +} + +std::optional<lldb::pid_t> MinidumpParser::GetPid() { + const MinidumpMiscInfo *misc_info = GetMiscInfo(); + if (misc_info != nullptr) { + return misc_info->GetPid(); + } + + std::optional<LinuxProcStatus> proc_status = GetLinuxProcStatus(); + if (proc_status) { + return proc_status->GetPid(); + } + + return std::nullopt; +} + +llvm::ArrayRef<minidump::Module> MinidumpParser::GetModuleList() { + auto ExpectedModules = GetMinidumpFile().getModuleList(); + if (ExpectedModules) + return *ExpectedModules; + + LLDB_LOG_ERROR(GetLog(LLDBLog::Modules), ExpectedModules.takeError(), + "Failed to read module list: {0}"); + return {}; +} + +static bool +CreateRegionsCacheFromLinuxMaps(MinidumpParser &parser, + std::vector<MemoryRegionInfo> ®ions) { + auto data = parser.GetStream(StreamType::LinuxMaps); + if (data.empty()) + return false; + + Log *log = GetLog(LLDBLog::Expressions); + ParseLinuxMapRegions( + llvm::toStringRef(data), + [®ions, &log](llvm::Expected<MemoryRegionInfo> region) -> bool { + if (region) + regions.push_back(*region); + else + LLDB_LOG_ERROR(log, region.takeError(), + "Reading memory region from minidump failed: {0}"); + return true; + }); + return !regions.empty(); +} + +/// Check for the memory regions starting at \a load_addr for a contiguous +/// section that has execute permissions that matches the module path. +/// +/// When we load a breakpad generated minidump file, we might have the +/// /proc/<pid>/maps text for a process that details the memory map of the +/// process that the minidump is describing. This checks the sorted memory +/// regions for a section that has execute permissions. A sample maps files +/// might look like: +/// +/// 00400000-00401000 r--p 00000000 fd:01 2838574 /tmp/a.out +/// 00401000-00402000 r-xp 00001000 fd:01 2838574 /tmp/a.out +/// 00402000-00403000 r--p 00002000 fd:01 2838574 /tmp/a.out +/// 00403000-00404000 r--p 00002000 fd:01 2838574 /tmp/a.out +/// 00404000-00405000 rw-p 00003000 fd:01 2838574 /tmp/a.out +/// ... +/// +/// This function should return true when given 0x00400000 and "/tmp/a.out" +/// is passed in as the path since it has a consecutive memory region for +/// "/tmp/a.out" that has execute permissions at 0x00401000. This will help us +/// differentiate if a file has been memory mapped into a process for reading +/// and breakpad ends up saving a minidump file that has two module entries for +/// a given file: one that is read only for the entire file, and then one that +/// is the real executable that is loaded into memory for execution. For memory +/// mapped files they will typically show up and r--p permissions and a range +/// matcning the entire range of the file on disk: +/// +/// 00800000-00805000 r--p 00000000 fd:01 2838574 /tmp/a.out +/// 00805000-00806000 r-xp 00001000 fd:01 1234567 /usr/lib/libc.so +/// +/// This function should return false when asked about 0x00800000 with +/// "/tmp/a.out" as the path. +/// +/// \param[in] path +/// The path to the module to check for in the memory regions. Only sequential +/// memory regions whose paths match this path will be considered when looking +/// for execute permissions. +/// +/// \param[in] regions +/// A sorted list of memory regions obtained from a call to +/// CreateRegionsCacheFromLinuxMaps. +/// +/// \param[in] base_of_image +/// The load address of this module from BaseOfImage in the modules list. +/// +/// \return +/// True if a contiguous region of memory belonging to the module with a +/// matching path exists that has executable permissions. Returns false if +/// \a regions is empty or if there are no regions with execute permissions +/// that match \a path. + +static bool CheckForLinuxExecutable(ConstString path, + const MemoryRegionInfos ®ions, + lldb::addr_t base_of_image) { + if (regions.empty()) + return false; + lldb::addr_t addr = base_of_image; + MemoryRegionInfo region = MinidumpParser::GetMemoryRegionInfo(regions, addr); + while (region.GetName() == path) { + if (region.GetExecutable() == MemoryRegionInfo::eYes) + return true; + addr += region.GetRange().GetByteSize(); + region = MinidumpParser::GetMemoryRegionInfo(regions, addr); + } + return false; +} + +std::vector<const minidump::Module *> MinidumpParser::GetFilteredModuleList() { + Log *log = GetLog(LLDBLog::Modules); + auto ExpectedModules = GetMinidumpFile().getModuleList(); + if (!ExpectedModules) { + LLDB_LOG_ERROR(log, ExpectedModules.takeError(), + "Failed to read module list: {0}"); + return {}; + } + + // Create memory regions from the linux maps only. We do this to avoid issues + // with breakpad generated minidumps where if someone has mmap'ed a shared + // library into memory to access its data in the object file, we can get a + // minidump with two mappings for a binary: one whose base image points to a + // memory region that is read + execute and one that is read only. + MemoryRegionInfos linux_regions; + if (CreateRegionsCacheFromLinuxMaps(*this, linux_regions)) + llvm::sort(linux_regions); + + // map module_name -> filtered_modules index + typedef llvm::StringMap<size_t> MapType; + MapType module_name_to_filtered_index; + + std::vector<const minidump::Module *> filtered_modules; + + for (const auto &module : *ExpectedModules) { + auto ExpectedName = m_file->getString(module.ModuleNameRVA); + if (!ExpectedName) { + LLDB_LOG_ERROR(log, ExpectedName.takeError(), + "Failed to get module name: {0}"); + continue; + } + + MapType::iterator iter; + bool inserted; + // See if we have inserted this module aready into filtered_modules. If we + // haven't insert an entry into module_name_to_filtered_index with the + // index where we will insert it if it isn't in the vector already. + std::tie(iter, inserted) = module_name_to_filtered_index.try_emplace( + *ExpectedName, filtered_modules.size()); + + if (inserted) { + // This module has not been seen yet, insert it into filtered_modules at + // the index that was inserted into module_name_to_filtered_index using + // "filtered_modules.size()" above. + filtered_modules.push_back(&module); + } else { + // We have a duplicate module entry. Check the linux regions to see if + // either module is not really a mapped executable. If one but not the + // other is a real mapped executable, prefer the executable one. This + // can happen when a process mmap's in the file for an executable in + // order to read bytes from the executable file. A memory region mapping + // will exist for the mmap'ed version and for the loaded executable, but + // only one will have a consecutive region that is executable in the + // memory regions. + auto dup_module = filtered_modules[iter->second]; + ConstString name(*ExpectedName); + bool is_executable = + CheckForLinuxExecutable(name, linux_regions, module.BaseOfImage); + bool dup_is_executable = + CheckForLinuxExecutable(name, linux_regions, dup_module->BaseOfImage); + + if (is_executable != dup_is_executable) { + if (is_executable) + filtered_modules[iter->second] = &module; + continue; + } + // This module has been seen. Modules are sometimes mentioned multiple + // times when they are mapped discontiguously, so find the module with + // the lowest "base_of_image" and use that as the filtered module. + if (module.BaseOfImage < dup_module->BaseOfImage) + filtered_modules[iter->second] = &module; + } + } + return filtered_modules; +} + +const minidump::ExceptionStream *MinidumpParser::GetExceptionStream() { + auto ExpectedStream = GetMinidumpFile().getExceptionStream(); + if (ExpectedStream) + return &*ExpectedStream; + + LLDB_LOG_ERROR(GetLog(LLDBLog::Process), ExpectedStream.takeError(), + "Failed to read minidump exception stream: {0}"); + return nullptr; +} + +std::optional<minidump::Range> +MinidumpParser::FindMemoryRange(lldb::addr_t addr) { + llvm::ArrayRef<uint8_t> data64 = GetStream(StreamType::Memory64List); + Log *log = GetLog(LLDBLog::Modules); + + auto ExpectedMemory = GetMinidumpFile().getMemoryList(); + if (!ExpectedMemory) { + LLDB_LOG_ERROR(log, ExpectedMemory.takeError(), + "Failed to read memory list: {0}"); + } else { + for (const auto &memory_desc : *ExpectedMemory) { + const LocationDescriptor &loc_desc = memory_desc.Memory; + const lldb::addr_t range_start = memory_desc.StartOfMemoryRange; + const size_t range_size = loc_desc.DataSize; + + if (loc_desc.RVA + loc_desc.DataSize > GetData().size()) + return std::nullopt; + + if (range_start <= addr && addr < range_start + range_size) { + auto ExpectedSlice = GetMinidumpFile().getRawData(loc_desc); + if (!ExpectedSlice) { + LLDB_LOG_ERROR(log, ExpectedSlice.takeError(), + "Failed to get memory slice: {0}"); + return std::nullopt; + } + return minidump::Range(range_start, *ExpectedSlice); + } + } + } + + // 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 std::nullopt; + + 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 std::nullopt; + + 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 std::nullopt; +} + +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. + std::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); +} + +static bool +CreateRegionsCacheFromMemoryInfoList(MinidumpParser &parser, + std::vector<MemoryRegionInfo> ®ions) { + Log *log = GetLog(LLDBLog::Modules); + auto ExpectedInfo = parser.GetMinidumpFile().getMemoryInfoList(); + if (!ExpectedInfo) { + LLDB_LOG_ERROR(log, ExpectedInfo.takeError(), + "Failed to read memory info list: {0}"); + return false; + } + constexpr auto yes = MemoryRegionInfo::eYes; + constexpr auto no = MemoryRegionInfo::eNo; + for (const MemoryInfo &entry : *ExpectedInfo) { + MemoryRegionInfo region; + region.GetRange().SetRangeBase(entry.BaseAddress); + region.GetRange().SetByteSize(entry.RegionSize); + + MemoryProtection prot = entry.Protect; + region.SetReadable(bool(prot & MemoryProtection::NoAccess) ? no : yes); + region.SetWritable( + bool(prot & (MemoryProtection::ReadWrite | MemoryProtection::WriteCopy | + MemoryProtection::ExecuteReadWrite | + MemoryProtection::ExeciteWriteCopy)) + ? yes + : no); + region.SetExecutable( + bool(prot & (MemoryProtection::Execute | MemoryProtection::ExecuteRead | + MemoryProtection::ExecuteReadWrite | + MemoryProtection::ExeciteWriteCopy)) + ? yes + : no); + region.SetMapped(entry.State != MemoryState::Free ? yes : no); + regions.push_back(region); + } + return !regions.empty(); +} + +static bool +CreateRegionsCacheFromMemoryList(MinidumpParser &parser, + std::vector<MemoryRegionInfo> ®ions) { + Log *log = GetLog(LLDBLog::Modules); + auto ExpectedMemory = parser.GetMinidumpFile().getMemoryList(); + if (!ExpectedMemory) { + LLDB_LOG_ERROR(log, ExpectedMemory.takeError(), + "Failed to read memory list: {0}"); + return false; + } + regions.reserve(ExpectedMemory->size()); + for (const MemoryDescriptor &memory_desc : *ExpectedMemory) { + if (memory_desc.Memory.DataSize == 0) + continue; + MemoryRegionInfo region; + region.GetRange().SetRangeBase(memory_desc.StartOfMemoryRange); + region.GetRange().SetByteSize(memory_desc.Memory.DataSize); + region.SetReadable(MemoryRegionInfo::eYes); + region.SetMapped(MemoryRegionInfo::eYes); + regions.push_back(region); + } + regions.shrink_to_fit(); + return !regions.empty(); +} + +static bool +CreateRegionsCacheFromMemory64List(MinidumpParser &parser, + std::vector<MemoryRegionInfo> ®ions) { + llvm::ArrayRef<uint8_t> data = + parser.GetStream(StreamType::Memory64List); + if (data.empty()) + return false; + llvm::ArrayRef<MinidumpMemoryDescriptor64> memory64_list; + uint64_t base_rva; + std::tie(memory64_list, base_rva) = + MinidumpMemoryDescriptor64::ParseMemory64List(data); + + if (memory64_list.empty()) + return false; + + regions.reserve(memory64_list.size()); + for (const auto &memory_desc : memory64_list) { + if (memory_desc.data_size == 0) + continue; + MemoryRegionInfo region; + region.GetRange().SetRangeBase(memory_desc.start_of_memory_range); + region.GetRange().SetByteSize(memory_desc.data_size); + region.SetReadable(MemoryRegionInfo::eYes); + region.SetMapped(MemoryRegionInfo::eYes); + regions.push_back(region); + } + regions.shrink_to_fit(); + return !regions.empty(); +} + +std::pair<MemoryRegionInfos, bool> MinidumpParser::BuildMemoryRegions() { + // We create the region cache using the best source. We start with + // the linux maps since they are the most complete and have names for the + // regions. Next we try the MemoryInfoList since it has + // read/write/execute/map data, and then fall back to the MemoryList and + // Memory64List to just get a list of the memory that is mapped in this + // core file + MemoryRegionInfos result; + const auto &return_sorted = [&](bool is_complete) { + llvm::sort(result); + return std::make_pair(std::move(result), is_complete); + }; + if (CreateRegionsCacheFromLinuxMaps(*this, result)) + return return_sorted(true); + if (CreateRegionsCacheFromMemoryInfoList(*this, result)) + return return_sorted(true); + if (CreateRegionsCacheFromMemoryList(*this, result)) + return return_sorted(false); + CreateRegionsCacheFromMemory64List(*this, result); + return return_sorted(false); +} + +#define ENUM_TO_CSTR(ST) \ + case StreamType::ST: \ + return #ST + +llvm::StringRef +MinidumpParser::GetStreamTypeAsString(StreamType stream_type) { + switch (stream_type) { + ENUM_TO_CSTR(Unused); + ENUM_TO_CSTR(ThreadList); + ENUM_TO_CSTR(ModuleList); + ENUM_TO_CSTR(MemoryList); + ENUM_TO_CSTR(Exception); + ENUM_TO_CSTR(SystemInfo); + ENUM_TO_CSTR(ThreadExList); + ENUM_TO_CSTR(Memory64List); + ENUM_TO_CSTR(CommentA); + ENUM_TO_CSTR(CommentW); + ENUM_TO_CSTR(HandleData); + ENUM_TO_CSTR(FunctionTable); + ENUM_TO_CSTR(UnloadedModuleList); + ENUM_TO_CSTR(MiscInfo); + ENUM_TO_CSTR(MemoryInfoList); + ENUM_TO_CSTR(ThreadInfoList); + ENUM_TO_CSTR(HandleOperationList); + ENUM_TO_CSTR(Token); + ENUM_TO_CSTR(JavascriptData); + ENUM_TO_CSTR(SystemMemoryInfo); + ENUM_TO_CSTR(ProcessVMCounters); + ENUM_TO_CSTR(LastReserved); + ENUM_TO_CSTR(BreakpadInfo); + ENUM_TO_CSTR(AssertionInfo); + ENUM_TO_CSTR(LinuxCPUInfo); + ENUM_TO_CSTR(LinuxProcStatus); + ENUM_TO_CSTR(LinuxLSBRelease); + ENUM_TO_CSTR(LinuxCMDLine); + ENUM_TO_CSTR(LinuxEnviron); + ENUM_TO_CSTR(LinuxAuxv); + ENUM_TO_CSTR(LinuxMaps); + ENUM_TO_CSTR(LinuxDSODebug); + ENUM_TO_CSTR(LinuxProcStat); + ENUM_TO_CSTR(LinuxProcUptime); + ENUM_TO_CSTR(LinuxProcFD); + ENUM_TO_CSTR(FacebookAppCustomData); + ENUM_TO_CSTR(FacebookBuildID); + ENUM_TO_CSTR(FacebookAppVersionName); + ENUM_TO_CSTR(FacebookJavaStack); + ENUM_TO_CSTR(FacebookDalvikInfo); + ENUM_TO_CSTR(FacebookUnwindSymbols); + ENUM_TO_CSTR(FacebookDumpErrorLog); + ENUM_TO_CSTR(FacebookAppStateLog); + ENUM_TO_CSTR(FacebookAbortReason); + ENUM_TO_CSTR(FacebookThreadName); + ENUM_TO_CSTR(FacebookLogcat); + } + return "unknown stream type"; +} + +MemoryRegionInfo +MinidumpParser::GetMemoryRegionInfo(const MemoryRegionInfos ®ions, + lldb::addr_t load_addr) { + MemoryRegionInfo region; + auto pos = llvm::upper_bound(regions, load_addr); + if (pos != regions.begin() && + std::prev(pos)->GetRange().Contains(load_addr)) { + return *std::prev(pos); + } + + if (pos == regions.begin()) + region.GetRange().SetRangeBase(0); + else + region.GetRange().SetRangeBase(std::prev(pos)->GetRange().GetRangeEnd()); + + if (pos == regions.end()) + region.GetRange().SetRangeEnd(UINT64_MAX); + else + region.GetRange().SetRangeEnd(pos->GetRange().GetRangeBase()); + + region.SetReadable(MemoryRegionInfo::eNo); + region.SetWritable(MemoryRegionInfo::eNo); + region.SetExecutable(MemoryRegionInfo::eNo); + region.SetMapped(MemoryRegionInfo::eNo); + return region; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.h new file mode 100644 index 000000000000..050ba086f46f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.h @@ -0,0 +1,113 @@ +//===-- MinidumpParser.h -----------------------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_MINIDUMPPARSER_H +#define LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_MINIDUMPPARSER_H + +#include "MinidumpTypes.h" + +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/DataBuffer.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/UUID.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/Minidump.h" + +// C includes + +// C++ includes +#include <cstring> +#include <optional> +#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) {} + + friend bool operator==(const Range &lhs, const Range &rhs) { + return lhs.start == rhs.start && lhs.range_ref == rhs.range_ref; + } +}; + +class MinidumpParser { +public: + static llvm::Expected<MinidumpParser> + Create(const lldb::DataBufferSP &data_buf_sp); + + llvm::ArrayRef<uint8_t> GetData(); + + llvm::ArrayRef<uint8_t> GetStream(StreamType stream_type); + + UUID GetModuleUUID(const minidump::Module *module); + + llvm::ArrayRef<minidump::Thread> GetThreads(); + + llvm::ArrayRef<uint8_t> GetThreadContext(const LocationDescriptor &location); + + llvm::ArrayRef<uint8_t> GetThreadContext(const minidump::Thread &td); + + llvm::ArrayRef<uint8_t> GetThreadContextWow64(const minidump::Thread &td); + + ArchSpec GetArchitecture(); + + const MinidumpMiscInfo *GetMiscInfo(); + + std::optional<LinuxProcStatus> GetLinuxProcStatus(); + + std::optional<lldb::pid_t> GetPid(); + + llvm::ArrayRef<minidump::Module> 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 minidump::Module *> GetFilteredModuleList(); + + const llvm::minidump::ExceptionStream *GetExceptionStream(); + + std::optional<Range> FindMemoryRange(lldb::addr_t addr); + + llvm::ArrayRef<uint8_t> GetMemory(lldb::addr_t addr, size_t size); + + /// Returns a list of memory regions and a flag indicating whether the list is + /// complete (includes all regions mapped into the process memory). + std::pair<MemoryRegionInfos, bool> BuildMemoryRegions(); + + static llvm::StringRef GetStreamTypeAsString(StreamType stream_type); + + llvm::object::MinidumpFile &GetMinidumpFile() { return *m_file; } + + static MemoryRegionInfo GetMemoryRegionInfo(const MemoryRegionInfos ®ions, + lldb::addr_t load_addr); + +private: + MinidumpParser(lldb::DataBufferSP data_sp, + std::unique_ptr<llvm::object::MinidumpFile> file); + + lldb::DataBufferSP m_data_sp; + std::unique_ptr<llvm::object::MinidumpFile> m_file; + ArchSpec m_arch; +}; + +} // end namespace minidump +} // end namespace lldb_private +#endif // LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_MINIDUMPPARSER_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp new file mode 100644 index 000000000000..5b919828428f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp @@ -0,0 +1,79 @@ +//===-- MinidumpTypes.cpp -------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "MinidumpTypes.h" +#include <optional> + +// C includes +// C++ includes + +using namespace lldb_private; +using namespace minidump; + +// MinidumpMiscInfo +const MinidumpMiscInfo *MinidumpMiscInfo::Parse(llvm::ArrayRef<uint8_t> &data) { + const MinidumpMiscInfo *misc_info; + Status error = consumeObject(data, misc_info); + if (error.Fail()) + return nullptr; + + return misc_info; +} + +std::optional<lldb::pid_t> MinidumpMiscInfo::GetPid() const { + uint32_t pid_flag = static_cast<uint32_t>(MinidumpMiscInfoFlags::ProcessID); + if (flags1 & pid_flag) + return std::optional<lldb::pid_t>(process_id); + + return std::nullopt; +} + +// Linux Proc Status +// it's stored as an ascii string in the file +std::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 std::nullopt; +} + +lldb::pid_t LinuxProcStatus::GetPid() const { return pid; } + +std::pair<llvm::ArrayRef<MinidumpMemoryDescriptor64>, uint64_t> +MinidumpMemoryDescriptor64::ParseMemory64List(llvm::ArrayRef<uint8_t> &data) { + const llvm::support::ulittle64_t *mem_ranges_count; + Status 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::ArrayRef( + reinterpret_cast<const MinidumpMemoryDescriptor64 *>(data.data()), + *mem_ranges_count), + *base_rva); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.h new file mode 100644 index 000000000000..fe99abf9e24e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.h @@ -0,0 +1,107 @@ +//===-- MinidumpTypes.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_MINIDUMPTYPES_H +#define LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_MINIDUMPTYPES_H + +#include "lldb/Utility/Status.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitmaskEnum.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/Minidump.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/Endian.h" +#include <optional> + +// 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 { + +using namespace llvm::minidump; + +LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); + +enum class CvSignature : uint32_t { + Pdb70 = 0x53445352, // RSDS + ElfBuildId = 0x4270454c, // BpEL (Breakpad/Crashpad minidumps) +}; + +enum class MinidumpMiscInfoFlags : uint32_t { + ProcessID = (1 << 0), + ProcessTimes = (1 << 1), + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ ProcessTimes) +}; + +template <typename T> +Status consumeObject(llvm::ArrayRef<uint8_t> &Buffer, const T *&Object) { + Status 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; +} + +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!"); + +// 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); + + std::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 std::optional<LinuxProcStatus> Parse(llvm::ArrayRef<uint8_t> &data); + + lldb::pid_t GetPid() const; + +private: + LinuxProcStatus() = default; +}; + +} // namespace minidump +} // namespace lldb_private +#endif // LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_MINIDUMPTYPES_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/NtStructures.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/NtStructures.h new file mode 100644 index 000000000000..1dafbe4a4f50 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/NtStructures.h @@ -0,0 +1,42 @@ +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_NTSTRUCTURES_H + +#define LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_NTSTRUCTURES_H + +//===-- NtStructures.h ------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_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 + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp new file mode 100644 index 000000000000..13599f4a1553 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp @@ -0,0 +1,929 @@ +//===-- ProcessMinidump.cpp -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ProcessMinidump.h" + +#include "ThreadMinidump.h" + +#include "lldb/Core/DumpDataExtractor.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Interpreter/OptionGroupBoolean.h" +#include "lldb/Target/JITLoaderList.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" +#include "llvm/BinaryFormat/Magic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Threading.h" + +#include "Plugins/ObjectFile/Placeholder/ObjectFilePlaceholder.h" +#include "Plugins/Process/Utility/StopInfoMachException.h" + +#include <memory> +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace minidump; + +LLDB_PLUGIN_DEFINE(ProcessMinidump) + +namespace { + +/// Duplicate the HashElfTextSection() from the breakpad sources. +/// +/// Breakpad, a Google crash log reporting tool suite, creates minidump files +/// for many different architectures. When using Breakpad to create ELF +/// minidumps, it will check for a GNU build ID when creating a minidump file +/// and if one doesn't exist in the file, it will say the UUID of the file is a +/// checksum of up to the first 4096 bytes of the .text section. Facebook also +/// uses breakpad and modified this hash to avoid collisions so we can +/// calculate and check for this as well. +/// +/// The breakpad code might end up hashing up to 15 bytes that immediately +/// follow the .text section in the file, so this code must do exactly what it +/// does so we can get an exact match for the UUID. +/// +/// \param[in] module_sp The module to grab the .text section from. +/// +/// \param[in,out] breakpad_uuid A vector that will receive the calculated +/// breakpad .text hash. +/// +/// \param[in,out] facebook_uuid A vector that will receive the calculated +/// facebook .text hash. +/// +void HashElfTextSection(ModuleSP module_sp, std::vector<uint8_t> &breakpad_uuid, + std::vector<uint8_t> &facebook_uuid) { + SectionList *sect_list = module_sp->GetSectionList(); + if (sect_list == nullptr) + return; + SectionSP sect_sp = sect_list->FindSectionByName(ConstString(".text")); + if (!sect_sp) + return; + constexpr size_t kMDGUIDSize = 16; + constexpr size_t kBreakpadPageSize = 4096; + // The breakpad code has a bug where it might access beyond the end of a + // .text section by up to 15 bytes, so we must ensure we round up to the + // next kMDGUIDSize byte boundary. + DataExtractor data; + const size_t text_size = sect_sp->GetFileSize(); + const size_t read_size = std::min<size_t>( + llvm::alignTo(text_size, kMDGUIDSize), kBreakpadPageSize); + sect_sp->GetObjectFile()->GetData(sect_sp->GetFileOffset(), read_size, data); + + breakpad_uuid.assign(kMDGUIDSize, 0); + facebook_uuid.assign(kMDGUIDSize, 0); + + // The only difference between the breakpad hash and the facebook hash is the + // hashing of the text section size into the hash prior to hashing the .text + // contents. + for (size_t i = 0; i < kMDGUIDSize; i++) + facebook_uuid[i] ^= text_size % 255; + + // This code carefully duplicates how the hash was created in Breakpad + // sources, including the error where it might has an extra 15 bytes past the + // end of the .text section if the .text section is less than a page size in + // length. + const uint8_t *ptr = data.GetDataStart(); + const uint8_t *ptr_end = data.GetDataEnd(); + while (ptr < ptr_end) { + for (unsigned i = 0; i < kMDGUIDSize; i++) { + breakpad_uuid[i] ^= ptr[i]; + facebook_uuid[i] ^= ptr[i]; + } + ptr += kMDGUIDSize; + } +} + +} // namespace + +llvm::StringRef ProcessMinidump::GetPluginDescriptionStatic() { + return "Minidump plug-in."; +} + +lldb::ProcessSP ProcessMinidump::CreateInstance(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec *crash_file, + bool can_connect) { + if (!crash_file || can_connect) + return nullptr; + + lldb::ProcessSP process_sp; + // Read enough data for the Minidump header + constexpr size_t header_size = sizeof(Header); + auto DataPtr = FileSystem::Instance().CreateDataBuffer(crash_file->GetPath(), + header_size, 0); + if (!DataPtr) + return nullptr; + + lldbassert(DataPtr->GetByteSize() == header_size); + if (identify_magic(toStringRef(DataPtr->GetData())) != llvm::file_magic::minidump) + return nullptr; + + auto AllData = + FileSystem::Instance().CreateDataBuffer(crash_file->GetPath(), -1, 0); + if (!AllData) + return nullptr; + + return std::make_shared<ProcessMinidump>(target_sp, listener_sp, *crash_file, + std::move(AllData)); +} + +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, + DataBufferSP core_data) + : PostMortemProcess(target_sp, listener_sp, core_file), + m_core_data(std::move(core_data)), m_active_exception(nullptr), + 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(true /* destructing */); +} + +void ProcessMinidump::Initialize() { + static llvm::once_flag g_once_flag; + + llvm::call_once(g_once_flag, []() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), + ProcessMinidump::CreateInstance); + }); +} + +void ProcessMinidump::Terminate() { + PluginManager::UnregisterPlugin(ProcessMinidump::CreateInstance); +} + +Status ProcessMinidump::DoLoadCore() { + auto expected_parser = MinidumpParser::Create(m_core_data); + if (!expected_parser) + return Status(expected_parser.takeError()); + m_minidump_parser = std::move(*expected_parser); + + Status error; + + // Do we support the minidump's architecture? + ArchSpec arch = GetArchitecture(); + switch (arch.GetMachine()) { + case llvm::Triple::x86: + case llvm::Triple::x86_64: + case llvm::Triple::arm: + case llvm::Triple::aarch64: + // Any supported architectures must be listed here and also supported in + // ThreadMinidump::CreateRegisterContextForFrame(). + break; + default: + error.SetErrorStringWithFormat("unsupported minidump architecture: %s", + arch.GetArchitectureName()); + return error; + } + GetTarget().SetArchitecture(arch, true /*set_platform*/); + + m_thread_list = m_minidump_parser->GetThreads(); + m_active_exception = m_minidump_parser->GetExceptionStream(); + + SetUnixSignals(UnixSignals::Create(GetArchitecture())); + + ReadModuleList(); + if (ModuleSP module = GetTarget().GetExecutableModule()) + GetTarget().MergeArchitecture(module->GetArchitecture()); + std::optional<lldb::pid_t> pid = m_minidump_parser->GetPid(); + if (!pid) { + Debugger::ReportWarning("unable to retrieve process ID from minidump file, " + "setting process ID to 1", + GetTarget().GetDebugger().GetID()); + pid = 1; + } + SetID(*pid); + + return error; +} + +Status ProcessMinidump::DoDestroy() { return Status(); } + +void ProcessMinidump::RefreshStateAfterStop() { + + if (!m_active_exception) + return; + + constexpr uint32_t BreakpadDumpRequested = 0xFFFFFFFF; + if (m_active_exception->ExceptionRecord.ExceptionCode == + BreakpadDumpRequested) { + // This "ExceptionCode" value is a sentinel that is sometimes used + // when generating a dump for a process that hasn't crashed. + + // TODO: The definition and use of this "dump requested" constant + // in Breakpad are actually Linux-specific, and for similar use + // cases on Mac/Windows it defines different constants, referring + // to them as "simulated" exceptions; consider moving this check + // down to the OS-specific paths and checking each OS for its own + // constant. + return; + } + + lldb::StopInfoSP stop_info; + lldb::ThreadSP stop_thread; + + Process::m_thread_list.SetSelectedThreadByID(m_active_exception->ThreadId); + stop_thread = Process::m_thread_list.GetSelectedThread(); + ArchSpec arch = GetArchitecture(); + + if (arch.GetTriple().getOS() == llvm::Triple::Linux) { + uint32_t signo = m_active_exception->ExceptionRecord.ExceptionCode; + + if (signo == 0) { + // No stop. + return; + } + + stop_info = StopInfo::CreateStopReasonWithSignal( + *stop_thread, signo); + } else if (arch.GetTriple().getVendor() == llvm::Triple::Apple) { + stop_info = StopInfoMachException::CreateStopReasonWithMachException( + *stop_thread, m_active_exception->ExceptionRecord.ExceptionCode, 2, + m_active_exception->ExceptionRecord.ExceptionFlags, + m_active_exception->ExceptionRecord.ExceptionAddress, 0); + } else { + std::string desc; + llvm::raw_string_ostream desc_stream(desc); + desc_stream << "Exception " + << llvm::format_hex( + m_active_exception->ExceptionRecord.ExceptionCode, 8) + << " encountered at address " + << llvm::format_hex( + m_active_exception->ExceptionRecord.ExceptionAddress, 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, + Status &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, + Status &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); +} + +void ProcessMinidump::BuildMemoryRegions() { + if (m_memory_regions) + return; + m_memory_regions.emplace(); + bool is_complete; + std::tie(*m_memory_regions, is_complete) = + m_minidump_parser->BuildMemoryRegions(); + + if (is_complete) + return; + + MemoryRegionInfos to_add; + ModuleList &modules = GetTarget().GetImages(); + SectionLoadList &load_list = GetTarget().GetSectionLoadList(); + modules.ForEach([&](const ModuleSP &module_sp) { + SectionList *sections = module_sp->GetSectionList(); + for (size_t i = 0; i < sections->GetSize(); ++i) { + SectionSP section_sp = sections->GetSectionAtIndex(i); + addr_t load_addr = load_list.GetSectionLoadAddress(section_sp); + if (load_addr == LLDB_INVALID_ADDRESS) + continue; + MemoryRegionInfo::RangeType section_range(load_addr, + section_sp->GetByteSize()); + MemoryRegionInfo region = + MinidumpParser::GetMemoryRegionInfo(*m_memory_regions, load_addr); + if (region.GetMapped() != MemoryRegionInfo::eYes && + region.GetRange().GetRangeBase() <= section_range.GetRangeBase() && + section_range.GetRangeEnd() <= region.GetRange().GetRangeEnd()) { + to_add.emplace_back(); + to_add.back().GetRange() = section_range; + to_add.back().SetLLDBPermissions(section_sp->GetPermissions()); + to_add.back().SetMapped(MemoryRegionInfo::eYes); + to_add.back().SetName(module_sp->GetFileSpec().GetPath().c_str()); + } + } + return true; + }); + m_memory_regions->insert(m_memory_regions->end(), to_add.begin(), + to_add.end()); + llvm::sort(*m_memory_regions); +} + +Status ProcessMinidump::DoGetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo ®ion) { + BuildMemoryRegions(); + region = MinidumpParser::GetMemoryRegionInfo(*m_memory_regions, load_addr); + return Status(); +} + +Status ProcessMinidump::GetMemoryRegions(MemoryRegionInfos ®ion_list) { + BuildMemoryRegions(); + region_list = *m_memory_regions; + return Status(); +} + +void ProcessMinidump::Clear() { Process::m_thread_list.Clear(); } + +bool ProcessMinidump::DoUpdateThreadList(ThreadList &old_thread_list, + ThreadList &new_thread_list) { + for (const minidump::Thread &thread : m_thread_list) { + LocationDescriptor context_location = thread.Context; + + // If the minidump contains an exception context, use it + if (m_active_exception != nullptr && + m_active_exception->ThreadId == thread.ThreadId) { + context_location = m_active_exception->ThreadContext; + } + + llvm::ArrayRef<uint8_t> context; + if (!m_is_wow64) + context = m_minidump_parser->GetThreadContext(context_location); + else + context = m_minidump_parser->GetThreadContextWow64(thread); + + lldb::ThreadSP thread_sp(new ThreadMinidump(*this, thread, context)); + new_thread_list.AddThread(thread_sp); + } + return new_thread_list.GetSize(false) > 0; +} + +ModuleSP ProcessMinidump::GetOrCreateModule(UUID minidump_uuid, + llvm::StringRef name, + ModuleSpec module_spec) { + Log *log = GetLog(LLDBLog::DynamicLoader); + Status error; + + ModuleSP module_sp = + GetTarget().GetOrCreateModule(module_spec, true /* notify */, &error); + if (!module_sp) + return module_sp; + // We consider the module to be a match if the minidump UUID is a + // prefix of the actual UUID, or if either of the UUIDs are empty. + const auto dmp_bytes = minidump_uuid.GetBytes(); + const auto mod_bytes = module_sp->GetUUID().GetBytes(); + const bool match = dmp_bytes.empty() || mod_bytes.empty() || + mod_bytes.take_front(dmp_bytes.size()) == dmp_bytes; + if (match) { + LLDB_LOG(log, "Partial uuid match for {0}.", name); + return module_sp; + } + + // Breakpad generates minindump files, and if there is no GNU build + // ID in the binary, it will calculate a UUID by hashing first 4096 + // bytes of the .text section and using that as the UUID for a module + // in the minidump. Facebook uses a modified breakpad client that + // uses a slightly modified this hash to avoid collisions. Check for + // UUIDs from the minindump that match these cases and accept the + // module we find if they do match. + std::vector<uint8_t> breakpad_uuid; + std::vector<uint8_t> facebook_uuid; + HashElfTextSection(module_sp, breakpad_uuid, facebook_uuid); + if (dmp_bytes == llvm::ArrayRef<uint8_t>(breakpad_uuid)) { + LLDB_LOG(log, "Breakpad .text hash match for {0}.", name); + return module_sp; + } + if (dmp_bytes == llvm::ArrayRef<uint8_t>(facebook_uuid)) { + LLDB_LOG(log, "Facebook .text hash match for {0}.", name); + return module_sp; + } + // The UUID wasn't a partial match and didn't match the .text hash + // so remove the module from the target, we will need to create a + // placeholder object file. + GetTarget().GetImages().Remove(module_sp); + module_sp.reset(); + return module_sp; +} + +void ProcessMinidump::ReadModuleList() { + std::vector<const minidump::Module *> filtered_modules = + m_minidump_parser->GetFilteredModuleList(); + + Log *log = GetLog(LLDBLog::DynamicLoader); + + for (auto module : filtered_modules) { + std::string name = cantFail(m_minidump_parser->GetMinidumpFile().getString( + module->ModuleNameRVA)); + const uint64_t load_addr = module->BaseOfImage; + const uint64_t load_size = module->SizeOfImage; + LLDB_LOG(log, "found module: name: {0} {1:x10}-{2:x10} size: {3}", name, + load_addr, load_addr + load_size, load_size); + + // check if the process is wow64 - a 32 bit windows process running on a + // 64 bit windows + if (llvm::StringRef(name).ends_with_insensitive("wow64.dll")) { + m_is_wow64 = true; + } + + const auto uuid = m_minidump_parser->GetModuleUUID(module); + auto file_spec = FileSpec(name, GetArchitecture().GetTriple()); + ModuleSpec module_spec(file_spec, uuid); + module_spec.GetArchitecture() = GetArchitecture(); + Status error; + // Try and find a module with a full UUID that matches. This function will + // add the module to the target if it finds one. + lldb::ModuleSP module_sp = GetTarget().GetOrCreateModule(module_spec, + true /* notify */, &error); + if (module_sp) { + LLDB_LOG(log, "Full uuid match for {0}.", name); + } else { + // We couldn't find a module with an exactly-matching UUID. Sometimes + // a minidump UUID is only a partial match or is a hash. So try again + // without specifying the UUID, then again without specifying the + // directory if that fails. This will allow us to find modules with + // partial matches or hash UUIDs in user-provided sysroots or search + // directories (target.exec-search-paths). + ModuleSpec partial_module_spec = module_spec; + partial_module_spec.GetUUID().Clear(); + module_sp = GetOrCreateModule(uuid, name, partial_module_spec); + if (!module_sp) { + partial_module_spec.GetFileSpec().ClearDirectory(); + module_sp = GetOrCreateModule(uuid, name, partial_module_spec); + } + } + if (module_sp) { + // Watch out for place holder modules that have different paths, but the + // same UUID. If the base address is different, create a new module. If + // we don't then we will end up setting the load address of a different + // ObjectFilePlaceholder and an assertion will fire. + auto *objfile = module_sp->GetObjectFile(); + if (objfile && + objfile->GetPluginName() == + ObjectFilePlaceholder::GetPluginNameStatic()) { + if (((ObjectFilePlaceholder *)objfile)->GetBaseImageAddress() != + load_addr) + module_sp.reset(); + } + } + if (!module_sp) { + // We failed to locate a matching local object file. Fortunately, the + // minidump format encodes enough information about each module's memory + // range to allow us to create placeholder modules. + // + // This enables most LLDB functionality involving address-to-module + // translations (ex. identifing the module for a stack frame PC) and + // modules/sections commands (ex. target modules list, ...) + LLDB_LOG(log, + "Unable to locate the matching object file, creating a " + "placeholder module for: {0}", + name); + + module_sp = Module::CreateModuleFromObjectFile<ObjectFilePlaceholder>( + module_spec, load_addr, load_size); + GetTarget().GetImages().Append(module_sp, true /* notify */); + } + + bool load_addr_changed = false; + module_sp->SetLoadAddress(GetTarget(), load_addr, 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; +} + +// For minidumps there's no runtime generated code so we don't need JITLoader(s) +// Avoiding them will also speed up minidump loading since JITLoaders normally +// try to set up symbolic breakpoints, which in turn may force loading more +// debug information than needed. +JITLoaderList &ProcessMinidump::GetJITLoaders() { + if (!m_jit_loaders_up) { + m_jit_loaders_up = std::make_unique<JITLoaderList>(); + } + return *m_jit_loaders_up; +} + +#define INIT_BOOL(VAR, LONG, SHORT, DESC) \ + VAR(LLDB_OPT_SET_1, false, LONG, SHORT, DESC, false, true) +#define APPEND_OPT(VAR) \ + m_option_group.Append(&VAR, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1) + +class CommandObjectProcessMinidumpDump : public CommandObjectParsed { +private: + OptionGroupOptions m_option_group; + OptionGroupBoolean m_dump_all; + OptionGroupBoolean m_dump_directory; + OptionGroupBoolean m_dump_linux_cpuinfo; + OptionGroupBoolean m_dump_linux_proc_status; + OptionGroupBoolean m_dump_linux_lsb_release; + OptionGroupBoolean m_dump_linux_cmdline; + OptionGroupBoolean m_dump_linux_environ; + OptionGroupBoolean m_dump_linux_auxv; + OptionGroupBoolean m_dump_linux_maps; + OptionGroupBoolean m_dump_linux_proc_stat; + OptionGroupBoolean m_dump_linux_proc_uptime; + OptionGroupBoolean m_dump_linux_proc_fd; + OptionGroupBoolean m_dump_linux_all; + OptionGroupBoolean m_fb_app_data; + OptionGroupBoolean m_fb_build_id; + OptionGroupBoolean m_fb_version; + OptionGroupBoolean m_fb_java_stack; + OptionGroupBoolean m_fb_dalvik; + OptionGroupBoolean m_fb_unwind; + OptionGroupBoolean m_fb_error_log; + OptionGroupBoolean m_fb_app_state; + OptionGroupBoolean m_fb_abort; + OptionGroupBoolean m_fb_thread; + OptionGroupBoolean m_fb_logcat; + OptionGroupBoolean m_fb_all; + + void SetDefaultOptionsIfNoneAreSet() { + if (m_dump_all.GetOptionValue().GetCurrentValue() || + m_dump_linux_all.GetOptionValue().GetCurrentValue() || + m_fb_all.GetOptionValue().GetCurrentValue() || + m_dump_directory.GetOptionValue().GetCurrentValue() || + m_dump_linux_cpuinfo.GetOptionValue().GetCurrentValue() || + m_dump_linux_proc_status.GetOptionValue().GetCurrentValue() || + m_dump_linux_lsb_release.GetOptionValue().GetCurrentValue() || + m_dump_linux_cmdline.GetOptionValue().GetCurrentValue() || + m_dump_linux_environ.GetOptionValue().GetCurrentValue() || + m_dump_linux_auxv.GetOptionValue().GetCurrentValue() || + m_dump_linux_maps.GetOptionValue().GetCurrentValue() || + m_dump_linux_proc_stat.GetOptionValue().GetCurrentValue() || + m_dump_linux_proc_uptime.GetOptionValue().GetCurrentValue() || + m_dump_linux_proc_fd.GetOptionValue().GetCurrentValue() || + m_fb_app_data.GetOptionValue().GetCurrentValue() || + m_fb_build_id.GetOptionValue().GetCurrentValue() || + m_fb_version.GetOptionValue().GetCurrentValue() || + m_fb_java_stack.GetOptionValue().GetCurrentValue() || + m_fb_dalvik.GetOptionValue().GetCurrentValue() || + m_fb_unwind.GetOptionValue().GetCurrentValue() || + m_fb_error_log.GetOptionValue().GetCurrentValue() || + m_fb_app_state.GetOptionValue().GetCurrentValue() || + m_fb_abort.GetOptionValue().GetCurrentValue() || + m_fb_thread.GetOptionValue().GetCurrentValue() || + m_fb_logcat.GetOptionValue().GetCurrentValue()) + return; + // If no options were set, then dump everything + m_dump_all.GetOptionValue().SetCurrentValue(true); + } + bool DumpAll() const { + return m_dump_all.GetOptionValue().GetCurrentValue(); + } + bool DumpDirectory() const { + return DumpAll() || + m_dump_directory.GetOptionValue().GetCurrentValue(); + } + bool DumpLinux() const { + return DumpAll() || m_dump_linux_all.GetOptionValue().GetCurrentValue(); + } + bool DumpLinuxCPUInfo() const { + return DumpLinux() || + m_dump_linux_cpuinfo.GetOptionValue().GetCurrentValue(); + } + bool DumpLinuxProcStatus() const { + return DumpLinux() || + m_dump_linux_proc_status.GetOptionValue().GetCurrentValue(); + } + bool DumpLinuxProcStat() const { + return DumpLinux() || + m_dump_linux_proc_stat.GetOptionValue().GetCurrentValue(); + } + bool DumpLinuxLSBRelease() const { + return DumpLinux() || + m_dump_linux_lsb_release.GetOptionValue().GetCurrentValue(); + } + bool DumpLinuxCMDLine() const { + return DumpLinux() || + m_dump_linux_cmdline.GetOptionValue().GetCurrentValue(); + } + bool DumpLinuxEnviron() const { + return DumpLinux() || + m_dump_linux_environ.GetOptionValue().GetCurrentValue(); + } + bool DumpLinuxAuxv() const { + return DumpLinux() || + m_dump_linux_auxv.GetOptionValue().GetCurrentValue(); + } + bool DumpLinuxMaps() const { + return DumpLinux() || + m_dump_linux_maps.GetOptionValue().GetCurrentValue(); + } + bool DumpLinuxProcUptime() const { + return DumpLinux() || + m_dump_linux_proc_uptime.GetOptionValue().GetCurrentValue(); + } + bool DumpLinuxProcFD() const { + return DumpLinux() || + m_dump_linux_proc_fd.GetOptionValue().GetCurrentValue(); + } + bool DumpFacebook() const { + return DumpAll() || m_fb_all.GetOptionValue().GetCurrentValue(); + } + bool DumpFacebookAppData() const { + return DumpFacebook() || m_fb_app_data.GetOptionValue().GetCurrentValue(); + } + bool DumpFacebookBuildID() const { + return DumpFacebook() || m_fb_build_id.GetOptionValue().GetCurrentValue(); + } + bool DumpFacebookVersionName() const { + return DumpFacebook() || m_fb_version.GetOptionValue().GetCurrentValue(); + } + bool DumpFacebookJavaStack() const { + return DumpFacebook() || m_fb_java_stack.GetOptionValue().GetCurrentValue(); + } + bool DumpFacebookDalvikInfo() const { + return DumpFacebook() || m_fb_dalvik.GetOptionValue().GetCurrentValue(); + } + bool DumpFacebookUnwindSymbols() const { + return DumpFacebook() || m_fb_unwind.GetOptionValue().GetCurrentValue(); + } + bool DumpFacebookErrorLog() const { + return DumpFacebook() || m_fb_error_log.GetOptionValue().GetCurrentValue(); + } + bool DumpFacebookAppStateLog() const { + return DumpFacebook() || m_fb_app_state.GetOptionValue().GetCurrentValue(); + } + bool DumpFacebookAbortReason() const { + return DumpFacebook() || m_fb_abort.GetOptionValue().GetCurrentValue(); + } + bool DumpFacebookThreadName() const { + return DumpFacebook() || m_fb_thread.GetOptionValue().GetCurrentValue(); + } + bool DumpFacebookLogcat() const { + return DumpFacebook() || m_fb_logcat.GetOptionValue().GetCurrentValue(); + } +public: + CommandObjectProcessMinidumpDump(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "process plugin dump", + "Dump information from the minidump file.", nullptr), + m_option_group(), + INIT_BOOL(m_dump_all, "all", 'a', + "Dump the everything in the minidump."), + INIT_BOOL(m_dump_directory, "directory", 'd', + "Dump the minidump directory map."), + INIT_BOOL(m_dump_linux_cpuinfo, "cpuinfo", 'C', + "Dump linux /proc/cpuinfo."), + INIT_BOOL(m_dump_linux_proc_status, "status", 's', + "Dump linux /proc/<pid>/status."), + INIT_BOOL(m_dump_linux_lsb_release, "lsb-release", 'r', + "Dump linux /etc/lsb-release."), + INIT_BOOL(m_dump_linux_cmdline, "cmdline", 'c', + "Dump linux /proc/<pid>/cmdline."), + INIT_BOOL(m_dump_linux_environ, "environ", 'e', + "Dump linux /proc/<pid>/environ."), + INIT_BOOL(m_dump_linux_auxv, "auxv", 'x', + "Dump linux /proc/<pid>/auxv."), + INIT_BOOL(m_dump_linux_maps, "maps", 'm', + "Dump linux /proc/<pid>/maps."), + INIT_BOOL(m_dump_linux_proc_stat, "stat", 'S', + "Dump linux /proc/<pid>/stat."), + INIT_BOOL(m_dump_linux_proc_uptime, "uptime", 'u', + "Dump linux process uptime."), + INIT_BOOL(m_dump_linux_proc_fd, "fd", 'f', + "Dump linux /proc/<pid>/fd."), + INIT_BOOL(m_dump_linux_all, "linux", 'l', + "Dump all linux streams."), + INIT_BOOL(m_fb_app_data, "fb-app-data", 1, + "Dump Facebook application custom data."), + INIT_BOOL(m_fb_build_id, "fb-build-id", 2, + "Dump the Facebook build ID."), + INIT_BOOL(m_fb_version, "fb-version", 3, + "Dump Facebook application version string."), + INIT_BOOL(m_fb_java_stack, "fb-java-stack", 4, + "Dump Facebook java stack."), + INIT_BOOL(m_fb_dalvik, "fb-dalvik-info", 5, + "Dump Facebook Dalvik info."), + INIT_BOOL(m_fb_unwind, "fb-unwind-symbols", 6, + "Dump Facebook unwind symbols."), + INIT_BOOL(m_fb_error_log, "fb-error-log", 7, + "Dump Facebook error log."), + INIT_BOOL(m_fb_app_state, "fb-app-state-log", 8, + "Dump Facebook java stack."), + INIT_BOOL(m_fb_abort, "fb-abort-reason", 9, + "Dump Facebook abort reason."), + INIT_BOOL(m_fb_thread, "fb-thread-name", 10, + "Dump Facebook thread name."), + INIT_BOOL(m_fb_logcat, "fb-logcat", 11, + "Dump Facebook logcat."), + INIT_BOOL(m_fb_all, "facebook", 12, "Dump all Facebook streams.") { + APPEND_OPT(m_dump_all); + APPEND_OPT(m_dump_directory); + APPEND_OPT(m_dump_linux_cpuinfo); + APPEND_OPT(m_dump_linux_proc_status); + APPEND_OPT(m_dump_linux_lsb_release); + APPEND_OPT(m_dump_linux_cmdline); + APPEND_OPT(m_dump_linux_environ); + APPEND_OPT(m_dump_linux_auxv); + APPEND_OPT(m_dump_linux_maps); + APPEND_OPT(m_dump_linux_proc_stat); + APPEND_OPT(m_dump_linux_proc_uptime); + APPEND_OPT(m_dump_linux_proc_fd); + APPEND_OPT(m_dump_linux_all); + APPEND_OPT(m_fb_app_data); + APPEND_OPT(m_fb_build_id); + APPEND_OPT(m_fb_version); + APPEND_OPT(m_fb_java_stack); + APPEND_OPT(m_fb_dalvik); + APPEND_OPT(m_fb_unwind); + APPEND_OPT(m_fb_error_log); + APPEND_OPT(m_fb_app_state); + APPEND_OPT(m_fb_abort); + APPEND_OPT(m_fb_thread); + APPEND_OPT(m_fb_logcat); + APPEND_OPT(m_fb_all); + m_option_group.Finalize(); + } + + ~CommandObjectProcessMinidumpDump() override = default; + + Options *GetOptions() override { return &m_option_group; } + + void DoExecute(Args &command, CommandReturnObject &result) override { + const size_t argc = command.GetArgumentCount(); + if (argc > 0) { + result.AppendErrorWithFormat("'%s' take no arguments, only options", + m_cmd_name.c_str()); + return; + } + SetDefaultOptionsIfNoneAreSet(); + + ProcessMinidump *process = static_cast<ProcessMinidump *>( + m_interpreter.GetExecutionContext().GetProcessPtr()); + result.SetStatus(eReturnStatusSuccessFinishResult); + Stream &s = result.GetOutputStream(); + MinidumpParser &minidump = *process->m_minidump_parser; + if (DumpDirectory()) { + s.Printf("RVA SIZE TYPE StreamType\n"); + s.Printf("---------- ---------- ---------- --------------------------\n"); + for (const auto &stream_desc : minidump.GetMinidumpFile().streams()) + s.Printf( + "0x%8.8x 0x%8.8x 0x%8.8x %s\n", (uint32_t)stream_desc.Location.RVA, + (uint32_t)stream_desc.Location.DataSize, + (unsigned)(StreamType)stream_desc.Type, + MinidumpParser::GetStreamTypeAsString(stream_desc.Type).data()); + s.Printf("\n"); + } + auto DumpTextStream = [&](StreamType stream_type, + llvm::StringRef label) -> void { + auto bytes = minidump.GetStream(stream_type); + if (!bytes.empty()) { + if (label.empty()) + label = MinidumpParser::GetStreamTypeAsString(stream_type); + s.Printf("%s:\n%s\n\n", label.data(), bytes.data()); + } + }; + auto DumpBinaryStream = [&](StreamType stream_type, + llvm::StringRef label) -> void { + auto bytes = minidump.GetStream(stream_type); + if (!bytes.empty()) { + if (label.empty()) + label = MinidumpParser::GetStreamTypeAsString(stream_type); + s.Printf("%s:\n", label.data()); + DataExtractor data(bytes.data(), bytes.size(), eByteOrderLittle, + process->GetAddressByteSize()); + DumpDataExtractor(data, &s, 0, lldb::eFormatBytesWithASCII, 1, + bytes.size(), 16, 0, 0, 0); + s.Printf("\n\n"); + } + }; + + if (DumpLinuxCPUInfo()) + DumpTextStream(StreamType::LinuxCPUInfo, "/proc/cpuinfo"); + if (DumpLinuxProcStatus()) + DumpTextStream(StreamType::LinuxProcStatus, "/proc/PID/status"); + if (DumpLinuxLSBRelease()) + DumpTextStream(StreamType::LinuxLSBRelease, "/etc/lsb-release"); + if (DumpLinuxCMDLine()) + DumpTextStream(StreamType::LinuxCMDLine, "/proc/PID/cmdline"); + if (DumpLinuxEnviron()) + DumpTextStream(StreamType::LinuxEnviron, "/proc/PID/environ"); + if (DumpLinuxAuxv()) + DumpBinaryStream(StreamType::LinuxAuxv, "/proc/PID/auxv"); + if (DumpLinuxMaps()) + DumpTextStream(StreamType::LinuxMaps, "/proc/PID/maps"); + if (DumpLinuxProcStat()) + DumpTextStream(StreamType::LinuxProcStat, "/proc/PID/stat"); + if (DumpLinuxProcUptime()) + DumpTextStream(StreamType::LinuxProcUptime, "uptime"); + if (DumpLinuxProcFD()) + DumpTextStream(StreamType::LinuxProcFD, "/proc/PID/fd"); + if (DumpFacebookAppData()) + DumpTextStream(StreamType::FacebookAppCustomData, + "Facebook App Data"); + if (DumpFacebookBuildID()) { + auto bytes = minidump.GetStream(StreamType::FacebookBuildID); + if (bytes.size() >= 4) { + DataExtractor data(bytes.data(), bytes.size(), eByteOrderLittle, + process->GetAddressByteSize()); + lldb::offset_t offset = 0; + uint32_t build_id = data.GetU32(&offset); + s.Printf("Facebook Build ID:\n"); + s.Printf("%u\n", build_id); + s.Printf("\n"); + } + } + if (DumpFacebookVersionName()) + DumpTextStream(StreamType::FacebookAppVersionName, + "Facebook Version String"); + if (DumpFacebookJavaStack()) + DumpTextStream(StreamType::FacebookJavaStack, + "Facebook Java Stack"); + if (DumpFacebookDalvikInfo()) + DumpTextStream(StreamType::FacebookDalvikInfo, + "Facebook Dalvik Info"); + if (DumpFacebookUnwindSymbols()) + DumpBinaryStream(StreamType::FacebookUnwindSymbols, + "Facebook Unwind Symbols Bytes"); + if (DumpFacebookErrorLog()) + DumpTextStream(StreamType::FacebookDumpErrorLog, + "Facebook Error Log"); + if (DumpFacebookAppStateLog()) + DumpTextStream(StreamType::FacebookAppStateLog, + "Faceook Application State Log"); + if (DumpFacebookAbortReason()) + DumpTextStream(StreamType::FacebookAbortReason, + "Facebook Abort Reason"); + if (DumpFacebookThreadName()) + DumpTextStream(StreamType::FacebookThreadName, + "Facebook Thread Name"); + if (DumpFacebookLogcat()) + DumpTextStream(StreamType::FacebookLogcat, "Facebook Logcat"); + } +}; + +class CommandObjectMultiwordProcessMinidump : public CommandObjectMultiword { +public: + CommandObjectMultiwordProcessMinidump(CommandInterpreter &interpreter) + : CommandObjectMultiword(interpreter, "process plugin", + "Commands for operating on a ProcessMinidump process.", + "process plugin <subcommand> [<subcommand-options>]") { + LoadSubCommand("dump", + CommandObjectSP(new CommandObjectProcessMinidumpDump(interpreter))); + } + + ~CommandObjectMultiwordProcessMinidump() override = default; +}; + +CommandObject *ProcessMinidump::GetPluginCommandObject() { + if (!m_command_sp) + m_command_sp = std::make_shared<CommandObjectMultiwordProcessMinidump>( + GetTarget().GetDebugger().GetCommandInterpreter()); + return m_command_sp.get(); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.h new file mode 100644 index 000000000000..3f3123a0a8b5 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.h @@ -0,0 +1,123 @@ +//===-- ProcessMinidump.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_PROCESSMINIDUMP_H +#define LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_PROCESSMINIDUMP_H + +#include "MinidumpParser.h" +#include "MinidumpTypes.h" + +#include "lldb/Target/PostMortemProcess.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/Status.h" + +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <optional> + +namespace lldb_private { + +namespace minidump { + +class ProcessMinidump : public PostMortemProcess { +public: + static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec *crash_file_path, + bool can_connect); + + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "minidump"; } + + static llvm::StringRef GetPluginDescriptionStatic(); + + ProcessMinidump(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, + const FileSpec &core_file, lldb::DataBufferSP code_data); + + ~ProcessMinidump() override; + + bool CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) override; + + CommandObject *GetPluginCommandObject() override; + + Status DoLoadCore() override; + + DynamicLoader *GetDynamicLoader() override { return nullptr; } + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + SystemRuntime *GetSystemRuntime() override { return nullptr; } + + Status DoDestroy() override; + + void RefreshStateAfterStop() override; + + bool IsAlive() override; + + bool WarnBeforeDetach() const override; + + size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) override; + + size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) override; + + ArchSpec GetArchitecture(); + + Status GetMemoryRegions( + lldb_private::MemoryRegionInfos ®ion_list) override; + + bool GetProcessInfo(ProcessInstanceInfo &info) override; + + Status WillResume() override { + Status error; + error.SetErrorStringWithFormatv( + "error: {0} does not support resuming processes", GetPluginName()); + return error; + } + + std::optional<MinidumpParser> m_minidump_parser; + +protected: + void Clear(); + + bool DoUpdateThreadList(ThreadList &old_thread_list, + ThreadList &new_thread_list) override; + + Status DoGetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info) override; + + void ReadModuleList(); + + lldb::ModuleSP GetOrCreateModule(lldb_private::UUID minidump_uuid, + llvm::StringRef name, + lldb_private::ModuleSpec module_spec); + + JITLoaderList &GetJITLoaders() override; + +private: + lldb::DataBufferSP m_core_data; + llvm::ArrayRef<minidump::Thread> m_thread_list; + const minidump::ExceptionStream *m_active_exception; + lldb::CommandObjectSP m_command_sp; + bool m_is_wow64; + std::optional<MemoryRegionInfos> m_memory_regions; + + void BuildMemoryRegions(); +}; + +} // namespace minidump +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_PROCESSMINIDUMP_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.cpp new file mode 100644 index 000000000000..0004d5d8d07e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.cpp @@ -0,0 +1,550 @@ +//===-- RegisterContextMinidump_ARM.cpp -----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextMinidump_ARM.h" + +#include "Utility/ARM_DWARF_Registers.h" +#include "Utility/ARM_ehframe_Registers.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/lldb-enumerations.h" + +// C includes +#include <cassert> + +// C++ includes + +using namespace lldb; +using namespace lldb_private; +using namespace minidump; + +#define INV LLDB_INVALID_REGNUM +#define OFFSET(r) (offsetof(RegisterContextMinidump_ARM::Context, r)) + +#define DEF_R(i) \ + { \ + "r" #i, nullptr, 4, OFFSET(r) + i * 4, eEncodingUint, eFormatHex, \ + {ehframe_r##i, dwarf_r##i, INV, INV, reg_r##i}, nullptr, nullptr, \ + nullptr, \ + } + +#define DEF_R_ARG(i, n) \ + { \ + "r" #i, "arg" #n, 4, OFFSET(r) + i * 4, eEncodingUint, eFormatHex, \ + {ehframe_r##i, dwarf_r##i, LLDB_REGNUM_GENERIC_ARG1 + i, INV, \ + reg_r##i}, \ + nullptr, nullptr, nullptr, \ + } + +#define DEF_D(i) \ + { \ + "d" #i, nullptr, 8, OFFSET(d) + i * 8, eEncodingVector, \ + eFormatVectorOfUInt8, {dwarf_d##i, dwarf_d##i, INV, INV, reg_d##i}, \ + nullptr, nullptr, nullptr, \ + } + +#define DEF_S(i) \ + { \ + "s" #i, nullptr, 4, OFFSET(s) + i * 4, eEncodingIEEE754, eFormatFloat, \ + {dwarf_s##i, dwarf_s##i, INV, INV, reg_s##i}, nullptr, nullptr, \ + nullptr, \ + } + +#define DEF_Q(i) \ + { \ + "q" #i, nullptr, 16, OFFSET(q) + i * 16, eEncodingVector, \ + eFormatVectorOfUInt8, {dwarf_q##i, dwarf_q##i, INV, INV, reg_q##i}, \ + nullptr, nullptr, nullptr, \ + } + +// Zero based LLDB register numbers for this register context +enum { + // General Purpose Registers + reg_r0, + reg_r1, + reg_r2, + reg_r3, + reg_r4, + reg_r5, + reg_r6, + reg_r7, + reg_r8, + reg_r9, + reg_r10, + reg_r11, + reg_r12, + reg_sp, + reg_lr, + reg_pc, + reg_cpsr, + // Floating Point Registers + reg_fpscr, + reg_d0, + reg_d1, + reg_d2, + reg_d3, + reg_d4, + reg_d5, + reg_d6, + reg_d7, + reg_d8, + reg_d9, + reg_d10, + reg_d11, + reg_d12, + reg_d13, + reg_d14, + reg_d15, + reg_d16, + reg_d17, + reg_d18, + reg_d19, + reg_d20, + reg_d21, + reg_d22, + reg_d23, + reg_d24, + reg_d25, + reg_d26, + reg_d27, + reg_d28, + reg_d29, + reg_d30, + reg_d31, + reg_s0, + reg_s1, + reg_s2, + reg_s3, + reg_s4, + reg_s5, + reg_s6, + reg_s7, + reg_s8, + reg_s9, + reg_s10, + reg_s11, + reg_s12, + reg_s13, + reg_s14, + reg_s15, + reg_s16, + reg_s17, + reg_s18, + reg_s19, + reg_s20, + reg_s21, + reg_s22, + reg_s23, + reg_s24, + reg_s25, + reg_s26, + reg_s27, + reg_s28, + reg_s29, + reg_s30, + reg_s31, + reg_q0, + reg_q1, + reg_q2, + reg_q3, + reg_q4, + reg_q5, + reg_q6, + reg_q7, + reg_q8, + reg_q9, + reg_q10, + reg_q11, + reg_q12, + reg_q13, + reg_q14, + reg_q15, + k_num_regs +}; + +static RegisterInfo g_reg_info_apple_fp = { + "fp", + "r7", + 4, + OFFSET(r) + 7 * 4, + eEncodingUint, + eFormatHex, + {ehframe_r7, dwarf_r7, LLDB_REGNUM_GENERIC_FP, INV, reg_r7}, + nullptr, + nullptr, + nullptr, +}; + +static RegisterInfo g_reg_info_fp = { + "fp", + "r11", + 4, + OFFSET(r) + 11 * 4, + eEncodingUint, + eFormatHex, + {ehframe_r11, dwarf_r11, LLDB_REGNUM_GENERIC_FP, INV, reg_r11}, + nullptr, + nullptr, + nullptr, +}; + +// Register info definitions for this register context +static RegisterInfo g_reg_infos[] = { + DEF_R_ARG(0, 1), + DEF_R_ARG(1, 2), + DEF_R_ARG(2, 3), + DEF_R_ARG(3, 4), + DEF_R(4), + DEF_R(5), + DEF_R(6), + DEF_R(7), + DEF_R(8), + DEF_R(9), + DEF_R(10), + DEF_R(11), + DEF_R(12), + {"sp", + "r13", + 4, + OFFSET(r) + 13 * 4, + eEncodingUint, + eFormatHex, + {ehframe_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP, INV, reg_sp}, + nullptr, + nullptr, + nullptr, + }, + {"lr", + "r14", + 4, + OFFSET(r) + 14 * 4, + eEncodingUint, + eFormatHex, + {ehframe_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA, INV, reg_lr}, + nullptr, + nullptr, + nullptr, + }, + {"pc", + "r15", + 4, + OFFSET(r) + 15 * 4, + eEncodingUint, + eFormatHex, + {ehframe_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, INV, reg_pc}, + nullptr, + nullptr, + nullptr, + }, + {"cpsr", + "psr", + 4, + OFFSET(cpsr), + eEncodingUint, + eFormatHex, + {ehframe_cpsr, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS, INV, reg_cpsr}, + nullptr, + nullptr, + nullptr, + }, + {"fpscr", + nullptr, + 8, + OFFSET(fpscr), + eEncodingUint, + eFormatHex, + {INV, INV, INV, INV, reg_fpscr}, + nullptr, + nullptr, + nullptr, + }, + DEF_D(0), + DEF_D(1), + DEF_D(2), + DEF_D(3), + DEF_D(4), + DEF_D(5), + DEF_D(6), + DEF_D(7), + DEF_D(8), + DEF_D(9), + DEF_D(10), + DEF_D(11), + DEF_D(12), + DEF_D(13), + DEF_D(14), + DEF_D(15), + DEF_D(16), + DEF_D(17), + DEF_D(18), + DEF_D(19), + DEF_D(20), + DEF_D(21), + DEF_D(22), + DEF_D(23), + DEF_D(24), + DEF_D(25), + DEF_D(26), + DEF_D(27), + DEF_D(28), + DEF_D(29), + DEF_D(30), + DEF_D(31), + DEF_S(0), + DEF_S(1), + DEF_S(2), + DEF_S(3), + DEF_S(4), + DEF_S(5), + DEF_S(6), + DEF_S(7), + DEF_S(8), + DEF_S(9), + DEF_S(10), + DEF_S(11), + DEF_S(12), + DEF_S(13), + DEF_S(14), + DEF_S(15), + DEF_S(16), + DEF_S(17), + DEF_S(18), + DEF_S(19), + DEF_S(20), + DEF_S(21), + DEF_S(22), + DEF_S(23), + DEF_S(24), + DEF_S(25), + DEF_S(26), + DEF_S(27), + DEF_S(28), + DEF_S(29), + DEF_S(30), + DEF_S(31), + DEF_Q(0), + DEF_Q(1), + DEF_Q(2), + DEF_Q(3), + DEF_Q(4), + DEF_Q(5), + DEF_Q(6), + DEF_Q(7), + DEF_Q(8), + DEF_Q(9), + DEF_Q(10), + DEF_Q(11), + DEF_Q(12), + DEF_Q(13), + DEF_Q(14), + DEF_Q(15)}; + +constexpr size_t k_num_reg_infos = std::size(g_reg_infos); + +// ARM general purpose registers. +const uint32_t g_gpr_regnums[] = { + reg_r0, + reg_r1, + reg_r2, + reg_r3, + reg_r4, + reg_r5, + reg_r6, + reg_r7, + reg_r8, + reg_r9, + reg_r10, + reg_r11, + reg_r12, + reg_sp, + reg_lr, + reg_pc, + reg_cpsr, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +const uint32_t g_fpu_regnums[] = { + reg_fpscr, + reg_d0, + reg_d1, + reg_d2, + reg_d3, + reg_d4, + reg_d5, + reg_d6, + reg_d7, + reg_d8, + reg_d9, + reg_d10, + reg_d11, + reg_d12, + reg_d13, + reg_d14, + reg_d15, + reg_d16, + reg_d17, + reg_d18, + reg_d19, + reg_d20, + reg_d21, + reg_d22, + reg_d23, + reg_d24, + reg_d25, + reg_d26, + reg_d27, + reg_d28, + reg_d29, + reg_d30, + reg_d31, + reg_s0, + reg_s1, + reg_s2, + reg_s3, + reg_s4, + reg_s5, + reg_s6, + reg_s7, + reg_s8, + reg_s9, + reg_s10, + reg_s11, + reg_s12, + reg_s13, + reg_s14, + reg_s15, + reg_s16, + reg_s17, + reg_s18, + reg_s19, + reg_s20, + reg_s21, + reg_s22, + reg_s23, + reg_s24, + reg_s25, + reg_s26, + reg_s27, + reg_s28, + reg_s29, + reg_s30, + reg_s31, + reg_q0, + reg_q1, + reg_q2, + reg_q3, + reg_q4, + reg_q5, + reg_q6, + reg_q7, + reg_q8, + reg_q9, + reg_q10, + reg_q11, + reg_q12, + reg_q13, + reg_q14, + reg_q15, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; + +// Skip the last LLDB_INVALID_REGNUM in each count below by subtracting 1 +constexpr size_t k_num_gpr_regs = std::size(g_gpr_regnums) - 1; +constexpr size_t k_num_fpu_regs = std::size(g_fpu_regnums) - 1; + +static RegisterSet g_reg_sets[] = { + {"General Purpose Registers", "gpr", k_num_gpr_regs, g_gpr_regnums}, + {"Floating Point Registers", "fpu", k_num_fpu_regs, g_fpu_regnums}, +}; + +constexpr size_t k_num_reg_sets = std::size(g_reg_sets); + +RegisterContextMinidump_ARM::RegisterContextMinidump_ARM( + lldb_private::Thread &thread, const DataExtractor &data, bool apple) + : RegisterContext(thread, 0), m_apple(apple) { + lldb::offset_t offset = 0; + m_regs.context_flags = data.GetU32(&offset); + for (unsigned i = 0; i < std::size(m_regs.r); ++i) + m_regs.r[i] = data.GetU32(&offset); + m_regs.cpsr = data.GetU32(&offset); + m_regs.fpscr = data.GetU64(&offset); + for (unsigned i = 0; i < std::size(m_regs.d); ++i) + m_regs.d[i] = data.GetU64(&offset); + lldbassert(k_num_regs == k_num_reg_infos); +} + +size_t RegisterContextMinidump_ARM::GetRegisterCountStatic() { + return k_num_regs; +} + +// Used for unit testing so we can verify register info is filled in for +// all register flavors (DWARF, EH Frame, generic, etc). +size_t RegisterContextMinidump_ARM::GetRegisterCount() { + return GetRegisterCountStatic(); +} + +// Used for unit testing so we can verify register info is filled in. +const RegisterInfo * +RegisterContextMinidump_ARM::GetRegisterInfoAtIndexStatic(size_t reg, + bool apple) { + if (reg < k_num_reg_infos) { + if (apple) { + if (reg == reg_r7) + return &g_reg_info_apple_fp; + } else { + if (reg == reg_r11) + return &g_reg_info_fp; + } + return &g_reg_infos[reg]; + } + return nullptr; +} + +const RegisterInfo * +RegisterContextMinidump_ARM::GetRegisterInfoAtIndex(size_t reg) { + return GetRegisterInfoAtIndexStatic(reg, m_apple); +} + +size_t RegisterContextMinidump_ARM::GetRegisterSetCount() { + return k_num_reg_sets; +} + +const RegisterSet *RegisterContextMinidump_ARM::GetRegisterSet(size_t set) { + if (set < k_num_reg_sets) + return &g_reg_sets[set]; + return nullptr; +} + +const char *RegisterContextMinidump_ARM::GetRegisterName(unsigned reg) { + if (reg < k_num_reg_infos) + return g_reg_infos[reg].name; + return nullptr; +} + +bool RegisterContextMinidump_ARM::ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) { + Status error; + reg_value.SetFromMemoryData( + *reg_info, (const uint8_t *)&m_regs + reg_info->byte_offset, + reg_info->byte_size, lldb::eByteOrderLittle, error); + return error.Success(); +} + +bool RegisterContextMinidump_ARM::WriteRegister(const RegisterInfo *, + const RegisterValue &) { + return false; +} + +uint32_t RegisterContextMinidump_ARM::ConvertRegisterKindToRegisterNumber( + lldb::RegisterKind kind, uint32_t num) { + for (size_t i = 0; i < k_num_regs; ++i) { + if (g_reg_infos[i].kinds[kind] == num) + return i; + } + return LLDB_INVALID_REGNUM; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.h new file mode 100644 index 000000000000..857f9c0a3767 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.h @@ -0,0 +1,98 @@ +//===-- RegisterContextMinidump_ARM.h ---------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_REGISTERCONTEXTMINIDUMP_ARM_H +#define LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_REGISTERCONTEXTMINIDUMP_ARM_H + +#include "MinidumpTypes.h" + +#include "Plugins/Process/Utility/RegisterInfoInterface.h" + +#include "lldb/Target/RegisterContext.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitmaskEnum.h" + +// C includes +// C++ includes + +namespace lldb_private { + +namespace minidump { + +LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); + +class RegisterContextMinidump_ARM : public lldb_private::RegisterContext { +public: + RegisterContextMinidump_ARM(lldb_private::Thread &thread, + const DataExtractor &data, bool apple); + + ~RegisterContextMinidump_ARM() override = default; + + void InvalidateAllRegisters() override { + // Do nothing... registers are always valid... + } + + // Used for unit testing. + static size_t GetRegisterCountStatic(); + // Used for unit testing. + static const lldb_private::RegisterInfo * + GetRegisterInfoAtIndexStatic(size_t reg, bool apple); + + size_t GetRegisterCount() override; + + const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const lldb_private::RegisterSet *GetRegisterSet(size_t set) override; + + const char *GetRegisterName(unsigned reg); + + bool ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) override; + + bool WriteRegister(const RegisterInfo *reg_info, + const RegisterValue ®_value) override; + + uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, + uint32_t num) override; + + // Reference: see breakpad/crashpad source + struct QRegValue { + uint64_t lo; + uint64_t hi; + }; + + struct Context { + uint32_t context_flags; + uint32_t r[16]; + uint32_t cpsr; + uint64_t fpscr; + union { + uint64_t d[32]; + uint32_t s[32]; + QRegValue q[16]; + }; + uint32_t extra[8]; + }; + +protected: + enum class Flags : uint32_t { + ARM_Flag = 0x40000000, + Integer = ARM_Flag | 0x00000002, + FloatingPoint = ARM_Flag | 0x00000004, + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ FloatingPoint) + }; + Context m_regs; + const bool m_apple; // True if this is an Apple ARM where FP is R7 +}; + +} // end namespace minidump +} // end namespace lldb_private +#endif // LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_REGISTERCONTEXTMINIDUMP_ARM_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.cpp new file mode 100644 index 000000000000..a0476c962070 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.cpp @@ -0,0 +1,833 @@ +//===-- RegisterContextMinidump_ARM64.cpp ---------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextMinidump_ARM64.h" + +#include "Utility/ARM64_DWARF_Registers.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/lldb-enumerations.h" + +// C includes +#include <cassert> + +// C++ includes + +using namespace lldb; +using namespace lldb_private; +using namespace minidump; + +#define INV LLDB_INVALID_REGNUM +#define OFFSET(r) (offsetof(RegisterContextMinidump_ARM64::Context, r)) + +#define DEF_X(i) \ + { \ + "x" #i, nullptr, 8, OFFSET(x) + i * 8, eEncodingUint, eFormatHex, \ + {arm64_dwarf::x##i, arm64_dwarf::x##i, INV, INV, reg_x##i}, \ + nullptr, nullptr, nullptr, \ + } + +#define DEF_W(i) \ + { \ + "w" #i, nullptr, 4, OFFSET(x) + i * 8, eEncodingUint, eFormatHex, \ + {INV, INV, INV, INV, reg_w##i}, nullptr, nullptr, nullptr, \ + } + +#define DEF_X_ARG(i, n) \ + { \ + "x" #i, "arg" #n, 8, OFFSET(x) + i * 8, eEncodingUint, eFormatHex, \ + {arm64_dwarf::x##i, arm64_dwarf::x##i, LLDB_REGNUM_GENERIC_ARG1 + i, \ + INV, reg_x##i}, nullptr, nullptr, nullptr, \ + } + +#define DEF_V(i) \ + { \ + "v" #i, nullptr, 16, OFFSET(v) + i * 16, eEncodingVector, \ + eFormatVectorOfUInt8, {arm64_dwarf::v##i, arm64_dwarf::v##i, INV, INV, \ + reg_v##i}, nullptr, nullptr, nullptr, \ + } + +#define DEF_D(i) \ + { \ + "d" #i, nullptr, 8, OFFSET(v) + i * 16, eEncodingVector, \ + eFormatVectorOfUInt8, {INV, INV, INV, INV, reg_d##i}, nullptr, \ + nullptr, nullptr, \ + } + +#define DEF_S(i) \ + { \ + "s" #i, nullptr, 4, OFFSET(v) + i * 16, eEncodingVector, \ + eFormatVectorOfUInt8, {INV, INV, INV, INV, reg_s##i}, nullptr, \ + nullptr, nullptr, \ + } + +#define DEF_H(i) \ + { \ + "h" #i, nullptr, 2, OFFSET(v) + i * 16, eEncodingVector, \ + eFormatVectorOfUInt8, {INV, INV, INV, INV, reg_h##i}, nullptr, \ + nullptr, nullptr, \ + } + +// Zero based LLDB register numbers for this register context +enum { + // General Purpose Registers + reg_x0 = 0, + reg_x1, + reg_x2, + reg_x3, + reg_x4, + reg_x5, + reg_x6, + reg_x7, + reg_x8, + reg_x9, + reg_x10, + reg_x11, + reg_x12, + reg_x13, + reg_x14, + reg_x15, + reg_x16, + reg_x17, + reg_x18, + reg_x19, + reg_x20, + reg_x21, + reg_x22, + reg_x23, + reg_x24, + reg_x25, + reg_x26, + reg_x27, + reg_x28, + reg_fp, + reg_lr, + reg_sp, + reg_pc, + reg_w0, + reg_w1, + reg_w2, + reg_w3, + reg_w4, + reg_w5, + reg_w6, + reg_w7, + reg_w8, + reg_w9, + reg_w10, + reg_w11, + reg_w12, + reg_w13, + reg_w14, + reg_w15, + reg_w16, + reg_w17, + reg_w18, + reg_w19, + reg_w20, + reg_w21, + reg_w22, + reg_w23, + reg_w24, + reg_w25, + reg_w26, + reg_w27, + reg_w28, + reg_w29, + reg_w30, + reg_w31, + reg_cpsr, + // Floating Point Registers + reg_fpsr, + reg_fpcr, + reg_v0, + reg_v1, + reg_v2, + reg_v3, + reg_v4, + reg_v5, + reg_v6, + reg_v7, + reg_v8, + reg_v9, + reg_v10, + reg_v11, + reg_v12, + reg_v13, + reg_v14, + reg_v15, + reg_v16, + reg_v17, + reg_v18, + reg_v19, + reg_v20, + reg_v21, + reg_v22, + reg_v23, + reg_v24, + reg_v25, + reg_v26, + reg_v27, + reg_v28, + reg_v29, + reg_v30, + reg_v31, + reg_d0, + reg_d1, + reg_d2, + reg_d3, + reg_d4, + reg_d5, + reg_d6, + reg_d7, + reg_d8, + reg_d9, + reg_d10, + reg_d11, + reg_d12, + reg_d13, + reg_d14, + reg_d15, + reg_d16, + reg_d17, + reg_d18, + reg_d19, + reg_d20, + reg_d21, + reg_d22, + reg_d23, + reg_d24, + reg_d25, + reg_d26, + reg_d27, + reg_d28, + reg_d29, + reg_d30, + reg_d31, + reg_s0, + reg_s1, + reg_s2, + reg_s3, + reg_s4, + reg_s5, + reg_s6, + reg_s7, + reg_s8, + reg_s9, + reg_s10, + reg_s11, + reg_s12, + reg_s13, + reg_s14, + reg_s15, + reg_s16, + reg_s17, + reg_s18, + reg_s19, + reg_s20, + reg_s21, + reg_s22, + reg_s23, + reg_s24, + reg_s25, + reg_s26, + reg_s27, + reg_s28, + reg_s29, + reg_s30, + reg_s31, + reg_h0, + reg_h1, + reg_h2, + reg_h3, + reg_h4, + reg_h5, + reg_h6, + reg_h7, + reg_h8, + reg_h9, + reg_h10, + reg_h11, + reg_h12, + reg_h13, + reg_h14, + reg_h15, + reg_h16, + reg_h17, + reg_h18, + reg_h19, + reg_h20, + reg_h21, + reg_h22, + reg_h23, + reg_h24, + reg_h25, + reg_h26, + reg_h27, + reg_h28, + reg_h29, + reg_h30, + reg_h31, + k_num_regs +}; + +// Register info definitions for this register context +static RegisterInfo g_reg_infos[] = { + DEF_X_ARG(0, 1), + DEF_X_ARG(1, 2), + DEF_X_ARG(2, 3), + DEF_X_ARG(3, 4), + DEF_X_ARG(4, 5), + DEF_X_ARG(5, 6), + DEF_X_ARG(6, 7), + DEF_X_ARG(7, 8), + DEF_X(8), + DEF_X(9), + DEF_X(10), + DEF_X(11), + DEF_X(12), + DEF_X(13), + DEF_X(14), + DEF_X(15), + DEF_X(16), + DEF_X(17), + DEF_X(18), + DEF_X(19), + DEF_X(20), + DEF_X(21), + DEF_X(22), + DEF_X(23), + DEF_X(24), + DEF_X(25), + DEF_X(26), + DEF_X(27), + DEF_X(28), + {"fp", + "x29", + 8, + OFFSET(x) + 29 * 8, + eEncodingUint, + eFormatHex, + {arm64_dwarf::x29, arm64_dwarf::x29, LLDB_REGNUM_GENERIC_FP, INV, reg_fp}, + nullptr, + nullptr, + nullptr, + }, + {"lr", + "x30", + 8, + OFFSET(x) + 30 * 8, + eEncodingUint, + eFormatHex, + {arm64_dwarf::x30, arm64_dwarf::x30, LLDB_REGNUM_GENERIC_RA, INV, reg_lr}, + nullptr, + nullptr, + nullptr, + }, + {"sp", + "x31", + 8, + OFFSET(x) + 31 * 8, + eEncodingUint, + eFormatHex, + {arm64_dwarf::x31, arm64_dwarf::x31, LLDB_REGNUM_GENERIC_SP, INV, reg_sp}, + nullptr, + nullptr, + nullptr, + }, + {"pc", + nullptr, + 8, + OFFSET(pc), + eEncodingUint, + eFormatHex, + {arm64_dwarf::pc, arm64_dwarf::pc, LLDB_REGNUM_GENERIC_PC, INV, reg_pc}, + nullptr, + nullptr, + nullptr, + }, + // w0 - w31 + DEF_W(0), + DEF_W(1), + DEF_W(2), + DEF_W(3), + DEF_W(4), + DEF_W(5), + DEF_W(6), + DEF_W(7), + DEF_W(8), + DEF_W(9), + DEF_W(10), + DEF_W(11), + DEF_W(12), + DEF_W(13), + DEF_W(14), + DEF_W(15), + DEF_W(16), + DEF_W(17), + DEF_W(18), + DEF_W(19), + DEF_W(20), + DEF_W(21), + DEF_W(22), + DEF_W(23), + DEF_W(24), + DEF_W(25), + DEF_W(26), + DEF_W(27), + DEF_W(28), + DEF_W(29), + DEF_W(30), + DEF_W(31), + {"cpsr", + "psr", + 4, + OFFSET(cpsr), + eEncodingUint, + eFormatHex, + {INV, arm64_dwarf::cpsr, LLDB_REGNUM_GENERIC_FLAGS, INV, reg_cpsr}, + nullptr, + nullptr, + nullptr, + }, + {"fpsr", + nullptr, + 4, + OFFSET(fpsr), + eEncodingUint, + eFormatHex, + {INV, INV, INV, INV, reg_fpsr}, + nullptr, + nullptr, + nullptr, + }, + {"fpcr", + nullptr, + 4, + OFFSET(fpcr), + eEncodingUint, + eFormatHex, + {INV, INV, INV, INV, reg_fpcr}, + nullptr, + nullptr, + nullptr, + }, + // v0 - v31 + DEF_V(0), + DEF_V(1), + DEF_V(2), + DEF_V(3), + DEF_V(4), + DEF_V(5), + DEF_V(6), + DEF_V(7), + DEF_V(8), + DEF_V(9), + DEF_V(10), + DEF_V(11), + DEF_V(12), + DEF_V(13), + DEF_V(14), + DEF_V(15), + DEF_V(16), + DEF_V(17), + DEF_V(18), + DEF_V(19), + DEF_V(20), + DEF_V(21), + DEF_V(22), + DEF_V(23), + DEF_V(24), + DEF_V(25), + DEF_V(26), + DEF_V(27), + DEF_V(28), + DEF_V(29), + DEF_V(30), + DEF_V(31), + // d0 - d31 + DEF_D(0), + DEF_D(1), + DEF_D(2), + DEF_D(3), + DEF_D(4), + DEF_D(5), + DEF_D(6), + DEF_D(7), + DEF_D(8), + DEF_D(9), + DEF_D(10), + DEF_D(11), + DEF_D(12), + DEF_D(13), + DEF_D(14), + DEF_D(15), + DEF_D(16), + DEF_D(17), + DEF_D(18), + DEF_D(19), + DEF_D(20), + DEF_D(21), + DEF_D(22), + DEF_D(23), + DEF_D(24), + DEF_D(25), + DEF_D(26), + DEF_D(27), + DEF_D(28), + DEF_D(29), + DEF_D(30), + DEF_D(31), + // s0 - s31 + DEF_S(0), + DEF_S(1), + DEF_S(2), + DEF_S(3), + DEF_S(4), + DEF_S(5), + DEF_S(6), + DEF_S(7), + DEF_S(8), + DEF_S(9), + DEF_S(10), + DEF_S(11), + DEF_S(12), + DEF_S(13), + DEF_S(14), + DEF_S(15), + DEF_S(16), + DEF_S(17), + DEF_S(18), + DEF_S(19), + DEF_S(20), + DEF_S(21), + DEF_S(22), + DEF_S(23), + DEF_S(24), + DEF_S(25), + DEF_S(26), + DEF_S(27), + DEF_S(28), + DEF_S(29), + DEF_S(30), + DEF_S(31), + // h0 - h31 + DEF_H(0), + DEF_H(1), + DEF_H(2), + DEF_H(3), + DEF_H(4), + DEF_H(5), + DEF_H(6), + DEF_H(7), + DEF_H(8), + DEF_H(9), + DEF_H(10), + DEF_H(11), + DEF_H(12), + DEF_H(13), + DEF_H(14), + DEF_H(15), + DEF_H(16), + DEF_H(17), + DEF_H(18), + DEF_H(19), + DEF_H(20), + DEF_H(21), + DEF_H(22), + DEF_H(23), + DEF_H(24), + DEF_H(25), + DEF_H(26), + DEF_H(27), + DEF_H(28), + DEF_H(29), + DEF_H(30), + DEF_H(31), +}; + +constexpr size_t k_num_reg_infos = std::size(g_reg_infos); + +// ARM64 general purpose registers. +const uint32_t g_gpr_regnums[] = { + reg_x0, + reg_x1, + reg_x2, + reg_x3, + reg_x4, + reg_x5, + reg_x6, + reg_x7, + reg_x8, + reg_x9, + reg_x10, + reg_x11, + reg_x12, + reg_x13, + reg_x14, + reg_x15, + reg_x16, + reg_x17, + reg_x18, + reg_x19, + reg_x20, + reg_x21, + reg_x22, + reg_x23, + reg_x24, + reg_x25, + reg_x26, + reg_x27, + reg_x28, + reg_fp, + reg_lr, + reg_sp, + reg_w0, + reg_w1, + reg_w2, + reg_w3, + reg_w4, + reg_w5, + reg_w6, + reg_w7, + reg_w8, + reg_w9, + reg_w10, + reg_w11, + reg_w12, + reg_w13, + reg_w14, + reg_w15, + reg_w16, + reg_w17, + reg_w18, + reg_w19, + reg_w20, + reg_w21, + reg_w22, + reg_w23, + reg_w24, + reg_w25, + reg_w26, + reg_w27, + reg_w28, + reg_w29, + reg_w30, + reg_w31, + reg_pc, + reg_cpsr, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +const uint32_t g_fpu_regnums[] = { + reg_v0, + reg_v1, + reg_v2, + reg_v3, + reg_v4, + reg_v5, + reg_v6, + reg_v7, + reg_v8, + reg_v9, + reg_v10, + reg_v11, + reg_v12, + reg_v13, + reg_v14, + reg_v15, + reg_v16, + reg_v17, + reg_v18, + reg_v19, + reg_v20, + reg_v21, + reg_v22, + reg_v23, + reg_v24, + reg_v25, + reg_v26, + reg_v27, + reg_v28, + reg_v29, + reg_v30, + reg_v31, + reg_d0, + reg_d1, + reg_d2, + reg_d3, + reg_d4, + reg_d5, + reg_d6, + reg_d7, + reg_d8, + reg_d9, + reg_d10, + reg_d11, + reg_d12, + reg_d13, + reg_d14, + reg_d15, + reg_d16, + reg_d17, + reg_d18, + reg_d19, + reg_d20, + reg_d21, + reg_d22, + reg_d23, + reg_d24, + reg_d25, + reg_d26, + reg_d27, + reg_d28, + reg_d29, + reg_d30, + reg_d31, + reg_s0, + reg_s1, + reg_s2, + reg_s3, + reg_s4, + reg_s5, + reg_s6, + reg_s7, + reg_s8, + reg_s9, + reg_s10, + reg_s11, + reg_s12, + reg_s13, + reg_s14, + reg_s15, + reg_s16, + reg_s17, + reg_s18, + reg_s19, + reg_s20, + reg_s21, + reg_s22, + reg_s23, + reg_s24, + reg_s25, + reg_s26, + reg_s27, + reg_s28, + reg_s29, + reg_s30, + reg_s31, + reg_h0, + reg_h1, + reg_h2, + reg_h3, + reg_h4, + reg_h5, + reg_h6, + reg_h7, + reg_h8, + reg_h9, + reg_h10, + reg_h11, + reg_h12, + reg_h13, + reg_h14, + reg_h15, + reg_h16, + reg_h17, + reg_h18, + reg_h19, + reg_h20, + reg_h21, + reg_h22, + reg_h23, + reg_h24, + reg_h25, + reg_h26, + reg_h27, + reg_h28, + reg_h29, + reg_h30, + reg_h31, + reg_fpsr, + reg_fpcr, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; + +// Skip the last LLDB_INVALID_REGNUM in each count below by subtracting 1 +constexpr size_t k_num_gpr_regs = std::size(g_gpr_regnums) - 1; +constexpr size_t k_num_fpu_regs = std::size(g_fpu_regnums) - 1; + +static RegisterSet g_reg_sets[] = { + {"General Purpose Registers", "gpr", k_num_gpr_regs, g_gpr_regnums}, + {"Floating Point Registers", "fpu", k_num_fpu_regs, g_fpu_regnums}, +}; + +constexpr size_t k_num_reg_sets = std::size(g_reg_sets); + +RegisterContextMinidump_ARM64::RegisterContextMinidump_ARM64( + lldb_private::Thread &thread, const DataExtractor &data) + : RegisterContext(thread, 0) { + lldb::offset_t offset = 0; + m_regs.context_flags = data.GetU64(&offset); + for (unsigned i = 0; i < 32; ++i) + m_regs.x[i] = data.GetU64(&offset); + m_regs.pc = data.GetU64(&offset); + m_regs.cpsr = data.GetU32(&offset); + m_regs.fpsr = data.GetU32(&offset); + m_regs.fpcr = data.GetU32(&offset); + auto regs_data = data.GetData(&offset, sizeof(m_regs.v)); + if (regs_data) + memcpy(m_regs.v, regs_data, sizeof(m_regs.v)); + static_assert(k_num_regs == k_num_reg_infos); +} +size_t RegisterContextMinidump_ARM64::GetRegisterCount() { return k_num_regs; } + +const RegisterInfo * +RegisterContextMinidump_ARM64::GetRegisterInfoAtIndex(size_t reg) { + if (reg < k_num_reg_infos) + return &g_reg_infos[reg]; + return nullptr; +} + +size_t RegisterContextMinidump_ARM64::GetRegisterSetCount() { + return k_num_reg_sets; +} + +const RegisterSet *RegisterContextMinidump_ARM64::GetRegisterSet(size_t set) { + if (set < k_num_reg_sets) + return &g_reg_sets[set]; + return nullptr; +} + +const char *RegisterContextMinidump_ARM64::GetRegisterName(unsigned reg) { + if (reg < k_num_reg_infos) + return g_reg_infos[reg].name; + return nullptr; +} + +bool RegisterContextMinidump_ARM64::ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) { + Status error; + reg_value.SetFromMemoryData( + *reg_info, (const uint8_t *)&m_regs + reg_info->byte_offset, + reg_info->byte_size, lldb::eByteOrderLittle, error); + return error.Success(); +} + +bool RegisterContextMinidump_ARM64::WriteRegister(const RegisterInfo *, + const RegisterValue &) { + return false; +} + +uint32_t RegisterContextMinidump_ARM64::ConvertRegisterKindToRegisterNumber( + lldb::RegisterKind kind, uint32_t num) { + for (size_t i = 0; i < k_num_regs; ++i) { + if (g_reg_infos[i].kinds[kind] == num) + return i; + } + return LLDB_INVALID_REGNUM; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.h new file mode 100644 index 000000000000..58cf8d62fb86 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.h @@ -0,0 +1,83 @@ +//===-- RegisterContextMinidump_ARM64.h -------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_REGISTERCONTEXTMINIDUMP_ARM64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_REGISTERCONTEXTMINIDUMP_ARM64_H + +#include "MinidumpTypes.h" + +#include "Plugins/Process/Utility/RegisterInfoInterface.h" +#include "lldb/Target/RegisterContext.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitmaskEnum.h" + +// C includes +// C++ includes + +namespace lldb_private { + +namespace minidump { + +LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); + +class RegisterContextMinidump_ARM64 : public lldb_private::RegisterContext { +public: + RegisterContextMinidump_ARM64(lldb_private::Thread &thread, + const DataExtractor &data); + + ~RegisterContextMinidump_ARM64() override = default; + + void InvalidateAllRegisters() override { + // Do nothing... registers are always valid... + } + + size_t GetRegisterCount() override; + + const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const lldb_private::RegisterSet *GetRegisterSet(size_t set) override; + + const char *GetRegisterName(unsigned reg); + + bool ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) override; + + bool WriteRegister(const RegisterInfo *reg_info, + const RegisterValue ®_value) override; + + uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, + uint32_t num) override; + + // Reference: see breakpad/crashpad source + struct Context { + uint64_t context_flags; + uint64_t x[32]; + uint64_t pc; + uint32_t cpsr; + uint32_t fpsr; + uint32_t fpcr; + uint8_t v[32 * 16]; // 32 128-bit floating point registers + }; + + enum class Flags : uint32_t { + ARM64_Flag = 0x80000000, + Integer = ARM64_Flag | 0x00000002, + FloatingPoint = ARM64_Flag | 0x00000004, + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ FloatingPoint) + }; + +protected: + Context m_regs; +}; + +} // end namespace minidump +} // end namespace lldb_private +#endif // LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_REGISTERCONTEXTMINIDUMP_ARM64_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.cpp new file mode 100644 index 000000000000..7681002c6fb8 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.cpp @@ -0,0 +1,96 @@ +//===-- RegisterContextMinidump_x86_32.cpp --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextMinidump_x86_32.h" + +#include "lldb/Utility/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::WritableDataBufferSP 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/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.h new file mode 100644 index 000000000000..4dffc4f9db0e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.h @@ -0,0 +1,135 @@ +//===-- RegisterContextMinidump_x86_32.h ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_REGISTERCONTEXTMINIDUMP_X86_32_H +#define LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_REGISTERCONTEXTMINIDUMP_X86_32_H + +#include "MinidumpTypes.h" + +#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 is 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 // LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_REGISTERCONTEXTMINIDUMP_X86_32_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp new file mode 100644 index 000000000000..917140cab297 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp @@ -0,0 +1,110 @@ +//===-- RegisterContextMinidump_x86_64.cpp --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextMinidump_x86_64.h" + +#include "lldb/Utility/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::WritableDataBufferSP 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/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h new file mode 100644 index 000000000000..d920ea9d823f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h @@ -0,0 +1,180 @@ +//===-- RegisterContextMinidump_x86_64.h ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_REGISTERCONTEXTMINIDUMP_X86_64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_REGISTERCONTEXTMINIDUMP_X86_64_H + +#include "MinidumpTypes.h" + +#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 // LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_REGISTERCONTEXTMINIDUMP_X86_64_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.cpp new file mode 100644 index 000000000000..1fbc52815238 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.cpp @@ -0,0 +1,118 @@ +//===-- ThreadMinidump.cpp ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ThreadMinidump.h" + +#include "ProcessMinidump.h" + +#include "RegisterContextMinidump_ARM.h" +#include "RegisterContextMinidump_ARM64.h" +#include "RegisterContextMinidump_x86_32.h" +#include "RegisterContextMinidump_x86_64.h" + +#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 "Plugins/Process/elf-core/RegisterUtilities.h" + +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Unwind.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Log.h" + +#include <memory> + +using namespace lldb; +using namespace lldb_private; +using namespace minidump; + +ThreadMinidump::ThreadMinidump(Process &process, const minidump::Thread &td, + llvm::ArrayRef<uint8_t> gpregset_data) + : Thread(process, td.ThreadId), m_thread_reg_ctx_sp(), + m_gpregset_data(gpregset_data) {} + +ThreadMinidump::~ThreadMinidump() = default; + +void ThreadMinidump::RefreshStateAfterStop() {} + +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; + + 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 gpregset(buf, lldb::eByteOrderLittle, 4); + m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_x86_64>( + *this, reg_interface, gpregset, + llvm::ArrayRef<lldb_private::CoreNote>()); + 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 gpregset(buf, lldb::eByteOrderLittle, 8); + m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_x86_64>( + *this, reg_interface, gpregset, + llvm::ArrayRef<lldb_private::CoreNote>()); + break; + } + case llvm::Triple::aarch64: { + DataExtractor data(m_gpregset_data.data(), m_gpregset_data.size(), + lldb::eByteOrderLittle, 8); + m_thread_reg_ctx_sp = + std::make_shared<RegisterContextMinidump_ARM64>(*this, data); + break; + } + case llvm::Triple::arm: { + DataExtractor data(m_gpregset_data.data(), m_gpregset_data.size(), + lldb::eByteOrderLittle, 8); + const bool apple = arch.GetTriple().getVendor() == llvm::Triple::Apple; + m_thread_reg_ctx_sp = + std::make_shared<RegisterContextMinidump_ARM>(*this, data, apple); + break; + } + default: + break; + } + + reg_ctx_sp = m_thread_reg_ctx_sp; + } else if (m_unwinder_up) { + reg_ctx_sp = m_unwinder_up->CreateRegisterContextForFrame(frame); + } + + return reg_ctx_sp; +} + +bool ThreadMinidump::CalculateStopInfo() { return false; } diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.h new file mode 100644 index 000000000000..aed7cfbc1b16 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.h @@ -0,0 +1,45 @@ +//===-- ThreadMinidump.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_THREADMINIDUMP_H +#define LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_THREADMINIDUMP_H + +#include "MinidumpTypes.h" + +#include "lldb/Target/Thread.h" + + +namespace lldb_private { + +namespace minidump { + +class ThreadMinidump : public Thread { +public: + ThreadMinidump(Process &process, const minidump::Thread &td, + llvm::ArrayRef<uint8_t> gpregset_data); + + ~ThreadMinidump() override; + + void RefreshStateAfterStop() override; + + lldb::RegisterContextSP GetRegisterContext() override; + + lldb::RegisterContextSP + CreateRegisterContextForFrame(StackFrame *frame) 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 // LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_THREADMINIDUMP_H |